int FileDecoder::Read(char *outBuffer, size_t size, off_t offset) { if (offset >= Size()) return 0; unsigned int sharesRequired = fecWrapper.GetSharesRequired(); // read some more in case size is not a multiple of required int bytesToRead = (size + sharesRequired - 1) / sharesRequired + 1; int minBytesRead = bytesToRead; // TODO better to have only one vector? - avoid re-allocating the vectors std::vector<std::vector<char> >& readBuffers(threadLocalData.Get().readBuffers); readBuffers.resize(sharesRequired); for (unsigned int i = 0; i < sharesRequired; ++i) { readBuffers[i].resize(bytesToRead); int bytesRead = encodedFiles[i]->Read(readBuffers[i].data(), bytesToRead, offset / sharesRequired + Metadata::size); minBytesRead = std::min(minBytesRead, bytesRead); } if (minBytesRead == 0) return 0; std::vector<const char*> fecInputPtrs(sharesRequired); std::vector<unsigned int> fecInputIndices(fileIndices.begin(), fileIndices.end()); for (unsigned int i = 0; i < sharesRequired; ++i) fecInputPtrs[i] = readBuffers[i].data(); NormalizeIndices(fecInputPtrs, fecInputIndices); std::vector<char>& workBuffer(threadLocalData.Get().workBuffer); workBuffer.resize(minBytesRead * sharesRequired); std::vector<char*> fecOutputPtrs(sharesRequired); for (unsigned int i = 0; i < sharesRequired; ++i) fecOutputPtrs[i] = workBuffer.data() + i * minBytesRead; fecWrapper.Decode(fecOutputPtrs.data(), fecInputPtrs.data(), fecInputIndices.data(), minBytesRead); unsigned int offsetCorrection = offset % sharesRequired; size = std::min<size_t>(std::min<size_t>(size, minBytesRead * sharesRequired - offsetCorrection), Size() - offset); for (unsigned int i = 0; i < sharesRequired; ++i) { const char* decoded = fecInputIndices[i] < sharesRequired ? fecInputPtrs[i] : fecOutputPtrs[i]; char* out = outBuffer + i - offsetCorrection; if (i < offsetCorrection) { out += sharesRequired; ++decoded; } CopyToNthElement(out, outBuffer + size, decoded, sharesRequired); } return size; }
bool BSCLib::decompressData(uint8 *& out, size_t & outSize, const uint8 * in, const size_t inSize) { // We need to first read the number of blocks if (!in || inSize < sizeof(uint32)) return setError(BadFormat); uint32 nBlocks = 0; size_t processedSize = 0; #define POP(X, S) if ((processedSize + S) <= inSize) { memcpy(X, &in[processedSize], S); processedSize += S; } else return setError(UnexpectedEOD); POP(&nBlocks, sizeof(nBlocks)); bool dryRun = out == 0; size_t requiredOut = 0; int8 recordSize = 1, sortingContext = 1; int64 inPos = 0; Utils::HeapBlock workBuffer(BSC::HeaderSize); size_t bufferSize = BSC::HeaderSize; uint32 curBlock = 0; do { // Check the block header #ifndef BreakCompatibility POP(&inPos, sizeof(inPos)); POP(&recordSize, sizeof(recordSize)); POP(&sortingContext, sizeof(sortingContext)); #endif // Unsupported features ? if (recordSize < 1 || sortingContext < 1 || sortingContext > 2) return setError(BadFormat); // Read the block header POP(workBuffer, BSC::HeaderSize); // Figure out the block informations size_t blockSize = 0, dataSize = 0; if (opaque->getBlockInfo(workBuffer, BSC::HeaderSize, blockSize, dataSize) != BSC::Success) return setError(BadFormat); // From now, we need to allocate the work buffer if (blockSize > bufferSize) bufferSize = blockSize; if (dataSize > bufferSize) bufferSize = dataSize; if (!workBuffer.resize(bufferSize)) return setError(NotEnoughMemory); POP(workBuffer + BSC::HeaderSize, blockSize - BSC::HeaderSize); if (dataSize != 0) { // Now decompress the cruft BSC::Error err = opaque->decompress(workBuffer, blockSize, workBuffer, dataSize); if (err != BSC::Success) { return err == BSC::NotEnoughMemory ? setError(NotEnoughMemory) : setError(DataCorrupt); } // Depending on the sorting context, we might need to reverse the blocks err = opaque->postProcess(workBuffer, dataSize, sortingContext, recordSize); if (err != BSC::Success) { return err == BSC::NotEnoughMemory ? setError(NotEnoughMemory) : setError(DataCorrupt); } // Write data now! if (out && requiredOut + dataSize <= outSize) memcpy(&out[requiredOut], workBuffer, dataSize); requiredOut += dataSize; } curBlock++; if (curBlock == nBlocks || dataSize == 0) { // Check for dry run if (dryRun) { if (!out && outSize) { outSize = requiredOut; return setError(UnexpectedEOD); } outSize = requiredOut; delete[] out; out = new uint8[requiredOut]; if (!out) return setError(NotEnoughMemory); // Ok, try again for real now dryRun = false; curBlock = 0; requiredOut = 0; processedSize = sizeof(nBlocks); } else break; // Done } } while (true); #undef POP return setError(Success); }