Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7b977ea0 authored by Alex Klyubin's avatar Alex Klyubin
Browse files

Improve DataSource interface.

1. This adds way to obtain a DataSource which represents a region of
   data contained in the DataSource.
2. This fixes a design bug in "feed" method where the size parameter
   was an int instead of long.
3. This fixes a bug in ByteBufferDataSource where its mSize field was
   a long instead of an int (ByteBuffer's length cannot be more than
   2^31).

Bug: 27461702
Change-Id: Ib0812784beb581f19d2412e667b8bd018f0a3c78
parent 8ed91b66
Loading
Loading
Loading
Loading
+53 −21
Original line number Diff line number Diff line
@@ -28,7 +28,7 @@ import java.nio.ByteBuffer;
public class ByteBufferDataSource implements DataSource {

    private final ByteBuffer mBuffer;
    private final long mSize;
    private final int mSize;

    /**
     * Constructs a new {@code ByteBufferDigestSource} based on the data contained in the provided
@@ -45,29 +45,39 @@ public class ByteBufferDataSource implements DataSource {
    }

    @Override
    public void feed(long offset, int size, DataSink sink) throws IOException {
        if (offset < 0) {
            throw new IllegalArgumentException("offset: " + offset);
    public ByteBufferDataSource slice(long offset, long size) {
        if ((offset == 0) && (size == mSize)) {
            return this;
        }
        if (size < 0) {
            throw new IllegalArgumentException("size: " + size);
        }
        if (offset > mSize) {
            throw new IllegalArgumentException(
                    "offset (" + offset + ") > source size (" + mSize + ")");
        }
        long endOffset = offset + size;
        if (endOffset < offset) {
            throw new IllegalArgumentException(
                    "offset (" + offset + ") + size (" + size + ") overflow");
        checkChunkValid(offset, size);

        // checkChunkValid ensures that it's OK to cast offset and size to int.
        int chunkPosition = (int) offset;
        int chunkLimit = (int) (chunkPosition + size);
        // Creating a slice of ByteBuffer modifies the state of the source ByteBuffer (position
        // and limit fields, to be more specific). We thus use synchronization around these
        // state-changing operations to make instances of this class thread-safe.
        synchronized (mBuffer) {
            // ByteBuffer.limit(int) and .position(int) check that that the position >= limit
            // invariant is not broken. Thus, the only way to safely change position and limit
            // without caring about their current values is to first set position to 0 or set the
            // limit to capacity.
            mBuffer.position(0);

            mBuffer.limit(chunkLimit);
            mBuffer.position(chunkPosition);
            // Create a ByteBufferDataSource for the slice of the buffer between limit and position.
            return new ByteBufferDataSource(mBuffer);
        }
        if (endOffset > mSize) {
            throw new IllegalArgumentException(
                    "offset (" + offset + ") + size (" + size + ") > source size (" + mSize  +")");
    }

        int chunkPosition = (int) offset; // safe to downcast because mSize <= Integer.MAX_VALUE
        int chunkLimit = (int) endOffset; // safe to downcast because mSize <= Integer.MAX_VALUE
    @Override
    public void feed(long offset, long size, DataSink sink) throws IOException {
        checkChunkValid(offset, size);

        // checkChunkValid ensures that it's OK to cast offset and size to int.
        int chunkPosition = (int) offset;
        int chunkLimit = (int) (chunkPosition + size);
        ByteBuffer chunk;
        // Creating a slice of ByteBuffer modifies the state of the source ByteBuffer (position
        // and limit fields, to be more specific). We thus use synchronization around these
@@ -86,4 +96,26 @@ public class ByteBufferDataSource implements DataSource {

        sink.consume(chunk);
    }

    private void checkChunkValid(long offset, long size) {
        if (offset < 0) {
            throw new IllegalArgumentException("offset: " + offset);
        }
        if (size < 0) {
            throw new IllegalArgumentException("size: " + size);
        }
        if (offset > mSize) {
            throw new IllegalArgumentException(
                    "offset (" + offset + ") > source size (" + mSize + ")");
        }
        long endOffset = offset + size;
        if (endOffset < offset) {
            throw new IllegalArgumentException(
                    "offset (" + offset + ") + size (" + size + ") overflow");
        }
        if (endOffset > mSize) {
            throw new IllegalArgumentException(
                    "offset (" + offset + ") + size (" + size + ") > source size (" + mSize  +")");
        }
    }
}
+7 −1
Original line number Diff line number Diff line
@@ -43,5 +43,11 @@ public interface DataSource {
     * @param offset index (in bytes) at which the chunk starts inside data source
     * @param size size (in bytes) of the chunk
     */
    void feed(long offset, int size, DataSink sink) throws IOException;
    void feed(long offset, long size, DataSink sink) throws IOException;

    /**
     * Returns a data source representing the specified region of data of this data source. Changes
     * to data represented by this data source will also be visible in the returned data source.
     */
    DataSource slice(long offset, long size);
}
+2 −1
Original line number Diff line number Diff line
@@ -12,7 +12,8 @@ public abstract class DataSources {

    /**
     * Returns a {@link DataSource} backed by the provided {@link ByteBuffer}. The data source
     * represents the data contained between the position and limit of the buffer.
     * represents the data contained between the position and limit of the buffer. Changes to the
     * buffer's contents will be visible in the data source.
     */
    public static DataSource asDataSource(ByteBuffer buffer) {
        if (buffer == null) {