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