зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1396026 - Update OTS to accept Awami Nastaliq. r=froydnj,jfkthame
MozReview-Commit-ID: EvF3YDhuwNn --HG-- extra : rebase_source : 20046aa4f2695f923574bf00207f06a113d1f246
This commit is contained in:
Родитель
9a59011a99
Коммит
a0a376f2f1
|
@ -2,7 +2,7 @@ This is the Sanitiser for OpenType project, from http://code.google.com/p/ots/.
|
|||
|
||||
Our reference repository is https://github.com/khaledhosny/ots/.
|
||||
|
||||
Current revision: e2d4b5daba24e746a48d240e90d92fe09a20b681 (5.2.0)
|
||||
Current revision: f87b4556191e4132ef5c47365762eb88ace97fc3 (6.0.0)
|
||||
|
||||
Upstream files included: LICENSE, src/, include/, tests/*.cc
|
||||
|
||||
|
|
|
@ -10,25 +10,23 @@ diff --git a/gfx/ots/src/glat.cc b/gfx/ots/src/glat.cc
|
|||
#include <list>
|
||||
|
||||
namespace ots {
|
||||
@@ -201,13 +201,15 @@ bool OpenTypeGLAT_v3::Parse(const uint8_t* data, size_t length,
|
||||
@@ -201,14 +201,15 @@ bool OpenTypeGLAT_v3::Parse(const uint8_t* data, size_t length,
|
||||
return DropGraphite("Illegal nested compression");
|
||||
}
|
||||
std::vector<uint8_t> decompressed(this->compHead & FULL_SIZE, 0);
|
||||
- int ret = LZ4_decompress_safe(
|
||||
- reinterpret_cast<const char*>(data + table.offset()),
|
||||
- reinterpret_cast<char*>(decompressed.data()),
|
||||
- table.remaining(),
|
||||
- decompressed.size());
|
||||
- if (ret < 0) {
|
||||
- return DropGraphite("Decompression failed with error code %d", ret);
|
||||
std::vector<uint8_t> decompressed(this->compHead & FULL_SIZE);
|
||||
- int ret = LZ4_decompress_safe_partial(
|
||||
+ size_t outputSize = 0;
|
||||
+ if (!mozilla::Compression::LZ4::decompress(
|
||||
+ reinterpret_cast<const char*>(data + table.offset()),
|
||||
+ table.remaining(),
|
||||
+ reinterpret_cast<char*>(decompressed.data()),
|
||||
+ decompressed.size(),
|
||||
+ &outputSize) ||
|
||||
+ outputSize != (this->compHead & FULL_SIZE)) {
|
||||
+ bool ret = mozilla::Compression::LZ4::decompressPartial(
|
||||
reinterpret_cast<const char*>(data + table.offset()),
|
||||
- reinterpret_cast<char*>(decompressed.data()),
|
||||
table.remaining(), // input buffer size (input size + padding)
|
||||
+ reinterpret_cast<char*>(decompressed.data()),
|
||||
decompressed.size(), // target output size
|
||||
- decompressed.size()); // output buffer size
|
||||
- if (ret != decompressed.size()) {
|
||||
- return DropGraphite("Decompression failed with error code %d", ret);
|
||||
+ &outputSize); // return output size
|
||||
+ if (!ret || outputSize != decompressed.size()) {
|
||||
+ return DropGraphite("Decompression failed");
|
||||
}
|
||||
return this->Parse(decompressed.data(), decompressed.size(), true);
|
||||
|
@ -45,25 +43,23 @@ diff --git a/gfx/ots/src/silf.cc b/gfx/ots/src/silf.cc
|
|||
#include <cmath>
|
||||
|
||||
namespace ots {
|
||||
@@ -39,13 +39,15 @@ bool OpenTypeSILF::Parse(const uint8_t* data, size_t length,
|
||||
@@ -39,14 +39,15 @@ bool OpenTypeSILF::Parse(const uint8_t* data, size_t length,
|
||||
return DropGraphite("Illegal nested compression");
|
||||
}
|
||||
std::vector<uint8_t> decompressed(this->compHead & FULL_SIZE, 0);
|
||||
- int ret = LZ4_decompress_safe(
|
||||
- reinterpret_cast<const char*>(data + table.offset()),
|
||||
- reinterpret_cast<char*>(decompressed.data()),
|
||||
- table.remaining(),
|
||||
- decompressed.size());
|
||||
- if (ret < 0) {
|
||||
- return DropGraphite("Decompression failed with error code %d", ret);
|
||||
std::vector<uint8_t> decompressed(this->compHead & FULL_SIZE);
|
||||
- int ret = LZ4_decompress_safe_partial(
|
||||
+ size_t outputSize = 0;
|
||||
+ if (!mozilla::Compression::LZ4::decompress(
|
||||
+ reinterpret_cast<const char*>(data + table.offset()),
|
||||
+ table.remaining(),
|
||||
+ reinterpret_cast<char*>(decompressed.data()),
|
||||
+ decompressed.size(),
|
||||
+ &outputSize) ||
|
||||
+ outputSize != (this->compHead & FULL_SIZE)) {
|
||||
+ bool ret = mozilla::Compression::LZ4::decompressPartial(
|
||||
reinterpret_cast<const char*>(data + table.offset()),
|
||||
- reinterpret_cast<char*>(decompressed.data()),
|
||||
table.remaining(), // input buffer size (input size + padding)
|
||||
+ reinterpret_cast<char*>(decompressed.data()),
|
||||
decompressed.size(), // target output size
|
||||
- decompressed.size()); // output buffer size
|
||||
- if (ret != decompressed.size()) {
|
||||
- return DropGraphite("Decompression failed with error code %d", ret);
|
||||
+ &outputSize); // return output size
|
||||
+ if (!ret || outputSize != decompressed.size()) {
|
||||
+ return DropGraphite("Decompression failed");
|
||||
}
|
||||
return this->Parse(decompressed.data(), decompressed.size(), true);
|
||||
|
|
|
@ -200,15 +200,15 @@ bool OpenTypeGLAT_v3::Parse(const uint8_t* data, size_t length,
|
|||
if (prevent_decompression) {
|
||||
return DropGraphite("Illegal nested compression");
|
||||
}
|
||||
std::vector<uint8_t> decompressed(this->compHead & FULL_SIZE, 0);
|
||||
std::vector<uint8_t> decompressed(this->compHead & FULL_SIZE);
|
||||
size_t outputSize = 0;
|
||||
if (!mozilla::Compression::LZ4::decompress(
|
||||
reinterpret_cast<const char*>(data + table.offset()),
|
||||
table.remaining(),
|
||||
reinterpret_cast<char*>(decompressed.data()),
|
||||
decompressed.size(),
|
||||
&outputSize) ||
|
||||
outputSize != (this->compHead & FULL_SIZE)) {
|
||||
bool ret = mozilla::Compression::LZ4::decompressPartial(
|
||||
reinterpret_cast<const char*>(data + table.offset()),
|
||||
table.remaining(), // input buffer size (input size + padding)
|
||||
reinterpret_cast<char*>(decompressed.data()),
|
||||
decompressed.size(), // target output size
|
||||
&outputSize); // return output size
|
||||
if (!ret || outputSize != decompressed.size()) {
|
||||
return DropGraphite("Decompression failed");
|
||||
}
|
||||
return this->Parse(decompressed.data(), decompressed.size(), true);
|
||||
|
@ -388,7 +388,7 @@ GlatEntry::ParsePart(Buffer& table) {
|
|||
}
|
||||
|
||||
//this->attributes.resize(this->num);
|
||||
for (unsigned i = 0; i < this->num; ++i) {
|
||||
for (int i = 0; i < this->num; ++i) {
|
||||
this->attributes.emplace_back();
|
||||
if (!table.ReadS16(&this->attributes[i])) {
|
||||
return parent->Error("GlatEntry: Failed to read attribute %u", i);
|
||||
|
|
|
@ -311,15 +311,15 @@ bool ParseClassDefFormat2(const ots::Font *font,
|
|||
|
||||
// Skip format field.
|
||||
if (!subtable.Skip(2)) {
|
||||
return OTS_FAILURE_MSG("Failed to skip format of class defintion header");
|
||||
return OTS_FAILURE_MSG("Failed to read class definition format");
|
||||
}
|
||||
|
||||
uint16_t range_count = 0;
|
||||
if (!subtable.ReadU16(&range_count)) {
|
||||
return OTS_FAILURE_MSG("Failed to read range count in class definition");
|
||||
return OTS_FAILURE_MSG("Failed to read classRangeCount");
|
||||
}
|
||||
if (range_count > num_glyphs) {
|
||||
return OTS_FAILURE_MSG("bad range count: %u", range_count);
|
||||
return OTS_FAILURE_MSG("classRangeCount > glyph count: %u > %u", range_count, num_glyphs);
|
||||
}
|
||||
|
||||
uint16_t last_end = 0;
|
||||
|
@ -330,13 +330,16 @@ bool ParseClassDefFormat2(const ots::Font *font,
|
|||
if (!subtable.ReadU16(&start) ||
|
||||
!subtable.ReadU16(&end) ||
|
||||
!subtable.ReadU16(&class_value)) {
|
||||
return OTS_FAILURE_MSG("Failed to read class definition reange %d", i);
|
||||
return OTS_FAILURE_MSG("Failed to read ClassRangeRecord %d", i);
|
||||
}
|
||||
if (start > end || (last_end && start <= last_end)) {
|
||||
return OTS_FAILURE_MSG("glyph range is overlapping.in range %d", i);
|
||||
if (start > end) {
|
||||
return OTS_FAILURE_MSG("ClassRangeRecord %d, start > end: %u > %u", i, start, end);
|
||||
}
|
||||
if (last_end && start <= last_end) {
|
||||
return OTS_FAILURE_MSG("ClassRangeRecord %d start overlaps with end of the previous one: %u <= %u", i, start, last_end);
|
||||
}
|
||||
if (class_value > num_classes) {
|
||||
return OTS_FAILURE_MSG("bad class value: %u", class_value);
|
||||
return OTS_FAILURE_MSG("ClassRangeRecord %d class > number of classes: %u > %u", i, class_value, num_classes);
|
||||
}
|
||||
last_end = end;
|
||||
}
|
||||
|
|
|
@ -38,15 +38,15 @@ bool OpenTypeSILF::Parse(const uint8_t* data, size_t length,
|
|||
if (prevent_decompression) {
|
||||
return DropGraphite("Illegal nested compression");
|
||||
}
|
||||
std::vector<uint8_t> decompressed(this->compHead & FULL_SIZE, 0);
|
||||
std::vector<uint8_t> decompressed(this->compHead & FULL_SIZE);
|
||||
size_t outputSize = 0;
|
||||
if (!mozilla::Compression::LZ4::decompress(
|
||||
reinterpret_cast<const char*>(data + table.offset()),
|
||||
table.remaining(),
|
||||
reinterpret_cast<char*>(decompressed.data()),
|
||||
decompressed.size(),
|
||||
&outputSize) ||
|
||||
outputSize != (this->compHead & FULL_SIZE)) {
|
||||
bool ret = mozilla::Compression::LZ4::decompressPartial(
|
||||
reinterpret_cast<const char*>(data + table.offset()),
|
||||
table.remaining(), // input buffer size (input size + padding)
|
||||
reinterpret_cast<char*>(decompressed.data()),
|
||||
decompressed.size(), // target output size
|
||||
&outputSize); // return output size
|
||||
if (!ret || outputSize != decompressed.size()) {
|
||||
return DropGraphite("Decompression failed");
|
||||
}
|
||||
return this->Parse(decompressed.data(), decompressed.size(), true);
|
||||
|
@ -255,8 +255,13 @@ bool OpenTypeSILF::SILSub::ParsePart(Buffer& table) {
|
|||
}
|
||||
}
|
||||
|
||||
if (!table.ReadU16(&this->lbGID) || this->lbGID > this->maxGlyphID) {
|
||||
return parent->Error("SILSub: Failed to read valid lbGID");
|
||||
if (!table.ReadU16(&this->lbGID)) {
|
||||
return parent->Error("SILSub: Failed to read lbGID");
|
||||
}
|
||||
if (this->lbGID > this->maxGlyphID) {
|
||||
parent->Warning("SILSub: lbGID %u outside range 0..%u, replaced with 0",
|
||||
this->lbGID, this->maxGlyphID);
|
||||
this->lbGID = 0;
|
||||
}
|
||||
|
||||
if (parent->version >> 16 >= 3 &&
|
||||
|
@ -280,19 +285,27 @@ bool OpenTypeSILF::SILSub::ParsePart(Buffer& table) {
|
|||
if (!table.ReadU16(&this->numPseudo)) {
|
||||
return parent->Error("SILSub: Failed to read numPseudo");
|
||||
}
|
||||
if (!table.ReadU16(&this->searchPseudo) || this->searchPseudo !=
|
||||
(this->numPseudo == 0 ? 0 : // protect against log2(0)
|
||||
(unsigned)std::pow(2, std::floor(std::log2(this->numPseudo))))) {
|
||||
return parent->Error("SILSub: Failed to read valid searchPseudo");
|
||||
|
||||
// The following three fields are deprecated and ignored. We fix them up here
|
||||
// just for internal consistency, but the Graphite engine doesn't care.
|
||||
if (!table.ReadU16(&this->searchPseudo) ||
|
||||
!table.ReadU16(&this->pseudoSelector) ||
|
||||
!table.ReadU16(&this->pseudoShift)) {
|
||||
return parent->Error("SILSub: Failed to read searchPseudo..pseudoShift");
|
||||
}
|
||||
if (!table.ReadU16(&this->pseudoSelector) || this->pseudoSelector !=
|
||||
(this->numPseudo == 0 ? 0 : // protect against log2(0)
|
||||
(unsigned)std::floor(std::log2(this->numPseudo)))) {
|
||||
return parent->Error("SILSub: Failed to read valid pseudoSelector");
|
||||
}
|
||||
if (!table.ReadU16(&this->pseudoShift) ||
|
||||
this->pseudoShift != this->numPseudo - this->searchPseudo) {
|
||||
return parent->Error("SILSub: Failed to read valid pseudoShift");
|
||||
if (this->numPseudo == 0) {
|
||||
if (this->searchPseudo != 0 || this->pseudoSelector != 0 || this->pseudoShift != 0) {
|
||||
this->searchPseudo = this->pseudoSelector = this->pseudoShift = 0;
|
||||
}
|
||||
} else {
|
||||
unsigned floorLog2 = std::floor(std::log2(this->numPseudo));
|
||||
if (this->searchPseudo != 6 * (unsigned)std::pow(2, floorLog2) ||
|
||||
this->pseudoSelector != floorLog2 ||
|
||||
this->pseudoShift != 6 * this->numPseudo - this->searchPseudo) {
|
||||
this->searchPseudo = 6 * (unsigned)std::pow(2, floorLog2);
|
||||
this->pseudoSelector = floorLog2;
|
||||
this->pseudoShift = 6 * this->numPseudo - this->searchPseudo;
|
||||
}
|
||||
}
|
||||
|
||||
//this->pMaps.resize(this->numPseudo, parent);
|
||||
|
@ -541,19 +554,26 @@ LookupClass::ParsePart(Buffer& table) {
|
|||
if (!table.ReadU16(&this->numIDs)) {
|
||||
return parent->Error("LookupClass: Failed to read numIDs");
|
||||
}
|
||||
if (!table.ReadU16(&this->searchRange) || this->searchRange !=
|
||||
(this->numIDs == 0 ? 0 : // protect against log2(0)
|
||||
(unsigned)std::pow(2, std::floor(std::log2(this->numIDs))))) {
|
||||
return parent->Error("LookupClass: Failed to read valid searchRange");
|
||||
if (!table.ReadU16(&this->searchRange) ||
|
||||
!table.ReadU16(&this->entrySelector) ||
|
||||
!table.ReadU16(&this->rangeShift)) {
|
||||
return parent->Error("LookupClass: Failed to read searchRange..rangeShift");
|
||||
}
|
||||
if (!table.ReadU16(&this->entrySelector) || this->entrySelector !=
|
||||
(this->numIDs == 0 ? 0 : // protect against log2(0)
|
||||
(unsigned)std::floor(std::log2(this->numIDs)))) {
|
||||
return parent->Error("LookupClass: Failed to read valid entrySelector");
|
||||
}
|
||||
if (!table.ReadU16(&this->rangeShift) ||
|
||||
this->rangeShift != this->numIDs - this->searchRange) {
|
||||
return parent->Error("LookupClass: Failed to read valid rangeShift");
|
||||
if (this->numIDs == 0) {
|
||||
if (this->searchRange != 0 || this->entrySelector != 0 || this->rangeShift != 0) {
|
||||
parent->Warning("LookupClass: Correcting binary-search header for zero-length LookupPair list");
|
||||
this->searchRange = this->entrySelector = this->rangeShift = 0;
|
||||
}
|
||||
} else {
|
||||
unsigned floorLog2 = std::floor(std::log2(this->numIDs));
|
||||
if (this->searchRange != (unsigned)std::pow(2, floorLog2) ||
|
||||
this->entrySelector != floorLog2 ||
|
||||
this->rangeShift != this->numIDs - this->searchRange) {
|
||||
parent->Warning("LookupClass: Correcting binary-search header for LookupPair list");
|
||||
this->searchRange = (unsigned)std::pow(2, floorLog2);
|
||||
this->entrySelector = floorLog2;
|
||||
this->rangeShift = this->numIDs - this->searchRange;
|
||||
}
|
||||
}
|
||||
|
||||
//this->lookups.resize(this->numIDs, parent);
|
||||
|
@ -661,19 +681,27 @@ SILPass::ParsePart(Buffer& table, const size_t SILSub_init_offset,
|
|||
if (!table.ReadU16(&this->numRange)) {
|
||||
return parent->Error("SILPass: Failed to read numRange");
|
||||
}
|
||||
if (!table.ReadU16(&this->searchRange) || this->searchRange !=
|
||||
(this->numRange == 0 ? 0 : // protect against log2(0)
|
||||
(unsigned)std::pow(2, std::floor(std::log2(this->numRange))))) {
|
||||
return parent->Error("SILPass: Failed to read valid searchRange");
|
||||
|
||||
// The following three fields are deprecated and ignored. We fix them up here
|
||||
// just for internal consistency, but the Graphite engine doesn't care.
|
||||
if (!table.ReadU16(&this->searchRange) ||
|
||||
!table.ReadU16(&this->entrySelector) ||
|
||||
!table.ReadU16(&this->rangeShift)) {
|
||||
return parent->Error("SILPass: Failed to read searchRange..rangeShift");
|
||||
}
|
||||
if (!table.ReadU16(&this->entrySelector) || this->entrySelector !=
|
||||
(this->numRange == 0 ? 0 : // protect against log2(0)
|
||||
(unsigned)std::floor(std::log2(this->numRange)))) {
|
||||
return parent->Error("SILPass: Failed to read valid entrySelector");
|
||||
}
|
||||
if (!table.ReadU16(&this->rangeShift) ||
|
||||
this->rangeShift != this->numRange - this->searchRange) {
|
||||
return parent->Error("SILPass: Failed to read valid rangeShift");
|
||||
if (this->numRange == 0) {
|
||||
if (this->searchRange != 0 || this->entrySelector != 0 || this->rangeShift != 0) {
|
||||
this->searchRange = this->entrySelector = this->rangeShift = 0;
|
||||
}
|
||||
} else {
|
||||
unsigned floorLog2 = std::floor(std::log2(this->numRange));
|
||||
if (this->searchRange != 6 * (unsigned)std::pow(2, floorLog2) ||
|
||||
this->entrySelector != floorLog2 ||
|
||||
this->rangeShift != 6 * this->numRange - this->searchRange) {
|
||||
this->searchRange = 6 * (unsigned)std::pow(2, floorLog2);
|
||||
this->entrySelector = floorLog2;
|
||||
this->rangeShift = 6 * this->numRange - this->searchRange;
|
||||
}
|
||||
}
|
||||
|
||||
//this->ranges.resize(this->numRange, parent);
|
||||
|
|
|
@ -22,19 +22,27 @@ bool OpenTypeSILL::Parse(const uint8_t* data, size_t length) {
|
|||
if (!table.ReadU16(&this->numLangs)) {
|
||||
return Drop("Failed to read numLangs");
|
||||
}
|
||||
if (!table.ReadU16(&this->searchRange) || this->searchRange !=
|
||||
(this->numLangs == 0 ? 0 : // protect against log2(0)
|
||||
(unsigned)std::pow(2, std::floor(std::log2(this->numLangs))))) {
|
||||
return Drop("Failed to read valid searchRange");
|
||||
|
||||
// The following three fields are deprecated and ignored. We fix them up here
|
||||
// just for internal consistency, but the Graphite engine doesn't care.
|
||||
if (!table.ReadU16(&this->searchRange) ||
|
||||
!table.ReadU16(&this->entrySelector) ||
|
||||
!table.ReadU16(&this->rangeShift)) {
|
||||
return Drop("Failed to read searchRange..rangeShift");
|
||||
}
|
||||
if (!table.ReadU16(&this->entrySelector) || this->entrySelector !=
|
||||
(this->numLangs == 0 ? 0 : // protect against log2(0)
|
||||
(unsigned)std::floor(std::log2(this->numLangs)))) {
|
||||
return Drop("Failed to read valid entrySelector");
|
||||
}
|
||||
if (!table.ReadU16(&this->rangeShift) ||
|
||||
this->rangeShift != this->numLangs - this->searchRange) {
|
||||
return Drop("Failed to read valid rangeShift");
|
||||
if (this->numLangs == 0) {
|
||||
if (this->searchRange != 0 || this->entrySelector != 0 || this->rangeShift != 0) {
|
||||
this->searchRange = this->entrySelector = this->rangeShift = 0;
|
||||
}
|
||||
} else {
|
||||
unsigned floorLog2 = std::floor(std::log2(this->numLangs));
|
||||
if (this->searchRange != (unsigned)std::pow(2, floorLog2) ||
|
||||
this->entrySelector != floorLog2 ||
|
||||
this->rangeShift != this->numLangs - this->searchRange) {
|
||||
this->searchRange = (unsigned)std::pow(2, floorLog2);
|
||||
this->entrySelector = floorLog2;
|
||||
this->rangeShift = this->numLangs - this->searchRange;
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_set<size_t> unverified;
|
||||
|
|
|
@ -81,3 +81,24 @@ LZ4::decompress(const char* aSource, size_t aInputSize, char* aDest,
|
|||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
LZ4::decompressPartial(const char* aSource, size_t aInputSize, char* aDest,
|
||||
size_t aMaxOutputSize, size_t* aOutputSize)
|
||||
{
|
||||
CheckedInt<int> maxOutputSizeChecked = aMaxOutputSize;
|
||||
MOZ_ASSERT(maxOutputSizeChecked.isValid());
|
||||
CheckedInt<int> inputSizeChecked = aInputSize;
|
||||
MOZ_ASSERT(inputSizeChecked.isValid());
|
||||
|
||||
int ret = LZ4_decompress_safe_partial(aSource, aDest,
|
||||
inputSizeChecked.value(),
|
||||
maxOutputSizeChecked.value(),
|
||||
maxOutputSizeChecked.value());
|
||||
if (ret >= 0) {
|
||||
*aOutputSize = ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
*aOutputSize = 0;
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -96,6 +96,29 @@ public:
|
|||
decompress(const char* aSource, size_t aInputSize, char* aDest,
|
||||
size_t aMaxOutputSize, size_t* aOutputSize);
|
||||
|
||||
/**
|
||||
* If the source stream is malformed, the function will stop decoding
|
||||
* and return false.
|
||||
*
|
||||
* This function never writes beyond aDest + aMaxOutputSize, and is
|
||||
* therefore protected against malicious data packets. It also ignores
|
||||
* unconsumed input upon reaching aMaxOutputSize and can therefore be used
|
||||
* for partial decompression.
|
||||
*
|
||||
* Note: Destination buffer must be already allocated. This version is
|
||||
* slightly slower than the decompress without the aMaxOutputSize.
|
||||
*
|
||||
* @param aInputSize is the length of the input compressed data
|
||||
* @param aMaxOutputSize is the size of the destination buffer (which must be
|
||||
* already allocated)
|
||||
* @param aOutputSize the actual number of bytes decoded in the destination
|
||||
* buffer (necessarily <= aMaxOutputSize)
|
||||
* @return true on success, false on failure
|
||||
*/
|
||||
static MFBT_API MOZ_MUST_USE bool
|
||||
decompressPartial(const char* aSource, size_t aInputSize, char* aDest,
|
||||
size_t aMaxOutputSize, size_t* aOutputSize);
|
||||
|
||||
/*
|
||||
* Provides the maximum size that LZ4 may output in a "worst case"
|
||||
* scenario (input data not compressible) primarily useful for memory
|
||||
|
|
Загрузка…
Ссылка в новой задаче