/** * Split this buffer on the given delimiter. Save memory by reusing the provided {@code List}. The * delimiter is stripped from the end of the segment if {@code stripDelimiter} is {@code true}. * * @param views The already-allocated List to reuse. * @param delimiter The multi-byte delimiter. * @param stripDelimiter {@literal true} to ignore the delimiter, {@literal false} to leave it in * the returned data. * @return An {@link Iterable} of {@link View Views} that point to the segments of this buffer. */ public Iterable<View> split(List<View> views, Buffer delimiter, boolean stripDelimiter) { snapshot(); byte[] delimBytes = delimiter.asBytes(); if (delimBytes.length == 0) { return Collections.emptyList(); } int start = this.position; for (byte b : this) { if (b != delimBytes[0]) { continue; } int end = -1; for (int i = 1; i < delimBytes.length; i++) { if (read() == delimBytes[i]) { end = stripDelimiter ? buffer.position() - delimBytes.length : buffer.position(); } else { end = -1; break; } } if (end > 0) { views.add(createView(start, end)); start = end + (stripDelimiter ? delimBytes.length : 0); } } if (start != buffer.position()) { buffer.position(start); } reset(); return views; }
/** * Get the first {@code byte} from this {@literal Buffer}. * * @return The first {@code byte}. */ public byte first() { snapshot(); if (this.position > 0) { buffer.position(0); // got to the 1st position } byte b = buffer.get(); // get the 1st byte reset(); // go back to original pos return b; }
/** * Create a new {@code Buffer} by copying the underlying {@link ByteBuffer} into a newly-allocated * {@code Buffer}. * * @return the new {@code Buffer} */ public Buffer copy() { if (buffer == null) return new Buffer(); snapshot(); Buffer b = new Buffer(buffer.remaining(), false); b.append(buffer); reset(); return b.flip(); }
/** * Create a copy of the given range. * * @param start start of the range. * @param len end of the range. * @return A new {@link Buffer}, constructed from the contents of the given range. */ public Buffer slice(int start, int len) { snapshot(); ByteBuffer bb = ByteBuffer.allocate(len); buffer.position(start); bb.put(buffer); reset(); bb.flip(); return new Buffer(bb); }
/** * Slice a portion of this buffer and convert it to a String. * * @param start start of the range. * @param end end of the range. * @return The contents of the given range as a String. */ public String substring(int start, int end) { snapshot(); buffer.limit((end > start ? end : this.limit)); buffer.position(start); String s = asString(); reset(); return s; }
@Override public int read(ByteBuffer dst) throws IOException { snapshot(); if (dst.remaining() < this.limit) { buffer.limit(dst.remaining()); } int pos = dst.position(); dst.put(buffer); buffer.limit(this.limit); return dst.position() - pos; }
private void expand(int expandSize) { snapshot(); ByteBuffer newBuff = (buffer.isDirect() ? ByteBuffer.allocateDirect(buffer.capacity() + expandSize) : ByteBuffer.allocate(buffer.capacity() + expandSize)); buffer.flip(); newBuff.put(buffer); buffer = newBuff; reset(); }
/** * Return the contents of this buffer copied into a {@code byte[]}. * * @return The contents of this buffer as a {@code byte[]}. */ public byte[] asBytes() { if (null != buffer) { snapshot(); byte[] b = new byte[buffer.remaining()]; buffer.get(b); reset(); return b; } else { return null; } }
/** * Very efficient method for parsing an {@link Integer} from the given {@literal Buffer} range. * Much faster than {@link Integer#parseInt(String)}. * * @param b The {@literal Buffer} to slice. * @param start start of the range. * @param end end of the range. * @return The int value or {@literal null} if the {@literal Buffer} could not be read. */ public static Integer parseInt(Buffer b, int start, int end) { b.snapshot(); b.buffer.limit(end); b.buffer.position(start); Integer i = parseInt(b); b.reset(); return i; }
/** * Search the buffer and find the position of the first occurrence of the given {@code byte} * staring at the start position and searching until (and including) the end position. * * @param b the {@code byte} to search for * @param start the position to start searching * @param end the position at which to stop searching * @return the position of the char in the buffer or {@code -1} if not found */ public int indexOf(byte b, int start, int end) { snapshot(); if (buffer.position() != start) { buffer.position(start); } int pos = -1; while (buffer.hasRemaining() && buffer.position() < end) { if (buffer.get() == b) { pos = buffer.position(); break; } } reset(); return pos; }
/** * Slice this buffer at the given positions. Useful for extracting multiple segments of data from * a buffer when the exact indices of that data is already known. * * @param positions The start and end positions of the slices. * @return A list of {@link View Views} pointing to the slices. */ public List<View> slice(int... positions) { Assert.notNull(positions, "Positions cannot be null."); if (positions.length == 0) { return Collections.emptyList(); } snapshot(); List<View> views = new ArrayList<View>(); int len = positions.length; for (int i = 0; i < len; i++) { int start = positions[i]; int end = (i + 1 < len ? positions[++i] : this.limit); views.add(createView(start, end)); reset(); } return views; }
private void shift(int right) { ByteBuffer currentBuffer; if (null == buffer) { ensureCapacity(right); currentBuffer = buffer; } else { currentBuffer = buffer.slice(); } int len = buffer.remaining(); int pos = buffer.position(); ensureCapacity(right + len); buffer.position(pos + right); buffer.put(currentBuffer); buffer.position(pos); snapshot(); }
/** * Split this buffer on the given delimiter, save memory by reusing the given {@link List}, and * optionally leave the delimiter intact rather than stripping it. * * @param views The list to store {@link View Views} in. * @param delimiter The delimiter on which to split this buffer. * @param stripDelimiter {@literal true} to ignore the delimiter, {@literal false} to leave it in * the returned data. * @return A {@link List} of {@link View Views} that point to the segments of this buffer. */ public List<View> split(List<View> views, int delimiter, boolean stripDelimiter) { snapshot(); int start = this.position; for (byte b : this) { if (b == delimiter) { int end = stripDelimiter ? buffer.position() - 1 : buffer.position(); views.add(createView(start, end)); start = end + (stripDelimiter ? 1 : 0); } } if (start != buffer.position()) { buffer.position(start); } reset(); return views; }
/** * Very efficient method for parsing an {@link Integer} from the given {@literal Buffer}. Much * faster than {@link Integer#parseInt(String)}. * * @param b The {@literal Buffer} to slice. * @return The int value or {@literal null} if the {@literal Buffer} could not be read. */ public static Integer parseInt(Buffer b) { if (b.remaining() == 0) { return null; } b.snapshot(); int len = b.remaining(); int num = 0; int dec = 1; for (int i = (b.position + len); i > b.position; ) { char c = (char) b.buffer.get(--i); num += Character.getNumericValue(c) * dec; dec *= 10; } b.reset(); return num; }
private String decode() { if (null == decoder) { decoder = UTF8.newDecoder(); } snapshot(); try { if (null == chars || chars.remaining() < buffer.remaining()) { chars = CharBuffer.allocate(buffer.remaining()); } else { chars.rewind(); } decoder.reset(); CoderResult cr = decoder.decode(buffer.duplicate(), chars, true); if (cr.isUnderflow()) { decoder.flush(chars); } chars.flip(); return chars.toString(); } finally { reset(); } }
/** * Create a {@link View} of the current range of this {@link Buffer}. * * @return The view of the buffer * @see #position() * @see #limit() */ public View createView() { snapshot(); return new View(position, limit); }
/** * Create a {@link View} of the given range of this {@literal Buffer}. * * @param start start of the range. * @param end end of the range. * @return A new {@link View} object that represents the given range. */ public View createView(int start, int end) { snapshot(); return new View(start, end); }