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.Recycler;
19  
20  import java.util.AbstractList;
21  import java.util.RandomAccess;
22  
23  import static io.netty.util.internal.ObjectUtil.checkNotNull;
24  
25  /**
26   * Special {@link AbstractList} implementation which is used within our codec base classes.
27   */
28  final class CodecOutputList extends AbstractList<Object> implements RandomAccess {
29  
30      private static final Recycler<CodecOutputList> RECYCLER = new Recycler<CodecOutputList>() {
31          @Override
32          protected CodecOutputList newObject(Handle<CodecOutputList> handle) {
33              return new CodecOutputList(handle);
34          }
35      };
36  
37      static CodecOutputList newInstance() {
38          return RECYCLER.get();
39      }
40  
41      private final Recycler.Handle<CodecOutputList> handle;
42      private int size;
43      // Size of 16 should be good enough for 99 % of all users.
44      private Object[] array = new Object[16];
45      private boolean insertSinceRecycled;
46  
47      private CodecOutputList(Recycler.Handle<CodecOutputList> handle) {
48          this.handle = handle;
49      }
50  
51      @Override
52      public Object get(int index) {
53          checkIndex(index);
54          return array[index];
55      }
56  
57      @Override
58      public int size() {
59          return size;
60      }
61  
62      @Override
63      public boolean add(Object element) {
64          checkNotNull(element, "element");
65          try {
66              insert(size, element);
67          } catch (IndexOutOfBoundsException ignore) {
68              // This should happen very infrequently so we just catch the exception and try again.
69              expandArray();
70              insert(size, element);
71          }
72          ++ size;
73          return true;
74      }
75  
76      @Override
77      public Object set(int index, Object element) {
78          checkNotNull(element, "element");
79          checkIndex(index);
80  
81          Object old = array[index];
82          insert(index, element);
83          return old;
84      }
85  
86      @Override
87      public void add(int index, Object element) {
88          checkNotNull(element, "element");
89          checkIndex(index);
90  
91          if (size == array.length) {
92              expandArray();
93          }
94  
95          if (index != size - 1) {
96              System.arraycopy(array, index, array, index + 1, size - index);
97          }
98  
99          insert(index, element);
100         ++ size;
101     }
102 
103     @Override
104     public Object remove(int index) {
105         checkIndex(index);
106         Object old = array[index];
107 
108         int len = size - index - 1;
109         if (len > 0) {
110             System.arraycopy(array, index + 1, array, index, len);
111         }
112         array[-- size] = null;
113 
114         return old;
115     }
116 
117     @Override
118     public void clear() {
119         // We only set the size to 0 and not null out the array. Null out the array will explicit requested by
120         // calling recycle()
121         size = 0;
122     }
123 
124     /**
125      * Returns {@code true} if any elements where added or set. This will be reset once {@link #recycle()} was called.
126      */
127     boolean insertSinceRecycled() {
128         return insertSinceRecycled;
129     }
130 
131     /**
132      * Recycle the array which will clear it and null out all entries in the internal storage.
133      */
134     void recycle() {
135         for (int i = 0 ; i < size; i ++) {
136             array[i] = null;
137         }
138         clear();
139         insertSinceRecycled = false;
140         handle.recycle(this);
141     }
142 
143     /**
144      * Returns the element on the given index. This operation will not do any range-checks and so is considered unsafe.
145      */
146     Object getUnsafe(int index) {
147         return array[index];
148     }
149 
150     private void checkIndex(int index) {
151         if (index >= size) {
152             throw new IndexOutOfBoundsException();
153         }
154     }
155 
156     private void insert(int index, Object element) {
157         array[index] = element;
158         insertSinceRecycled = true;
159     }
160 
161     private void expandArray() {
162         // double capacity
163         int newCapacity = array.length << 1;
164 
165         if (newCapacity < 0) {
166             throw new OutOfMemoryError();
167         }
168 
169         Object[] newArray = new Object[newCapacity];
170         System.arraycopy(array, 0, newArray, 0, array.length);
171 
172         array = newArray;
173     }
174 }