blob: 3a1fc3c09921eae5f41350fbcf0622c2f2e6b0d7 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.build.apkzlib.sign;
import com.android.apksig.util.DataSink;
import com.android.apksig.util.DataSource;
import com.android.tools.build.apkzlib.zip.ZFile;
import com.google.common.base.Preconditions;
import java.io.EOFException;
import java.io.IOException;
import java.nio.ByteBuffer;
import javax.annotation.Nonnull;
/**
* {@link DataSource} backed by contents of {@link ZFile}.
*/
class ZFileDataSource implements DataSource {
private static final int MAX_READ_CHUNK_SIZE = 65536;
@Nonnull
private final ZFile file;
/**
* Offset (in bytes) relative to the start of file where the region visible in this data source
* starts.
*/
private final long offset;
/**
* Size (in bytes) of the file region visible in this data source or {@code -1} if the whole
* file is visible in this data source and thus its size may change if the file's size changes.
*/
private final long size;
/**
* Constructs a new {@code ZFileDataSource} based on the data contained in the file. Changes to
* the contents of the file, including the size of the file, will be visible in this data
* source.
*/
public ZFileDataSource(@Nonnull ZFile file) {
this.file = file;
offset = 0;
size = -1;
}
/**
* Constructs a new {@code ZFileDataSource} based on the data contained in the specified region
* of the provided file. Changes to the contents of this region of the file will be visible in
* this data source.
*/
public ZFileDataSource(@Nonnull ZFile file, long offset, long size) {
Preconditions.checkArgument(offset >= 0, "offset < 0");
Preconditions.checkArgument(size >= 0, "size < 0");
this.file = file;
this.offset = offset;
this.size = size;
}
@Override
public long size() {
if (size == -1) {
// Data source size is the current size of the file
try {
return file.directSize();
} catch (IOException e) {
return 0;
}
} else {
// Data source size is fixed
return size;
}
}
@Override
public DataSource slice(long offset, long size) {
long sourceSize = size();
checkChunkValid(offset, size, sourceSize);
if ((offset == 0) && (size == sourceSize)) {
return this;
}
return new ZFileDataSource(file, this.offset + offset, size);
}
@Override
public void feed(long offset, long size, @Nonnull DataSink sink) throws IOException {
long sourceSize = size();
checkChunkValid(offset, size, sourceSize);
if (size == 0) {
return;
}
long chunkOffsetInFile = this.offset + offset;
long remaining = size;
byte[] buf = new byte[(int) Math.min(remaining, MAX_READ_CHUNK_SIZE)];
while (remaining > 0) {
int chunkSize = (int) Math.min(remaining, buf.length);
int readSize = file.directRead(chunkOffsetInFile, buf, 0, chunkSize);
if (readSize == -1) {
throw new EOFException("Premature EOF");
}
if (readSize > 0) {
sink.consume(buf, 0, readSize);
chunkOffsetInFile += readSize;
remaining -= readSize;
}
}
}
@Override
public void copyTo(long offset, int size, @Nonnull ByteBuffer dest) throws IOException {
long sourceSize = size();
checkChunkValid(offset, size, sourceSize);
if (size == 0) {
return;
}
int prevLimit = dest.limit();
try {
file.directFullyRead(this.offset + offset, dest);
} finally {
dest.limit(prevLimit);
}
}
@Override
public ByteBuffer getByteBuffer(long offset, int size) throws IOException {
ByteBuffer result = ByteBuffer.allocate(size);
copyTo(offset, size, result);
result.flip();
return result;
}
private static void checkChunkValid(long offset, long size, long sourceSize) {
Preconditions.checkArgument(offset >= 0, "offset < 0");
Preconditions.checkArgument(size >= 0, "size < 0");
Preconditions.checkArgument(offset <= sourceSize, "offset > sourceSize");
long endOffset = offset + size;
Preconditions.checkArgument(offset <= endOffset, "offset > endOffset");
Preconditions.checkArgument(endOffset <= sourceSize, "endOffset > sourceSize");
}
}