View Javadoc
1   /*
2    * Copyright 2015 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty5.channel;
17  
18  import io.netty5.buffer.api.Buffer;
19  import io.netty5.buffer.api.BufferAllocator;
20  
21  import java.util.AbstractMap;
22  import java.util.Map.Entry;
23  import java.util.function.Predicate;
24  
25  import static io.netty5.util.internal.ObjectUtil.checkPositive;
26  
27  /**
28   * The {@link RecvBufferAllocator} that yields a buffer size prediction based upon decrementing the value from
29   * the max bytes per read.
30   */
31  public class DefaultMaxBytesRecvBufferAllocator implements MaxBytesRecvBufferAllocator {
32      private volatile int maxBytesPerRead;
33      private volatile int maxBytesPerIndividualRead;
34  
35      private final class HandleImpl implements Handle {
36          private int individualReadMax;
37          private int bytesToRead;
38          private int lastBytesRead;
39          private int attemptBytesRead;
40          private final Predicate<Handle> defaultMaybeMoreSupplier = h -> attemptBytesRead == lastBytesRead;
41  
42          @Override
43          public Buffer allocate(BufferAllocator alloc) {
44              return alloc.allocate(guess());
45          }
46  
47          @Override
48          public int guess() {
49              return Math.min(individualReadMax, bytesToRead);
50          }
51  
52          @Override
53          public void reset() {
54              bytesToRead = maxBytesPerRead();
55              individualReadMax = maxBytesPerIndividualRead();
56          }
57  
58          @Override
59          public void incMessagesRead(int amt) {
60          }
61  
62          @Override
63          public void lastBytesRead(int bytes) {
64              lastBytesRead = bytes;
65              // Ignore if bytes is negative, the interface contract states it will be detected externally after call.
66              // The value may be "invalid" after this point, but it doesn't matter because reading will be stopped.
67              bytesToRead -= bytes;
68          }
69  
70          @Override
71          public int lastBytesRead() {
72              return lastBytesRead;
73          }
74  
75          @Override
76          public boolean continueReading(boolean autoRead) {
77              return continueReading(autoRead, defaultMaybeMoreSupplier);
78          }
79  
80          @Override
81          public boolean continueReading(boolean autoRead, Predicate<Handle> maybeMoreDataSupplier) {
82              // Keep reading if we are allowed to read more bytes, and our last read filled up the buffer we provided.
83              return bytesToRead > 0 && maybeMoreDataSupplier.test(this);
84          }
85  
86          @Override
87          public void readComplete() {
88          }
89  
90          @Override
91          public void attemptedBytesRead(int bytes) {
92              attemptBytesRead = bytes;
93          }
94  
95          @Override
96          public int attemptedBytesRead() {
97              return attemptBytesRead;
98          }
99      }
100 
101     public DefaultMaxBytesRecvBufferAllocator() {
102         this(64 * 1024, 64 * 1024);
103     }
104 
105     public DefaultMaxBytesRecvBufferAllocator(int maxBytesPerRead, int maxBytesPerIndividualRead) {
106         checkMaxBytesPerReadPair(maxBytesPerRead, maxBytesPerIndividualRead);
107         this.maxBytesPerRead = maxBytesPerRead;
108         this.maxBytesPerIndividualRead = maxBytesPerIndividualRead;
109     }
110 
111     @Override
112     public Handle newHandle() {
113         return new HandleImpl();
114     }
115 
116     @Override
117     public int maxBytesPerRead() {
118         return maxBytesPerRead;
119     }
120 
121     @Override
122     public DefaultMaxBytesRecvBufferAllocator maxBytesPerRead(int maxBytesPerRead) {
123         checkPositive(maxBytesPerRead, "maxBytesPerRead");
124         // There is a dependency between this.maxBytesPerRead and this.maxBytesPerIndividualRead (a < b).
125         // Write operations must be synchronized, but independent read operations can just be volatile.
126         synchronized (this) {
127             final int maxBytesPerIndividualRead = maxBytesPerIndividualRead();
128             if (maxBytesPerRead < maxBytesPerIndividualRead) {
129                 throw new IllegalArgumentException(
130                         "maxBytesPerRead cannot be less than " +
131                                 "maxBytesPerIndividualRead (" + maxBytesPerIndividualRead + "): " + maxBytesPerRead);
132             }
133 
134             this.maxBytesPerRead = maxBytesPerRead;
135         }
136         return this;
137     }
138 
139     @Override
140     public int maxBytesPerIndividualRead() {
141         return maxBytesPerIndividualRead;
142     }
143 
144     @Override
145     public DefaultMaxBytesRecvBufferAllocator maxBytesPerIndividualRead(int maxBytesPerIndividualRead) {
146         checkPositive(maxBytesPerIndividualRead, "maxBytesPerIndividualRead");
147         // There is a dependency between this.maxBytesPerRead and this.maxBytesPerIndividualRead (a < b).
148         // Write operations must be synchronized, but independent read operations can just be volatile.
149         synchronized (this) {
150             final int maxBytesPerRead = maxBytesPerRead();
151             if (maxBytesPerIndividualRead > maxBytesPerRead) {
152                 throw new IllegalArgumentException(
153                         "maxBytesPerIndividualRead cannot be greater than " +
154                                 "maxBytesPerRead (" + maxBytesPerRead + "): " + maxBytesPerIndividualRead);
155             }
156 
157             this.maxBytesPerIndividualRead = maxBytesPerIndividualRead;
158         }
159         return this;
160     }
161 
162     @Override
163     public synchronized Entry<Integer, Integer> maxBytesPerReadPair() {
164         return new AbstractMap.SimpleEntry<>(maxBytesPerRead, maxBytesPerIndividualRead);
165     }
166 
167     private static void checkMaxBytesPerReadPair(int maxBytesPerRead, int maxBytesPerIndividualRead) {
168         checkPositive(maxBytesPerRead, "maxBytesPerRead");
169         checkPositive(maxBytesPerIndividualRead, "maxBytesPerIndividualRead");
170         if (maxBytesPerRead < maxBytesPerIndividualRead) {
171             throw new IllegalArgumentException(
172                     "maxBytesPerRead cannot be less than " +
173                             "maxBytesPerIndividualRead (" + maxBytesPerIndividualRead + "): " + maxBytesPerRead);
174         }
175     }
176 
177     @Override
178     public DefaultMaxBytesRecvBufferAllocator maxBytesPerReadPair(int maxBytesPerRead,
179                                                                   int maxBytesPerIndividualRead) {
180         checkMaxBytesPerReadPair(maxBytesPerRead, maxBytesPerIndividualRead);
181         // There is a dependency between this.maxBytesPerRead and this.maxBytesPerIndividualRead (a < b).
182         // Write operations must be synchronized, but independent read operations can just be volatile.
183         synchronized (this) {
184             this.maxBytesPerRead = maxBytesPerRead;
185             this.maxBytesPerIndividualRead = maxBytesPerIndividualRead;
186         }
187         return this;
188     }
189 }