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

io.permazen.util.ByteReader Maven / Gradle / Ivy

There is a newer version: 5.1.0
Show newest version

/*
 * Copyright (C) 2015 Archie L. Cobbs. All rights reserved.
 */

package io.permazen.util;

import com.google.common.base.Preconditions;

/**
 * Reads bytes from a buffer.
 */
public class ByteReader {

    final byte[] buf;
    final int max;
    int off;

    /**
     * Constructor. The provided array is read from directly; no copy is made.
     *
     * @param buf array to read from
     * @throws NullPointerException if {@code buf} is null
     */
    public ByteReader(byte[] buf) {
        this.buf = buf;
        this.max = buf.length;
        this.off = 0;
    }

    /**
     * Constructor. The provided array is read from directly; no copy is made.
     *
     * @param buf array to read from
     * @param off starting offset into {@code buf}
     * @throws IndexOutOfBoundsException if {@code off} is out of bounds
     * @throws NullPointerException if {@code buf} is null
     */
    public ByteReader(byte[] buf, int off) {
        this(buf, off, buf.length - off);
    }

    /**
     * Constructor. The provided array is read from directly; no copy is made.
     *
     * @param buf array to read from
     * @param off offset into {@code buf}
     * @param len number of bytes to read
     * @throws IndexOutOfBoundsException if {@code off} or {@code len} are out of bounds
     * @throws NullPointerException if {@code buf} is null
     */
    public ByteReader(byte[] buf, int off, int len) {
        if (off < 0 || len < 0 || off > buf.length || off + len < 0 || off + len > buf.length)
            throw new IndexOutOfBoundsException("buf.length = " + buf.length + ", off = " + off + ", len = " + len);
        this.buf = buf;
        this.max = off + len;
        this.off = off;
    }

    /**
     * Constructor. Takes a snapshot of the given writer's entire content.
     *
     * @param writer {@code ByteWriter} to read data from
     * @throws NullPointerException if {@code writer} is null
     */
    public ByteReader(ByteWriter writer) {
        this(writer.buf, 0, writer.len);
    }

    /**
     * Constructor. Takes a snapshot of the given writer's content starting at the specified position.
     *
     * @param writer {@code ByteWriter} to read data from
     * @param mark position previously returned by {@link ByteWriter#mark}
     * @throws IndexOutOfBoundsException if {@code mark} is out of bounds
     * @throws NullPointerException if {@code writer} is null
     */
    public ByteReader(ByteWriter writer, int mark) {
        this(writer.buf, mark, writer.len - mark);
    }

    /**
     * Peek at next byte, if any.
     *
     * @return next byte (0-255)
     * @throws IndexOutOfBoundsException if there are no more bytes
     */
    public int peek() {
        if (this.off == this.max)
            throw new IndexOutOfBoundsException();
        return this.buf[this.off] & 0xff;
    }

    /**
     * Read the next byte.
     *
     * @return next byte (0-255)
     * @throws IndexOutOfBoundsException if there are no more bytes
     */
    public int readByte() {
        if (this.off == this.max)
            throw new IndexOutOfBoundsException();
        return this.buf[this.off++] & 0xff;
    }

    /**
     * Unread the previously read byte. Equivalent to {@code unread(1)}.
     *
     * @throws IndexOutOfBoundsException if there are no more bytes to unread
     */
    public void unread() {
        if (this.off == 0)
            throw new IndexOutOfBoundsException();
        this.off--;
    }

    /**
     * Unread the given number of previously read bytes.
     *
     * @param len the number of bytes to unread
     * @throws IndexOutOfBoundsException if there are no more bytes to unread
     */
    public void unread(int len) {
        if (this.off - len < 0)
            throw new IndexOutOfBoundsException();
        this.off -= len;
    }

    /**
     * Read the specified number of bytes.
     *
     * @param len number of bytes to read
     * @return bytes read
     * @throws IndexOutOfBoundsException if there are not enough bytes
     * @throws IllegalArgumentException if {@code len} is negative
     */
    public byte[] readBytes(int len) {
        Preconditions.checkArgument(len >= 0, "len < 0");
        if (this.off + len > this.max)
            throw new IndexOutOfBoundsException();
        final byte[] result = new byte[len];
        System.arraycopy(this.buf, this.off, result, 0, len);
        this.off += len;
        return result;
    }

    /**
     * Get the number of bytes remaining.
     *
     * @return bytes remaining
     */
    public int remain() {
        return this.max - this.off;
    }

    /**
     * Skip over bytes.
     *
     * @param num the number of bytes to skip
     * @throws IndexOutOfBoundsException if {@code num} is negative
     * @throws IndexOutOfBoundsException if less than {@code num} bytes remain
     */
    public void skip(int num) {
        if (num < 0 || this.off + num > this.max)
            throw new IndexOutOfBoundsException();
        this.off += num;
    }

    /**
     * Get current offset into buffer.
     *
     * @return current offset
     */
    public int getOffset() {
        return this.off;
    }

    /**
     * Get maximum offset into buffer.
     *
     * @return maximum offset
     */
    public int getMax() {
        return this.max;
    }

    /**
     * Copy a range of bytes from the buffer. Does not change the read position.
     *
     * @param off offset into buffer
     * @param len number of bytes
     * @return copy of the specified byte range
     * @throws IndexOutOfBoundsException if {@code off} and/or {@code len} is out of bounds
     */
    public byte[] getBytes(int off, int len) {
        if (off < 0 || len < 0 || off + len > this.max)
            throw new IndexOutOfBoundsException();
        final byte[] data = new byte[len];
        System.arraycopy(this.buf, off, data, 0, len);
        return data;
    }

    /**
     * Copy a range of bytes from the given offset to the end of the buffer. Does not change the read position.
     *
     * @param off offset into buffer
     * @return copy of the specified byte range
     * @throws IndexOutOfBoundsException if {@code off} is out of bounds
     */
    public byte[] getBytes(int off) {
        return this.getBytes(off, this.max - off);
    }

    /**
     * Copy all the of bytes in the buffer. Does not change the read position.
     *
     * @return copy of the entire buffer
     */
    public byte[] getBytes() {
        return this.max == this.buf.length ? this.buf.clone() : this.getBytes(0);
    }

    /**
     * Mark current read position.
     *
     * @return the current offset
     */
    public int mark() {
        return this.off;
    }

    /**
     * Reset read position to a previously marked position.
     *
     * @param mark value previously returned by {@link #mark}
     * @throws IndexOutOfBoundsException if {@code mark} is out of bounds
     */
    public void reset(int mark) {
        if (mark < 0 || mark > this.max)
            throw new IndexOutOfBoundsException();
        this.off = mark;
    }
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy