#include <package/hpkg/PackageFileHeapAccessorBase.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <new>
#if defined(_KERNEL_MODE) && !defined(_BOOT_MODE)
#include <util/AutoLock.h>
#include <slab/Slab.h>
#endif
#include <ByteOrder.h>
#include <DataIO.h>
#include <package/hpkg/ErrorOutput.h>
#include <AutoDeleter.h>
#include <CompressionAlgorithm.h>
namespace BPackageKit {
namespace BHPKG {
namespace BPrivate {
#if defined(_KERNEL_MODE) && !defined(_BOOT_MODE)
void* PackageFileHeapAccessorBase::sQuadChunkCache = NULL;
void* PackageFileHeapAccessorBase::sQuadChunkFallbackBuffer = NULL;
static mutex sFallbackBufferLock = MUTEX_INITIALIZER("PackageFileHeapAccessorBase fallback buffer");
#endif
PackageFileHeapAccessorBase::OffsetArray::OffsetArray()
:
fOffsets(NULL)
{
}
PackageFileHeapAccessorBase::OffsetArray::~OffsetArray()
{
delete[] fOffsets;
}
bool
PackageFileHeapAccessorBase::OffsetArray::InitUncompressedChunksOffsets(
size_t totalChunkCount)
{
if (totalChunkCount <= 1)
return true;
const size_t max32BitChunks = (uint64(1) << 32) / kChunkSize;
size_t actual32BitChunks = totalChunkCount;
if (totalChunkCount - 1 > max32BitChunks) {
actual32BitChunks = max32BitChunks;
fOffsets = _AllocateOffsetArray(totalChunkCount, max32BitChunks);
} else
fOffsets = _AllocateOffsetArray(totalChunkCount, 0);
if (fOffsets == NULL)
return false;
{
uint32 offset = kChunkSize;
for (size_t i = 1; i < actual32BitChunks; i++, offset += kChunkSize)
fOffsets[i] = offset;
}
if (actual32BitChunks < totalChunkCount) {
uint64 offset = actual32BitChunks * kChunkSize;
uint32* offsets = fOffsets + actual32BitChunks;
for (size_t i = actual32BitChunks; i < totalChunkCount;
i++, offset += kChunkSize) {
*offsets++ = (uint32)offset;
*offsets++ = uint32(offset >> 32);
}
}
return true;
}
bool
PackageFileHeapAccessorBase::OffsetArray::InitChunksOffsets(
size_t totalChunkCount, size_t baseIndex, const uint16* chunkSizes,
size_t chunkCount)
{
if (totalChunkCount <= 1)
return true;
if (fOffsets == NULL) {
fOffsets = _AllocateOffsetArray(totalChunkCount, totalChunkCount);
if (fOffsets == NULL)
return false;
}
uint64 offset = (*this)[baseIndex];
for (size_t i = 0; i < chunkCount; i++) {
offset += (uint64)B_BENDIAN_TO_HOST_INT16(chunkSizes[i]) + 1;
size_t index = baseIndex + i + 1;
if (offset <= ~(uint32)0) {
fOffsets[index] = (uint32)offset;
} else {
if (fOffsets[0] == 0) {
uint32* newOffsets = _AllocateOffsetArray(totalChunkCount,
index);
if (newOffsets == NULL)
return false;
fOffsets[0] = index;
memcpy(newOffsets, fOffsets, sizeof(newOffsets[0]) * index);
delete[] fOffsets;
fOffsets = newOffsets;
}
index += index - fOffsets[0];
fOffsets[index] = (uint32)offset;
fOffsets[index + 1] = uint32(offset >> 32);
}
}
return true;
}
bool
PackageFileHeapAccessorBase::OffsetArray::Init(size_t totalChunkCount,
const OffsetArray& other)
{
if (other.fOffsets == NULL)
return true;
size_t elementCount = other.fOffsets[0] == 0
? totalChunkCount
: 2 * totalChunkCount - other.fOffsets[0];
fOffsets = new(std::nothrow) uint32[elementCount];
if (fOffsets == NULL)
return false;
memcpy(fOffsets, other.fOffsets, elementCount * sizeof(fOffsets[0]));
return true;
}
uint32*
PackageFileHeapAccessorBase::OffsetArray::_AllocateOffsetArray(
size_t totalChunkCount, size_t offset32BitChunkCount)
{
uint32* offsets = new(std::nothrow) uint32[
2 * totalChunkCount - offset32BitChunkCount];
if (offsets != NULL) {
offsets[0] = offset32BitChunkCount == totalChunkCount
? 0 : offset32BitChunkCount;
}
return offsets;
}
PackageFileHeapAccessorBase::PackageFileHeapAccessorBase(
BErrorOutput* errorOutput, BPositionIO* file, off_t heapOffset,
DecompressionAlgorithmOwner* decompressionAlgorithm)
:
fErrorOutput(errorOutput),
fFile(file),
fHeapOffset(heapOffset),
fCompressedHeapSize(0),
fUncompressedHeapSize(0),
fDecompressionAlgorithm(decompressionAlgorithm)
{
if (fDecompressionAlgorithm != NULL)
fDecompressionAlgorithm->AcquireReference();
}
PackageFileHeapAccessorBase::~PackageFileHeapAccessorBase()
{
if (fDecompressionAlgorithm != NULL)
fDecompressionAlgorithm->ReleaseReference();
}
status_t
PackageFileHeapAccessorBase::ReadDataToOutput(off_t offset, size_t size,
BDataIO* output)
{
if (size == 0)
return B_OK;
if (offset < 0 || (uint64)offset > fUncompressedHeapSize
|| size > fUncompressedHeapSize - offset) {
return B_BAD_VALUE;
}
uint16* compressedDataBuffer, *uncompressedDataBuffer;
iovec* scratch = NULL;
#if defined(_KERNEL_MODE) && !defined(_BOOT_MODE)
struct ObjectCacheDeleter {
object_cache* cache;
void* object;
ObjectCacheDeleter(object_cache* c)
: cache(c)
, object(NULL)
{
}
~ObjectCacheDeleter()
{
if (cache != NULL && object != NULL)
object_cache_free(cache, object, 0);
}
};
ObjectCacheDeleter chunkBufferDeleter((object_cache*)sQuadChunkCache);
uint8* quadChunkBuffer = (uint8*)object_cache_alloc((object_cache*)sQuadChunkCache,
CACHE_DONT_WAIT_FOR_MEMORY);
chunkBufferDeleter.object = quadChunkBuffer;
MutexLocker fallbackBufferLocker(sFallbackBufferLock, false, false);
if (quadChunkBuffer == NULL) {
fallbackBufferLocker.Lock();
quadChunkBuffer = (uint8*)sQuadChunkFallbackBuffer;
}
iovec localScratch;
compressedDataBuffer = (uint16*)(quadChunkBuffer + 0);
uncompressedDataBuffer = (uint16*)(quadChunkBuffer + kChunkSize);
localScratch.iov_base = (quadChunkBuffer + (kChunkSize * 2));
localScratch.iov_len = kChunkSize * 2;
scratch = &localScratch;
#else
MemoryDeleter compressedMemoryDeleter, uncompressedMemoryDeleter;
compressedDataBuffer = (uint16*)malloc(kChunkSize);
uncompressedDataBuffer = (uint16*)malloc(kChunkSize);
compressedMemoryDeleter.SetTo(compressedDataBuffer);
uncompressedMemoryDeleter.SetTo(uncompressedDataBuffer);
#endif
if (compressedDataBuffer == NULL || uncompressedDataBuffer == NULL)
return B_NO_MEMORY;
size_t chunkIndex = size_t(offset / kChunkSize);
size_t inChunkOffset = (uint64)offset - (uint64)chunkIndex * kChunkSize;
size_t remainingBytes = size;
while (remainingBytes > 0) {
status_t error = ReadAndDecompressChunk(chunkIndex,
compressedDataBuffer, uncompressedDataBuffer, scratch);
if (error != B_OK)
return error;
size_t toWrite = std::min((size_t)kChunkSize - inChunkOffset,
remainingBytes);
error = output->WriteExactly(
(char*)uncompressedDataBuffer + inChunkOffset, toWrite);
if (error != B_OK)
return error;
remainingBytes -= toWrite;
chunkIndex++;
inChunkOffset = 0;
}
return B_OK;
}
status_t
PackageFileHeapAccessorBase::ReadAndDecompressChunkData(uint64 offset,
size_t compressedSize, size_t uncompressedSize,
void* compressedDataBuffer, void* uncompressedDataBuffer,
iovec* scratchBuffer)
{
if (compressedSize == uncompressedSize)
return ReadFileData(offset, uncompressedDataBuffer, compressedSize);
status_t error = ReadFileData(offset, compressedDataBuffer, compressedSize);
if (error != B_OK)
return error;
iovec compressed = { compressedDataBuffer, compressedSize },
uncompressed = { uncompressedDataBuffer, uncompressedSize };
return DecompressChunkData(compressed, uncompressed, scratchBuffer);
}
status_t
PackageFileHeapAccessorBase::DecompressChunkData(const iovec& compressed,
iovec& uncompressed, iovec* scratchBuffer)
{
const size_t uncompressedSize = uncompressed.iov_len;
status_t error = fDecompressionAlgorithm->algorithm->DecompressBuffer(
compressed, uncompressed, fDecompressionAlgorithm->parameters, scratchBuffer);
if (error != B_OK) {
fErrorOutput->PrintError("Failed to decompress chunk data: %s\n",
strerror(error));
return error;
}
if (uncompressed.iov_len != uncompressedSize) {
fErrorOutput->PrintError("Failed to decompress chunk data: size "
"mismatch\n");
return B_ERROR;
}
return B_OK;
}
status_t
PackageFileHeapAccessorBase::ReadFileData(uint64 offset, void* buffer,
size_t size)
{
status_t error = fFile->ReadAtExactly(fHeapOffset + (off_t)offset, buffer,
size);
if (error != B_OK) {
fErrorOutput->PrintError("ReadFileData(%" B_PRIu64 ", %p, %zu) failed "
"to read data: %s\n", offset, buffer, size, strerror(error));
return error;
}
return B_OK;
}
}
}
}