View Javadoc
1   /*
2    * Copyright 2025 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.netty.util.internal;
17  
18  import java.util.ArrayList;
19  import java.util.List;
20  
21  import static io.netty.util.internal.ObjectUtil.checkPositive;
22  import static java.lang.Math.max;
23  import static java.lang.Math.min;
24  
25  /**
26   * Calculate sizes in a adaptive way.
27   */
28  public final class AdaptiveCalculator {
29      private static final int INDEX_INCREMENT = 4;
30      private static final int INDEX_DECREMENT = 1;
31  
32      private static final int[] SIZE_TABLE;
33  
34      static {
35          List<Integer> sizeTable = new ArrayList<Integer>();
36          for (int i = 16; i < 512; i += 16) {
37              sizeTable.add(i);
38          }
39  
40          // Suppress a warning since i becomes negative when an integer overflow happens
41          for (int i = 512; i > 0; i <<= 1) {
42              sizeTable.add(i);
43          }
44  
45          SIZE_TABLE = new int[sizeTable.size()];
46          for (int i = 0; i < SIZE_TABLE.length; i ++) {
47              SIZE_TABLE[i] = sizeTable.get(i);
48          }
49      }
50  
51      private static int getSizeTableIndex(final int size) {
52          for (int low = 0, high = SIZE_TABLE.length - 1;;) {
53              if (high < low) {
54                  return low;
55              }
56              if (high == low) {
57                  return high;
58              }
59  
60              int mid = low + high >>> 1;
61              int a = SIZE_TABLE[mid];
62              int b = SIZE_TABLE[mid + 1];
63              if (size > b) {
64                  low = mid + 1;
65              } else if (size < a) {
66                  high = mid - 1;
67              } else if (size == a) {
68                  return mid;
69              } else {
70                  return mid + 1;
71              }
72          }
73      }
74  
75      private final int minIndex;
76      private final int maxIndex;
77      private final int minCapacity;
78      private final int maxCapacity;
79      private int index;
80      private int nextSize;
81      private boolean decreaseNow;
82  
83      public AdaptiveCalculator(int minimum, int initial, int maximum) {
84          checkPositive(minimum, "minimum");
85          if (initial < minimum) {
86              throw new IllegalArgumentException("initial: " + initial);
87          }
88          if (maximum < initial) {
89              throw new IllegalArgumentException("maximum: " + maximum);
90          }
91  
92          int minIndex = getSizeTableIndex(minimum);
93          if (SIZE_TABLE[minIndex] < minimum) {
94              this.minIndex = minIndex + 1;
95          } else {
96              this.minIndex = minIndex;
97          }
98  
99          int maxIndex = getSizeTableIndex(maximum);
100         if (SIZE_TABLE[maxIndex] > maximum) {
101             this.maxIndex = maxIndex - 1;
102         } else {
103             this.maxIndex = maxIndex;
104         }
105 
106         int initialIndex = getSizeTableIndex(initial);
107         if (SIZE_TABLE[initialIndex] > initial) {
108             this.index = initialIndex - 1;
109         } else {
110             this.index = initialIndex;
111         }
112         this.minCapacity = minimum;
113         this.maxCapacity = maximum;
114         nextSize = max(SIZE_TABLE[index], minCapacity);
115     }
116 
117     public void record(int size) {
118         if (size <= SIZE_TABLE[max(0, index - INDEX_DECREMENT)]) {
119             if (decreaseNow) {
120                 index = max(index - INDEX_DECREMENT, minIndex);
121                 nextSize = max(SIZE_TABLE[index], minCapacity);
122                 decreaseNow = false;
123             } else {
124                 decreaseNow = true;
125             }
126         } else if (size >= nextSize) {
127             index = min(index + INDEX_INCREMENT, maxIndex);
128             nextSize = min(SIZE_TABLE[index], maxCapacity);
129             decreaseNow = false;
130         }
131     }
132 
133     public int nextSize() {
134         return nextSize;
135     }
136 }