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    *   http://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 Object[] array;
100     private boolean insertSinceRecycled;
101 
102     private CodecOutputList(CodecOutputListRecycler recycler, int size) {
103         this.recycler = recycler;
104         array = new Object[size];
105     }
106 
107     @Override
108     public Object get(int index) {
109         checkIndex(index);
110         return array[index];
111     }
112 
113     @Override
114     public int size() {
115         return size;
116     }
117 
118     @Override
119     public boolean add(Object element) {
120         checkNotNull(element, "element");
121         try {
122             insert(size, element);
123         } catch (IndexOutOfBoundsException ignore) {
124             // This should happen very infrequently so we just catch the exception and try again.
125             expandArray();
126             insert(size, element);
127         }
128         ++ size;
129         return true;
130     }
131 
132     @Override
133     public Object set(int index, Object element) {
134         checkNotNull(element, "element");
135         checkIndex(index);
136 
137         Object old = array[index];
138         insert(index, element);
139         return old;
140     }
141 
142     @Override
143     public void add(int index, Object element) {
144         checkNotNull(element, "element");
145         checkIndex(index);
146 
147         if (size == array.length) {
148             expandArray();
149         }
150 
151         if (index != size - 1) {
152             System.arraycopy(array, index, array, index + 1, size - index);
153         }
154 
155         insert(index, element);
156         ++ size;
157     }
158 
159     @Override
160     public Object remove(int index) {
161         checkIndex(index);
162         Object old = array[index];
163 
164         int len = size - index - 1;
165         if (len > 0) {
166             System.arraycopy(array, index + 1, array, index, len);
167         }
168         array[-- size] = null;
169 
170         return old;
171     }
172 
173     @Override
174     public void clear() {
175         // We only set the size to 0 and not null out the array. Null out the array will explicit requested by
176         // calling recycle()
177         size = 0;
178     }
179 
180     /**
181      * Returns {@code true} if any elements where added or set. This will be reset once {@link #recycle()} was called.
182      */
183     boolean insertSinceRecycled() {
184         return insertSinceRecycled;
185     }
186 
187     /**
188      * Recycle the array which will clear it and null out all entries in the internal storage.
189      */
190     void recycle() {
191         for (int i = 0 ; i < size; i ++) {
192             array[i] = null;
193         }
194         size = 0;
195         insertSinceRecycled = false;
196 
197         recycler.recycle(this);
198     }
199 
200     /**
201      * Returns the element on the given index. This operation will not do any range-checks and so is considered unsafe.
202      */
203     Object getUnsafe(int index) {
204         return array[index];
205     }
206 
207     private void checkIndex(int index) {
208         if (index >= size) {
209             throw new IndexOutOfBoundsException();
210         }
211     }
212 
213     private void insert(int index, Object element) {
214         array[index] = element;
215         insertSinceRecycled = true;
216     }
217 
218     private void expandArray() {
219         // double capacity
220         int newCapacity = array.length << 1;
221 
222         if (newCapacity < 0) {
223             throw new OutOfMemoryError();
224         }
225 
226         Object[] newArray = new Object[newCapacity];
227         System.arraycopy(array, 0, newArray, 0, array.length);
228 
229         array = newArray;
230     }
231 }