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  
33          @Override
34          public void recycle(CodecOutputList object) {
35              // drop on the floor and let the GC handle it.
36          }
37      };
38  
39      private static final FastThreadLocal<CodecOutputLists> CODEC_OUTPUT_LISTS_POOL =
40              new FastThreadLocal<CodecOutputLists>() {
41                  @Override
42                  protected CodecOutputLists initialValue() throws Exception {
43                      // 16 CodecOutputList per Thread are cached.
44                      return new CodecOutputLists(16);
45                  }
46              };
47  
48      private interface CodecOutputListRecycler {
49          void recycle(CodecOutputList codecOutputList);
50      }
51  
52      private static final class CodecOutputLists implements CodecOutputListRecycler {
53          private final CodecOutputList[] elements;
54          private final int mask;
55  
56          private int currentIdx;
57          private int count;
58  
59          CodecOutputLists(int numElements) {
60              elements = new CodecOutputList[MathUtil.safeFindNextPositivePowerOfTwo(numElements)];
61              for (int i = 0; i < elements.length; ++i) {
62                  // Size of 16 should be good enough for the majority of all users as an initial capacity.
63                  elements[i] = new CodecOutputList(this, 16);
64              }
65              count = elements.length;
66              currentIdx = elements.length;
67              mask = elements.length - 1;
68          }
69  
70          public CodecOutputList getOrCreate() {
71              if (count == 0) {
72                  // Return a new CodecOutputList which will not be cached. We use a size of 4 to keep the overhead
73                  // low.
74                  return new CodecOutputList(NOOP_RECYCLER, 4);
75              }
76              --count;
77  
78              int idx = (currentIdx - 1) & mask;
79              CodecOutputList list = elements[idx];
80              currentIdx = idx;
81              return list;
82          }
83  
84          @Override
85          public void recycle(CodecOutputList codecOutputList) {
86              int idx = currentIdx;
87              elements[idx] = codecOutputList;
88              currentIdx = (idx + 1) & mask;
89              ++count;
90              assert count <= elements.length;
91          }
92      }
93  
94      static CodecOutputList newInstance() {
95          return CODEC_OUTPUT_LISTS_POOL.get().getOrCreate();
96      }
97  
98      private final CodecOutputListRecycler recycler;
99      private int size;
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 - 1) {
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         size = 0;
179     }
180 
181     /**
182      * Returns {@code true} if any elements where added or set. This will be reset once {@link #recycle()} was called.
183      */
184     boolean insertSinceRecycled() {
185         return insertSinceRecycled;
186     }
187 
188     /**
189      * Recycle the array which will clear it and null out all entries in the internal storage.
190      */
191     void recycle() {
192         for (int i = 0 ; i < size; i ++) {
193             array[i] = null;
194         }
195         size = 0;
196         insertSinceRecycled = false;
197 
198         recycler.recycle(this);
199     }
200 
201     /**
202      * Returns the element on the given index. This operation will not do any range-checks and so is considered unsafe.
203      */
204     Object getUnsafe(int index) {
205         return array[index];
206     }
207 
208     private void checkIndex(int index) {
209         if (index >= size) {
210             throw new IndexOutOfBoundsException();
211         }
212     }
213 
214     private void insert(int index, Object element) {
215         array[index] = element;
216         insertSinceRecycled = true;
217     }
218 
219     private void expandArray() {
220         // double capacity
221         int newCapacity = array.length << 1;
222 
223         if (newCapacity < 0) {
224             throw new OutOfMemoryError();
225         }
226 
227         Object[] newArray = new Object[newCapacity];
228         System.arraycopy(array, 0, newArray, 0, array.length);
229 
230         array = newArray;
231     }
232 }