Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.apksig.internal.util;
     18 
     19 import com.android.apksig.util.DataSink;
     20 import com.android.apksig.util.DataSource;
     21 import java.io.IOException;
     22 import java.nio.ByteBuffer;
     23 
     24 /**
     25  * {@link DataSource} backed by a {@link ByteBuffer}.
     26  */
     27 public class ByteBufferDataSource implements DataSource {
     28 
     29     private final ByteBuffer mBuffer;
     30     private final int mSize;
     31 
     32     /**
     33      * Constructs a new {@code ByteBufferDigestSource} based on the data contained in the provided
     34      * buffer between the buffer's position and limit.
     35      */
     36     public ByteBufferDataSource(ByteBuffer buffer) {
     37         this(buffer, true);
     38     }
     39 
     40     /**
     41      * Constructs a new {@code ByteBufferDigestSource} based on the data contained in the provided
     42      * buffer between the buffer's position and limit.
     43      */
     44     private ByteBufferDataSource(ByteBuffer buffer, boolean sliceRequired) {
     45         mBuffer = (sliceRequired) ? buffer.slice() : buffer;
     46         mSize = buffer.remaining();
     47     }
     48 
     49     @Override
     50     public long size() {
     51         return mSize;
     52     }
     53 
     54     @Override
     55     public ByteBuffer getByteBuffer(long offset, int size) {
     56         checkChunkValid(offset, size);
     57 
     58         // checkChunkValid ensures that it's OK to cast offset to int.
     59         int chunkPosition = (int) offset;
     60         int chunkLimit = chunkPosition + size;
     61         // Creating a slice of ByteBuffer modifies the state of the source ByteBuffer (position
     62         // and limit fields, to be more specific). We thus use synchronization around these
     63         // state-changing operations to make instances of this class thread-safe.
     64         synchronized (mBuffer) {
     65             // ByteBuffer.limit(int) and .position(int) check that that the position >= limit
     66             // invariant is not broken. Thus, the only way to safely change position and limit
     67             // without caring about their current values is to first set position to 0 or set the
     68             // limit to capacity.
     69             mBuffer.position(0);
     70 
     71             mBuffer.limit(chunkLimit);
     72             mBuffer.position(chunkPosition);
     73             return mBuffer.slice();
     74         }
     75     }
     76 
     77     @Override
     78     public void copyTo(long offset, int size, ByteBuffer dest) {
     79         dest.put(getByteBuffer(offset, size));
     80     }
     81 
     82     @Override
     83     public void feed(long offset, long size, DataSink sink) throws IOException {
     84         if ((size < 0) || (size > mSize)) {
     85             throw new IndexOutOfBoundsException("size: " + size + ", source size: " + mSize);
     86         }
     87         sink.consume(getByteBuffer(offset, (int) size));
     88     }
     89 
     90     @Override
     91     public ByteBufferDataSource slice(long offset, long size) {
     92         if ((offset == 0) && (size == mSize)) {
     93             return this;
     94         }
     95         if ((size < 0) || (size > mSize)) {
     96             throw new IndexOutOfBoundsException("size: " + size + ", source size: " + mSize);
     97         }
     98         return new ByteBufferDataSource(
     99                 getByteBuffer(offset, (int) size),
    100                 false // no need to slice -- it's already a slice
    101                 );
    102     }
    103 
    104     private void checkChunkValid(long offset, long size) {
    105         if (offset < 0) {
    106             throw new IndexOutOfBoundsException("offset: " + offset);
    107         }
    108         if (size < 0) {
    109             throw new IndexOutOfBoundsException("size: " + size);
    110         }
    111         if (offset > mSize) {
    112             throw new IndexOutOfBoundsException(
    113                     "offset (" + offset + ") > source size (" + mSize + ")");
    114         }
    115         long endOffset = offset + size;
    116         if (endOffset < offset) {
    117             throw new IndexOutOfBoundsException(
    118                     "offset (" + offset + ") + size (" + size + ") overflow");
    119         }
    120         if (endOffset > mSize) {
    121             throw new IndexOutOfBoundsException(
    122                     "offset (" + offset + ") + size (" + size + ") > source size (" + mSize  +")");
    123         }
    124     }
    125 }
    126