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