xref: /haiku/src/kits/package/hpkg/PackageFileHeapAccessorBase.cpp (revision b3263ad3e1662cc419849a2d2cf539f9636613dc)
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 status_t
148 PackageFileHeapAccessorBase::ReadDataToOutput(off_t offset, size_t size,
149 	BDataIO* output)
150 {
151 	if (size == 0)
152 		return B_OK;
153 
154 	if (offset < 0 || (uint64)offset > fUncompressedHeapSize
155 		|| size > fUncompressedHeapSize - offset) {
156 		return B_BAD_VALUE;
157 	}
158 
159 	// allocate buffers for compressed and uncompressed data
160 	uint16* compressedDataBuffer = (uint16*)malloc(kChunkSize);
161 	uint16* uncompressedDataBuffer = (uint16*)malloc(kChunkSize);
162 	MemoryDeleter compressedDataBufferDeleter(compressedDataBuffer);
163 	MemoryDeleter uncompressedDataBufferDeleter(uncompressedDataBuffer);
164 	if (compressedDataBuffer == NULL || uncompressedDataBuffer == NULL)
165 		return B_NO_MEMORY;
166 
167 	// read the data
168 	size_t chunkIndex = size_t(offset / kChunkSize);
169 	size_t inChunkOffset = (uint64)offset - (uint64)chunkIndex * kChunkSize;
170 	size_t remainingBytes = size;
171 
172 	while (remainingBytes > 0) {
173 		status_t error = ReadAndDecompressChunk(chunkIndex,
174 			compressedDataBuffer, uncompressedDataBuffer);
175 		if (error != B_OK)
176 			return error;
177 
178 		size_t toWrite = std::min((size_t)kChunkSize - inChunkOffset,
179 			remainingBytes);
180 			// The last chunk may be shorter than kChunkSize, but since
181 			// size (and thus remainingSize) had been clamped, that doesn't
182 			// harm.
183 		error = output->WriteExactly(
184 			(char*)uncompressedDataBuffer + inChunkOffset, toWrite);
185 		if (error != B_OK)
186 			return error;
187 
188 		remainingBytes -= toWrite;
189 		chunkIndex++;
190 		inChunkOffset = 0;
191 	}
192 
193 	return B_OK;
194 }
195 
196 
197 status_t
198 PackageFileHeapAccessorBase::ReadAndDecompressChunkData(uint64 offset,
199 	size_t compressedSize, size_t uncompressedSize, void* compressedDataBuffer,
200 	void* uncompressedDataBuffer)
201 {
202 	// if uncompressed, read directly into the uncompressed data buffer
203 	if (compressedSize == uncompressedSize)
204 		return ReadFileData(offset, uncompressedDataBuffer, compressedSize);
205 
206 	// otherwise read into the other buffer and decompress
207 	status_t error = ReadFileData(offset, compressedDataBuffer, compressedSize);
208 	if (error != B_OK)
209 		return error;
210 
211 	return DecompressChunkData(compressedDataBuffer, compressedSize,
212 		uncompressedDataBuffer, uncompressedSize);
213 }
214 
215 
216 status_t
217 PackageFileHeapAccessorBase::DecompressChunkData(void* compressedDataBuffer,
218 	size_t compressedSize, void* uncompressedDataBuffer,
219 	size_t uncompressedSize)
220 {
221 	size_t actualSize;
222 	status_t error = fDecompressionAlgorithm->algorithm->DecompressBuffer(
223 		compressedDataBuffer, compressedSize, uncompressedDataBuffer,
224 		uncompressedSize, actualSize, fDecompressionAlgorithm->parameters);
225 	if (error != B_OK) {
226 		fErrorOutput->PrintError("Failed to decompress chunk data: %s\n",
227 			strerror(error));
228 		return error;
229 	}
230 
231 	if (actualSize != uncompressedSize) {
232 		fErrorOutput->PrintError("Failed to decompress chunk data: size "
233 			"mismatch\n");
234 		return B_ERROR;
235 	}
236 
237 	return B_OK;
238 }
239 
240 
241 status_t
242 PackageFileHeapAccessorBase::ReadFileData(uint64 offset, void* buffer,
243 	size_t size)
244 {
245 	ssize_t bytesRead = pread(fFD, buffer, size, fHeapOffset + (off_t)offset);
246 	if (bytesRead < 0) {
247 		fErrorOutput->PrintError("ReadFileData(%" B_PRIu64 ", %p, %zu) failed "
248 			"to read data: %s\n", offset, buffer, size, strerror(errno));
249 		return errno;
250 	}
251 	if ((size_t)bytesRead != size) {
252 		fErrorOutput->PrintError("ReadFileData(%" B_PRIu64 ", %p, %zu) could "
253 			"read only %zd bytes\n", offset, buffer, size, bytesRead);
254 		return B_ERROR;
255 	}
256 
257 	return B_OK;
258 }
259 
260 
261 }	// namespace BPrivate
262 
263 }	// namespace BHPKG
264 
265 }	// namespace BPackageKit
266