/*
* Copyright 2013-2014, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include <package/hpkg/PackageFileHeapReader.h>
#include <algorithm>
#include <new>
#include <package/hpkg/ErrorOutput.h>
#include <package/hpkg/HPKGDefs.h>
#include <AutoDeleter.h>
#include <package/hpkg/PoolBuffer.h>
namespace BPackageKit {
namespace BHPKG {
namespace BPrivate {
PackageFileHeapReader::PackageFileHeapReader(BErrorOutput* errorOutput,
BPositionIO* file, off_t heapOffset, off_t compressedHeapSize,
uint64 uncompressedHeapSize,
DecompressionAlgorithmOwner* decompressionAlgorithm)
:
PackageFileHeapAccessorBase(errorOutput, file, heapOffset,
decompressionAlgorithm),
fOffsets()
{
fCompressedHeapSize = compressedHeapSize;
fUncompressedHeapSize = uncompressedHeapSize;
}
PackageFileHeapReader::~PackageFileHeapReader()
{
}
status_t
PackageFileHeapReader::Init()
{
if (fUncompressedHeapSize == 0) {
if (fCompressedHeapSize != 0) {
fErrorOutput->PrintError(
"Invalid total compressed heap size (!= 0, empty heap)\n");
return B_BAD_DATA;
}
return B_OK;
}
// Determine number of chunks and adjust the compressed heap size (subtract
// the size of the chunk size array at the end). Note that the size of the
// last chunk has not been saved, since its size is implied.
ssize_t chunkCount = (fUncompressedHeapSize + kChunkSize - 1) / kChunkSize;
if (chunkCount == 0)
return B_OK;
// If no compression is used at all, the chunk size table is omitted. Handle
// this case.
if (fDecompressionAlgorithm == NULL) {
if (fUncompressedHeapSize != fCompressedHeapSize) {
fErrorOutput->PrintError(
"Compressed and uncompressed heap sizes (%" B_PRIu64 " vs. "
"%" B_PRIu64 ") don't match for uncompressed heap.\n",
fCompressedHeapSize, fUncompressedHeapSize);
return B_BAD_DATA;
}
if (!fOffsets.InitUncompressedChunksOffsets(chunkCount))
return B_NO_MEMORY;
return B_OK;
}
size_t chunkSizeTableSize = (chunkCount - 1) * 2;
if (fCompressedHeapSize <= chunkSizeTableSize) {
fErrorOutput->PrintError(
"Invalid total compressed heap size (%" B_PRIu64 ", "
"uncompressed %" B_PRIu64 ")\n", fCompressedHeapSize,
fUncompressedHeapSize);
return B_BAD_DATA;
}
fCompressedHeapSize -= chunkSizeTableSize;
// allocate a buffer
uint16* buffer = (uint16*)malloc(kChunkSize);
if (buffer == NULL)
return B_NO_MEMORY;
MemoryDeleter bufferDeleter(buffer);
// read the chunk size array
size_t remainingChunks = chunkCount - 1;
size_t index = 0;
uint64 offset = fCompressedHeapSize;
while (remainingChunks > 0) {
size_t toRead = std::min(remainingChunks, kChunkSize / 2);
status_t error = ReadFileData(offset, buffer, toRead * 2);
if (error != B_OK)
return error;
if (!fOffsets.InitChunksOffsets(chunkCount, index, buffer, toRead))
return B_NO_MEMORY;
remainingChunks -= toRead;
index += toRead;
offset += toRead * 2;
}
// Sanity check: The sum of the chunk sizes must match the compressed heap
// size. The information aren't stored redundantly, so we check, if things
// look at least plausible.
uint64 lastChunkOffset = fOffsets[chunkCount - 1];
if (lastChunkOffset >= fCompressedHeapSize
|| fCompressedHeapSize - lastChunkOffset > kChunkSize
|| fCompressedHeapSize - lastChunkOffset
> fUncompressedHeapSize - (chunkCount - 1) * kChunkSize) {
fErrorOutput->PrintError(
"Invalid total compressed heap size (%" B_PRIu64 ", uncompressed: "
"%" B_PRIu64 ", last chunk offset: %" B_PRIu64 ")\n",
fCompressedHeapSize, fUncompressedHeapSize, lastChunkOffset);
return B_BAD_DATA;
}
return B_OK;
}
PackageFileHeapReader*
PackageFileHeapReader::Clone() const
{
PackageFileHeapReader* clone = new(std::nothrow) PackageFileHeapReader(
fErrorOutput, fFile, fHeapOffset, fCompressedHeapSize,
fUncompressedHeapSize, fDecompressionAlgorithm);
if (clone == NULL)
return NULL;
ssize_t chunkCount = (fUncompressedHeapSize + kChunkSize - 1) / kChunkSize;
if (!clone->fOffsets.Init(chunkCount, fOffsets)) {
delete clone;
return NULL;
}
return clone;
}
status_t
PackageFileHeapReader::ReadAndDecompressChunk(size_t chunkIndex,
void* compressedDataBuffer, void* uncompressedDataBuffer)
{
uint64 offset = fOffsets[chunkIndex];
bool isLastChunk
= uint64(chunkIndex + 1) * kChunkSize >= fUncompressedHeapSize;
size_t compressedSize = isLastChunk
? fCompressedHeapSize - offset
: fOffsets[chunkIndex + 1] - offset;
size_t uncompressedSize = isLastChunk
? fUncompressedHeapSize - (uint64)chunkIndex * kChunkSize
: kChunkSize;
return ReadAndDecompressChunkData(offset, compressedSize, uncompressedSize,
compressedDataBuffer, uncompressedDataBuffer);
}
} // namespace BPrivate
} // namespace BHPKG
} // namespace BPackageKit
↑ V1028 Possible overflow. Consider casting operands of the 'chunkIndex + 1' operator to the 'uint64' type, not the result.