1 //
2 // Copyright (c) ZeroC, Inc. All rights reserved.
3 //
4 
5 package com.zeroc.IceInternal;
6 
7 //
8 // An instance of java.nio.ByteBuffer cannot grow beyond its initial capacity.
9 // This class wraps a ByteBuffer and supports reallocation.
10 //
11 public class Buffer
12 {
Buffer(boolean direct)13     public Buffer(boolean direct)
14     {
15         this(direct, java.nio.ByteOrder.LITTLE_ENDIAN);
16     }
17 
Buffer(boolean direct, java.nio.ByteOrder order)18     public Buffer(boolean direct, java.nio.ByteOrder order)
19     {
20         b = _emptyBuffer;
21         _size = 0;
22         _capacity = 0;
23         _direct = direct;
24         _order = order;
25     }
26 
Buffer(byte[] data)27     public Buffer(byte[] data)
28     {
29         this(data, java.nio.ByteOrder.LITTLE_ENDIAN);
30     }
31 
Buffer(byte[] data, java.nio.ByteOrder order)32     public Buffer(byte[] data, java.nio.ByteOrder order)
33     {
34         b = java.nio.ByteBuffer.wrap(data);
35         b.order(order);
36         _size = data.length;
37         _capacity = 0;
38         _direct = false;
39         _order = order;
40     }
41 
Buffer(java.nio.ByteBuffer data)42     public Buffer(java.nio.ByteBuffer data)
43     {
44         this(data, java.nio.ByteOrder.LITTLE_ENDIAN);
45     }
46 
Buffer(java.nio.ByteBuffer data, java.nio.ByteOrder order)47     public Buffer(java.nio.ByteBuffer data, java.nio.ByteOrder order)
48     {
49         b = data;
50         b.order(order);
51         _size = data.remaining();
52         _capacity = 0;
53         _direct = false;
54         _order = order;
55     }
56 
Buffer(Buffer buf, boolean adopt)57     public Buffer(Buffer buf, boolean adopt)
58     {
59         b = buf.b;
60         _size = buf._size;
61         _capacity = buf._capacity;
62         _direct = buf._direct;
63         _shrinkCounter = buf._shrinkCounter;
64         _order = buf._order;
65 
66         if(adopt)
67         {
68             buf.clear();
69         }
70     }
71 
position(int newPosition)72     public java.nio.Buffer position(int newPosition)
73     {
74         // Cast to java.nio.Buffer to avoid incompatible covariant
75         // return type used in Java 9 java.nio.ByteBuffer
76         return ((java.nio.Buffer)b).position(newPosition);
77     }
78 
limit(int newLimit)79     public java.nio.Buffer limit(int newLimit)
80     {
81         // Cast to java.nio.Buffer to avoid incompatible covariant
82         // return type used in Java 9 java.nio.ByteBuffer
83         return ((java.nio.Buffer)b).limit(newLimit);
84     }
85 
flip()86     public java.nio.Buffer flip()
87     {
88         // Cast to java.nio.Buffer to avoid incompatible covariant
89         // return type used in Java 9 java.nio.ByteBuffer
90         return ((java.nio.Buffer)b).flip();
91     }
92 
swap(Buffer buf)93     public void swap(Buffer buf)
94     {
95         final java.nio.ByteBuffer bb = buf.b;
96         final int size = buf._size;
97         final int capacity = buf._capacity;
98         final boolean direct = buf._direct;
99         final int shrinkCounter = buf._shrinkCounter;
100         final java.nio.ByteOrder order = buf._order;
101 
102         buf.b = b;
103         buf._size = _size;
104         buf._capacity = _capacity;
105         buf._direct = _direct;
106         buf._shrinkCounter = _shrinkCounter;
107         buf._order = _order;
108 
109         b = bb;
110         _size = size;
111         _capacity = capacity;
112         _direct = direct;
113         _shrinkCounter = shrinkCounter;
114         _order = order;
115     }
116 
size()117     public int size()
118     {
119         return _size;
120     }
121 
empty()122     public boolean empty()
123     {
124         return _size == 0;
125     }
126 
clear()127     public void clear()
128     {
129         b = _emptyBuffer;
130         _size = 0;
131         _capacity = 0;
132         _shrinkCounter = 0;
133     }
134 
135     //
136     // Call expand(n) to add room for n additional bytes. Note that expand()
137     // examines the current position of the buffer first; we don't want to
138     // expand the buffer if the caller is writing to a location that is
139     // already in the buffer.
140     //
expand(int n)141     public void expand(int n)
142     {
143         final int sz = (b == _emptyBuffer) ? n : b.position() + n;
144         if(sz > _size)
145         {
146             resize(sz, false);
147         }
148     }
149 
resize(int n, boolean reading)150     public void resize(int n, boolean reading)
151     {
152         assert(b == _emptyBuffer || _capacity > 0);
153 
154         if(n == 0)
155         {
156             clear();
157         }
158         else if(n > _capacity)
159         {
160             reserve(n);
161         }
162         _size = n;
163 
164         //
165         // When used for reading, we want to set the buffer's limit to the new size.
166         //
167         if(reading)
168         {
169             limit(_size);
170         }
171     }
172 
reset()173     public void reset()
174     {
175         if(_size > 0 && _size * 2 < _capacity)
176         {
177             //
178             // If the current buffer size is smaller than the
179             // buffer capacity, we shrink the buffer memory to the
180             // current size. This is to avoid holding on to too much
181             // memory if it's not needed anymore.
182             //
183             if(++_shrinkCounter > 2)
184             {
185                 reserve(_size);
186                 _shrinkCounter = 0;
187             }
188         }
189         else
190         {
191             _shrinkCounter = 0;
192         }
193         _size = 0;
194         if(b != _emptyBuffer)
195         {
196             limit(b.capacity());
197             position(0);
198         }
199     }
200 
reserve(int n)201     private void reserve(int n)
202     {
203         if(n > _capacity)
204         {
205             _capacity = java.lang.Math.max(n, 2 * _capacity);
206             _capacity = java.lang.Math.max(240, _capacity);
207         }
208         else if(n < _capacity)
209         {
210             _capacity = n;
211         }
212         else
213         {
214             return;
215         }
216 
217         try
218         {
219             java.nio.ByteBuffer buf;
220 
221             if(_direct)
222             {
223                 buf = java.nio.ByteBuffer.allocateDirect(_capacity);
224             }
225             else
226             {
227                 buf = java.nio.ByteBuffer.allocate(_capacity);
228             }
229 
230             if(b == _emptyBuffer)
231             {
232                 b = buf;
233             }
234             else
235             {
236                 final int pos = b.position();
237                 position(0);
238                 limit(java.lang.Math.min(_capacity, b.capacity()));
239                 buf.put(b);
240                 b = buf;
241                 limit(b.capacity());
242                 position(pos);
243             }
244 
245             b.order(_order); // Preserve the original order.
246         }
247         catch(OutOfMemoryError ex)
248         {
249             _capacity = b.capacity(); // Restore the previous capacity.
250             throw ex;
251         }
252     }
253 
254     public java.nio.ByteBuffer b;
255     // Sentinel used for null buffer.
256     public java.nio.ByteBuffer _emptyBuffer = java.nio.ByteBuffer.allocate(0);
257 
258     private int _size;
259     private int _capacity; // Cache capacity to avoid excessive method calls.
260     private boolean _direct; // Use direct buffers?
261     private int _shrinkCounter;
262     private java.nio.ByteOrder _order;
263 }
264