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

io.kaitai.struct.ByteBufferKaitaiStream Maven / Gradle / Ivy

Go to download

Kaitai Struct is a declarative language used for describe various binary data structures using .ksy format. .ksy format can be compiled into the parser source code in target language. This library is a small collection of runtime methods used by the code generated by Kaitai Struct for Java.

The newest version!
package io.kaitai.struct;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

/**
 * An implementation of {@link KaitaiStream} backed by a {@link ByteBuffer}.
 * It can be either a {@link MappedByteBuffer} backed by {@link FileChannel},
 * or a regular wrapper over a given byte array).
 */
public class ByteBufferKaitaiStream extends KaitaiStream {
    private final FileChannel fc;
    private final ByteBuffer bb;

    /**
     * Initializes a stream, reading from a local file with specified fileName.
     * Internally, FileChannel + MappedByteBuffer will be used.
     * @param fileName file to read
     * @throws IOException if file can't be read
     */
    public ByteBufferKaitaiStream(String fileName) throws IOException {
        fc = FileChannel.open(Paths.get(fileName), StandardOpenOption.READ);
        bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
    }

    /**
     * Initializes a stream that will get data from given byte array when read.
     * Internally, ByteBuffer wrapping given array will be used.
     * @param arr byte array to read
     */
    public ByteBufferKaitaiStream(byte[] arr) {
        fc = null;
        bb = ByteBuffer.wrap(arr);
    }

    /**
     * Initializes a stream that will get data from given ByteBuffer when read.
     * @param buffer ByteBuffer to read
     */
    public ByteBufferKaitaiStream(ByteBuffer buffer) {
        fc = null;
        bb = buffer;
    }

    /**
     * Provide a read-only version of the {@link ByteBuffer} backing the data of this instance.
     * 

* This way one can access the underlying raw bytes associated with this structure, but it is * important to note that the caller needs to know what this raw data is: Depending on the * hierarchy of user types, how the format has been described and how a user type is actually * used, it might be that one accesses all data of some format or only a special substream * view of it. We can't know currently, so one needs to keep that in mind when authoring a KSY * and e.g. use substreams with user types whenever such a type most likely needs to access its * underlying raw data. Using a substream in KSY and directly passing some raw data to a user * type outside of normal KS parse order is equivalent and will provide the same results. If no * substream is used instead, the here provided data might differ depending on the context in * which the associated type was parsed, because the underlying {@link ByteBuffer} might * contain the data of all parent types and such as well and not only the one the caller is * actually interested in. *

*

* The returned {@link ByteBuffer} is always rewinded to position 0, because this stream was * most likely used to parse a type already, in which case the former position would have been * at the end of the buffer. Such a position doesn't help a common reading user much and that * fact can easily be forgotten, repositioning to another index than the start is pretty easy * as well. Rewinding/repositioning doesn't even harm performance in any way. *

* @return read-only {@link ByteBuffer} to access raw data for the associated type. */ public ByteBuffer asRoBuffer() { ByteBuffer retVal = this.bb.asReadOnlyBuffer(); retVal.rewind(); return retVal; } /** * Closes the stream safely. If there was an open file associated with it, closes that file. * For streams that were reading from in-memory array, does nothing. * @throws IOException if FileChannel can't be closed */ @Override public void close() throws IOException { if (fc != null) fc.close(); } //region Stream positioning @Override public boolean isEof() { return !bb.hasRemaining(); } @Override public void seek(int newPos) { bb.position(newPos); } @Override public void seek(long newPos) { if (newPos > Integer.MAX_VALUE) { throw new IllegalArgumentException("Java ByteBuffer can't be seeked past Integer.MAX_VALUE"); } bb.position((int) newPos); } @Override public int pos() { return bb.position(); } @Override public long size() { return bb.limit(); } //endregion //region Integer numbers //region Signed /** * Reads one signed 1-byte integer, returning it properly as Java's "byte" type. * @return 1-byte integer read from a stream */ @Override public byte readS1() { return bb.get(); } //region Big-endian @Override public short readS2be() { bb.order(ByteOrder.BIG_ENDIAN); return bb.getShort(); } @Override public int readS4be() { bb.order(ByteOrder.BIG_ENDIAN); return bb.getInt(); } @Override public long readS8be() { bb.order(ByteOrder.BIG_ENDIAN); return bb.getLong(); } //endregion //region Little-endian @Override public short readS2le() { bb.order(ByteOrder.LITTLE_ENDIAN); return bb.getShort(); } @Override public int readS4le() { bb.order(ByteOrder.LITTLE_ENDIAN); return bb.getInt(); } @Override public long readS8le() { bb.order(ByteOrder.LITTLE_ENDIAN); return bb.getLong(); } //endregion //endregion //region Unsigned @Override public int readU1() { return bb.get() & 0xff; } //region Big-endian @Override public int readU2be() { bb.order(ByteOrder.BIG_ENDIAN); return bb.getShort() & 0xffff; } @Override public long readU4be() { bb.order(ByteOrder.BIG_ENDIAN); return bb.getInt() & 0xffffffffL; } //endregion //region Little-endian @Override public int readU2le() { bb.order(ByteOrder.LITTLE_ENDIAN); return bb.getShort() & 0xffff; } @Override public long readU4le() { bb.order(ByteOrder.LITTLE_ENDIAN); return bb.getInt() & 0xffffffffL; } //endregion //endregion //endregion //region Floating point numbers //region Big-endian @Override public float readF4be() { bb.order(ByteOrder.BIG_ENDIAN); return bb.getFloat(); } @Override public double readF8be() { bb.order(ByteOrder.BIG_ENDIAN); return bb.getDouble(); } //endregion //region Little-endian @Override public float readF4le() { bb.order(ByteOrder.LITTLE_ENDIAN); return bb.getFloat(); } @Override public double readF8le() { bb.order(ByteOrder.LITTLE_ENDIAN); return bb.getDouble(); } //endregion //endregion //region Byte arrays /** * Reads designated number of bytes from the stream. * @param n number of bytes to read * @return read bytes as byte array */ @Override public byte[] readBytes(long n) { byte[] buf = new byte[toByteArrayLength(n)]; bb.get(buf); return buf; } /** * Reads all the remaining bytes in a stream as byte array. * @return all remaining bytes in a stream as byte array */ @Override public byte[] readBytesFull() { byte[] buf = new byte[bb.remaining()]; bb.get(buf); return buf; } @Override public byte[] readBytesTerm(int term, boolean includeTerm, boolean consumeTerm, boolean eosError) { ByteArrayOutputStream buf = new ByteArrayOutputStream(); while (true) { if (!bb.hasRemaining()) { if (eosError) { throw new RuntimeException("End of stream reached, but no terminator " + term + " found"); } else { return buf.toByteArray(); } } int c = bb.get(); if (c == term) { if (includeTerm) buf.write(c); if (!consumeTerm) bb.position(bb.position() - 1); return buf.toByteArray(); } buf.write(c); } } //endregion }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy