status_t PackageWriter::_AddData(DataReader& dataReader, off_t size) { // add short data inline if (size <= B_HPKG_MAX_INLINE_DATA_SIZE) { uint8 buffer[B_HPKG_MAX_INLINE_DATA_SIZE]; status_t error = dataReader.ReadData(0, buffer, size); if (error != B_OK) { fprintf(stderr, "Error: Failed to read data: %s\n", strerror(error)); return error; } _AddDataAttribute(B_HPKG_ATTRIBUTE_NAME_DATA, size, buffer); return B_OK; } // longer data -- try to compress uint64 dataOffset = fHeapEnd; uint64 compression = B_HPKG_COMPRESSION_NONE; uint64 compressedSize; status_t error = _WriteZlibCompressedData(dataReader, size, dataOffset, compressedSize); if (error == B_OK) { compression = B_HPKG_COMPRESSION_ZLIB; } else { error = _WriteUncompressedData(dataReader, size, dataOffset); compressedSize = size; } if (error != B_OK) return error; fHeapEnd = dataOffset + compressedSize; // add data attribute Attribute* dataAttribute = _AddDataAttribute(B_HPKG_ATTRIBUTE_NAME_DATA, compressedSize, dataOffset - fHeapOffset); Stacker<Attribute> attributeAttributeStacker(fTopAttribute, dataAttribute); // if compressed, add compression attributes if (compression != B_HPKG_COMPRESSION_NONE) { _AddAttribute(B_HPKG_ATTRIBUTE_NAME_DATA_COMPRESSION, compression); _AddAttribute(B_HPKG_ATTRIBUTE_NAME_DATA_SIZE, (uint64)size); // uncompressed size } return B_OK; }
status_t PackageWriter::_WriteUncompressedData(DataReader& dataReader, off_t size, uint64 writeOffset) { // copy the data to the heap off_t readOffset = 0; off_t remainingSize = size; while (remainingSize > 0) { // read data size_t toCopy = std::min(remainingSize, (off_t)fDataBufferSize); status_t error = dataReader.ReadData(readOffset, fDataBuffer, toCopy); if (error != B_OK) { fprintf(stderr, "Error: Failed to read data: %s\n", strerror(error)); return error; } // write to heap ssize_t bytesWritten = pwrite(fFD, fDataBuffer, toCopy, writeOffset); if (bytesWritten < 0) { fprintf(stderr, "Error: Failed to write data: %s\n", strerror(errno)); return errno; } if ((size_t)bytesWritten != toCopy) { fprintf(stderr, "Error: Failed to write all data\n"); return B_ERROR; } remainingSize -= toCopy; readOffset += toCopy; writeOffset += toCopy; } return B_OK; }
status_t PackageWriter::_WriteZlibCompressedData(DataReader& dataReader, off_t size, uint64 writeOffset, uint64& _compressedSize) { // Use zlib compression only for data large enough. if (size < kZlibCompressionSizeThreshold) return B_BAD_VALUE; // fDataBuffer is 2 * B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB, so split it into // two halves we can use for reading and compressing const size_t chunkSize = B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB; uint8* inputBuffer = (uint8*)fDataBuffer; uint8* outputBuffer = (uint8*)fDataBuffer + chunkSize; // account for the offset table uint64 chunkCount = (size + (chunkSize - 1)) / chunkSize; off_t offsetTableOffset = writeOffset; uint64* offsetTable = NULL; if (chunkCount > 1) { offsetTable = new uint64[chunkCount - 1]; writeOffset = offsetTableOffset + (chunkCount - 1) * sizeof(uint64); } ArrayDeleter<uint64> offsetTableDeleter(offsetTable); const uint64 dataOffset = writeOffset; const uint64 dataEndLimit = offsetTableOffset + size; // read the data, compress them and write them to the heap off_t readOffset = 0; off_t remainingSize = size; uint64 chunkIndex = 0; while (remainingSize > 0) { // read data size_t toCopy = std::min(remainingSize, (off_t)chunkSize); status_t error = dataReader.ReadData(readOffset, inputBuffer, toCopy); if (error != B_OK) { fprintf(stderr, "Error: Failed to read data: %s\n", strerror(error)); return error; } // compress size_t compressedSize; error = ZlibCompressor::CompressSingleBuffer(inputBuffer, toCopy, outputBuffer, toCopy, compressedSize); const void* writeBuffer; size_t bytesToWrite; if (error == B_OK) { writeBuffer = outputBuffer; bytesToWrite = compressedSize; } else { if (error != B_BUFFER_OVERFLOW) return error; writeBuffer = inputBuffer; bytesToWrite = toCopy; } // check the total compressed data size if (writeOffset + bytesToWrite >= dataEndLimit) return B_BUFFER_OVERFLOW; if (chunkIndex > 0) offsetTable[chunkIndex - 1] = writeOffset - dataOffset; // write to heap ssize_t bytesWritten = pwrite(fFD, writeBuffer, bytesToWrite, writeOffset); if (bytesWritten < 0) { fprintf(stderr, "Error: Failed to write data: %s\n", strerror(errno)); return errno; } if ((size_t)bytesWritten != bytesToWrite) { fprintf(stderr, "Error: Failed to write all data\n"); return B_ERROR; } remainingSize -= toCopy; readOffset += toCopy; writeOffset += bytesToWrite; chunkIndex++; } // write the offset table if (chunkCount > 1) { size_t bytesToWrite = (chunkCount - 1) * sizeof(uint64); ssize_t bytesWritten = pwrite(fFD, offsetTable, bytesToWrite, offsetTableOffset); if (bytesWritten < 0) { fprintf(stderr, "Error: Failed to write data: %s\n", strerror(errno)); return errno; } if ((size_t)bytesWritten != bytesToWrite) { fprintf(stderr, "Error: Failed to write all data\n"); return B_ERROR; } } _compressedSize = writeOffset - offsetTableOffset; return B_OK; }