1/*
2 * Copyright 2013-2014, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <package/hpkg/PackageFileHeapReader.h>
8
9#include <algorithm>
10#include <new>
11
12#include <package/hpkg/ErrorOutput.h>
13#include <package/hpkg/HPKGDefs.h>
14
15#include <AutoDeleter.h>
16#include <package/hpkg/PoolBuffer.h>
17
18
19namespace BPackageKit {
20
21namespace BHPKG {
22
23namespace BPrivate {
24
25
26PackageFileHeapReader::PackageFileHeapReader(BErrorOutput* errorOutput,
27	BPositionIO* file, off_t heapOffset, off_t compressedHeapSize,
28	uint64 uncompressedHeapSize,
29	DecompressionAlgorithmOwner* decompressionAlgorithm)
30	:
31	PackageFileHeapAccessorBase(errorOutput, file, heapOffset,
32		decompressionAlgorithm),
33	fOffsets()
34{
35	fCompressedHeapSize = compressedHeapSize;
36	fUncompressedHeapSize = uncompressedHeapSize;
37}
38
39
40PackageFileHeapReader::~PackageFileHeapReader()
41{
42}
43
44
45status_t
46PackageFileHeapReader::Init()
47{
48	if (fUncompressedHeapSize == 0) {
49		if (fCompressedHeapSize != 0) {
50			fErrorOutput->PrintError(
51				"Invalid total compressed heap size (!= 0, empty heap)\n");
52			return B_BAD_DATA;
53		}
54		return B_OK;
55	}
56
57	// Determine number of chunks and adjust the compressed heap size (subtract
58	// the size of the chunk size array at the end). Note that the size of the
59	// last chunk has not been saved, since its size is implied.
60	ssize_t chunkCount = (fUncompressedHeapSize + kChunkSize - 1) / kChunkSize;
61	if (chunkCount == 0)
62		return B_OK;
63
64	// If no compression is used at all, the chunk size table is omitted. Handle
65	// this case.
66	if (fDecompressionAlgorithm == NULL) {
67		if (fUncompressedHeapSize != fCompressedHeapSize) {
68			fErrorOutput->PrintError(
69				"Compressed and uncompressed heap sizes (%" B_PRIu64 " vs. "
70				"%" B_PRIu64 ") don't match for uncompressed heap.\n",
71				fCompressedHeapSize, fUncompressedHeapSize);
72			return B_BAD_DATA;
73		}
74
75		if (!fOffsets.InitUncompressedChunksOffsets(chunkCount))
76			return B_NO_MEMORY;
77
78		return B_OK;
79	}
80
81	size_t chunkSizeTableSize = (chunkCount - 1) * 2;
82	if (fCompressedHeapSize <= chunkSizeTableSize) {
83		fErrorOutput->PrintError(
84			"Invalid total compressed heap size (%" B_PRIu64 ", "
85			"uncompressed %" B_PRIu64 ")\n", fCompressedHeapSize,
86			fUncompressedHeapSize);
87		return B_BAD_DATA;
88	}
89
90	fCompressedHeapSize -= chunkSizeTableSize;
91
92	// allocate a buffer
93	uint16* buffer = (uint16*)malloc(kChunkSize);
94	if (buffer == NULL)
95		return B_NO_MEMORY;
96	MemoryDeleter bufferDeleter(buffer);
97
98	// read the chunk size array
99	size_t remainingChunks = chunkCount - 1;
100	size_t index = 0;
101	uint64 offset = fCompressedHeapSize;
102	while (remainingChunks > 0) {
103		size_t toRead = std::min(remainingChunks, kChunkSize / 2);
104		status_t error = ReadFileData(offset, buffer, toRead * 2);
105		if (error != B_OK)
106			return error;
107
108		if (!fOffsets.InitChunksOffsets(chunkCount, index, buffer, toRead))
109			return B_NO_MEMORY;
110
111		remainingChunks -= toRead;
112		index += toRead;
113		offset += toRead * 2;
114	}
115
116	// Sanity check: The sum of the chunk sizes must match the compressed heap
117	// size. The information aren't stored redundantly, so we check, if things
118	// look at least plausible.
119	uint64 lastChunkOffset = fOffsets[chunkCount - 1];
120	if (lastChunkOffset >= fCompressedHeapSize
121			|| fCompressedHeapSize - lastChunkOffset > kChunkSize
122			|| fCompressedHeapSize - lastChunkOffset
123				> fUncompressedHeapSize - (chunkCount - 1) * kChunkSize) {
124		fErrorOutput->PrintError(
125			"Invalid total compressed heap size (%" B_PRIu64 ", uncompressed: "
126			"%" B_PRIu64 ", last chunk offset: %" B_PRIu64 ")\n",
127			fCompressedHeapSize, fUncompressedHeapSize, lastChunkOffset);
128		return B_BAD_DATA;
129	}
130
131	return B_OK;
132}
133
134
135PackageFileHeapReader*
136PackageFileHeapReader::Clone() const
137{
138	PackageFileHeapReader* clone = new(std::nothrow) PackageFileHeapReader(
139		fErrorOutput, fFile, fHeapOffset, fCompressedHeapSize,
140		fUncompressedHeapSize, fDecompressionAlgorithm);
141	if (clone == NULL)
142		return NULL;
143
144	ssize_t chunkCount = (fUncompressedHeapSize + kChunkSize - 1) / kChunkSize;
145	if (!clone->fOffsets.Init(chunkCount, fOffsets)) {
146		delete clone;
147		return NULL;
148	}
149
150	return clone;
151}
152
153
154status_t
155PackageFileHeapReader::ReadAndDecompressChunk(size_t chunkIndex,
156	void* compressedDataBuffer, void* uncompressedDataBuffer)
157{
158	uint64 offset = fOffsets[chunkIndex];
159	bool isLastChunk
160		= ((uint64)chunkIndex + 1) * kChunkSize >= fUncompressedHeapSize;
161	size_t compressedSize = isLastChunk
162		? fCompressedHeapSize - offset
163		: fOffsets[chunkIndex + 1] - offset;
164	size_t uncompressedSize = isLastChunk
165		? fUncompressedHeapSize - (uint64)chunkIndex * kChunkSize
166		: kChunkSize;
167
168	return ReadAndDecompressChunkData(offset, compressedSize, uncompressedSize,
169		compressedDataBuffer, uncompressedDataBuffer);
170}
171
172
173}	// namespace BPrivate
174
175}	// namespace BHPKG
176
177}	// namespace BPackageKit
178