All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.rapidoidx.buffer.MultiBuf Maven / Gradle / Ivy

There is a newer version: 5.0.3
Show newest version
package org.rapidoidx.buffer;

/*
 * #%L
 * rapidoid-x-buffer
 * %%
 * Copyright (C) 2014 - 2015 Nikolche Mihajlovski and contributors
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see .
 * #L%
 */

import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;

import org.rapidoid.annotation.Authors;
import org.rapidoid.annotation.Since;
import org.rapidoid.arr.Arr;
import org.rapidoid.pool.Pool;
import org.rapidoid.u.U;
import org.rapidoid.util.Constants;
import org.rapidoid.util.D;
import org.rapidoid.wrap.LongWrap;
import org.rapidoidx.bytes.ByteBufferBytes;
import org.rapidoidx.bytes.Bytes;
import org.rapidoidx.bytes.BytesUtil;
import org.rapidoidx.data.Range;
import org.rapidoidx.data.Ranges;

@Authors("Nikolche Mihajlovski")
@Since("3.0.0")
public class MultiBuf implements Buf, Constants {

	private final byte[] HELPER = new byte[20];

	private final Range HELPER_RANGE = new Range();

	private static final int TO_BYTES = 1;

	private static final int TO_CHANNEL = 2;

	private static final int TO_BUFFER = 3;

	private final Pool bufPool;

	private final long factor;

	private final long addrMask;

	private final int singleCap;

	private ByteBuffer[] bufs = new ByteBuffer[10];

	private int bufN;

	private long shrinkN;

	private final String name;

	private long _position;

	private long _limit;

	private long _checkpoint;

	private OutputStream outputStream;

	private final ByteBufferBytes singleBytes = new ByteBufferBytes();

	private final Bytes multiBytes = new BufBytes(this);

	private Bytes _bytes = multiBytes;

	private long _size;

	private boolean readOnly = false;

	public MultiBuf(Pool bufPool, int factor, String name) {
		this.bufPool = bufPool;
		this.name = name;
		this.singleCap = (int) Math.pow(2, factor);
		this.factor = factor;
		this.addrMask = addrMask();

		assert invariant(true);
	}

	private long addrMask() {
		long mask = 1;

		for (long i = 0; i < factor - 1; i++) {
			mask <<= 1;
			mask |= 1;
		}

		return mask;
	}

	@Override
	public boolean isSingle() {
		assert invariant(false);
		return bufN == 1;
	}

	@Override
	public byte get(long position) {
		assert invariant(false);
		assert position >= 0;

		validatePos(position, 1);

		position += shrinkN;

		ByteBuffer buf = bufs[(int) (position >> factor)];
		assert buf != null;

		assert invariant(false);
		return buf.get((int) (position & addrMask));
	}

	private void validatePos(long pos, long space) {
		if (pos < 0) {
			throw U.rte("Invalid position: " + pos);
		}

		long least = pos + space;

		boolean hasEnough = least <= _size() && least <= _limit;

		if (!hasEnough) {
			throw INCOMPLETE_READ;
		}
	}

	@Override
	public void put(long position, byte value) {
		assert invariant(true);
		assert position >= 0;

		validatePos(position, 1);

		position += shrinkN;

		ByteBuffer buf = bufs[(int) (position >> factor)];
		assert buf != null;

		buf.put((int) (position & addrMask), value);

		assert invariant(true);
	}

	@Override
	public long size() {
		assert invariant(false);

		assert _size == _size();

		return _size;
	}

	private long _size() {
		if (bufN > 0) {
			long s = (bufN - 1);

			s *= singleCap;
			s += bufs[bufN - 1].position();
			s -= shrinkN;

			return s;
		} else {
			return 0;
		}
	}

	private void expandUnit() {
		if (bufN == bufs.length) {
			bufs = Arr.expand(bufs, 2);
		}

		bufs[bufN] = bufPool.get();
		bufs[bufN].clear();

		bufN++;
	}

	@Override
	public void append(byte value) {
		assert invariant(true);

		writableBuf().put(value);

		sizeChanged();

		assert invariant(true);
	}

	/**
	 * Reads data from the channel and appends it to the buffer.
	 * 
	 * Precondition: received event that the channel has data to be read.
	 */
	@Override
	public long append(ReadableByteChannel channel) throws IOException {
		assert invariant(true);

		long totalRead = 0;

		try {

			boolean done;

			// precondition: the channel has data

			do {
				ByteBuffer dest = writableBuf();

				long space = dest.remaining();
				assert space > 0;

				long read = channel.read(dest);
				if (read >= 0) {
					totalRead += read;
				} else {
					// end of stream (e.g. the other end closed the connection)
					removeLastBufferIfEmpty();
					sizeChanged();

					assert invariant(true);
					return -1;
				}

				// if buffer wasn't filled -> no data is available in channel
				done = read < space;
			} while (!done);

		} finally {
			removeLastBufferIfEmpty();
			sizeChanged();
			assert invariant(true);
		}

		return totalRead;
	}

	@Override
	public void append(ByteBuffer src) {
		assert invariant(true);

		int theLimit = src.limit();

		while (src.hasRemaining()) {
			ByteBuffer dest = writableBuf();

			int space = dest.remaining();
			assert space > 0;

			if (src.remaining() > space) {
				// set limit to match only available space in dest
				src.limit(src.position() + space);
			}

			dest.put(src);

			// restore original limit
			src.limit(theLimit);
		}

		sizeChanged();

		assert invariant(true);
	}

	@Override
	public void append(byte[] src, int offset, int length) {
		assert invariant(true);

		long sizeBefore = _size();

		if (length > 0) {
			ByteBuffer buf = writableBuf();

			if (length <= buf.remaining()) {
				buf.put(src, offset, length);
			} else {
				int partLen = buf.remaining();
				buf.put(src, offset, partLen);
				assert buf.remaining() == 0;
				append(src, offset + partLen, length - partLen);
			}
		}

		sizeChanged();

		assert _size() - sizeBefore == length;

		assert invariant(true);
	}

	private ByteBuffer writableBuf() {
		if (bufN == 0) {
			expandUnit();
			return last();
		}

		ByteBuffer cbuf = last();

		if (!cbuf.hasRemaining()) {
			expandUnit();
			cbuf = last();
		}

		assert cbuf.hasRemaining();
		return cbuf;
	}

	private ByteBuffer last() {
		assert bufN > 0;
		return bufs[bufN - 1];
	}

	@Override
	public ByteBuffer first() {
		assert invariant(false);
		assert bufN > 0;
		return bufs[0];
	}

	@Override
	public ByteBuffer bufAt(int index) {
		assert invariant(false);
		assert bufN > index;
		return bufs[index];
	}

	@Override
	public long append(String s) {
		assert invariant(true);

		byte[] bytes = s.getBytes();
		append(bytes);

		sizeChanged();

		assert invariant(true);
		return bytes.length;
	}

	@Override
	public String toString() {
		return String.format("Buf " + name + " [size=" + _size() + ", units=" + unitCount() + ", trash=" + shrinkN
				+ ", pos=" + position() + ", limit=" + limit() + "] " + super.toString());
	}

	@Override
	public String data() {
		assert invariant(false);

		byte[] bytes = new byte[(int) _size()];
		long total = readAll(bytes, 0, 0, bytes.length);

		assert total == bytes.length;

		assert invariant(false);
		return new String(bytes);
	}

	@Override
	public byte[] getBytes(Range range) {
		assert invariant(false);

		if (range.isEmpty()) {
			return new byte[0];
		}

		byte[] bytes = new byte[(int) range.length];
		long total = readAll(bytes, 0, range.start, range.length);

		assert total == bytes.length;

		assert invariant(false);
		return bytes;
	}

	@Override
	public String get(Range range) {
		assert invariant(false);

		byte[] bytes = getBytes(range);

		assert invariant(false);
		return new String(bytes);
	}

	@Override
	public void get(Range range, byte[] dest, int offset) {
		assert invariant(false);

		long total = readAll(dest, offset, range.start, range.length);

		assert total == range.length;
		assert invariant(false);
	}

	private long writeToHelper(Range range) {
		assert invariant(false);
		return readAll(HELPER, 0, range.start, range.length);
	}

	private long readAll(byte[] bytes, long destOffset, long offset, long length) {
		assert invariant(false);

		if (offset + length > _size()) {
			throw new IllegalArgumentException("offset + length > buffer size!");
		}

		long wrote;
		try {
			wrote = writeTo(TO_BYTES, offset, length, bytes, null, null, destOffset);
		} catch (IOException e) {
			throw U.rte(e);
		}

		assert invariant(false);
		return wrote;
	}

	@Override
	public long writeTo(WritableByteChannel channel) throws IOException {
		assert invariant(true);

		long wrote = writeTo(TO_CHANNEL, 0, _size(), null, channel, null, 0);
		assert U.must(wrote <= _size(), "Incorrect write to channel!");

		assert invariant(true);
		return wrote;
	}

	@Override
	public long writeTo(ByteBuffer buffer) {
		assert invariant(true);

		try {
			long wrote = writeTo(TO_BUFFER, 0, _size(), null, null, buffer, 0);
			assert wrote == _size();
			assert invariant(true);
			return wrote;
		} catch (IOException e) {
			assert invariant(true);
			throw U.rte(e);
		}
	}

	private long writeTo(int mode, long offset, long length, byte[] bytes, WritableByteChannel channel,
			ByteBuffer buffer, long destOffset) throws IOException {
		if (_size() == 0) {
			assert length == 0;
			return 0;
		}

		long fromPos = offset + shrinkN;
		long toPos = fromPos + length - 1;

		int fromInd = (int) (fromPos >> factor);
		int toInd = (int) (toPos >> factor);

		int fromAddr = (int) (fromPos & addrMask);
		int toAddr = (int) (toPos & addrMask);

		assert fromInd <= toInd;

		if (fromInd == toInd) {
			return writePart(bufs[fromInd], fromAddr, toAddr + 1, mode, bytes, channel, buffer, destOffset, -1);
		} else {
			return multiWriteTo(mode, fromInd, toInd, fromAddr, toAddr, bytes, channel, buffer, destOffset);
		}
	}

	private long multiWriteTo(int mode, int fromIndex, int toIndex, int fromAddr, int toAddr, byte[] bytes,
			WritableByteChannel channel, ByteBuffer buffer, long destOffset) throws IOException {

		ByteBuffer first = bufs[fromIndex];
		long len = singleCap - fromAddr;

		long wrote = writePart(first, fromAddr, singleCap, mode, bytes, channel, buffer, destOffset, len);
		if (wrote < len) {
			return wrote;
		}

		long wroteTotal = wrote;

		for (int i = fromIndex + 1; i < toIndex; i++) {

			wrote = writePart(bufs[i], 0, singleCap, mode, bytes, channel, buffer, destOffset + wroteTotal, singleCap);

			wroteTotal += wrote;

			if (wrote < singleCap) {
				return wroteTotal;
			}
		}

		ByteBuffer last = bufs[toIndex];
		wroteTotal += writePart(last, 0, toAddr + 1, mode, bytes, channel, buffer, destOffset + wroteTotal, toAddr + 1);

		return wroteTotal;
	}

	private long writePart(ByteBuffer src, int pos, int limit, int mode, byte[] bytes, WritableByteChannel channel,
			ByteBuffer buffer, long destOffset, long len) throws IOException {

		// backup buf positions
		int posBackup = src.position();
		int limitBackup = src.limit();

		src.position(pos);
		src.limit(limit);

		assert src.remaining() == len || len < 0;

		long count;

		switch (mode) {
		case TO_BYTES:
			if (len >= 0) {
				src.get(bytes, (int) destOffset, (int) len);
				count = len;
			} else {
				count = src.remaining();
				src.get(bytes, (int) destOffset, (int) count);
			}
			break;

		case TO_CHANNEL:
			count = 0;
			while (src.hasRemaining()) {
				long wrote = channel.write(src);
				count += wrote;
				if (wrote == 0) {
					break;
				}
			}
			break;

		case TO_BUFFER:
			count = src.remaining();
			buffer.put(src);
			break;

		default:
			throw U.notExpected();
		}

		// restore buf positions
		src.limit(limitBackup);
		src.position(posBackup);

		return count;
	}

	private boolean invariant(boolean writing) {
		if (this.readOnly) {
			assert !writing;
		}

		try {

			assert bufN >= 0;

			for (int i = 0; i < bufN - 1; i++) {
				ByteBuffer buf = bufs[i];
				assert buf.position() == singleCap;
				assert buf.limit() == singleCap;
				assert buf.capacity() == singleCap;
			}

			if (bufN > 0) {
				ByteBuffer buf = bufs[bufN - 1];
				assert buf == last();
				assert buf.position() > 0;
				assert buf.capacity() == singleCap;
			}

			return true;

		} catch (AssertionError e) {
			dumpBuffers();
			throw e;
		}
	}

	private void dumpBuffers() {
		System.out.println(">> BUFFER " + name + " HAS " + bufN + " PARTS:");

		for (int i = 0; i < bufN - 1; i++) {
			ByteBuffer buf = bufs[i];
			D.print(i + "]" + buf);
		}

		if (bufN > 0) {
			ByteBuffer buf = bufs[bufN - 1];
			D.print("LAST]" + buf);
		}
	}

	@Override
	public void deleteBefore(long count) {
		assert invariant(true);

		if (count == _size()) {
			clear();
			return;
		}

		shrinkN += count;

		while (shrinkN >= singleCap) {
			removeFirstBuf();
			shrinkN -= singleCap;
		}

		_position -= count;
		if (_position < 0) {
			_position = 0;
		}

		sizeChanged();

		assert invariant(true);
	}

	private void removeFirstBuf() {
		bufs[0].clear();
		bufPool.release(bufs[0]);

		for (int i = 0; i < bufN - 1; i++) {
			bufs[i] = bufs[i + 1];
		}

		bufN--;
	}

	private void removeLastBuf() {
		bufs[bufN - 1].clear();
		bufPool.release(bufs[bufN - 1]);
		bufN--;
		if (bufN == 0) {
			shrinkN = 0;
		}
	}

	private void removeLastBufferIfEmpty() {
		if (bufN > 0) {
			if (last().position() == 0) {
				removeLastBuf();
			}
		}
	}

	@Override
	public int unitCount() {
		assert invariant(false);
		return bufN;
	}

	@Override
	public int unitSize() {
		assert invariant(false);
		return singleCap;
	}

	@Override
	public void put(long position, byte[] bytes, int offset, int length) {
		assert invariant(true);

		// TODO optimize
		long pos = position;
		for (int i = offset; i < offset + length; i++) {
			put(pos++, bytes[i]);
		}

		assert invariant(true);
	}

	@Override
	public void append(byte[] bytes) {
		assert invariant(true);

		append(bytes, 0, bytes.length);

		assert invariant(true);
	}

	@Override
	public void deleteAfter(long position) {
		assert invariant(true);

		if (bufN == 0 || position == _size()) {
			assert invariant(true);
			return;
		}

		assert validPosition(position);

		if (bufN == 1) {
			long newPos = position + shrinkN;
			assert newPos <= Integer.MAX_VALUE && newPos <= singleCap;
			first().position((int) newPos);
			if (newPos == 0) {
				removeLastBuf();
			}
		} else {
			position += shrinkN;
			int index = (int) (position >> factor);
			int addr = (int) (position & addrMask);

			// make it the last buffer
			while (index < bufN - 1) {
				removeLastBuf();
			}

			ByteBuffer last = bufs[index];
			assert last() == last;

			if (addr > 0) {
				last.position(addr);
			} else {
				removeLastBuf();
				if (bufN > 0) {
					last().position(singleCap);
				}
			}
		}

		removeLastBufferIfEmpty();
		sizeChanged();

		assert invariant(true);
	}

	@Override
	public void deleteLast(long count) {
		assert invariant(true);

		deleteAfter(_size() - count);

		assert invariant(true);
	}

	private boolean validPosition(long position) {
		assert U.must(position >= 0 && position < _size(), "Invalid position: %s", position);
		return true;
	}

	@Override
	public void clear() {
		// don't assert invariant() here, invalid state is allowed before clear/reset

		for (int i = 0; i < bufN; i++) {
			bufs[i].clear();
			bufPool.release(bufs[i]);
		}

		readOnly = false;
		shrinkN = 0;
		bufN = 0;
		_position = 0;

		sizeChanged();

		assert invariant(true);
	}

	@Override
	public long getN(Range range) {
		assert invariant(false);

		assert range.length >= 1;

		if (range.length > 20) {
			assert invariant(false);
			throw U.rte("Too many digits!");
		}

		long count = writeToHelper(range);

		long value = 0;

		boolean negative = HELPER[0] == '-';
		int start = negative ? 1 : 0;

		for (int i = start; i < count; i++) {
			byte b = HELPER[i];
			if (b >= '0' && b <= '9') {
				long digit = b - '0';
				value = value * 10 + digit;
			} else {
				assert invariant(false);
				throw U.rte("Invalid number: '%s'", get(range));
			}
		}

		assert invariant(false);
		return negative ? -value : value;
	}

	@Override
	public ByteBuffer getSingle() {
		assert invariant(false);
		return isSingle() ? first() : null;
	}

	@Override
	public long putNumAsText(long position, long n, boolean forward) {
		assert invariant(true);

		long direction = forward ? 0 : -1;

		long space;

		if (n >= 0) {
			if (n < 10) {
				put(position, (byte) (n + '0'));
				space = 1;
			} else if (n < 100) {
				long dig1 = n / 10;
				long dig2 = n % 10;
				put(position + direction, (byte) (dig1 + '0'));
				put(position + direction + 1, (byte) (dig2 + '0'));
				space = 2;
			} else {
				long digitsN = (long) Math.ceil(Math.log10(n + 1));

				long pos = position + digitsN - 1 + direction * digitsN;
				if (!forward) {
					pos++;
				}

				while (true) {
					long digit = n % 10;
					byte dig = (byte) (digit + 48);
					put(pos--, dig);

					if (n < 10) {
						break;
					}
					n = n / 10;
				}
				space = digitsN;
			}
		} else {
			if (forward) {
				put(position, (byte) ('-'));
				space = putNumAsText(position + 1, -n, forward) + 1;
			} else {
				long digits = putNumAsText(position, -n, forward);
				put(position - digits, (byte) ('-'));
				space = digits + 1;
			}
		}

		assert invariant(true);
		return space;
	}

	@SuppressWarnings("unused")
	private long rebase(long pos, long bufInd) {
		return (bufInd << factor) + pos - shrinkN;
	}

	@Override
	public byte next() {
		assert invariant(false);

		byte b = get(_position++);

		assert invariant(false);
		return b;
	}

	@Override
	public void back(long count) {
		assert invariant(false);

		_position--;

		assert invariant(false);
	}

	@Override
	public byte peek() {
		assert invariant(false);

		byte b = get(_position);

		assert invariant(false);
		return b;
	}

	@Override
	public boolean hasRemaining() {
		assert invariant(false);

		boolean result = remaining() > 0;

		assert invariant(false);
		return result;
	}

	@Override
	public long remaining() {
		assert invariant(false);
		return _limit - _position;
	}

	@Override
	public long position() {
		assert invariant(false);
		return _position;
	}

	@Override
	public long limit() {
		assert invariant(false);
		return _limit;
	}

	private void sizeChanged() {
		_size = _size();
		_limit = _size();

		if (bufN == 1) {
			singleBytes.setBuf(bufs[0]);
			_bytes = singleBytes;
		} else {
			_bytes = multiBytes;
		}
	}

	@Override
	public void position(long position) {
		assert invariant(false);
		_position = position;
		assert invariant(false);
	}

	@Override
	public void limit(long limit) {
		assert invariant(false);
		_limit = limit;
		assert invariant(false);
	}

	@Override
	public void upto(byte value, Range range) {
		assert invariant(false);

		range.starts(_position);

		while (get(_position) != value) {
			_position++;
		}

		range.ends(_position);

		_position++;

		assert invariant(false);
	}

	@Override
	public ByteBuffer exposed() {
		assert invariant(false);

		ByteBuffer first = first();

		assert invariant(false);
		return first;
	}

	@Override
	public void scanUntil(byte value, Range range) {
		assert invariant(false);

		requireRemaining(1);

		long start = position();
		long limit = limit();
		long last = limit - 1;

		long fromPos = start + shrinkN;
		long toPos = last + shrinkN;

		int fromInd = (int) (fromPos >> factor);
		int toInd = (int) (toPos >> factor);

		int fromAddr = (int) (fromPos & addrMask);
		int toAddr = (int) (toPos & addrMask);

		assert U.must(fromInd >= 0, "bad start: %s", start);
		assert U.must(toInd >= 0, "bad end: %s", last);

		ByteBuffer src = bufs[fromInd];

		long absPos = start;

		for (int pos = fromAddr; pos < singleCap; pos++) {
			byte b = src.get(pos);

			if (b == value) {
				range.setInterval(start, absPos);
				position(absPos + 1);
				assert invariant(false);
				return;
			}

			absPos++;
		}

		for (int i = fromInd + 1; i < toInd; i++) {
			src = bufs[i];

			for (int pos = 0; pos < singleCap; pos++) {
				byte b = src.get(pos);

				if (b == value) {
					range.setInterval(start, absPos);
					position(absPos + 1);
					assert invariant(false);
					return;
				}

				absPos++;
			}
		}

		if (fromInd < toInd) {
			src = bufs[toInd];

			for (int pos = 0; pos <= toAddr; pos++) {
				byte b = src.get(pos);

				if (b == value) {
					range.setInterval(start, absPos);
					position(absPos + 1);
					assert invariant(false);
					return;
				}

				absPos++;
			}
		}

		position(limit);

		assert invariant(false);
		throw INCOMPLETE_READ;
	}

	@Override
	public void scanWhile(byte value, Range range) {
		assert invariant(false);

		requireRemaining(1);

		long start = position();
		long limit = limit();
		long last = limit - 1;

		long fromPos = start + shrinkN;
		long toPos = last + shrinkN;

		int fromInd = (int) (fromPos >> factor);
		int toInd = (int) (toPos >> factor);

		int fromAddr = (int) (fromPos & addrMask);
		int toAddr = (int) (toPos & addrMask);

		assert U.must(fromInd >= 0, "bad start: %s", start);
		assert U.must(toInd >= 0, "bad end: %s", last);

		ByteBuffer src = bufs[fromInd];

		long absPos = start;

		for (int pos = fromAddr; pos < singleCap; pos++) {
			byte b = src.get(pos);

			if (b != value) {
				range.setInterval(start, absPos);
				position(absPos);
				assert invariant(false);
				return;
			}

			absPos++;
		}

		for (int i = fromInd + 1; i < toInd; i++) {
			src = bufs[i];

			for (int pos = 0; pos < singleCap; pos++) {
				byte b = src.get(pos);

				if (b != value) {
					range.setInterval(start, absPos);
					position(absPos);
					assert invariant(false);
					return;
				}

				absPos++;
			}
		}

		if (fromInd < toInd) {
			src = bufs[toInd];

			for (int pos = 0; pos <= toAddr; pos++) {
				byte b = src.get(pos);

				if (b != value) {
					range.setInterval(start, absPos);
					position(absPos);
					assert invariant(false);
					return;
				}

				absPos++;
			}
		}

		position(limit);

		assert invariant(false);
		throw INCOMPLETE_READ;
	}

	private void requireRemaining(long n) {
		if (remaining() < n) {
			throw Buf.INCOMPLETE_READ;
		}
	}

	@Override
	public void skip(long count) {
		assert invariant(false);

		requireRemaining(count);
		_position += count;

		assert invariant(false);
	}

	@Override
	public int bufferIndexOf(long position) {
		assert invariant(false);

		assert position >= 0;

		validatePos(position, 1);

		position += shrinkN;

		int index = (int) (position >> factor);
		assert bufs[index] != null;

		assert invariant(false);
		return index;
	}

	@Override
	public int bufferOffsetOf(long position) {
		assert invariant(false);

		assert position >= 0;

		validatePos(position, 1);

		position += shrinkN;

		assert invariant(false);
		return (int) (position & addrMask);
	}

	@Override
	public int bufCount() {
		assert invariant(false);
		return bufN;
	}

	@Override
	public OutputStream asOutputStream() {
		assert invariant(false);

		if (outputStream == null) {
			outputStream = new OutputStream() {
				@Override
				public void write(int b) throws IOException {
					assert invariant(true);
					append((byte) b);
				}
			};
		}

		assert invariant(false);
		return outputStream;
	}

	@Override
	public String asText() {
		return get(new Range(0, size()));
	}

	@Override
	public Bytes bytes() {
		assert invariant(false);
		return _bytes;
	}

	@Override
	public void scanLn(Range line) {
		assert invariant(false);
		long pos = BytesUtil.parseLine(bytes(), line, position(), size());

		if (pos < 0) {
			assert invariant(false);
			throw INCOMPLETE_READ;
		}

		_position = pos;
		assert invariant(false);
	}

	@Override
	public void scanLnLn(Ranges lines) {
		assert invariant(false);

		long pos = BytesUtil.parseLines(bytes(), lines, position(), size());

		if (pos < 0) {
			assert invariant(false);
			throw INCOMPLETE_READ;
		}

		_position = pos;
		assert invariant(false);
	}

	@Override
	public void scanN(long count, Range range) {
		assert invariant(false);

		get(_position + count - 1);
		range.set(_position, count);
		_position += count;

		assert invariant(false);
	}

	@Override
	public String readLn() {
		assert invariant(false);

		scanLn(HELPER_RANGE);
		String result = get(HELPER_RANGE);

		assert invariant(false);
		return result;
	}

	@Override
	public String readN(long count) {
		assert invariant(false);

		scanN(count, HELPER_RANGE);
		String result = get(HELPER_RANGE);

		assert invariant(false);
		return result;
	}

	@Override
	public byte[] readNbytes(int count) {
		assert invariant(false);

		scanN(count, HELPER_RANGE);
		byte[] bytes = new byte[count];
		get(HELPER_RANGE, bytes, 0);

		assert invariant(false);
		return bytes;
	}

	@Override
	public void scanTo(byte sep, Range range, boolean failOnLimit) {
		assert invariant(false);

		long pos = BytesUtil.find(bytes(), _position, _limit, sep, true);

		if (pos >= 0) {
			consumeAndSkip(pos, range, 1);
		} else {
			if (failOnLimit) {
				assert invariant(false);
				throw INCOMPLETE_READ;
			} else {
				consumeAndSkip(_limit, range, 0);
			}
		}

		assert invariant(false);
	}

	@Override
	public long scanTo(byte sep1, byte sep2, Range range, boolean failOnLimit) {
		assert invariant(false);

		long pos1 = BytesUtil.find(bytes(), _position, _limit, sep1, true);
		long pos2 = BytesUtil.find(bytes(), _position, _limit, sep2, true);

		boolean found1 = pos1 >= 0;
		boolean found2 = pos2 >= 0;

		if (found1 && found2) {
			if (pos1 <= pos2) {
				consumeAndSkip(pos1, range, 1);
				assert invariant(false);
				return 1;
			} else {
				consumeAndSkip(pos2, range, 1);
				assert invariant(false);
				return 2;
			}
		} else if (found1 && !found2) {
			consumeAndSkip(pos1, range, 1);
			assert invariant(false);
			return 1;
		} else if (!found1 && found2) {
			consumeAndSkip(pos2, range, 1);
			assert invariant(false);
			return 2;
		} else {
			if (failOnLimit) {
				assert invariant(false);
				throw INCOMPLETE_READ;
			} else {
				consumeAndSkip(_limit, range, 0);
				assert invariant(false);
				return 0;
			}
		}
	}

	private void consumeAndSkip(long toPos, Range range, long skip) {
		range.setInterval(_position, toPos);
		_position = toPos + skip;
	}

	@Override
	public void scanLnLn(Ranges ranges, LongWrap result, byte end1, byte end2) {
		assert invariant(false);

		long nextPos = BytesUtil.parseLines(bytes(), ranges, result, _position, _limit, end1, end2);

		if (nextPos < 0) {
			throw Buf.INCOMPLETE_READ;
		}

		_position = nextPos;

		assert invariant(false);
	}

	@Override
	public void setReadOnly(boolean readOnly) {
		this.readOnly = readOnly;
	}

	@Override
	public long checkpoint() {
		return _checkpoint;
	}

	@Override
	public void checkpoint(long checkpoint) {
		this._checkpoint = checkpoint;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy