1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty5.channel;
17
18 import java.util.ArrayList;
19 import java.util.List;
20
21 import static io.netty5.util.internal.ObjectUtil.checkPositive;
22 import static java.lang.Math.max;
23 import static java.lang.Math.min;
24
25
26
27
28
29
30
31
32
33
34
35 public class AdaptiveRecvBufferAllocator extends DefaultMaxMessagesRecvBufferAllocator {
36
37 static final int DEFAULT_MINIMUM = 64;
38
39 static final int DEFAULT_INITIAL = 2048;
40 static final int DEFAULT_MAXIMUM = 65536;
41
42 private static final int INDEX_INCREMENT = 4;
43 private static final int INDEX_DECREMENT = 1;
44
45 private static final int[] SIZE_TABLE;
46
47 static {
48 List<Integer> sizeTable = new ArrayList<>();
49 for (int i = 16; i < 512; i += 16) {
50 sizeTable.add(i);
51 }
52
53
54 for (int i = 512; i > 0; i <<= 1) {
55 sizeTable.add(i);
56 }
57
58 SIZE_TABLE = new int[sizeTable.size()];
59 for (int i = 0; i < SIZE_TABLE.length; i ++) {
60 SIZE_TABLE[i] = sizeTable.get(i);
61 }
62 }
63
64
65
66
67 @Deprecated
68 public static final AdaptiveRecvBufferAllocator DEFAULT = new AdaptiveRecvBufferAllocator();
69
70 private static int getSizeTableIndex(final int size) {
71 for (int low = 0, high = SIZE_TABLE.length - 1;;) {
72 if (high < low) {
73 return low;
74 }
75 if (high == low) {
76 return high;
77 }
78
79 int mid = low + high >>> 1;
80 int a = SIZE_TABLE[mid];
81 int b = SIZE_TABLE[mid + 1];
82 if (size > b) {
83 low = mid + 1;
84 } else if (size < a) {
85 high = mid - 1;
86 } else if (size == a) {
87 return mid;
88 } else {
89 return mid + 1;
90 }
91 }
92 }
93
94 private final class HandleImpl extends MaxMessageHandle {
95 private final int minIndex;
96 private final int maxIndex;
97 private int index;
98 private int nextReceiveBufferSize;
99 private boolean decreaseNow;
100
101 HandleImpl(int minIndex, int maxIndex, int initial) {
102 this.minIndex = minIndex;
103 this.maxIndex = maxIndex;
104
105 index = getSizeTableIndex(initial);
106 nextReceiveBufferSize = SIZE_TABLE[index];
107 }
108
109 @Override
110 public void lastBytesRead(int bytes) {
111
112
113
114
115 if (bytes == attemptedBytesRead()) {
116 record(bytes);
117 }
118 super.lastBytesRead(bytes);
119 }
120
121 @Override
122 public int guess() {
123 return nextReceiveBufferSize;
124 }
125
126 private void record(int actualReadBytes) {
127 if (actualReadBytes <= SIZE_TABLE[max(0, index - INDEX_DECREMENT)]) {
128 if (decreaseNow) {
129 index = max(index - INDEX_DECREMENT, minIndex);
130 nextReceiveBufferSize = SIZE_TABLE[index];
131 decreaseNow = false;
132 } else {
133 decreaseNow = true;
134 }
135 } else if (actualReadBytes >= nextReceiveBufferSize) {
136 index = min(index + INDEX_INCREMENT, maxIndex);
137 nextReceiveBufferSize = SIZE_TABLE[index];
138 decreaseNow = false;
139 }
140 }
141
142 @Override
143 public void readComplete() {
144 record(totalBytesRead());
145 }
146 }
147
148 private final int minIndex;
149 private final int maxIndex;
150 private final int initial;
151
152
153
154
155
156
157 public AdaptiveRecvBufferAllocator() {
158 this(DEFAULT_MINIMUM, DEFAULT_INITIAL, DEFAULT_MAXIMUM);
159 }
160
161
162
163
164
165
166
167
168 public AdaptiveRecvBufferAllocator(int minimum, int initial, int maximum) {
169 checkPositive(minimum, "minimum");
170 if (initial < minimum) {
171 throw new IllegalArgumentException("initial: " + initial);
172 }
173 if (maximum < initial) {
174 throw new IllegalArgumentException("maximum: " + maximum);
175 }
176
177 int minIndex = getSizeTableIndex(minimum);
178 if (SIZE_TABLE[minIndex] < minimum) {
179 this.minIndex = minIndex + 1;
180 } else {
181 this.minIndex = minIndex;
182 }
183
184 int maxIndex = getSizeTableIndex(maximum);
185 if (SIZE_TABLE[maxIndex] > maximum) {
186 this.maxIndex = maxIndex - 1;
187 } else {
188 this.maxIndex = maxIndex;
189 }
190
191 this.initial = initial;
192 }
193
194 @Override
195 public Handle newHandle() {
196 return new HandleImpl(minIndex, maxIndex, initial);
197 }
198 }