extern "C" Lz4MtResult lz4mtDecompress(Lz4MtContext* lz4MtContext, Lz4MtStreamDescriptor* sd) { assert(lz4MtContext); assert(sd); Context ctx_(lz4MtContext); Context* ctx = &ctx_; std::atomic<bool> quit(false); ctx->setResult(LZ4MT_RESULT_OK); while(!quit && !ctx->error() && !ctx->readEof()) { const auto magic = ctx->readU32(); if(ctx->error()) { if(ctx->readEof()) { ctx->setResult(LZ4MT_RESULT_OK); } else { ctx->setResult(LZ4MT_RESULT_INVALID_HEADER); } break; } if(isSkippableMagicNumber(magic)) { const auto size = ctx->readU32(); if(ctx->error()) { ctx->setResult(LZ4MT_RESULT_INVALID_HEADER); break; } const auto s = ctx->readSkippable(magic, size); if(s < 0 || ctx->error()) { ctx->setResult(LZ4MT_RESULT_INVALID_HEADER); break; } continue; } if(LZ4S_MAGICNUMBER != magic) { ctx->readSeek(-4); ctx->setResult(LZ4MT_RESULT_INVALID_MAGIC_NUMBER); break; } char d[LZ4S_MAX_HEADER_SIZE] = { 0 }; auto* p = d; const auto* sumBegin = p; if(2 != ctx->read(p, 2)) { ctx->setResult(LZ4MT_RESULT_INVALID_HEADER); break; } sd->flg = charToFlg(*p++); sd->bd = charToBc(*p++); const auto r = validateStreamDescriptor(sd); if(LZ4MT_RESULT_OK != r) { ctx->setResult(r); break; } const int nExInfo = (sd->flg.streamSize ? sizeof(uint64_t) : 0) + (sd->flg.presetDictionary ? sizeof(uint32_t) : 0) + 1 ; if(nExInfo != ctx->read(p, nExInfo)) { ctx->setResult(LZ4MT_RESULT_INVALID_HEADER); break; } if(sd->flg.streamSize) { sd->streamSize = loadU64(p); p += sizeof(uint64_t); } if(sd->flg.presetDictionary) { sd->dictId = loadU32(p); p += sizeof(uint32_t); } const auto sumSize = static_cast<int>(p - sumBegin); const auto calHash32 = Lz4Mt::Xxh32(sumBegin, sumSize, LZ4S_CHECKSUM_SEED).digest(); const auto calHash = static_cast<char>(getCheckBits_FromXXH(calHash32)); const auto srcHash = *p++; assert(p <= std::end(d)); if(srcHash != calHash) { ctx->setResult(LZ4MT_RESULT_INVALID_HEADER_CHECKSUM); break; } const auto nBlockMaximumSize = getBlockSize(sd->bd.blockMaximumSize); const auto nBlockCheckSum = sd->flg.blockChecksum ? 4 : 0; const bool streamChecksum = 0 != sd->flg.streamChecksum; const bool singleThread = 0 != (ctx->mode() & LZ4MT_MODE_SEQUENTIAL); const auto nConcurrency = Lz4Mt::getHardwareConcurrency(); const auto nPool = singleThread ? 1 : nConcurrency + 1; const auto launch = singleThread ? Lz4Mt::launch::deferred : std::launch::async; Lz4Mt::MemPool srcBufferPool(nBlockMaximumSize, nPool); Lz4Mt::MemPool dstBufferPool(nBlockMaximumSize, nPool); std::vector<std::future<Lz4MtResult>> futures; Lz4Mt::Xxh32 xxhStream(LZ4S_CHECKSUM_SEED); const auto f = [ &futures, &dstBufferPool, &xxhStream, &quit , ctx, nBlockCheckSum, streamChecksum, launch ] (int i, Lz4Mt::MemPool::Buffer* srcRaw, bool incompressible, uint32_t blockChecksum) -> Lz4MtResult { BufferPtr src(srcRaw); if(ctx->error() || quit) { return LZ4MT_RESULT_OK; } const auto* srcPtr = src->data(); const auto srcSize = static_cast<int>(src->size()); std::future<uint32_t> futureBlockHash; if(nBlockCheckSum) { futureBlockHash = std::async(launch, [=] { return Lz4Mt::Xxh32(srcPtr, srcSize, LZ4S_CHECKSUM_SEED).digest(); }); } if(incompressible) { if(i > 0) { futures[i-1].wait(); } std::future<void> futureStreamHash; if(streamChecksum) { futureStreamHash = std::async( launch , [&xxhStream, srcPtr, srcSize] { xxhStream.update(srcPtr, srcSize); } ); } ctx->writeBin(srcPtr, srcSize); if(futureStreamHash.valid()) { futureStreamHash.wait(); } } else { BufferPtr dst(dstBufferPool.alloc()); auto* dstPtr = dst->data(); const auto dstSize = dst->size(); const auto decSize = ctx->decompress( srcPtr, dstPtr, srcSize, static_cast<int>(dstSize)); if(decSize < 0) { quit = true; return LZ4MT_RESULT_DECOMPRESS_FAIL; } if(i > 0) { futures[i-1].wait(); } std::future<void> futureStreamHash; if(streamChecksum) { futureStreamHash = std::async( launch , [&xxhStream, dstPtr, decSize] { xxhStream.update(dstPtr, decSize); } ); } ctx->writeBin(dstPtr, decSize); if(futureStreamHash.valid()) { futureStreamHash.wait(); } } if(futureBlockHash.valid()) { auto bh = futureBlockHash.get(); if(bh != blockChecksum) { quit = true; return LZ4MT_RESULT_BLOCK_CHECKSUM_MISMATCH; } } return LZ4MT_RESULT_OK; }; for(int i = 0; !quit && !ctx->readEof(); ++i) { const auto srcBits = ctx->readU32(); if(ctx->error()) { quit = true; ctx->setResult(LZ4MT_RESULT_CANNOT_READ_BLOCK_SIZE); break; } if(LZ4S_EOS == srcBits) { break; } const auto incompMask = (1 << 31); const bool incompressible = 0 != (srcBits & incompMask); const auto srcSize = static_cast<int>(srcBits & ~incompMask); auto src = srcBufferPool.alloc(); const auto readSize = ctx->read(src->data(), srcSize); if(srcSize != readSize || ctx->error()) { quit = true; ctx->setResult(LZ4MT_RESULT_CANNOT_READ_BLOCK_DATA); break; } src->resize(readSize); const auto blockCheckSum = nBlockCheckSum ? ctx->readU32() : 0; if(ctx->error()) { quit = true; ctx->setResult(LZ4MT_RESULT_CANNOT_READ_BLOCK_CHECKSUM); break; } if(singleThread) { f(0, src, incompressible, blockCheckSum); } else { futures.emplace_back(std::async( launch , f, i, src, incompressible, blockCheckSum )); } } for(auto& e : futures) { const auto r = e.get(); if(LZ4MT_RESULT_OK != r) { ctx->setResult(r); } } if(!ctx->error() && streamChecksum) { const auto srcStreamChecksum = ctx->readU32(); if(ctx->error()) { ctx->setResult(LZ4MT_RESULT_CANNOT_READ_STREAM_CHECKSUM); break; } if(xxhStream.digest() != srcStreamChecksum) { ctx->setResult(LZ4MT_RESULT_STREAM_CHECKSUM_MISMATCH); break; } } } return ctx->result(); }
extern "C" Lz4MtResult lz4mtDecompress(Lz4MtContext* lz4MtContext, Lz4MtStreamDescriptor* sd) { assert(lz4MtContext); assert(sd); Ctx ctx(lz4MtContext); bool magicNumberRecognized = false; ctx.setResult(LZ4MT_RESULT_OK); while(!ctx.isQuit() && !ctx.error() && !ctx.readEof()) { const auto magic = ctx.readU32(); if(ctx.error()) { if(ctx.readEof()) { ctx.setResult(LZ4MT_RESULT_OK); } else { ctx.setResult(LZ4MT_RESULT_INVALID_HEADER); } continue; } if(! isMagicNumber(magic)) { if(isSkippableMagicNumber(magic)) { const auto size = ctx.readU32(); if(ctx.error()) { ctx.setResult(LZ4MT_RESULT_INVALID_HEADER_SKIPPABLE_SIZE_UNREADABLE); } else { const auto s = ctx.readSkippable(magic, size); if(s < 0 || ctx.error()) { ctx.setResult(LZ4MT_RESULT_INVALID_HEADER_CANNOT_SKIP_SKIPPABLE_AREA); } } } else { ctx.readSeek(-4); if(magicNumberRecognized) { ctx.setResult(LZ4MT_RESULT_OK); } else { ctx.setResult(LZ4MT_RESULT_INVALID_MAGIC_NUMBER); } } continue; } magicNumberRecognized = true; const auto readHeaderResult = readHeader(ctx, sd); if(LZ4MT_RESULT_OK != readHeaderResult) { continue; } const Params params(lz4MtContext, sd); Lz4Mt::Xxh32 xxhStream(LZ4S_CHECKSUM_SEED); if(params.blockIndependence) { decompress(ctx, params, xxhStream); } else { decompressBlockDependency(ctx, params, xxhStream); } if(!ctx.error() && params.streamChecksum) { const auto srcStreamChecksum = ctx.readU32(); if(ctx.error()) { ctx.setResult(LZ4MT_RESULT_CANNOT_READ_STREAM_CHECKSUM); continue; } if(xxhStream.digest() != srcStreamChecksum) { ctx.setResult(LZ4MT_RESULT_STREAM_CHECKSUM_MISMATCH); continue; } } } return ctx.result(); }