View Javadoc
1   /*
2    * Copyright 2016 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.handler.codec;
17  
18  import io.netty.util.concurrent.FastThreadLocal;
19  import io.netty.util.internal.MathUtil;
20  
21  import java.util.AbstractList;
22  import java.util.RandomAccess;
23  
24  import static io.netty.util.internal.ObjectUtil.checkNotNull;
25  
26  /**
27   * Special {@link AbstractList} implementation which is used within our codec base classes.
28   */
29  final class CodecOutputList extends AbstractList<Object> implements RandomAccess {
30  
31      private static final CodecOutputListRecycler NOOP_RECYCLER = new CodecOutputListRecycler() {
32          @Override
33          public void recycle(CodecOutputList object) {
34              // drop on the floor and let the GC handle it.
35          }
36      };
37  
38      private static final FastThreadLocal<CodecOutputLists> CODEC_OUTPUT_LISTS_POOL =
39              new FastThreadLocal<CodecOutputLists>() {
40                  @Override
41                  protected CodecOutputLists initialValue() throws Exception {
42                      // 16 CodecOutputList per Thread are cached.
43                      return new CodecOutputLists(16);
44                  }
45              };
46  
47      private interface CodecOutputListRecycler {
48          void recycle(CodecOutputList codecOutputList);
49      }
50  
51      private static final class CodecOutputLists implements CodecOutputListRecycler {
52          private final CodecOutputList[] elements;
53          private final int mask;
54  
55          private int currentIdx;
56          private int count;
57  
58          CodecOutputLists(int numElements) {
59              elements = new CodecOutputList[MathUtil.safeFindNextPositivePowerOfTwo(numElements)];
60              for (int i = 0; i < elements.length; ++i) {
61                  // Size of 16 should be good enough for the majority of all users as an initial capacity.
62                  elements[i] = new CodecOutputList(this, 16);
63              }
64              count = elements.length;
65              currentIdx = elements.length;
66              mask = elements.length - 1;
67          }
68  
69          public CodecOutputList getOrCreate() {
70              if (count == 0) {
71                  // Return a new CodecOutputList which will not be cached. We use a size of 4 to keep the overhead
72                  // low.
73                  return new CodecOutputList(NOOP_RECYCLER, 4);
74              }
75              --count;
76  
77              int idx = (currentIdx - 1) & mask;
78              CodecOutputList list = elements[idx];
79              currentIdx = idx;
80              return list;
81          }
82  
83          @Override
84          public void recycle(CodecOutputList codecOutputList) {
85              int idx = currentIdx;
86              elements[idx] = codecOutputList;
87              currentIdx = (idx + 1) & mask;
88              ++count;
89              assert count <= elements.length;
90          }
91      }
92  
93      static CodecOutputList newInstance() {
94          return CODEC_OUTPUT_LISTS_POOL.get().getOrCreate();
95      }
96  
97      private final CodecOutputListRecycler recycler;
98      private int size;
99      private int maxSeenSize;
100     private Object[] array;
101     private boolean insertSinceRecycled;
102 
103     private CodecOutputList(CodecOutputListRecycler recycler, int size) {
104         this.recycler = recycler;
105         array = new Object[size];
106     }
107 
108     @Override
109     public Object get(int index) {
110         checkIndex(index);
111         return array[index];
112     }
113 
114     @Override
115     public int size() {
116         return size;
117     }
118 
119     @Override
120     public boolean add(Object element) {
121         checkNotNull(element, "element");
122         try {
123             insert(size, element);
124         } catch (IndexOutOfBoundsException ignore) {
125             // This should happen very infrequently so we just catch the exception and try again.
126             expandArray();
127             insert(size, element);
128         }
129         ++ size;
130         return true;
131     }
132 
133     @Override
134     public Object set(int index, Object element) {
135         checkNotNull(element, "element");
136         checkIndex(index);
137 
138         Object old = array[index];
139         insert(index, element);
140         return old;
141     }
142 
143     @Override
144     public void add(int index, Object element) {
145         checkNotNull(element, "element");
146         checkIndex(index);
147 
148         if (size == array.length) {
149             expandArray();
150         }
151 
152         if (index != size) {
153             System.arraycopy(array, index, array, index + 1, size - index);
154         }
155 
156         insert(index, element);
157         ++ size;
158     }
159 
160     @Override
161     public Object remove(int index) {
162         checkIndex(index);
163         Object old = array[index];
164 
165         int len = size - index - 1;
166         if (len > 0) {
167             System.arraycopy(array, index + 1, array, index, len);
168         }
169         array[-- size] = null;
170 
171         return old;
172     }
173 
174     @Override
175     public void clear() {
176         // We only set the size to 0 and not null out the array. Null out the array will explicit requested by
177         // calling recycle()
178         maxSeenSize = Math.max(maxSeenSize, size);
179         size = 0;
180     }
181 
182     /**
183      * Returns {@code true} if any elements where added or set. This will be reset once {@link #recycle()} was called.
184      */
185     boolean insertSinceRecycled() {
186         return insertSinceRecycled;
187     }
188 
189     /**
190      * Recycle the array which will clear it and null out all entries in the internal storage.
191      */
192     void recycle() {
193         int len = Math.max(maxSeenSize, size);
194         for (int i = 0; i < len; i ++) {
195             array[i] = null;
196         }
197         size = 0;
198         maxSeenSize = 0;
199         insertSinceRecycled = false;
200 
201         recycler.recycle(this);
202     }
203 
204     /**
205      * Returns the element on the given index. This operation will not do any range-checks and so is considered unsafe.
206      */
207     Object getUnsafe(int index) {
208         return array[index];
209     }
210 
211     private void checkIndex(int index) {
212         if (index >= size) {
213             throw new IndexOutOfBoundsException("expected: index < ("
214                     + size + "),but actual is (" + size + ")");
215         }
216     }
217 
218     private void insert(int index, Object element) {
219         array[index] = element;
220         insertSinceRecycled = true;
221     }
222 
223     private void expandArray() {
224         // double capacity
225         int newCapacity = array.length << 1;
226 
227         if (newCapacity < 0) {
228             throw new OutOfMemoryError();
229         }
230 
231         Object[] newArray = new Object[newCapacity];
232         System.arraycopy(array, 0, newArray, 0, array.length);
233 
234         array = newArray;
235     }
236 }