PackageFileHeapAccessorBase.cpp revision 520a7a76c7c6528f654a4e9a6d0d291df63e7532
1/*
2 * Copyright 2013, 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 <errno.h>
10#include <stdlib.h>
11#include <string.h>
12
13#include <algorithm>
14#include <new>
15
16#include <package/hpkg/DataOutput.h>
17#include <package/hpkg/ErrorOutput.h>
18
19#include <AutoDeleter.h>
20#include <package/hpkg/ZlibDecompressor.h>
21
22
23namespace BPackageKit {
24
25namespace BHPKG {
26
27namespace BPrivate {
28
29
30// #pragma mark - OffsetArray
31
32
33PackageFileHeapAccessorBase::OffsetArray::OffsetArray()
34	:
35	fOffsets(NULL)
36{
37}
38
39
40PackageFileHeapAccessorBase::OffsetArray::~OffsetArray()
41{
42	delete[] fOffsets;
43}
44
45
46bool
47PackageFileHeapAccessorBase::OffsetArray::InitChunksOffsets(
48	size_t totalChunkCount, size_t baseIndex, const uint16* chunkSizes,
49	size_t chunkCount)
50{
51	if (totalChunkCount <= 1)
52		return true;
53
54	if (fOffsets == NULL) {
55		fOffsets = new(std::nothrow) uint32[totalChunkCount];
56		if (fOffsets == NULL)
57			return false;
58		fOffsets[0] = 0;
59			// Value that serves as a marker that all offsets are 32 bit. We'll
60			// replace it, when necessary.
61	}
62
63	uint64 offset = (*this)[baseIndex];
64	for (size_t i = 0; i < chunkCount; i++) {
65		offset += (uint64)chunkSizes[i] + 1;
66			// the stored value is chunkSize - 1
67		size_t index = baseIndex + i + 1;
68			// (baseIndex + i) is the index of the chunk whose size is stored in
69			// chunkSizes[i]. We compute the offset of the following element
70			// which we store at index (baseIndex + i + 1).
71
72		if (offset <= ~(uint32)0) {
73			fOffsets[index] = (uint32)offset;
74		} else {
75			if (fOffsets[0] == 0) {
76				// Not scaled to allow for 64 bit offsets yet. Do that.
77				fOffsets[0] = index;
78				uint32* newOffsets = new(std::nothrow) uint32[
79					2 * totalChunkCount - fOffsets[0]];
80				if (newOffsets == NULL)
81					return false;
82
83				memcpy(newOffsets, fOffsets,
84					sizeof(newOffsets[0]) * fOffsets[0]);
85
86				delete[] fOffsets;
87				fOffsets = newOffsets;
88			}
89
90			index += index - fOffsets[0];
91			fOffsets[index] = (uint32)offset;
92			fOffsets[index + 1] = uint32(offset >> 32);
93		}
94	}
95
96	return true;
97}
98
99
100bool
101PackageFileHeapAccessorBase::OffsetArray::Init(size_t totalChunkCount,
102	const OffsetArray& other)
103{
104	if (other.fOffsets == NULL)
105		return true;
106
107	size_t elementCount = other.fOffsets[0] == 0
108		? totalChunkCount
109		: 2 * totalChunkCount - other.fOffsets[0];
110
111	fOffsets = new(std::nothrow) uint32[elementCount];
112	if (fOffsets == NULL)
113		return false;
114
115	memcpy(fOffsets, other.fOffsets, elementCount * sizeof(fOffsets[0]));
116	return true;
117}
118
119
120// #pragma mark - PackageFileHeapAccessorBase
121
122
123PackageFileHeapAccessorBase::PackageFileHeapAccessorBase(
124	BErrorOutput* errorOutput, int fd, off_t heapOffset)
125	:
126	fErrorOutput(errorOutput),
127	fFD(fd),
128	fHeapOffset(heapOffset),
129	fCompressedHeapSize(0),
130	fUncompressedHeapSize(0)
131{
132}
133
134
135PackageFileHeapAccessorBase::~PackageFileHeapAccessorBase()
136{
137}
138
139
140status_t
141PackageFileHeapAccessorBase::ReadDataToOutput(off_t offset, size_t size,
142	BDataOutput* output)
143{
144	if (size == 0)
145		return B_OK;
146
147	if (offset < 0 || (uint64)offset > fUncompressedHeapSize
148		|| size > fUncompressedHeapSize - offset) {
149		return B_BAD_VALUE;
150	}
151
152	// allocate buffers for compressed and uncompressed data
153	uint16* compressedDataBuffer = (uint16*)malloc(kChunkSize);
154	uint16* uncompressedDataBuffer = (uint16*)malloc(kChunkSize);
155	MemoryDeleter compressedDataBufferDeleter(compressedDataBuffer);
156	MemoryDeleter uncompressedDataBufferDeleter(uncompressedDataBuffer);
157	if (compressedDataBuffer == NULL || uncompressedDataBuffer == NULL)
158		return B_NO_MEMORY;
159
160	// read the data
161	size_t chunkIndex = size_t(offset / kChunkSize);
162	size_t inChunkOffset = (uint64)offset - (uint64)chunkIndex * kChunkSize;
163	size_t remainingBytes = size;
164
165	while (remainingBytes > 0) {
166		status_t error = ReadAndDecompressChunk(chunkIndex,
167			compressedDataBuffer, uncompressedDataBuffer);
168		if (error != B_OK)
169			return error;
170
171		size_t toWrite = std::min((size_t)kChunkSize - inChunkOffset,
172			remainingBytes);
173			// The last chunk may be shorter than kChunkSize, but since
174			// size (and thus remainingSize) had been clamped, that doesn't
175			// harm.
176		error = output->WriteData(
177			(char*)uncompressedDataBuffer + inChunkOffset, toWrite);
178		if (error != B_OK)
179			return error;
180
181		remainingBytes -= toWrite;
182		chunkIndex++;
183		inChunkOffset = 0;
184	}
185
186	return B_OK;
187}
188
189
190status_t
191PackageFileHeapAccessorBase::ReadAndDecompressChunkData(uint64 offset,
192	size_t compressedSize, size_t uncompressedSize, void* compressedDataBuffer,
193	void* uncompressedDataBuffer)
194{
195	// if uncompressed, read directly into the uncompressed data buffer
196	if (compressedSize == uncompressedSize)
197		return ReadFileData(offset, uncompressedDataBuffer, compressedSize);
198
199	// otherwise read into the other buffer and decompress
200	status_t error = ReadFileData(offset, compressedDataBuffer, compressedSize);
201	if (error != B_OK)
202		return error;
203
204	return DecompressChunkData(compressedDataBuffer, compressedSize,
205		uncompressedDataBuffer, uncompressedSize);
206}
207
208
209status_t
210PackageFileHeapAccessorBase::DecompressChunkData(void* compressedDataBuffer,
211	size_t compressedSize, void* uncompressedDataBuffer,
212	size_t uncompressedSize)
213{
214	size_t actualSize;
215	status_t error = ZlibDecompressor::DecompressSingleBuffer(
216		compressedDataBuffer, compressedSize, uncompressedDataBuffer,
217		uncompressedSize, actualSize);
218	if (error != B_OK) {
219		fErrorOutput->PrintError("Failed to decompress chunk data: %s\n",
220			strerror(error));
221		return error;
222	}
223
224	if (actualSize != uncompressedSize) {
225		fErrorOutput->PrintError("Failed to decompress chunk data: size "
226			"mismatch\n");
227		return B_ERROR;
228	}
229
230	return B_OK;
231}
232
233
234status_t
235PackageFileHeapAccessorBase::ReadFileData(uint64 offset, void* buffer,
236	size_t size)
237{
238	ssize_t bytesRead = pread(fFD, buffer, size, fHeapOffset + (off_t)offset);
239	if (bytesRead < 0) {
240		fErrorOutput->PrintError("ReadFileData(%" B_PRIu64 "%p, %zu) failed to "
241			"read data: %s\n", offset, buffer, size, strerror(errno));
242		return errno;
243	}
244	if ((size_t)bytesRead != size) {
245		fErrorOutput->PrintError("ReadFileData(%" B_PRIu64 ", %p, %zu) could "
246			"read only %zd bytes\n", offset, buffer, size, bytesRead);
247		return B_ERROR;
248	}
249
250	return B_OK;
251}
252
253
254}	// namespace BPrivate
255
256}	// namespace BHPKG
257
258}	// namespace BPackageKit
259