xref: /haiku/src/kits/package/hpkg/PackageFileHeapReader.cpp (revision a3e794ae459fec76826407f8ba8c94cd3535f128)
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 
19 namespace BPackageKit {
20 
21 namespace BHPKG {
22 
23 namespace BPrivate {
24 
25 
26 PackageFileHeapReader::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 
40 PackageFileHeapReader::~PackageFileHeapReader()
41 {
42 }
43 
44 
45 status_t
46 PackageFileHeapReader::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 
135 PackageFileHeapReader*
136 PackageFileHeapReader::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 
154 status_t
155 PackageFileHeapReader::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