11f633814SIngo Weinhold/*
232832cbeSIngo Weinhold * Copyright 2013-2014, Ingo Weinhold, ingo_weinhold@gmx.de.
31f633814SIngo Weinhold * Distributed under the terms of the MIT License.
41f633814SIngo Weinhold */
51f633814SIngo Weinhold
61f633814SIngo Weinhold
71f633814SIngo Weinhold#include <package/hpkg/PackageFileHeapAccessorBase.h>
81f633814SIngo Weinhold
91f633814SIngo Weinhold#include <stdlib.h>
101f633814SIngo Weinhold#include <string.h>
111f633814SIngo Weinhold
121f633814SIngo Weinhold#include <algorithm>
131f633814SIngo Weinhold#include <new>
141f633814SIngo Weinhold
15e2f30519SIngo Weinhold#include <ByteOrder.h>
1632832cbeSIngo Weinhold#include <DataIO.h>
171f633814SIngo Weinhold#include <package/hpkg/ErrorOutput.h>
181f633814SIngo Weinhold
191f633814SIngo Weinhold#include <AutoDeleter.h>
20b3263ad3SIngo Weinhold#include <CompressionAlgorithm.h>
211f633814SIngo Weinhold
221f633814SIngo Weinhold
231f633814SIngo Weinholdnamespace BPackageKit {
241f633814SIngo Weinhold
251f633814SIngo Weinholdnamespace BHPKG {
261f633814SIngo Weinhold
271f633814SIngo Weinholdnamespace BPrivate {
281f633814SIngo Weinhold
291f633814SIngo Weinhold
301f633814SIngo Weinhold// #pragma mark - OffsetArray
311f633814SIngo Weinhold
321f633814SIngo Weinhold
331f633814SIngo WeinholdPackageFileHeapAccessorBase::OffsetArray::OffsetArray()
341f633814SIngo Weinhold	:
351f633814SIngo Weinhold	fOffsets(NULL)
361f633814SIngo Weinhold{
371f633814SIngo Weinhold}
381f633814SIngo Weinhold
391f633814SIngo Weinhold
401f633814SIngo WeinholdPackageFileHeapAccessorBase::OffsetArray::~OffsetArray()
411f633814SIngo Weinhold{
421f633814SIngo Weinhold	delete[] fOffsets;
431f633814SIngo Weinhold}
441f633814SIngo Weinhold
451f633814SIngo Weinhold
468f5130edSIngo Weinholdbool
478f5130edSIngo WeinholdPackageFileHeapAccessorBase::OffsetArray::InitUncompressedChunksOffsets(
488f5130edSIngo Weinhold	size_t totalChunkCount)
498f5130edSIngo Weinhold{
508f5130edSIngo Weinhold	if (totalChunkCount <= 1)
518f5130edSIngo Weinhold		return true;
528f5130edSIngo Weinhold
538f5130edSIngo Weinhold	const size_t max32BitChunks = (uint64(1) << 32) / kChunkSize;
548f5130edSIngo Weinhold	size_t actual32BitChunks = totalChunkCount;
558f5130edSIngo Weinhold	if (totalChunkCount - 1 > max32BitChunks) {
568f5130edSIngo Weinhold		actual32BitChunks = max32BitChunks;
578f5130edSIngo Weinhold		fOffsets = _AllocateOffsetArray(totalChunkCount, max32BitChunks);
588f5130edSIngo Weinhold	} else
598f5130edSIngo Weinhold		fOffsets = _AllocateOffsetArray(totalChunkCount, 0);
608f5130edSIngo Weinhold
618f5130edSIngo Weinhold	if (fOffsets == NULL)
628f5130edSIngo Weinhold		return false;
638f5130edSIngo Weinhold
648f5130edSIngo Weinhold	{
658f5130edSIngo Weinhold		uint32 offset = kChunkSize;
668f5130edSIngo Weinhold		for (size_t i = 1; i < actual32BitChunks; i++, offset += kChunkSize)
678f5130edSIngo Weinhold			fOffsets[i] = offset;
688f5130edSIngo Weinhold
698f5130edSIngo Weinhold	}
708f5130edSIngo Weinhold
718f5130edSIngo Weinhold	if (actual32BitChunks < totalChunkCount) {
728f5130edSIngo Weinhold		uint64 offset = actual32BitChunks * kChunkSize;
738f5130edSIngo Weinhold		uint32* offsets = fOffsets + actual32BitChunks;
748f5130edSIngo Weinhold		for (size_t i = actual32BitChunks; i < totalChunkCount;
758f5130edSIngo Weinhold				i++, offset += kChunkSize) {
768f5130edSIngo Weinhold			*offsets++ = (uint32)offset;
778f5130edSIngo Weinhold			*offsets++ = uint32(offset >> 32);
788f5130edSIngo Weinhold		}
798f5130edSIngo Weinhold	}
808f5130edSIngo Weinhold
818f5130edSIngo Weinhold	return true;
828f5130edSIngo Weinhold}
838f5130edSIngo Weinhold
848f5130edSIngo Weinhold
851f633814SIngo Weinholdbool
861f633814SIngo WeinholdPackageFileHeapAccessorBase::OffsetArray::InitChunksOffsets(
871f633814SIngo Weinhold	size_t totalChunkCount, size_t baseIndex, const uint16* chunkSizes,
881f633814SIngo Weinhold	size_t chunkCount)
891f633814SIngo Weinhold{
901f633814SIngo Weinhold	if (totalChunkCount <= 1)
911f633814SIngo Weinhold		return true;
921f633814SIngo Weinhold
931f633814SIngo Weinhold	if (fOffsets == NULL) {
948f5130edSIngo Weinhold		fOffsets = _AllocateOffsetArray(totalChunkCount, totalChunkCount);
951f633814SIngo Weinhold		if (fOffsets == NULL)
961f633814SIngo Weinhold			return false;
971f633814SIngo Weinhold	}
981f633814SIngo Weinhold
991f633814SIngo Weinhold	uint64 offset = (*this)[baseIndex];
1001f633814SIngo Weinhold	for (size_t i = 0; i < chunkCount; i++) {
101e2f30519SIngo Weinhold		offset += (uint64)B_BENDIAN_TO_HOST_INT16(chunkSizes[i]) + 1;
1021f633814SIngo Weinhold			// the stored value is chunkSize - 1
1031f633814SIngo Weinhold		size_t index = baseIndex + i + 1;
1041f633814SIngo Weinhold			// (baseIndex + i) is the index of the chunk whose size is stored in
1051f633814SIngo Weinhold			// chunkSizes[i]. We compute the offset of the following element
1061f633814SIngo Weinhold			// which we store at index (baseIndex + i + 1).
1071f633814SIngo Weinhold
1081f633814SIngo Weinhold		if (offset <= ~(uint32)0) {
1091f633814SIngo Weinhold			fOffsets[index] = (uint32)offset;
1101f633814SIngo Weinhold		} else {
1111f633814SIngo Weinhold			if (fOffsets[0] == 0) {
1121f633814SIngo Weinhold				// Not scaled to allow for 64 bit offsets yet. Do that.
1138f5130edSIngo Weinhold				uint32* newOffsets = _AllocateOffsetArray(totalChunkCount,
1148f5130edSIngo Weinhold					index);
1151f633814SIngo Weinhold				if (newOffsets == NULL)
1161f633814SIngo Weinhold					return false;
1171f633814SIngo Weinhold
1188f5130edSIngo Weinhold				fOffsets[0] = index;
1198f5130edSIngo Weinhold				memcpy(newOffsets, fOffsets, sizeof(newOffsets[0]) * index);
1201f633814SIngo Weinhold
1211f633814SIngo Weinhold				delete[] fOffsets;
1221f633814SIngo Weinhold				fOffsets = newOffsets;
1231f633814SIngo Weinhold			}
1241f633814SIngo Weinhold
1251f633814SIngo Weinhold			index += index - fOffsets[0];
1261f633814SIngo Weinhold			fOffsets[index] = (uint32)offset;
1271f633814SIngo Weinhold			fOffsets[index + 1] = uint32(offset >> 32);
1281f633814SIngo Weinhold		}
1291f633814SIngo Weinhold	}
1301f633814SIngo Weinhold
1311f633814SIngo Weinhold	return true;
1321f633814SIngo Weinhold}
1331f633814SIngo Weinhold
1341f633814SIngo Weinhold
1351f633814SIngo Weinholdbool
1361f633814SIngo WeinholdPackageFileHeapAccessorBase::OffsetArray::Init(size_t totalChunkCount,
1371f633814SIngo Weinhold	const OffsetArray& other)
1381f633814SIngo Weinhold{
1391f633814SIngo Weinhold	if (other.fOffsets == NULL)
1401f633814SIngo Weinhold		return true;
1411f633814SIngo Weinhold
1421f633814SIngo Weinhold	size_t elementCount = other.fOffsets[0] == 0
1431f633814SIngo Weinhold		? totalChunkCount
1441f633814SIngo Weinhold		: 2 * totalChunkCount - other.fOffsets[0];
1451f633814SIngo Weinhold
1461f633814SIngo Weinhold	fOffsets = new(std::nothrow) uint32[elementCount];
1471f633814SIngo Weinhold	if (fOffsets == NULL)
1481f633814SIngo Weinhold		return false;
1491f633814SIngo Weinhold
1501f633814SIngo Weinhold	memcpy(fOffsets, other.fOffsets, elementCount * sizeof(fOffsets[0]));
1511f633814SIngo Weinhold	return true;
1521f633814SIngo Weinhold}
1531f633814SIngo Weinhold
1541f633814SIngo Weinhold
1558f5130edSIngo Weinhold/*static*/ uint32*
1568f5130edSIngo WeinholdPackageFileHeapAccessorBase::OffsetArray::_AllocateOffsetArray(
1578f5130edSIngo Weinhold	size_t totalChunkCount, size_t offset32BitChunkCount)
1588f5130edSIngo Weinhold{
1598f5130edSIngo Weinhold	uint32* offsets = new(std::nothrow) uint32[
1608f5130edSIngo Weinhold		2 * totalChunkCount - offset32BitChunkCount];
1618f5130edSIngo Weinhold
1628f5130edSIngo Weinhold	if (offsets != NULL) {
1638f5130edSIngo Weinhold		offsets[0] = offset32BitChunkCount == totalChunkCount
1648f5130edSIngo Weinhold			? 0 : offset32BitChunkCount;
1658f5130edSIngo Weinhold			// 0 means that all offsets are 32 bit. Otherwise it's the index of
1668f5130edSIngo Weinhold			// the first 64 bit offset.
1678f5130edSIngo Weinhold	}
1688f5130edSIngo Weinhold
1698f5130edSIngo Weinhold	return offsets;
1708f5130edSIngo Weinhold}
1718f5130edSIngo Weinhold
1728f5130edSIngo Weinhold
1731f633814SIngo Weinhold// #pragma mark - PackageFileHeapAccessorBase
1741f633814SIngo Weinhold
1751f633814SIngo Weinhold
1761f633814SIngo WeinholdPackageFileHeapAccessorBase::PackageFileHeapAccessorBase(
177e527b796SIngo Weinhold	BErrorOutput* errorOutput, BPositionIO* file, off_t heapOffset,
178b3263ad3SIngo Weinhold	DecompressionAlgorithmOwner* decompressionAlgorithm)
1791f633814SIngo Weinhold	:
1801f633814SIngo Weinhold	fErrorOutput(errorOutput),
181e527b796SIngo Weinhold	fFile(file),
1821f633814SIngo Weinhold	fHeapOffset(heapOffset),
1831f633814SIngo Weinhold	fCompressedHeapSize(0),
184b3263ad3SIngo Weinhold	fUncompressedHeapSize(0),
185b3263ad3SIngo Weinhold	fDecompressionAlgorithm(decompressionAlgorithm)
1861f633814SIngo Weinhold{
187b3263ad3SIngo Weinhold	if (fDecompressionAlgorithm != NULL)
188b3263ad3SIngo Weinhold		fDecompressionAlgorithm->AcquireReference();
1891f633814SIngo Weinhold}
1901f633814SIngo Weinhold
1911f633814SIngo Weinhold
1921f633814SIngo WeinholdPackageFileHeapAccessorBase::~PackageFileHeapAccessorBase()
1931f633814SIngo Weinhold{
194b3263ad3SIngo Weinhold	if (fDecompressionAlgorithm != NULL)
195b3263ad3SIngo Weinhold		fDecompressionAlgorithm->ReleaseReference();
1961f633814SIngo Weinhold}
1971f633814SIngo Weinhold
1981f633814SIngo Weinhold
1991f633814SIngo Weinholdstatus_t
2001f633814SIngo WeinholdPackageFileHeapAccessorBase::ReadDataToOutput(off_t offset, size_t size,
20132832cbeSIngo Weinhold	BDataIO* output)
2021f633814SIngo Weinhold{
2031f633814SIngo Weinhold	if (size == 0)
2041f633814SIngo Weinhold		return B_OK;
2051f633814SIngo Weinhold
2061f633814SIngo Weinhold	if (offset < 0 || (uint64)offset > fUncompressedHeapSize
2071f633814SIngo Weinhold		|| size > fUncompressedHeapSize - offset) {
2081f633814SIngo Weinhold		return B_BAD_VALUE;
2091f633814SIngo Weinhold	}
2101f633814SIngo Weinhold
2111f633814SIngo Weinhold	// allocate buffers for compressed and uncompressed data
2121f633814SIngo Weinhold	uint16* compressedDataBuffer = (uint16*)malloc(kChunkSize);
2131f633814SIngo Weinhold	uint16* uncompressedDataBuffer = (uint16*)malloc(kChunkSize);
2141f633814SIngo Weinhold	MemoryDeleter compressedDataBufferDeleter(compressedDataBuffer);
2151f633814SIngo Weinhold	MemoryDeleter uncompressedDataBufferDeleter(uncompressedDataBuffer);
2161f633814SIngo Weinhold	if (compressedDataBuffer == NULL || uncompressedDataBuffer == NULL)
2171f633814SIngo Weinhold		return B_NO_MEMORY;
2181f633814SIngo Weinhold
2191f633814SIngo Weinhold	// read the data
2201f633814SIngo Weinhold	size_t chunkIndex = size_t(offset / kChunkSize);
2211f633814SIngo Weinhold	size_t inChunkOffset = (uint64)offset - (uint64)chunkIndex * kChunkSize;
2221f633814SIngo Weinhold	size_t remainingBytes = size;
2231f633814SIngo Weinhold
2241f633814SIngo Weinhold	while (remainingBytes > 0) {
2251f633814SIngo Weinhold		status_t error = ReadAndDecompressChunk(chunkIndex,
2261f633814SIngo Weinhold			compressedDataBuffer, uncompressedDataBuffer);
2271f633814SIngo Weinhold		if (error != B_OK)
2281f633814SIngo Weinhold			return error;
2291f633814SIngo Weinhold
2301f633814SIngo Weinhold		size_t toWrite = std::min((size_t)kChunkSize - inChunkOffset,
2311f633814SIngo Weinhold			remainingBytes);
2321f633814SIngo Weinhold			// The last chunk may be shorter than kChunkSize, but since
2331f633814SIngo Weinhold			// size (and thus remainingSize) had been clamped, that doesn't
2341f633814SIngo Weinhold			// harm.
23532832cbeSIngo Weinhold		error = output->WriteExactly(
236d2d1af83SIngo Weinhold			(char*)uncompressedDataBuffer + inChunkOffset, toWrite);
2374c235c74SIngo Weinhold		if (error != B_OK)
2381f633814SIngo Weinhold			return error;
2391f633814SIngo Weinhold
2401f633814SIngo Weinhold		remainingBytes -= toWrite;
2411f633814SIngo Weinhold		chunkIndex++;
2421f633814SIngo Weinhold		inChunkOffset = 0;
2431f633814SIngo Weinhold	}
2441f633814SIngo Weinhold
2451f633814SIngo Weinhold	return B_OK;
2461f633814SIngo Weinhold}
2471f633814SIngo Weinhold
2481f633814SIngo Weinhold
2491f633814SIngo Weinholdstatus_t
2501f633814SIngo WeinholdPackageFileHeapAccessorBase::ReadAndDecompressChunkData(uint64 offset,
2511f633814SIngo Weinhold	size_t compressedSize, size_t uncompressedSize, void* compressedDataBuffer,
2521f633814SIngo Weinhold	void* uncompressedDataBuffer)
2531f633814SIngo Weinhold{
2541f633814SIngo Weinhold	// if uncompressed, read directly into the uncompressed data buffer
2551f633814SIngo Weinhold	if (compressedSize == uncompressedSize)
2561f633814SIngo Weinhold		return ReadFileData(offset, uncompressedDataBuffer, compressedSize);
2571f633814SIngo Weinhold
2581f633814SIngo Weinhold	// otherwise read into the other buffer and decompress
2591f633814SIngo Weinhold	status_t error = ReadFileData(offset, compressedDataBuffer, compressedSize);
2601f633814SIngo Weinhold	if (error != B_OK)
2611f633814SIngo Weinhold		return error;
2621f633814SIngo Weinhold
263520a7a76SIngo Weinhold	return DecompressChunkData(compressedDataBuffer, compressedSize,
264520a7a76SIngo Weinhold		uncompressedDataBuffer, uncompressedSize);
265520a7a76SIngo Weinhold}
266520a7a76SIngo Weinhold
267520a7a76SIngo Weinhold
268520a7a76SIngo Weinholdstatus_t
269520a7a76SIngo WeinholdPackageFileHeapAccessorBase::DecompressChunkData(void* compressedDataBuffer,
270520a7a76SIngo Weinhold	size_t compressedSize, void* uncompressedDataBuffer,
271520a7a76SIngo Weinhold	size_t uncompressedSize)
272520a7a76SIngo Weinhold{
2731f633814SIngo Weinhold	size_t actualSize;
274b3263ad3SIngo Weinhold	status_t error = fDecompressionAlgorithm->algorithm->DecompressBuffer(
275520a7a76SIngo Weinhold		compressedDataBuffer, compressedSize, uncompressedDataBuffer,
276b3263ad3SIngo Weinhold		uncompressedSize, actualSize, fDecompressionAlgorithm->parameters);
2771f633814SIngo Weinhold	if (error != B_OK) {
278520a7a76SIngo Weinhold		fErrorOutput->PrintError("Failed to decompress chunk data: %s\n",
2791f633814SIngo Weinhold			strerror(error));
2801f633814SIngo Weinhold		return error;
2811f633814SIngo Weinhold	}
2821f633814SIngo Weinhold
2831f633814SIngo Weinhold	if (actualSize != uncompressedSize) {
284520a7a76SIngo Weinhold		fErrorOutput->PrintError("Failed to decompress chunk data: size "
285520a7a76SIngo Weinhold			"mismatch\n");
2861f633814SIngo Weinhold		return B_ERROR;
2871f633814SIngo Weinhold	}
2881f633814SIngo Weinhold
2891f633814SIngo Weinhold	return B_OK;
2901f633814SIngo Weinhold}
2911f633814SIngo Weinhold
2921f633814SIngo Weinhold
2931f633814SIngo Weinholdstatus_t
2941f633814SIngo WeinholdPackageFileHeapAccessorBase::ReadFileData(uint64 offset, void* buffer,
2951f633814SIngo Weinhold	size_t size)
2961f633814SIngo Weinhold{
297e527b796SIngo Weinhold	status_t error = fFile->ReadAtExactly(fHeapOffset + (off_t)offset, buffer,
298e527b796SIngo Weinhold		size);
299e527b796SIngo Weinhold	if (error != B_OK) {
300c3bd329fSIngo Weinhold		fErrorOutput->PrintError("ReadFileData(%" B_PRIu64 ", %p, %zu) failed "
301e527b796SIngo Weinhold			"to read data: %s\n", offset, buffer, size, strerror(error));
302e527b796SIngo Weinhold		return error;
3031f633814SIngo Weinhold	}
3041f633814SIngo Weinhold
3051f633814SIngo Weinhold	return B_OK;
3061f633814SIngo Weinhold}
3071f633814SIngo Weinhold
3081f633814SIngo Weinhold
3091f633814SIngo Weinhold}	// namespace BPrivate
3101f633814SIngo Weinhold
3111f633814SIngo Weinhold}	// namespace BHPKG
3121f633814SIngo Weinhold
3131f633814SIngo Weinhold}	// namespace BPackageKit
314