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 <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 <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> + <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 }