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