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