1 /*
2  * Hunt - A refined core library for D programming language.
3  *
4  * Copyright (C) 2018-2019 HuntLabs
5  *
6  * Website: https://www.huntlabs.net/
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module hunt.collection.Buffer;
13 
14 import hunt.Exceptions;
15 
16 import std.conv;
17 
18 abstract class Buffer
19 {
20     // Invariants: mark <= position <= limit <= capacity
21     private int _mark = -1;
22     private int _position = 0;
23     private int _limit;
24     private int _capacity;
25 
26     // Creates a new buffer with the given mark, position, limit, and capacity,
27     // after checking invariants.
28     //
29     this(int mark, int pos, int lim, int cap) {       // package-private
30         if (cap < 0)
31             throw new IllegalArgumentException("Negative capacity: " ~ to!string(cap));
32         this._capacity = cap;
33         limit(lim);
34         position(pos);
35         if (mark >= 0) {
36             if (mark > pos)
37                 throw new IllegalArgumentException("mark > position: ("
38                                                    ~ to!string( mark) ~ " > " ~ to!string(pos) ~ ")");
39             this._mark = mark;
40         }
41     }
42 
43     /**
44      * Returns this buffer's capacity.
45      *
46      * @return  The capacity of this buffer
47      */
48     final int capacity() {
49         return _capacity;
50     }
51 
52     /**
53      * Returns this buffer's position.
54      *
55      * @return  The position of this buffer
56      */
57     final int position() {
58         return _position;
59     }
60 
61     /**
62      * Sets this buffer's position.  If the mark is defined and larger than the
63      * new position then it is discarded.
64      *
65      * @param  newPosition
66      *         The new position value; must be non-negative
67      *         and no larger than the current limit
68      *
69      * @return  This buffer
70      *
71      * @throws  IllegalArgumentException
72      *          If the preconditions on <tt>newPosition</tt> do not hold
73      */
74     final Buffer position(int newPosition) {
75         if ((newPosition > _limit) || (newPosition < 0))
76             throw new IllegalArgumentException("");
77         _position = newPosition;
78         if (_mark > _position) _mark = -1;
79         return this;
80     }
81 
82     /**
83      * Returns this buffer's limit.
84      *
85      * @return  The limit of this buffer
86      */
87     final int limit() {
88         return _limit;
89     }
90 
91     /**
92      * Sets this buffer's limit.  If the position is larger than the new limit
93      * then it is set to the new limit.  If the mark is defined and larger than
94      * the new limit then it is discarded.
95      *
96      * @param  newLimit
97      *         The new limit value; must be non-negative
98      *         and no larger than this buffer's capacity
99      *
100      * @return  This buffer
101      *
102      * @throws  IllegalArgumentException
103      *          If the preconditions on <tt>newLimit</tt> do not hold
104      */
105     final Buffer limit(int newLimit) {
106         if ((newLimit > _capacity) || (newLimit < 0))
107             throw new IllegalArgumentException("");
108         _limit = newLimit;
109         if (_position > _limit) _position = _limit;
110         if (_mark > _limit) _mark = -1;
111         return this;
112     }
113 
114     /**
115      * Sets this buffer's mark at its position.
116      *
117      * @return  This buffer
118      */
119     final Buffer mark() {
120         _mark = _position;
121         return this;
122     }
123 
124     /**
125      * Resets this buffer's position to the previously-marked position.
126      *
127      * <p> Invoking this method neither changes nor discards the mark's
128      * value. </p>
129      *
130      * @return  This buffer
131      *
132      * @throws  InvalidMarkException
133      *          If the mark has not been set
134      */
135     final Buffer reset() {
136         int m = _mark;
137         if (m < 0)
138             throw new InvalidMarkException("");
139         _position = m;
140         return this;
141     }
142 
143     /**
144      * Clears this buffer.  The position is set to zero, the limit is set to
145      * the capacity, and the mark is discarded.
146      *
147      * <p> Invoke this method before using a sequence of channel-read or
148      * <i>put</i> operations to fill this buffer.  For example:
149      *
150      * <blockquote><pre>
151      * buf.clear();     // Prepare buffer for reading
152      * in.read(buf);    // Read data</pre></blockquote>
153      *
154      * <p> This method does not actually erase the data in the buffer, but it
155      * is named as if it did because it will most often be used in situations
156      * in which that might as well be the case. </p>
157      *
158      * @return  This buffer
159      */
160     final Buffer clear() {
161         _position = 0;
162         _limit = _capacity;
163         _mark = -1;
164         return this;
165     }
166 
167     /**
168      * Flips this buffer.  The limit is set to the current position and then
169      * the position is set to zero.  If the mark is defined then it is
170      * discarded.
171      *
172      * <p> After a sequence of channel-read or <i>put</i> operations, invoke
173      * this method to prepare for a sequence of channel-write or relative
174      * <i>get</i> operations.  For example:
175      *
176      * <blockquote><pre>
177      * buf.put(magic);    // Prepend header
178      * in.read(buf);      // Read data into rest of buffer
179      * buf.flip();        // Flip buffer
180      * out.write(buf);    // Write header + data to channel</pre></blockquote>
181      *
182      * <p> This method is often used in conjunction with the {@link
183      * java.nio.ByteBuffer#compact compact} method when transferring data from
184      * one place to another.  </p>
185      *
186      * @return  This buffer
187      */
188     final Buffer flip() {
189         _limit = _position;
190         _position = 0;
191         _mark = -1;
192         return this;
193     }
194 
195     /**
196      * Rewinds this buffer.  The position is set to zero and the mark is
197      * discarded.
198      *
199      * <p> Invoke this method before a sequence of channel-write or <i>get</i>
200      * operations, assuming that the limit has already been set
201      * appropriately.  For example:
202      *
203      * <blockquote><pre>
204      * out.write(buf);    // Write remaining data
205      * buf.rewind();      // Rewind buffer
206      * buf.get(array);    // Copy data into array</pre></blockquote>
207      *
208      * @return  This buffer
209      */
210     final Buffer rewind() {
211         _position = 0;
212         _mark = -1;
213         return this;
214     }
215 
216     /**
217      * Returns the number of elements between the current position and the
218      * limit.
219      *
220      * @return  The number of elements remaining in this buffer
221      */
222     final int remaining() {
223         return _limit - _position;
224     }
225 
226     /**
227      * Tells whether there are any elements between the current position and
228      * the limit.
229      *
230      * @return  <tt>true</tt> if, and only if, there is at least one element
231      *          remaining in this buffer
232      */
233     final bool hasRemaining() {
234         return _position < _limit;
235     }
236 
237     /**
238      * Tells whether or not this buffer is read-only.
239      *
240      * @return  <tt>true</tt> if, and only if, this buffer is read-only
241      */
242     abstract bool isReadOnly();
243 
244     /**
245      * Tells whether or not this buffer is backed by an accessible
246      * array.
247      *
248      * <p> If this method returns <tt>true</tt> then the {@link #array() array}
249      * and {@link #arrayOffset() arrayOffset} methods may safely be invoked.
250      * </p>
251      *
252      * @return  <tt>true</tt> if, and only if, this buffer
253      *          is backed by an array and is not read-only
254      *
255      * @since 1.6
256      */
257     abstract bool hasArray();
258 
259     /**
260      * Returns the array that backs this
261      * buffer&nbsp;&nbsp;<i>(optional operation)</i>.
262      *
263      * <p> This method is intended to allow array-backed buffers to be
264      * passed to native code more efficiently. Concrete subclasses
265      * provide more strongly-typed return values for this method.
266      *
267      * <p> Modifications to this buffer's content will cause the returned
268      * array's content to be modified, and vice versa.
269      *
270      * <p> Invoke the {@link #hasArray hasArray} method before invoking this
271      * method in order to ensure that this buffer has an accessible backing
272      * array.  </p>
273      *
274      * @return  The array that backs this buffer
275      *
276      * @throws  ReadOnlyBufferException
277      *          If this buffer is backed by an array but is read-only
278      *
279      * @throws  UnsupportedOperationException
280      *          If this buffer is not backed by an accessible array
281      *
282      * @since 1.6
283      */
284     // abstract Object array();
285 
286     /**
287      * Returns the offset within this buffer's backing array of the first
288      * element of the buffer&nbsp;&nbsp;<i>(optional operation)</i>.
289      *
290      * <p> If this buffer is backed by an array then buffer position <i>p</i>
291      * corresponds to array index <i>p</i>&nbsp;+&nbsp;<tt>arrayOffset()</tt>.
292      *
293      * <p> Invoke the {@link #hasArray hasArray} method before invoking this
294      * method in order to ensure that this buffer has an accessible backing
295      * array.  </p>
296      *
297      * @return  The offset within this buffer's array
298      *          of the first element of the buffer
299      *
300      * @throws  ReadOnlyBufferException
301      *          If this buffer is backed by an array but is read-only
302      *
303      * @throws  UnsupportedOperationException
304      *          If this buffer is not backed by an accessible array
305      *
306      * @since 1.6
307      */
308     abstract int arrayOffset();
309 
310     /**
311      * Tells whether or not this buffer is
312      * <a href="ByteBuffer.html#direct"><i>direct</i></a>.
313      *
314      * @return  <tt>true</tt> if, and only if, this buffer is direct
315      *
316      * @since 1.6
317      */
318     abstract bool isDirect();
319 
320 
321     // -- Package-private methods for bounds checking, etc. --
322 
323     /**
324      * Checks the current position against the limit, throwing a {@link
325      * BufferUnderflowException} if it is not smaller than the limit, and then
326      * increments the position.
327      *
328      * @return  The current position value, before it is incremented
329      */
330     final int nextGetIndex() {                          // package-private
331         if (_position >= _limit)
332             throw new BufferUnderflowException("");
333         return _position++;
334     }
335 
336     final int nextGetIndex(int nb) {                    // package-private
337         if (_limit - _position < nb)
338             throw new BufferUnderflowException("");
339         int p = _position;
340         _position += nb;
341         return p;
342     }
343 
344     /**
345      * Checks the current position against the limit, throwing a {@link
346      * BufferOverflowException} if it is not smaller than the limit, and then
347      * increments the position.
348      *
349      * @return  The current position value, before it is incremented
350      */
351     final int nextPutIndex() {                          // package-private
352         if (_position >= _limit)
353             throw new BufferOverflowException("");
354         return _position++;
355     }
356 
357     final int nextPutIndex(int nb) {                    // package-private
358         if (_limit - _position < nb)
359             throw new BufferOverflowException("");
360         int p = _position;
361         _position += nb;
362         return p;
363     }
364 
365     /**
366      * Checks the given index against the limit, throwing an {@link
367      * IndexOutOfBoundsException} if it is not smaller than the limit
368      * or is smaller than zero.
369      */
370     final int checkIndex(int i) {                       // package-private
371         if ((i < 0) || (i >= _limit))
372             throw new IndexOutOfBoundsException("");
373         return i;
374     }
375 
376     final int checkIndex(int i, int nb) {               // package-private
377         if ((i < 0) || (nb > _limit - i))
378             throw new IndexOutOfBoundsException("");
379         return i;
380     }
381 
382     final int markValue() {                             // package-private
383         return _mark;
384     }
385 
386     final void truncate() {                             // package-private
387         _mark = -1;
388         _position = 0;
389         _limit = 0;
390         _capacity = 0;
391     }
392 
393     final void discardMark() {                          // package-private
394         _mark = -1;
395     }
396 
397     static void checkBounds(int off, int len, int size) { // package-private
398         if ((off | len | (off + len) | (size - (off + len))) < 0)
399             throw new IndexOutOfBoundsException("");
400     }
401 }