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

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

There is a newer version: 5.5.5
Show newest version
/*-
 * #%L
 * rapidoid-buffer
 * %%
 * Copyright (C) 2014 - 2018 Nikolche Mihajlovski and contributors
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

package org.rapidoid.buffer;

import org.rapidoid.annotation.Authors;
import org.rapidoid.annotation.Since;
import org.rapidoid.bytes.ByteBufferBytes;
import org.rapidoid.bytes.Bytes;
import org.rapidoid.bytes.BytesUtil;
import org.rapidoid.commons.Err;
import org.rapidoid.data.BufRange;
import org.rapidoid.data.BufRanges;
import org.rapidoid.pool.Pool;
import org.rapidoid.u.U;
import org.rapidoid.util.Constants;
import org.rapidoid.util.D;
import org.rapidoid.util.Msc;
import org.rapidoid.wrap.IntWrap;

import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;


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

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

	private final ThreadLocal tmpBufs = new ThreadLocal() {
		@Override
		protected ByteBuffer initialValue() {
			return ByteBuffer.allocateDirect(20 * 1024);
		}
	};

	private final BufRange HELPER_RANGE = new BufRange();

	private static final int TO_BYTES = 1;

	private static final int TO_CHANNEL = 2;

	private static final int TO_BUFFER = 3;

	private static final int TO_SSL_DEST = 4;

	private static final int NOT_RELEVANT = Integer.MIN_VALUE;

	private final Pool bufPool;

	private final int factor;

	private final int addrMask;

	private final int singleCap;

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

	private int bufN;

	private int shrinkN;

	private final String name;

	private int _position;

	private int _limit;

	private int _checkpoint;

	private final ByteBufferBytes singleBytes = new ByteBufferBytes();

	private final Bytes multiBytes = new BufBytes(this);

	private Bytes _bytes = multiBytes;

	private int _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 = Msc.bitMask(factor);

		assert invariant(true);
	}

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

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

		validatePos(position, 1);

		position += shrinkN;

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

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

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

		int least = pos + space;

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

		if (!hasEnough) {
			throw INCOMPLETE_READ;
		}
	}

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

		validatePos(position, 1);

		position += shrinkN;

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

		buf.put(position & addrMask, value);

		assert invariant(true);
	}

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

		assert _size == _size();

		return _size;
	}

	private int _size() {
		return bufN > 0 ? (bufN - 1) * singleCap + bufs[bufN - 1].position() - shrinkN : 0;
	}

	private void expandUnit() {
		if (bufN == bufs.length) {
			bufs = Msc.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 int append(ReadableByteChannel channel) throws IOException { assert invariant(true); int totalRead = 0; try { boolean done; // precondition: the channel has data do { ByteBuffer dest = writableBuf(); int space = dest.remaining(); assert space > 0; int 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); int 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 int 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[_size()]; int total = readAll(bytes, 0, 0, bytes.length); assert total == bytes.length; assert invariant(false); return new String(bytes); } @Override public String get(BufRange range) { assert invariant(false); if (range.isEmpty()) { return ""; } byte[] bytes = new byte[range.length]; int total = readAll(bytes, 0, range.start, range.length); assert total == bytes.length; assert invariant(false); return new String(bytes); } @Override public void get(BufRange range, byte[] dest, int offset) { assert invariant(false); int total = readAll(dest, offset, range.start, range.length); assert total == range.length; assert invariant(false); } private int writeToHelper(BufRange range) { assert invariant(false); return readAll(HELPER, 0, range.start, range.length); } private int readAll(byte[] bytes, int destOffset, int offset, int length) { assert invariant(false); if (offset + length > _size()) { throw new IllegalArgumentException("offset + length > buffer size!"); } int wrote; try { wrote = writeTo(TO_BYTES, offset, length, bytes, null, null, null, destOffset); } catch (IOException e) { throw U.rte(e); } assert invariant(false); return wrote; } @Override public int writeTo(WritableByteChannel channel) throws IOException { return writeTo(channel, 0, _size()); } @Override public int writeTo(WritableByteChannel channel, int srcOffset, int length) throws IOException { assert invariant(false); int wrote = writeTo(TO_CHANNEL, srcOffset, length, null, channel, null, null, NOT_RELEVANT); assert U.must(wrote <= _size(), "Incorrect write to channel!"); assert invariant(false); return wrote; } @Override public int writeTo(ByteBuffer buffer) { return writeTo(buffer, 0, _size()); } @Override public int writeTo(ByteBuffer buffer, int srcOffset, int length) { assert invariant(false); try { int wrote = writeTo(TO_BUFFER, srcOffset, length, null, null, buffer, null, NOT_RELEVANT); assert wrote == length; assert invariant(false); return wrote; } catch (IOException e) { assert invariant(false); throw U.rte(e); } } private int writeTo(int mode, int offset, int length, byte[] bytes, WritableByteChannel channel, ByteBuffer buffer, SSLDestination sslDest, int destOffset) throws IOException { if (_size() == 0) { assert length == 0; return 0; } int fromPos = (offset + shrinkN); int toPos = fromPos + length - 1; int fromInd = fromPos >> factor; int toInd = toPos >> factor; int fromAddr = fromPos & addrMask; int toAddr = toPos & addrMask; assert fromInd <= toInd; if (fromInd == toInd) { return writePart(bufs[fromInd], fromAddr, toAddr + 1, mode, bytes, channel, buffer, sslDest, destOffset, -1); } else { return multiWriteTo(mode, fromInd, toInd, fromAddr, toAddr, bytes, channel, buffer, sslDest, destOffset); } } private int multiWriteTo(int mode, int fromIndex, int toIndex, int fromAddr, int toAddr, byte[] bytes, WritableByteChannel channel, ByteBuffer buffer, SSLDestination sslDest, int destOffset) throws IOException { ByteBuffer first = bufs[fromIndex]; int len = singleCap - fromAddr; int wrote = writePart(first, fromAddr, singleCap, mode, bytes, channel, buffer, sslDest, destOffset, len); if (wrote < len) { return wrote; } int wroteTotal = wrote; for (int i = fromIndex + 1; i < toIndex; i++) { wrote = writePart(bufs[i], 0, singleCap, mode, bytes, channel, buffer, sslDest, destOffset + wroteTotal, singleCap); wroteTotal += wrote; if (wrote < singleCap) { return wroteTotal; } } ByteBuffer last = bufs[toIndex]; wroteTotal += writePart(last, 0, toAddr + 1, mode, bytes, channel, buffer, sslDest, destOffset + wroteTotal, toAddr + 1); return wroteTotal; } private int writePart(ByteBuffer src, int pos, int limit, int mode, byte[] bytes, WritableByteChannel channel, ByteBuffer buffer, SSLDestination sslDest, int destOffset, int 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; int count; switch (mode) { case TO_BYTES: if (len >= 0) { src.get(bytes, destOffset, len); count = len; } else { count = src.remaining(); src.get(bytes, destOffset, count); } break; case TO_CHANNEL: count = 0; while (src.hasRemaining()) { int wrote = channel.write(src); count += wrote; if (wrote == 0) { break; } } break; case TO_BUFFER: count = src.remaining(); buffer.put(src); // FIXME does the buffer have enough space? break; case TO_SSL_DEST: count = 0; ByteBuffer tmpBuf = tmpBufs.get(); while (src.hasRemaining()) { SSLEngineResult result; tmpBuf.clear(); try { result = sslDest.engine.wrap(src, tmpBuf); } catch (SSLException e) { throw U.rte(e); } tmpBuf.flip(); sslDest.dest.append(tmpBuf); count += result.bytesConsumed(); } break; default: throw Err.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() { U.print(">> 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(int 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(int position, byte[] bytes, int offset, int length) { assert invariant(true); // TODO optimize int 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(int position) { assert invariant(true); if (bufN == 0 || position == _size()) { assert invariant(true); return; } assert validPosition(position); if (bufN == 1) { int newPos = position + shrinkN; assert newPos <= singleCap; first().position(newPos); if (newPos == 0) { removeLastBuf(); } } else { position += shrinkN; int index = position >> factor; int addr = 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(int count) { assert invariant(true); deleteAfter(_size() - count); assert invariant(true); } private boolean validPosition(int 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(BufRange range) { assert invariant(false); assert range.length >= 1; if (range.length > 20) { assert invariant(false); throw U.rte("Too many digits!"); } int count = writeToHelper(range); int 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') { int 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 int putNumAsText(int position, long n, boolean forward) { assert invariant(true); boolean appending; int direction; if (forward) { direction = 0; appending = position == size(); } else { direction = -1; appending = false; } int space; if (n >= 0) { if (n < 10) { if (appending) { append((byte) (n + '0')); } else { put(position, (byte) (n + '0')); } space = 1; } else if (n < 100) { long dig1 = n / 10; long dig2 = n % 10; if (appending) { append((byte) (dig1 + '0')); append((byte) (dig2 + '0')); } else { put(position + direction, (byte) (dig1 + '0')); put(position + direction + 1, (byte) (dig2 + '0')); } space = 2; } else { if (appending) { String nums = "" + n; append(nums.getBytes()); space = nums.length(); } else { int digitsN = (int) Math.ceil(Math.log10(n + 1)); int 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 { int digits = putNumAsText(position, -n, forward); put(position - digits, (byte) ('-')); space = digits + 1; } } assert invariant(true); return space; } @SuppressWarnings("unused") private int rebase(int pos, int 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(int 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 int remaining() { assert invariant(false); return _limit - _position; } @Override public int position() { assert invariant(false); return _position; } @Override public int limit() { assert invariant(false); return _limit; } private void sizeChanged() { _size = _size(); _limit = _size(); if (bufN == 1) { singleBytes.setTarget(bufs[0], shrinkN, _limit); _bytes = singleBytes; } else { _bytes = multiBytes; } } @Override public void position(int position) { assert invariant(false); _position = position; assert invariant(false); } @Override public void limit(int limit) { assert invariant(false); _limit = limit; assert invariant(false); } @Override public void upto(byte value, BufRange 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, BufRange range) { assert invariant(false); requireRemaining(1); int start = position(); int limit = limit(); int last = limit - 1; int fromPos = (start + shrinkN); int toPos = (last + shrinkN); int fromInd = fromPos >> factor; int toInd = toPos >> factor; int fromAddr = fromPos & addrMask; int toAddr = toPos & addrMask; assert U.must(fromInd >= 0, "bad start: %s", start); assert U.must(toInd >= 0, "bad end: %s", last); ByteBuffer src = bufs[fromInd]; int 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, BufRange range) { assert invariant(false); requireRemaining(1); int start = position(); int limit = limit(); int last = limit - 1; int fromPos = (start + shrinkN); int toPos = (last + shrinkN); int fromInd = fromPos >> factor; int toInd = toPos >> factor; int fromAddr = fromPos & addrMask; int toAddr = toPos & addrMask; assert U.must(fromInd >= 0, "bad start: %s", start); assert U.must(toInd >= 0, "bad end: %s", last); ByteBuffer src = bufs[fromInd]; int 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(int n) { if (remaining() < n) { throw Buf.INCOMPLETE_READ; } } @Override public void skip(int count) { assert invariant(false); requireRemaining(count); _position += count; assert invariant(false); } @Override public int bufferIndexOf(int position) { assert invariant(false); assert position >= 0; validatePos(position, 1); position += shrinkN; int index = position >> factor; assert bufs[index] != null; assert invariant(false); return index; } @Override public int bufferOffsetOf(int position) { assert invariant(false); assert position >= 0; validatePos(position, 1); position += shrinkN; assert invariant(false); return position & addrMask; } @Override public int bufCount() { assert invariant(false); return bufN; } @Override public OutputStream asOutputStream() { return this; } @Override public String asText() { return get(new BufRange(0, size())); } @Override public Bytes bytes() { assert invariant(false); return _bytes; } @Override public void scanLn(BufRange line) { assert invariant(false); int 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(BufRanges lines) { assert invariant(false); int 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(int count, BufRange 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(int 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, BufRange range, boolean failOnLimit) { assert invariant(false); int 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 int scanTo(byte sep1, byte sep2, BufRange range, boolean failOnLimit) { assert invariant(false); int pos1 = BytesUtil.find(bytes(), _position, _limit, sep1, true); int 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(int toPos, BufRange range, int skip) { range.setInterval(_position, toPos); _position = toPos + skip; } @Override public void scanLnLn(BufRanges ranges, IntWrap result, byte end1, byte end2) { assert invariant(false); int 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 int checkpoint() { return _checkpoint; } @Override public void checkpoint(int checkpoint) { this._checkpoint = checkpoint; } @Override public void write(int byteValue) throws IOException { // used as OutputStream append((byte) byteValue); } @Override public void write(byte[] src, int off, int len) { // used as OutputStream append(src, off, len); } @Override public Buf unwrap() { return this; } @Override public void append(ByteArrayOutputStream src) { try { src.writeTo(asOutputStream()); } catch (IOException e) { throw U.rte(e); } } @Override public int sslWrap(SSLEngine engine, Buf dest) { assert invariant(false); SSLDestination sslDest = new SSLDestination(engine, dest); int consumed; try { consumed = writeTo(TO_SSL_DEST, 0, _size(), null, null, null, sslDest, NOT_RELEVANT); } catch (IOException e) { throw U.rte(e); } assert U.must(consumed <= _size(), "Incorrect write to channel!"); deleteBefore(consumed); assert invariant(false); return consumed; } @Override public void writeByte(byte byteValue) { append(byteValue); } @Override public void writeBytes(byte[] src) { append(src); } @Override public void writeBytes(byte[] src, int offset, int length) { append(src, offset, length); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy