// Path: plugin/src/main/java/com/android/apksigner/core/util/DataSink.java
// public interface DataSink {
// 
//     /**
//      * Consumes the provided chunk of data.
//      *
//      * <p>This data sink guarantees to not hold references to the provided buffer after this method
//      * terminates.
//      */
//     void consume(byte[] buf, int offset, int length) throws IOException;
// 
//     /**
//      * Consumes all remaining data in the provided buffer and advances the buffer's position
//      * to the buffer's limit.
//      *
//      * <p>This data sink guarantees to not hold references to the provided buffer after this method
//      * terminates.
//      */
//     void consume(ByteBuffer buf) throws IOException;
// }
// 
// Path: plugin/src/main/java/com/android/apksigner/core/util/DataSource.java
// public interface DataSource {
// 
//     /**
//      * Returns the amount of data (in bytes) contained in this data source.
//      */
//     long size();
// 
//     /**
//      * Feeds the specified chunk from this data source into the provided sink.
//      *
//      * @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, long size, DataSink sink) throws IOException;
// 
//     /**
//      * Returns a buffer holding the contents of the specified chunk of data from this data source.
//      * Changes to the data source are not guaranteed to be reflected in the returned buffer.
//      * Similarly, changes in the buffer are not guaranteed to be reflected in the data source.
//      *
//      * <p>The returned buffer's position is {@code 0}, and the buffer's limit and capacity is
//      * {@code size}.
//      *
//      * @param offset index (in bytes) at which the chunk starts inside data source
//      * @param size size (in bytes) of the chunk
//      */
//     ByteBuffer getByteBuffer(long offset, int size) throws IOException;
// 
//     /**
//      * Copies the specified chunk from this data source into the provided destination buffer,
//      * advancing the destination buffer's position by {@code size}.
//      *
//      * @param offset index (in bytes) at which the chunk starts inside data source
//      * @param size size (in bytes) of the chunk
//      */
//     void copyTo(long offset, int size, ByteBuffer dest) 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.
//      *
//      * @param offset index (in bytes) at which the region starts inside data source
//      * @param size size (in bytes) of the region
//      */
//     DataSource slice(long offset, long size);
// }


import com.android.apksigner.core.util.DataSink;
import com.android.apksigner.core.util.DataSource;
import java.io.IOException;
import java.nio.ByteBuffer;

    @Override
    public ByteBuffer getByteBuffer(long offset, int size) {
        checkChunkValid(offset, size);

        // checkChunkValid ensures that it's OK to cast offset to int.
        int chunkPosition = (int) offset;
        int chunkLimit = 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);
            return mBuffer.slice();
        }
    }

    @Override
    public void copyTo(long offset, int size, ByteBuffer dest) {
        dest.put(getByteBuffer(offset, size));
    }

    @Override
// Path: plugin/src/main/java/com/android/apksigner/core/util/DataSink.java
// public interface DataSink {
// 
//     /**
//      * Consumes the provided chunk of data.
//      *
//      * <p>This data sink guarantees to not hold references to the provided buffer after this method
//      * terminates.
//      */
//     void consume(byte[] buf, int offset, int length) throws IOException;
// 
//     /**
//      * Consumes all remaining data in the provided buffer and advances the buffer's position
//      * to the buffer's limit.
//      *
//      * <p>This data sink guarantees to not hold references to the provided buffer after this method
//      * terminates.
//      */
//     void consume(ByteBuffer buf) throws IOException;
// }
// 
// Path: plugin/src/main/java/com/android/apksigner/core/util/DataSource.java
// public interface DataSource {
// 
//     /**
//      * Returns the amount of data (in bytes) contained in this data source.
//      */
//     long size();
// 
//     /**
//      * Feeds the specified chunk from this data source into the provided sink.
//      *
//      * @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, long size, DataSink sink) throws IOException;
// 
//     /**
//      * Returns a buffer holding the contents of the specified chunk of data from this data source.
//      * Changes to the data source are not guaranteed to be reflected in the returned buffer.
//      * Similarly, changes in the buffer are not guaranteed to be reflected in the data source.
//      *
//      * <p>The returned buffer's position is {@code 0}, and the buffer's limit and capacity is
//      * {@code size}.
//      *
//      * @param offset index (in bytes) at which the chunk starts inside data source
//      * @param size size (in bytes) of the chunk
//      */
//     ByteBuffer getByteBuffer(long offset, int size) throws IOException;
// 
//     /**
//      * Copies the specified chunk from this data source into the provided destination buffer,
//      * advancing the destination buffer's position by {@code size}.
//      *
//      * @param offset index (in bytes) at which the chunk starts inside data source
//      * @param size size (in bytes) of the chunk
//      */
//     void copyTo(long offset, int size, ByteBuffer dest) 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.
//      *
//      * @param offset index (in bytes) at which the region starts inside data source
//      * @param size size (in bytes) of the region
//      */
//     DataSource slice(long offset, long size);
// }


// Path: plugin/src/main/java/com/android/apksigner/core/internal/util/ByteBufferDataSource.java
import com.android.apksigner.core.util.DataSink;
import com.android.apksigner.core.util.DataSource;
import java.io.IOException;
import java.nio.ByteBuffer;


    @Override
    public ByteBuffer getByteBuffer(long offset, int size) {
        checkChunkValid(offset, size);

        // checkChunkValid ensures that it's OK to cast offset to int.
        int chunkPosition = (int) offset;
        int chunkLimit = 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);
            return mBuffer.slice();
        }
    }

    @Override
    public void copyTo(long offset, int size, ByteBuffer dest) {
        dest.put(getByteBuffer(offset, size));
    }

    @Override
    public void feed(long offset, long size, DataSink sink) throws IOException {