xref: /haiku/src/kits/package/hpkg/PackageFileHeapAccessorBase.cpp (revision e81a954787e50e56a7f06f72705b7859b6ab06d1)
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/PackageFileHeapAccessorBase.h>
8 
9 #include <stdlib.h>
10 #include <string.h>
11 
12 #include <algorithm>
13 #include <new>
14 
15 #include <ByteOrder.h>
16 #include <DataIO.h>
17 #include <package/hpkg/ErrorOutput.h>
18 
19 #include <AutoDeleter.h>
20 #include <CompressionAlgorithm.h>
21 
22 
23 namespace BPackageKit {
24 
25 namespace BHPKG {
26 
27 namespace BPrivate {
28 
29 
30 // #pragma mark - OffsetArray
31 
32 
33 PackageFileHeapAccessorBase::OffsetArray::OffsetArray()
34 	:
35 	fOffsets(NULL)
36 {
37 }
38 
39 
40 PackageFileHeapAccessorBase::OffsetArray::~OffsetArray()
41 {
42 	delete[] fOffsets;
43 }
44 
45 
46 bool
47 PackageFileHeapAccessorBase::OffsetArray::InitUncompressedChunksOffsets(
48 	size_t totalChunkCount)
49 {
50 	if (totalChunkCount <= 1)
51 		return true;
52 
53 	const size_t max32BitChunks = (uint64(1) << 32) / kChunkSize;
54 	size_t actual32BitChunks = totalChunkCount;
55 	if (totalChunkCount - 1 > max32BitChunks) {
56 		actual32BitChunks = max32BitChunks;
57 		fOffsets = _AllocateOffsetArray(totalChunkCount, max32BitChunks);
58 	} else
59 		fOffsets = _AllocateOffsetArray(totalChunkCount, 0);
60 
61 	if (fOffsets == NULL)
62 		return false;
63 
64 	{
65 		uint32 offset = kChunkSize;
66 		for (size_t i = 1; i < actual32BitChunks; i++, offset += kChunkSize)
67 			fOffsets[i] = offset;
68 
69 	}
70 
71 	if (actual32BitChunks < totalChunkCount) {
72 		uint64 offset = actual32BitChunks * kChunkSize;
73 		uint32* offsets = fOffsets + actual32BitChunks;
74 		for (size_t i = actual32BitChunks; i < totalChunkCount;
75 				i++, offset += kChunkSize) {
76 			*offsets++ = (uint32)offset;
77 			*offsets++ = uint32(offset >> 32);
78 		}
79 	}
80 
81 	return true;
82 }
83 
84 
85 bool
86 PackageFileHeapAccessorBase::OffsetArray::InitChunksOffsets(
87 	size_t totalChunkCount, size_t baseIndex, const uint16* chunkSizes,
88 	size_t chunkCount)
89 {
90 	if (totalChunkCount <= 1)
91 		return true;
92 
93 	if (fOffsets == NULL) {
94 		fOffsets = _AllocateOffsetArray(totalChunkCount, totalChunkCount);
95 		if (fOffsets == NULL)
96 			return false;
97 	}
98 
99 	uint64 offset = (*this)[baseIndex];
100 	for (size_t i = 0; i < chunkCount; i++) {
101 		offset += (uint64)B_BENDIAN_TO_HOST_INT16(chunkSizes[i]) + 1;
102 			// the stored value is chunkSize - 1
103 		size_t index = baseIndex + i + 1;
104 			// (baseIndex + i) is the index of the chunk whose size is stored in
105 			// chunkSizes[i]. We compute the offset of the following element
106 			// which we store at index (baseIndex + i + 1).
107 
108 		if (offset <= ~(uint32)0) {
109 			fOffsets[index] = (uint32)offset;
110 		} else {
111 			if (fOffsets[0] == 0) {
112 				// Not scaled to allow for 64 bit offsets yet. Do that.
113 				uint32* newOffsets = _AllocateOffsetArray(totalChunkCount,
114 					index);
115 				if (newOffsets == NULL)
116 					return false;
117 
118 				fOffsets[0] = index;
119 				memcpy(newOffsets, fOffsets, sizeof(newOffsets[0]) * index);
120 
121 				delete[] fOffsets;
122 				fOffsets = newOffsets;
123 			}
124 
125 			index += index - fOffsets[0];
126 			fOffsets[index] = (uint32)offset;
127 			fOffsets[index + 1] = uint32(offset >> 32);
128 		}
129 	}
130 
131 	return true;
132 }
133 
134 
135 bool
136 PackageFileHeapAccessorBase::OffsetArray::Init(size_t totalChunkCount,
137 	const OffsetArray& other)
138 {
139 	if (other.fOffsets == NULL)
140 		return true;
141 
142 	size_t elementCount = other.fOffsets[0] == 0
143 		? totalChunkCount
144 		: 2 * totalChunkCount - other.fOffsets[0];
145 
146 	fOffsets = new(std::nothrow) uint32[elementCount];
147 	if (fOffsets == NULL)
148 		return false;
149 
150 	memcpy(fOffsets, other.fOffsets, elementCount * sizeof(fOffsets[0]));
151 	return true;
152 }
153 
154 
155 /*static*/ uint32*
156 PackageFileHeapAccessorBase::OffsetArray::_AllocateOffsetArray(
157 	size_t totalChunkCount, size_t offset32BitChunkCount)
158 {
159 	uint32* offsets = new(std::nothrow) uint32[
160 		2 * totalChunkCount - offset32BitChunkCount];
161 
162 	if (offsets != NULL) {
163 		offsets[0] = offset32BitChunkCount == totalChunkCount
164 			? 0 : offset32BitChunkCount;
165 			// 0 means that all offsets are 32 bit. Otherwise it's the index of
166 			// the first 64 bit offset.
167 	}
168 
169 	return offsets;
170 }
171 
172 
173 // #pragma mark - PackageFileHeapAccessorBase
174 
175 
176 PackageFileHeapAccessorBase::PackageFileHeapAccessorBase(
177 	BErrorOutput* errorOutput, BPositionIO* file, off_t heapOffset,
178 	DecompressionAlgorithmOwner* decompressionAlgorithm)
179 	:
180 	fErrorOutput(errorOutput),
181 	fFile(file),
182 	fHeapOffset(heapOffset),
183 	fCompressedHeapSize(0),
184 	fUncompressedHeapSize(0),
185 	fDecompressionAlgorithm(decompressionAlgorithm)
186 {
187 	if (fDecompressionAlgorithm != NULL)
188 		fDecompressionAlgorithm->AcquireReference();
189 }
190 
191 
192 PackageFileHeapAccessorBase::~PackageFileHeapAccessorBase()
193 {
194 	if (fDecompressionAlgorithm != NULL)
195 		fDecompressionAlgorithm->ReleaseReference();
196 }
197 
198 
199 status_t
200 PackageFileHeapAccessorBase::ReadDataToOutput(off_t offset, size_t size,
201 	BDataIO* output)
202 {
203 	if (size == 0)
204 		return B_OK;
205 
206 	if (offset < 0 || (uint64)offset > fUncompressedHeapSize
207 		|| size > fUncompressedHeapSize - offset) {
208 		return B_BAD_VALUE;
209 	}
210 
211 	// allocate buffers for compressed and uncompressed data
212 	uint16* compressedDataBuffer = (uint16*)malloc(kChunkSize);
213 	uint16* uncompressedDataBuffer = (uint16*)malloc(kChunkSize);
214 	MemoryDeleter compressedDataBufferDeleter(compressedDataBuffer);
215 	MemoryDeleter uncompressedDataBufferDeleter(uncompressedDataBuffer);
216 	if (compressedDataBuffer == NULL || uncompressedDataBuffer == NULL)
217 		return B_NO_MEMORY;
218 
219 	// read the data
220 	size_t chunkIndex = size_t(offset / kChunkSize);
221 	size_t inChunkOffset = (uint64)offset - (uint64)chunkIndex * kChunkSize;
222 	size_t remainingBytes = size;
223 
224 	while (remainingBytes > 0) {
225 		status_t error = ReadAndDecompressChunk(chunkIndex,
226 			compressedDataBuffer, uncompressedDataBuffer);
227 		if (error != B_OK)
228 			return error;
229 
230 		size_t toWrite = std::min((size_t)kChunkSize - inChunkOffset,
231 			remainingBytes);
232 			// The last chunk may be shorter than kChunkSize, but since
233 			// size (and thus remainingSize) had been clamped, that doesn't
234 			// harm.
235 		error = output->WriteExactly(
236 			(char*)uncompressedDataBuffer + inChunkOffset, toWrite);
237 		if (error != B_OK)
238 			return error;
239 
240 		remainingBytes -= toWrite;
241 		chunkIndex++;
242 		inChunkOffset = 0;
243 	}
244 
245 	return B_OK;
246 }
247 
248 
249 status_t
250 PackageFileHeapAccessorBase::ReadAndDecompressChunkData(uint64 offset,
251 	size_t compressedSize, size_t uncompressedSize, void* compressedDataBuffer,
252 	void* uncompressedDataBuffer)
253 {
254 	// if uncompressed, read directly into the uncompressed data buffer
255 	if (compressedSize == uncompressedSize)
256 		return ReadFileData(offset, uncompressedDataBuffer, compressedSize);
257 
258 	// otherwise read into the other buffer and decompress
259 	status_t error = ReadFileData(offset, compressedDataBuffer, compressedSize);
260 	if (error != B_OK)
261 		return error;
262 
263 	return DecompressChunkData(compressedDataBuffer, compressedSize,
264 		uncompressedDataBuffer, uncompressedSize);
265 }
266 
267 
268 status_t
269 PackageFileHeapAccessorBase::DecompressChunkData(void* compressedDataBuffer,
270 	size_t compressedSize, void* uncompressedDataBuffer,
271 	size_t uncompressedSize)
272 {
273 	size_t actualSize;
274 	status_t error = fDecompressionAlgorithm->algorithm->DecompressBuffer(
275 		compressedDataBuffer, compressedSize, uncompressedDataBuffer,
276 		uncompressedSize, actualSize, fDecompressionAlgorithm->parameters);
277 	if (error != B_OK) {
278 		fErrorOutput->PrintError("Failed to decompress chunk data: %s\n",
279 			strerror(error));
280 		return error;
281 	}
282 
283 	if (actualSize != uncompressedSize) {
284 		fErrorOutput->PrintError("Failed to decompress chunk data: size "
285 			"mismatch\n");
286 		return B_ERROR;
287 	}
288 
289 	return B_OK;
290 }
291 
292 
293 status_t
294 PackageFileHeapAccessorBase::ReadFileData(uint64 offset, void* buffer,
295 	size_t size)
296 {
297 	status_t error = fFile->ReadAtExactly(fHeapOffset + (off_t)offset, buffer,
298 		size);
299 	if (error != B_OK) {
300 		fErrorOutput->PrintError("ReadFileData(%" B_PRIu64 ", %p, %zu) failed "
301 			"to read data: %s\n", offset, buffer, size, strerror(error));
302 		return error;
303 	}
304 
305 	return B_OK;
306 }
307 
308 
309 }	// namespace BPrivate
310 
311 }	// namespace BHPKG
312 
313 }	// namespace BPackageKit
314