mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-05 23:38:38 +09:00
Merge remote-tracking branch 'origin/master' into custom
This commit is contained in:
commit
d2b5ffafc2
210 changed files with 10739 additions and 14561 deletions
|
|
@ -1198,7 +1198,7 @@ BrowserGlue.prototype = {
|
|||
},
|
||||
|
||||
_migrateUI: function BG__migrateUI() {
|
||||
const UI_VERSION = 19;
|
||||
const UI_VERSION = 20;
|
||||
const BROWSER_DOCURL = "chrome://browser/content/browser.xul#";
|
||||
let currentUIVersion = 0;
|
||||
try {
|
||||
|
|
@ -1433,6 +1433,11 @@ BrowserGlue.prototype = {
|
|||
}
|
||||
#endif
|
||||
|
||||
if (currentUIVersion < 20) {
|
||||
// HPKP change of UI preference; reset enforcement level
|
||||
Services.prefs.clearUserPref("security.cert_pinning.enforcement_level");
|
||||
}
|
||||
|
||||
// Update the migration version.
|
||||
Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ var gSecurityPane = {
|
|||
{
|
||||
this._pane = document.getElementById("paneSecurity");
|
||||
this._initMasterPasswordUI();
|
||||
this._initHPKPUI();
|
||||
},
|
||||
|
||||
// ADD-ONS
|
||||
|
|
@ -233,31 +232,5 @@ var gSecurityPane = {
|
|||
document.documentElement.openWindow("Toolkit:PasswordManager",
|
||||
"chrome://passwordmgr/content/passwordManager.xul",
|
||||
"", null);
|
||||
},
|
||||
|
||||
_initHPKPUI: function() {
|
||||
let checkbox = document.getElementById("enableHPKP");
|
||||
let HPKPpref = document.getElementById("security.cert_pinning.enforcement_level");
|
||||
|
||||
if (HPKPpref.value == 0) {
|
||||
checkbox.checked = false;
|
||||
} else {
|
||||
checkbox.checked = true;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the HPKP enforcement level to the proper value depending on checkbox
|
||||
* state.
|
||||
*/
|
||||
updateHPKPPref: function() {
|
||||
let checkbox = document.getElementById("enableHPKP");
|
||||
let HPKPpref = document.getElementById("security.cert_pinning.enforcement_level");
|
||||
|
||||
if (checkbox.checked) {
|
||||
HPKPpref.value = 2;
|
||||
} else {
|
||||
HPKPpref.value = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -46,9 +46,9 @@
|
|||
<preference id="network.stricttransportsecurity.enabled"
|
||||
name="network.stricttransportsecurity.enabled"
|
||||
type="bool"/>
|
||||
<preference id="security.cert_pinning.enforcement_level"
|
||||
name="security.cert_pinning.enforcement_level"
|
||||
type="int"/>
|
||||
<preference id="security.cert_pinning.hpkp.enabled"
|
||||
name="security.cert_pinning.hpkp.enabled"
|
||||
type="bool"/>
|
||||
|
||||
<!-- Opportunistic Encryption -->
|
||||
|
||||
|
|
@ -150,7 +150,7 @@
|
|||
<checkbox id="enableHPKP"
|
||||
label="&enableHPKP.label;"
|
||||
accesskey="&enableHPKP.accesskey;"
|
||||
oncommand="gSecurityPane.updateHPKPPref();"/>
|
||||
preference="security.cert_pinning.hpkp.enabled"/>
|
||||
</vbox>
|
||||
</groupbox>
|
||||
|
||||
|
|
|
|||
|
|
@ -14,14 +14,18 @@ const SJS_URL = "https://example.com/browser/devtools/client/webconsole/" +
|
|||
"test/test_hpkp-invalid-headers.sjs";
|
||||
const LEARN_MORE_URI = "https://developer.mozilla.org/docs/Web/Security/" +
|
||||
"Public_Key_Pinning" + DOCS_GA_PARAMS;
|
||||
const HPKP_ENABLED_PREF = "security.cert_pinning.hpkp.enabled";
|
||||
const NON_BUILTIN_ROOT_PREF = "security.cert_pinning.process_headers_from_" +
|
||||
"non_builtin_roots";
|
||||
|
||||
add_task(function* () {
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.clearUserPref(HPKP_ENABLED_PREF);
|
||||
Services.prefs.clearUserPref(NON_BUILTIN_ROOT_PREF);
|
||||
});
|
||||
|
||||
Services.prefs.setBoolPref(HPKP_ENABLED_PREF, true);
|
||||
|
||||
yield loadTab(TEST_URI);
|
||||
|
||||
let hud = yield openConsole();
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@
|
|||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
let gCurrentTestCase = -1;
|
||||
const HPKP_PREF = "security.cert_pinning.process_headers_from_non_builtin_roots";
|
||||
const HPKP_ENABLED_PREF = "security.cert_pinning.hpkp.enabled";
|
||||
const PROCESS_HPKP_FROM_NON_BUILTIN_ROOTS_PREF = "security.cert_pinning.process_headers_from_non_builtin_roots";
|
||||
|
||||
// Static pins tested by unit/test_security-info-static-hpkp.js.
|
||||
const TEST_CASES = [
|
||||
|
|
@ -41,11 +42,11 @@ const TEST_CASES = [
|
|||
|
||||
function startTest()
|
||||
{
|
||||
// Need to enable this pref or pinning headers are rejected due test
|
||||
// certificate.
|
||||
Services.prefs.setBoolPref(HPKP_PREF, true);
|
||||
Services.prefs.setBoolPref(HPKP_ENABLED_PREF, true);
|
||||
Services.prefs.setBoolPref(PROCESS_HPKP_FROM_NON_BUILTIN_ROOTS_PREF, true);
|
||||
SimpleTest.registerCleanupFunction(() => {
|
||||
Services.prefs.setBoolPref(HPKP_PREF, false);
|
||||
Services.prefs.setBoolPref(HPKP_ENABLED_PREF, false);
|
||||
Services.prefs.setBoolPref(PROCESS_HPKP_FROM_NON_BUILTIN_ROOTS_PREF, false);
|
||||
|
||||
// Reset pinning state.
|
||||
let gSSService = Cc["@mozilla.org/ssservice;1"]
|
||||
|
|
|
|||
|
|
@ -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: f87b4556191e4132ef5c47365762eb88ace97fc3 (6.0.0)
|
||||
Current revision: 8bba749d9d5401726a7d7609ab914fdb5e92bfbe (8.0.0)
|
||||
|
||||
Upstream files included: LICENSE, src/, include/, tests/*.cc
|
||||
|
||||
|
|
|
|||
|
|
@ -35,13 +35,17 @@ typedef int int32_t;
|
|||
typedef unsigned int uint32_t;
|
||||
typedef __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
#define ntohl(x) _byteswap_ulong (x)
|
||||
#define ntohs(x) _byteswap_ushort (x)
|
||||
#define htonl(x) _byteswap_ulong (x)
|
||||
#define htons(x) _byteswap_ushort (x)
|
||||
#define ots_ntohl(x) _byteswap_ulong (x)
|
||||
#define ots_ntohs(x) _byteswap_ushort (x)
|
||||
#define ots_htonl(x) _byteswap_ulong (x)
|
||||
#define ots_htons(x) _byteswap_ushort (x)
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#include <stdint.h>
|
||||
#define ots_ntohl(x) ntohl (x)
|
||||
#define ots_ntohs(x) ntohs (x)
|
||||
#define ots_htonl(x) htonl (x)
|
||||
#define ots_htons(x) htons (x)
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
|
@ -80,7 +84,7 @@ class OTSStream {
|
|||
const size_t l = std::min(length, static_cast<size_t>(4) - chksum_offset);
|
||||
uint32_t tmp = 0;
|
||||
std::memcpy(reinterpret_cast<uint8_t *>(&tmp) + chksum_offset, data, l);
|
||||
chksum_ += ntohl(tmp);
|
||||
chksum_ += ots_ntohl(tmp);
|
||||
length -= l;
|
||||
offset += l;
|
||||
}
|
||||
|
|
@ -89,7 +93,7 @@ class OTSStream {
|
|||
uint32_t tmp;
|
||||
std::memcpy(&tmp, reinterpret_cast<const uint8_t *>(data) + offset,
|
||||
sizeof(uint32_t));
|
||||
chksum_ += ntohl(tmp);
|
||||
chksum_ += ots_ntohl(tmp);
|
||||
length -= 4;
|
||||
offset += 4;
|
||||
}
|
||||
|
|
@ -99,7 +103,7 @@ class OTSStream {
|
|||
uint32_t tmp = 0;
|
||||
std::memcpy(&tmp,
|
||||
reinterpret_cast<const uint8_t*>(data) + offset, length);
|
||||
chksum_ += ntohl(tmp);
|
||||
chksum_ += ots_ntohl(tmp);
|
||||
}
|
||||
|
||||
return WriteRaw(data, orig_length);
|
||||
|
|
@ -127,27 +131,27 @@ class OTSStream {
|
|||
}
|
||||
|
||||
bool WriteU16(uint16_t v) {
|
||||
v = htons(v);
|
||||
v = ots_htons(v);
|
||||
return Write(&v, sizeof(v));
|
||||
}
|
||||
|
||||
bool WriteS16(int16_t v) {
|
||||
v = htons(v);
|
||||
v = ots_htons(v);
|
||||
return Write(&v, sizeof(v));
|
||||
}
|
||||
|
||||
bool WriteU24(uint32_t v) {
|
||||
v = htonl(v);
|
||||
v = ots_htonl(v);
|
||||
return Write(reinterpret_cast<uint8_t*>(&v)+1, 3);
|
||||
}
|
||||
|
||||
bool WriteU32(uint32_t v) {
|
||||
v = htonl(v);
|
||||
v = ots_htonl(v);
|
||||
return Write(&v, sizeof(v));
|
||||
}
|
||||
|
||||
bool WriteS32(int32_t v) {
|
||||
v = htonl(v);
|
||||
v = ots_htonl(v);
|
||||
return Write(&v, sizeof(v));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
diff --git a/gfx/ots/src/glat.cc b/gfx/ots/src/glat.cc
|
||||
--- a/gfx/ots/src/glat.cc
|
||||
+++ b/gfx/ots/src/glat.cc
|
||||
@@ -5,7 +5,7 @@
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
#include "glat.h"
|
||||
|
||||
#include "gloc.h"
|
||||
|
|
@ -10,10 +11,12 @@ diff --git a/gfx/ots/src/glat.cc b/gfx/ots/src/glat.cc
|
|||
#include <list>
|
||||
|
||||
namespace ots {
|
||||
@@ -201,14 +201,15 @@ bool OpenTypeGLAT_v3::Parse(const uint8_t* data, size_t length,
|
||||
return DropGraphite("Illegal nested compression");
|
||||
|
||||
@@ -212,16 +212,17 @@ bool OpenTypeGLAT_v3::Parse(const uint8_
|
||||
return DropGraphite("Decompressed size exceeds 30MB: %gMB",
|
||||
decompressed_size / (1024.0 * 1024.0));
|
||||
}
|
||||
std::vector<uint8_t> decompressed(this->compHead & FULL_SIZE);
|
||||
std::vector<uint8_t> decompressed(decompressed_size);
|
||||
- int ret = LZ4_decompress_safe_partial(
|
||||
+ size_t outputSize = 0;
|
||||
+ bool ret = mozilla::Compression::LZ4::decompressPartial(
|
||||
|
|
@ -23,7 +26,7 @@ diff --git a/gfx/ots/src/glat.cc b/gfx/ots/src/glat.cc
|
|||
+ reinterpret_cast<char*>(decompressed.data()),
|
||||
decompressed.size(), // target output size
|
||||
- decompressed.size()); // output buffer size
|
||||
- if (ret != decompressed.size()) {
|
||||
- if (ret < 0 || unsigned(ret) != decompressed.size()) {
|
||||
- return DropGraphite("Decompression failed with error code %d", ret);
|
||||
+ &outputSize); // return output size
|
||||
+ if (!ret || outputSize != decompressed.size()) {
|
||||
|
|
@ -31,10 +34,12 @@ diff --git a/gfx/ots/src/glat.cc b/gfx/ots/src/glat.cc
|
|||
}
|
||||
return this->Parse(decompressed.data(), decompressed.size(), true);
|
||||
}
|
||||
default:
|
||||
diff --git a/gfx/ots/src/silf.cc b/gfx/ots/src/silf.cc
|
||||
--- a/gfx/ots/src/silf.cc
|
||||
+++ b/gfx/ots/src/silf.cc
|
||||
@@ -5,7 +5,7 @@
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
#include "silf.h"
|
||||
|
||||
#include "name.h"
|
||||
|
|
@ -43,10 +48,12 @@ diff --git a/gfx/ots/src/silf.cc b/gfx/ots/src/silf.cc
|
|||
#include <cmath>
|
||||
|
||||
namespace ots {
|
||||
@@ -39,14 +39,15 @@ bool OpenTypeSILF::Parse(const uint8_t* data, size_t length,
|
||||
return DropGraphite("Illegal nested compression");
|
||||
|
||||
@@ -50,16 +50,17 @@ bool OpenTypeSILF::Parse(const uint8_t*
|
||||
return DropGraphite("Decompressed size exceeds 30MB: %gMB",
|
||||
decompressed_size / (1024.0 * 1024.0));
|
||||
}
|
||||
std::vector<uint8_t> decompressed(this->compHead & FULL_SIZE);
|
||||
std::vector<uint8_t> decompressed(decompressed_size);
|
||||
- int ret = LZ4_decompress_safe_partial(
|
||||
+ size_t outputSize = 0;
|
||||
+ bool ret = mozilla::Compression::LZ4::decompressPartial(
|
||||
|
|
@ -56,7 +63,7 @@ diff --git a/gfx/ots/src/silf.cc b/gfx/ots/src/silf.cc
|
|||
+ reinterpret_cast<char*>(decompressed.data()),
|
||||
decompressed.size(), // target output size
|
||||
- decompressed.size()); // output buffer size
|
||||
- if (ret != decompressed.size()) {
|
||||
- if (ret < 0 || unsigned(ret) != decompressed.size()) {
|
||||
- return DropGraphite("Decompression failed with error code %d", ret);
|
||||
+ &outputSize); // return output size
|
||||
+ if (!ret || outputSize != decompressed.size()) {
|
||||
|
|
@ -64,3 +71,4 @@ diff --git a/gfx/ots/src/silf.cc b/gfx/ots/src/silf.cc
|
|||
}
|
||||
return this->Parse(decompressed.data(), decompressed.size(), true);
|
||||
}
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
diff --git a/gfx/ots/include/opentype-sanitiser.h b/gfx/ots/include/opentype-sanitiser.h
|
||||
--- a/gfx/ots/include/opentype-sanitiser.h
|
||||
+++ b/gfx/ots/include/opentype-sanitiser.h
|
||||
@@ -5,6 +5,26 @@
|
||||
@@ -4,8 +4,28 @@
|
||||
|
||||
#ifndef OPENTYPE_SANITISER_H_
|
||||
#define OPENTYPE_SANITISER_H_
|
||||
|
||||
|
|
@ -28,7 +29,9 @@ diff --git a/gfx/ots/include/opentype-sanitiser.h b/gfx/ots/include/opentype-san
|
|||
#if defined(_WIN32)
|
||||
#include <stdlib.h>
|
||||
typedef signed char int8_t;
|
||||
@@ -161,7 +181,7 @@ enum TableAction {
|
||||
typedef unsigned char uint8_t;
|
||||
@@ -164,9 +184,9 @@ enum TableAction {
|
||||
TABLE_ACTION_PASSTHRU, // Serialize the table unchanged
|
||||
TABLE_ACTION_DROP // Drop the table
|
||||
};
|
||||
|
||||
|
|
@ -37,3 +40,4 @@ diff --git a/gfx/ots/include/opentype-sanitiser.h b/gfx/ots/include/opentype-san
|
|||
public:
|
||||
OTSContext() {}
|
||||
virtual ~OTSContext() {}
|
||||
|
||||
|
|
|
|||
109
gfx/ots/src/avar.cc
Normal file
109
gfx/ots/src/avar.cc
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
// Copyright (c) 2018 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "avar.h"
|
||||
|
||||
#include "fvar.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeAVAR
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool OpenTypeAVAR::Parse(const uint8_t* data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
if (!table.ReadU16(&this->majorVersion) ||
|
||||
!table.ReadU16(&this->minorVersion) ||
|
||||
!table.ReadU16(&this->reserved) ||
|
||||
!table.ReadU16(&this->axisCount)) {
|
||||
return Drop("Failed to read table header");
|
||||
}
|
||||
if (this->majorVersion != 1) {
|
||||
return Drop("Unknown table version");
|
||||
}
|
||||
if (this->minorVersion > 0) {
|
||||
// we only know how to serialize version 1.0
|
||||
Warning("Downgrading minor version to 0");
|
||||
this->minorVersion = 0;
|
||||
}
|
||||
if (this->reserved != 0) {
|
||||
Warning("Expected reserved=0");
|
||||
this->reserved = 0;
|
||||
}
|
||||
|
||||
OpenTypeFVAR* fvar = static_cast<OpenTypeFVAR*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_FVAR));
|
||||
if (!fvar) {
|
||||
return DropVariations("Required fvar table is missing");
|
||||
}
|
||||
if (axisCount != fvar->AxisCount()) {
|
||||
return Drop("Axis count mismatch");
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < this->axisCount; i++) {
|
||||
this->axisSegmentMaps.emplace_back();
|
||||
uint16_t positionMapCount;
|
||||
if (!table.ReadU16(&positionMapCount)) {
|
||||
return Drop("Failed to read position map count");
|
||||
}
|
||||
int foundRequiredMappings = 0;
|
||||
for (size_t j = 0; j < positionMapCount; j++) {
|
||||
AxisValueMap map;
|
||||
if (!table.ReadS16(&map.fromCoordinate) ||
|
||||
!table.ReadS16(&map.toCoordinate)) {
|
||||
return Drop("Failed to read axis value map");
|
||||
}
|
||||
if (map.fromCoordinate < -0x4000 ||
|
||||
map.fromCoordinate > 0x4000 ||
|
||||
map.toCoordinate < -0x4000 ||
|
||||
map.toCoordinate > 0x4000) {
|
||||
return Drop("Axis value map coordinate out of range");
|
||||
}
|
||||
if (j > 0) {
|
||||
if (map.fromCoordinate <= this->axisSegmentMaps[i].back().fromCoordinate ||
|
||||
map.toCoordinate < this->axisSegmentMaps[i].back().toCoordinate) {
|
||||
return Drop("Axis value map out of order");
|
||||
}
|
||||
}
|
||||
if ((map.fromCoordinate == -0x4000 && map.toCoordinate == -0x4000) ||
|
||||
(map.fromCoordinate == 0 && map.toCoordinate == 0) ||
|
||||
(map.fromCoordinate == 0x4000 && map.toCoordinate == 0x4000)) {
|
||||
++foundRequiredMappings;
|
||||
}
|
||||
this->axisSegmentMaps[i].push_back(map);
|
||||
}
|
||||
if (positionMapCount > 0 && foundRequiredMappings != 3) {
|
||||
return Drop("A required mapping (for -1, 0 or 1) is missing");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeAVAR::Serialize(OTSStream* out) {
|
||||
if (!out->WriteU16(this->majorVersion) ||
|
||||
!out->WriteU16(this->minorVersion) ||
|
||||
!out->WriteU16(this->reserved) ||
|
||||
!out->WriteU16(this->axisCount)) {
|
||||
return Error("Failed to write table");
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < this->axisCount; i++) {
|
||||
const auto& axisValueMap = this->axisSegmentMaps[i];
|
||||
if (!out->WriteU16(axisValueMap.size())) {
|
||||
return Error("Failed to write table");
|
||||
}
|
||||
for (size_t j = 0; j < axisValueMap.size(); j++) {
|
||||
if (!out->WriteS16(axisValueMap[j].fromCoordinate) ||
|
||||
!out->WriteS16(axisValueMap[j].toCoordinate)) {
|
||||
return Error("Failed to write table");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
42
gfx/ots/src/avar.h
Normal file
42
gfx/ots/src/avar.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) 2018 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_AVAR_H_
|
||||
#define OTS_AVAR_H_
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeAVAR Interface
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class OpenTypeAVAR : public Table {
|
||||
public:
|
||||
explicit OpenTypeAVAR(Font* font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t* data, size_t length);
|
||||
bool Serialize(OTSStream* out);
|
||||
|
||||
private:
|
||||
uint16_t majorVersion;
|
||||
uint16_t minorVersion;
|
||||
uint16_t reserved;
|
||||
uint16_t axisCount;
|
||||
|
||||
struct AxisValueMap {
|
||||
int16_t fromCoordinate;
|
||||
int16_t toCoordinate;
|
||||
};
|
||||
|
||||
std::vector<std::vector<AxisValueMap>> axisSegmentMaps;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_AVAR_H_
|
||||
1435
gfx/ots/src/cff.cc
1435
gfx/ots/src/cff.cc
File diff suppressed because it is too large
Load diff
|
|
@ -11,22 +11,28 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#undef major // glibc defines major!
|
||||
|
||||
namespace ots {
|
||||
|
||||
struct CFFIndex {
|
||||
CFFIndex()
|
||||
: count(0), off_size(0), offset_to_next(0) {}
|
||||
uint16_t count;
|
||||
uint32_t count;
|
||||
uint8_t off_size;
|
||||
std::vector<uint32_t> offsets;
|
||||
uint32_t offset_to_next;
|
||||
};
|
||||
|
||||
typedef std::map<uint32_t, uint16_t> CFFFDSelect;
|
||||
|
||||
class OpenTypeCFF : public Table {
|
||||
public:
|
||||
explicit OpenTypeCFF(Font *font, uint32_t tag)
|
||||
: Table(font, tag, tag),
|
||||
major(0),
|
||||
font_dict_length(0),
|
||||
charstrings_index(NULL),
|
||||
local_subrs(NULL),
|
||||
m_data(NULL),
|
||||
m_length(0) {
|
||||
|
|
@ -37,21 +43,46 @@ class OpenTypeCFF : public Table {
|
|||
bool Parse(const uint8_t *data, size_t length);
|
||||
bool Serialize(OTSStream *out);
|
||||
|
||||
// Major version number.
|
||||
uint8_t major;
|
||||
|
||||
// Name INDEX. This name is used in name.cc as a postscript font name.
|
||||
std::string name;
|
||||
|
||||
// The number of fonts the file has.
|
||||
size_t font_dict_length;
|
||||
// A map from glyph # to font #.
|
||||
std::map<uint16_t, uint8_t> fd_select;
|
||||
CFFFDSelect fd_select;
|
||||
|
||||
// A list of char strings.
|
||||
std::vector<CFFIndex *> char_strings_array;
|
||||
CFFIndex* charstrings_index;
|
||||
// A list of Local Subrs associated with FDArrays. Can be empty.
|
||||
std::vector<CFFIndex *> local_subrs_per_font;
|
||||
// A Local Subrs associated with Top DICT. Can be NULL.
|
||||
CFFIndex *local_subrs;
|
||||
|
||||
// CFF2 VariationStore regionIndexCount.
|
||||
std::vector<uint16_t> region_index_count;
|
||||
|
||||
protected:
|
||||
bool ValidateFDSelect(uint16_t num_glyphs);
|
||||
|
||||
private:
|
||||
const uint8_t *m_data;
|
||||
size_t m_length;
|
||||
};
|
||||
|
||||
class OpenTypeCFF2 : public OpenTypeCFF {
|
||||
public:
|
||||
explicit OpenTypeCFF2(Font *font, uint32_t tag)
|
||||
: OpenTypeCFF(font, tag),
|
||||
m_data(NULL),
|
||||
m_length(0) {
|
||||
}
|
||||
|
||||
bool Parse(const uint8_t *data, size_t length);
|
||||
bool Serialize(OTSStream *out);
|
||||
|
||||
private:
|
||||
const uint8_t *m_data;
|
||||
size_t m_length;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
// A parser for the Type 2 Charstring Format.
|
||||
// http://www.adobe.com/devnet/font/pdfs/5177.Type2.pdf
|
||||
|
||||
#include "cff_type2_charstring.h"
|
||||
#include "cff_charstring.h"
|
||||
|
||||
#include <climits>
|
||||
#include <cstdio>
|
||||
|
|
@ -22,7 +22,6 @@ namespace {
|
|||
// Note #5177.
|
||||
const int32_t kMaxSubrsCount = 65536;
|
||||
const size_t kMaxCharStringLength = 65535;
|
||||
const size_t kMaxArgumentStack = 48;
|
||||
const size_t kMaxNumberOfStemHints = 96;
|
||||
const size_t kMaxSubrNesting = 10;
|
||||
|
||||
|
|
@ -30,117 +29,130 @@ const size_t kMaxSubrNesting = 10;
|
|||
// will fail with the dummy value.
|
||||
const int32_t dummy_result = INT_MAX;
|
||||
|
||||
bool ExecuteType2CharString(ots::Font *font,
|
||||
size_t call_depth,
|
||||
const ots::CFFIndex& global_subrs_index,
|
||||
const ots::CFFIndex& local_subrs_index,
|
||||
ots::Buffer *cff_table,
|
||||
ots::Buffer *char_string,
|
||||
std::stack<int32_t> *argument_stack,
|
||||
bool *out_found_endchar,
|
||||
bool *out_found_width,
|
||||
size_t *in_out_num_stems);
|
||||
bool ExecuteCharString(ots::OpenTypeCFF& cff,
|
||||
size_t call_depth,
|
||||
const ots::CFFIndex& global_subrs_index,
|
||||
const ots::CFFIndex& local_subrs_index,
|
||||
ots::Buffer *cff_table,
|
||||
ots::Buffer *char_string,
|
||||
std::stack<int32_t> *argument_stack,
|
||||
bool *out_found_endchar,
|
||||
bool *out_found_width,
|
||||
size_t *in_out_num_stems,
|
||||
bool cff2);
|
||||
|
||||
bool ArgumentStackOverflows(std::stack<int32_t> *argument_stack, bool cff2) {
|
||||
if ((cff2 && argument_stack->size() > ots::kMaxCFF2ArgumentStack) ||
|
||||
(!cff2 && argument_stack->size() > ots::kMaxCFF1ArgumentStack)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DUMP_T2CHARSTRING
|
||||
// Converts |op| to a string and returns it.
|
||||
const char *Type2CharStringOperatorToString(ots::Type2CharStringOperator op) {
|
||||
const char *CharStringOperatorToString(ots::CharStringOperator op) {
|
||||
switch (op) {
|
||||
case ots::kHStem:
|
||||
return "HStem";
|
||||
return "hstem";
|
||||
case ots::kVStem:
|
||||
return "VStem";
|
||||
return "vstem";
|
||||
case ots::kVMoveTo:
|
||||
return "VMoveTo";
|
||||
return "vmoveto";
|
||||
case ots::kRLineTo:
|
||||
return "RLineTo";
|
||||
return "rlineto";
|
||||
case ots::kHLineTo:
|
||||
return "HLineTo";
|
||||
return "hlineto";
|
||||
case ots::kVLineTo:
|
||||
return "VLineTo";
|
||||
return "vlineto";
|
||||
case ots::kRRCurveTo:
|
||||
return "RRCurveTo";
|
||||
return "rrcurveto";
|
||||
case ots::kCallSubr:
|
||||
return "CallSubr";
|
||||
return "callsubr";
|
||||
case ots::kReturn:
|
||||
return "Return";
|
||||
return "return";
|
||||
case ots::kEndChar:
|
||||
return "EndChar";
|
||||
return "endchar";
|
||||
case ots::kVSIndex:
|
||||
return "vsindex";
|
||||
case ots::kBlend:
|
||||
return "blend";
|
||||
case ots::kHStemHm:
|
||||
return "HStemHm";
|
||||
return "hstemhm";
|
||||
case ots::kHintMask:
|
||||
return "HintMask";
|
||||
return "hintmask";
|
||||
case ots::kCntrMask:
|
||||
return "CntrMask";
|
||||
return "cntrmask";
|
||||
case ots::kRMoveTo:
|
||||
return "RMoveTo";
|
||||
return "rmoveto";
|
||||
case ots::kHMoveTo:
|
||||
return "HMoveTo";
|
||||
return "hmoveto";
|
||||
case ots::kVStemHm:
|
||||
return "VStemHm";
|
||||
return "vstemhm";
|
||||
case ots::kRCurveLine:
|
||||
return "RCurveLine";
|
||||
return "rcurveline";
|
||||
case ots::kRLineCurve:
|
||||
return "RLineCurve";
|
||||
return "rlinecurve";
|
||||
case ots::kVVCurveTo:
|
||||
return "VVCurveTo";
|
||||
case ots::kHHCurveTo:
|
||||
return "HHCurveTo";
|
||||
return "hhcurveto";
|
||||
case ots::kCallGSubr:
|
||||
return "CallGSubr";
|
||||
return "callgsubr";
|
||||
case ots::kVHCurveTo:
|
||||
return "VHCurveTo";
|
||||
return "vhcurveto";
|
||||
case ots::kHVCurveTo:
|
||||
return "HVCurveTo";
|
||||
case ots::kDotSection:
|
||||
return "DotSection";
|
||||
return "dotsection";
|
||||
case ots::kAnd:
|
||||
return "And";
|
||||
return "and";
|
||||
case ots::kOr:
|
||||
return "Or";
|
||||
return "or";
|
||||
case ots::kNot:
|
||||
return "Not";
|
||||
return "not";
|
||||
case ots::kAbs:
|
||||
return "Abs";
|
||||
return "abs";
|
||||
case ots::kAdd:
|
||||
return "Add";
|
||||
return "add";
|
||||
case ots::kSub:
|
||||
return "Sub";
|
||||
return "sub";
|
||||
case ots::kDiv:
|
||||
return "Div";
|
||||
return "div";
|
||||
case ots::kNeg:
|
||||
return "Neg";
|
||||
return "neg";
|
||||
case ots::kEq:
|
||||
return "Eq";
|
||||
return "eq";
|
||||
case ots::kDrop:
|
||||
return "Drop";
|
||||
return "drop";
|
||||
case ots::kPut:
|
||||
return "Put";
|
||||
return "put";
|
||||
case ots::kGet:
|
||||
return "Get";
|
||||
return "get";
|
||||
case ots::kIfElse:
|
||||
return "IfElse";
|
||||
return "ifelse";
|
||||
case ots::kRandom:
|
||||
return "Random";
|
||||
return "random";
|
||||
case ots::kMul:
|
||||
return "Mul";
|
||||
return "mul";
|
||||
case ots::kSqrt:
|
||||
return "Sqrt";
|
||||
return "sqrt";
|
||||
case ots::kDup:
|
||||
return "Dup";
|
||||
return "dup";
|
||||
case ots::kExch:
|
||||
return "Exch";
|
||||
return "exch";
|
||||
case ots::kIndex:
|
||||
return "Index";
|
||||
return "index";
|
||||
case ots::kRoll:
|
||||
return "Roll";
|
||||
return "roll";
|
||||
case ots::kHFlex:
|
||||
return "HFlex";
|
||||
return "hflex";
|
||||
case ots::kFlex:
|
||||
return "Flex";
|
||||
return "flex";
|
||||
case ots::kHFlex1:
|
||||
return "HFlex1";
|
||||
return "hflex1";
|
||||
case ots::kFlex1:
|
||||
return "Flex1";
|
||||
return "flex1";
|
||||
}
|
||||
|
||||
return "UNKNOWN";
|
||||
|
|
@ -150,9 +162,9 @@ const char *Type2CharStringOperatorToString(ots::Type2CharStringOperator op) {
|
|||
// Read one or more bytes from the |char_string| buffer and stores the number
|
||||
// read on |out_number|. If the number read is an operator (ex 'vstem'), sets
|
||||
// true on |out_is_operator|. Returns true if the function read a number.
|
||||
bool ReadNextNumberFromType2CharString(ots::Buffer *char_string,
|
||||
int32_t *out_number,
|
||||
bool *out_is_operator) {
|
||||
bool ReadNextNumberFromCharString(ots::Buffer *char_string,
|
||||
int32_t *out_number,
|
||||
bool *out_is_operator) {
|
||||
uint8_t v = 0;
|
||||
if (!char_string->ReadU8(&v)) {
|
||||
return OTS_FAILURE();
|
||||
|
|
@ -174,7 +186,7 @@ bool ReadNextNumberFromType2CharString(ots::Buffer *char_string,
|
|||
*out_is_operator = true;
|
||||
} else if (v <= 27) {
|
||||
// Special handling for v==19 and v==20 are implemented in
|
||||
// ExecuteType2CharStringOperator().
|
||||
// ExecuteCharStringOperator().
|
||||
*out_number = v;
|
||||
*out_is_operator = true;
|
||||
} else if (v == 28) {
|
||||
|
|
@ -221,23 +233,63 @@ bool ReadNextNumberFromType2CharString(ots::Buffer *char_string,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ValidCFF2Operator(int32_t op) {
|
||||
switch (op) {
|
||||
case ots::kReturn:
|
||||
case ots::kEndChar:
|
||||
case ots::kAbs:
|
||||
case ots::kAdd:
|
||||
case ots::kSub:
|
||||
case ots::kDiv:
|
||||
case ots::kNeg:
|
||||
case ots::kRandom:
|
||||
case ots::kMul:
|
||||
case ots::kSqrt:
|
||||
case ots::kDrop:
|
||||
case ots::kExch:
|
||||
case ots::kIndex:
|
||||
case ots::kRoll:
|
||||
case ots::kDup:
|
||||
case ots::kPut:
|
||||
case ots::kGet:
|
||||
case ots::kDotSection:
|
||||
case ots::kAnd:
|
||||
case ots::kOr:
|
||||
case ots::kNot:
|
||||
case ots::kEq:
|
||||
case ots::kIfElse:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Executes |op| and updates |argument_stack|. Returns true if the execution
|
||||
// succeeds. If the |op| is kCallSubr or kCallGSubr, the function recursively
|
||||
// calls ExecuteType2CharString() function. The arguments other than |op| and
|
||||
// calls ExecuteCharString() function. The arguments other than |op| and
|
||||
// |argument_stack| are passed for that reason.
|
||||
bool ExecuteType2CharStringOperator(ots::Font *font,
|
||||
int32_t op,
|
||||
size_t call_depth,
|
||||
const ots::CFFIndex& global_subrs_index,
|
||||
const ots::CFFIndex& local_subrs_index,
|
||||
ots::Buffer *cff_table,
|
||||
ots::Buffer *char_string,
|
||||
std::stack<int32_t> *argument_stack,
|
||||
bool *out_found_endchar,
|
||||
bool *in_out_found_width,
|
||||
size_t *in_out_num_stems) {
|
||||
bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff,
|
||||
int32_t op,
|
||||
size_t call_depth,
|
||||
const ots::CFFIndex& global_subrs_index,
|
||||
const ots::CFFIndex& local_subrs_index,
|
||||
ots::Buffer *cff_table,
|
||||
ots::Buffer *char_string,
|
||||
std::stack<int32_t> *argument_stack,
|
||||
bool *out_found_endchar,
|
||||
bool *in_out_found_width,
|
||||
size_t *in_out_num_stems,
|
||||
bool *in_out_have_blend,
|
||||
bool *in_out_have_visindex,
|
||||
int32_t *in_out_vsindex,
|
||||
bool cff2) {
|
||||
ots::Font* font = cff.GetFont();
|
||||
const size_t stack_size = argument_stack->size();
|
||||
|
||||
if (cff2 && !ValidCFF2Operator(op)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
switch (op) {
|
||||
case ots::kCallSubr:
|
||||
case ots::kCallGSubr: {
|
||||
|
|
@ -290,16 +342,17 @@ bool ExecuteType2CharStringOperator(ots::Font *font,
|
|||
}
|
||||
ots::Buffer char_string_to_jump(cff_table->buffer() + offset, length);
|
||||
|
||||
return ExecuteType2CharString(font,
|
||||
call_depth + 1,
|
||||
global_subrs_index,
|
||||
local_subrs_index,
|
||||
cff_table,
|
||||
&char_string_to_jump,
|
||||
argument_stack,
|
||||
out_found_endchar,
|
||||
in_out_found_width,
|
||||
in_out_num_stems);
|
||||
return ExecuteCharString(cff,
|
||||
call_depth + 1,
|
||||
global_subrs_index,
|
||||
local_subrs_index,
|
||||
cff_table,
|
||||
&char_string_to_jump,
|
||||
argument_stack,
|
||||
out_found_endchar,
|
||||
in_out_found_width,
|
||||
in_out_num_stems,
|
||||
cff2);
|
||||
}
|
||||
|
||||
case ots::kReturn:
|
||||
|
|
@ -310,6 +363,51 @@ bool ExecuteType2CharStringOperator(ots::Font *font,
|
|||
*in_out_found_width = true; // just in case.
|
||||
return true;
|
||||
|
||||
case ots::kVSIndex: {
|
||||
if (!cff2) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (stack_size != 1) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (*in_out_have_blend || *in_out_have_visindex) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (argument_stack->top() >= cff.region_index_count.size()) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
*in_out_have_visindex = true;
|
||||
*in_out_vsindex = argument_stack->top();
|
||||
while (!argument_stack->empty())
|
||||
argument_stack->pop();
|
||||
return true;
|
||||
}
|
||||
|
||||
case ots::kBlend: {
|
||||
if (!cff2) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (stack_size < 1) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (*in_out_vsindex >= cff.region_index_count.size()) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
uint16_t k = cff.region_index_count.at(*in_out_vsindex);
|
||||
uint16_t n = argument_stack->top();
|
||||
if (stack_size < n * (k + 1) + 1) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// Keep the 1st n operands on the stack for the next operator to use and
|
||||
// pop the rest. There can be multiple consecutive blend operator, so this
|
||||
// makes sure the operands of all of them are kept on the stack.
|
||||
while (argument_stack->size() > stack_size - ((n * k) + 1))
|
||||
argument_stack->pop();
|
||||
*in_out_have_blend = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
case ots::kHStem:
|
||||
case ots::kVStem:
|
||||
case ots::kHStemHm:
|
||||
|
|
@ -649,7 +747,7 @@ bool ExecuteType2CharStringOperator(ots::Font *font,
|
|||
argument_stack->pop();
|
||||
argument_stack->push(dummy_result);
|
||||
argument_stack->push(dummy_result);
|
||||
if (argument_stack->size() > kMaxArgumentStack) {
|
||||
if (ArgumentStackOverflows(argument_stack, cff2)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
// TODO(yusukes): Implement this. We should push a real value for all
|
||||
|
|
@ -729,26 +827,29 @@ bool ExecuteType2CharStringOperator(ots::Font *font,
|
|||
// in_out_found_width: true is set if |char_string| contains 'width' byte (which
|
||||
// is 0 or 1 byte.)
|
||||
// in_out_num_stems: total number of hstems and vstems processed so far.
|
||||
bool ExecuteType2CharString(ots::Font *font,
|
||||
size_t call_depth,
|
||||
const ots::CFFIndex& global_subrs_index,
|
||||
const ots::CFFIndex& local_subrs_index,
|
||||
ots::Buffer *cff_table,
|
||||
ots::Buffer *char_string,
|
||||
std::stack<int32_t> *argument_stack,
|
||||
bool *out_found_endchar,
|
||||
bool *in_out_found_width,
|
||||
size_t *in_out_num_stems) {
|
||||
bool ExecuteCharString(ots::OpenTypeCFF& cff,
|
||||
size_t call_depth,
|
||||
const ots::CFFIndex& global_subrs_index,
|
||||
const ots::CFFIndex& local_subrs_index,
|
||||
ots::Buffer *cff_table,
|
||||
ots::Buffer *char_string,
|
||||
std::stack<int32_t> *argument_stack,
|
||||
bool *out_found_endchar,
|
||||
bool *in_out_found_width,
|
||||
size_t *in_out_num_stems,
|
||||
bool cff2) {
|
||||
if (call_depth > kMaxSubrNesting) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
*out_found_endchar = false;
|
||||
|
||||
bool in_out_have_blend = false, in_out_have_visindex = false;
|
||||
int32_t in_out_vsindex = 0;
|
||||
const size_t length = char_string->length();
|
||||
while (char_string->offset() < length) {
|
||||
int32_t operator_or_operand = 0;
|
||||
bool is_operator = false;
|
||||
if (!ReadNextNumberFromType2CharString(char_string,
|
||||
if (!ReadNextNumberFromCharString(char_string,
|
||||
&operator_or_operand,
|
||||
&is_operator)) {
|
||||
return OTS_FAILURE();
|
||||
|
|
@ -761,35 +862,39 @@ bool ExecuteType2CharString(ots::Font *font,
|
|||
*/
|
||||
|
||||
if (!is_operator) {
|
||||
std::fprintf(stderr, "#%d# ", operator_or_operand);
|
||||
std::fprintf(stderr, "%d ", operator_or_operand);
|
||||
} else {
|
||||
std::fprintf(stderr, "#%s#\n",
|
||||
Type2CharStringOperatorToString(
|
||||
ots::Type2CharStringOperator(operator_or_operand))
|
||||
std::fprintf(stderr, "%s\n",
|
||||
CharStringOperatorToString(
|
||||
ots::CharStringOperator(operator_or_operand))
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!is_operator) {
|
||||
argument_stack->push(operator_or_operand);
|
||||
if (argument_stack->size() > kMaxArgumentStack) {
|
||||
if (ArgumentStackOverflows(argument_stack, cff2)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// An operator is found. Execute it.
|
||||
if (!ExecuteType2CharStringOperator(font,
|
||||
operator_or_operand,
|
||||
call_depth,
|
||||
global_subrs_index,
|
||||
local_subrs_index,
|
||||
cff_table,
|
||||
char_string,
|
||||
argument_stack,
|
||||
out_found_endchar,
|
||||
in_out_found_width,
|
||||
in_out_num_stems)) {
|
||||
if (!ExecuteCharStringOperator(cff,
|
||||
operator_or_operand,
|
||||
call_depth,
|
||||
global_subrs_index,
|
||||
local_subrs_index,
|
||||
cff_table,
|
||||
char_string,
|
||||
argument_stack,
|
||||
out_found_endchar,
|
||||
in_out_found_width,
|
||||
in_out_num_stems,
|
||||
&in_out_have_blend,
|
||||
&in_out_have_visindex,
|
||||
&in_out_vsindex,
|
||||
cff2)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (*out_found_endchar) {
|
||||
|
|
@ -801,37 +906,39 @@ bool ExecuteType2CharString(ots::Font *font,
|
|||
}
|
||||
|
||||
// No endchar operator is found.
|
||||
if (cff2)
|
||||
return true;
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// Selects a set of subroutings for |glyph_index| from |cff| and sets it on
|
||||
// |out_local_subrs_to_use|. Returns true on success.
|
||||
bool SelectLocalSubr(const std::map<uint16_t, uint8_t> &fd_select,
|
||||
const std::vector<ots::CFFIndex *> &local_subrs_per_font,
|
||||
const ots::CFFIndex *local_subrs,
|
||||
bool SelectLocalSubr(const ots::OpenTypeCFF& cff,
|
||||
uint16_t glyph_index, // 0-origin
|
||||
const ots::CFFIndex **out_local_subrs_to_use) {
|
||||
bool cff2 = (cff.major == 2);
|
||||
*out_local_subrs_to_use = NULL;
|
||||
|
||||
// First, find local subrs from |local_subrs_per_font|.
|
||||
if ((fd_select.size() > 0) &&
|
||||
(!local_subrs_per_font.empty())) {
|
||||
if ((cff.fd_select.size() > 0) &&
|
||||
(!cff.local_subrs_per_font.empty())) {
|
||||
// Look up FDArray index for the glyph.
|
||||
std::map<uint16_t, uint8_t>::const_iterator iter =
|
||||
fd_select.find(glyph_index);
|
||||
if (iter == fd_select.end()) {
|
||||
const auto& iter = cff.fd_select.find(glyph_index);
|
||||
if (iter == cff.fd_select.end()) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
const uint8_t fd_index = iter->second;
|
||||
if (fd_index >= local_subrs_per_font.size()) {
|
||||
const auto fd_index = iter->second;
|
||||
if (fd_index >= cff.local_subrs_per_font.size()) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
*out_local_subrs_to_use = local_subrs_per_font.at(fd_index);
|
||||
} else if (local_subrs) {
|
||||
*out_local_subrs_to_use = cff.local_subrs_per_font.at(fd_index);
|
||||
} else if (cff.local_subrs) {
|
||||
// Second, try to use |local_subrs|. Most Latin fonts don't have FDSelect
|
||||
// entries. If The font has a local subrs index associated with the Top
|
||||
// DICT (not FDArrays), use it.
|
||||
*out_local_subrs_to_use = local_subrs;
|
||||
*out_local_subrs_to_use = cff.local_subrs;
|
||||
} else if (cff2 && cff.local_subrs_per_font.size() == 1) {
|
||||
*out_local_subrs_to_use = cff.local_subrs_per_font.at(0);
|
||||
} else {
|
||||
// Just return NULL.
|
||||
*out_local_subrs_to_use = NULL;
|
||||
|
|
@ -844,18 +951,16 @@ bool SelectLocalSubr(const std::map<uint16_t, uint8_t> &fd_select,
|
|||
|
||||
namespace ots {
|
||||
|
||||
bool ValidateType2CharStringIndex(
|
||||
ots::Font *font,
|
||||
const CFFIndex& char_strings_index,
|
||||
bool ValidateCFFCharStrings(
|
||||
ots::OpenTypeCFF& cff,
|
||||
const CFFIndex& global_subrs_index,
|
||||
const std::map<uint16_t, uint8_t> &fd_select,
|
||||
const std::vector<CFFIndex *> &local_subrs_per_font,
|
||||
const CFFIndex *local_subrs,
|
||||
Buffer* cff_table) {
|
||||
const CFFIndex& char_strings_index = *(cff.charstrings_index);
|
||||
if (char_strings_index.offsets.size() == 0) {
|
||||
return OTS_FAILURE(); // no charstring.
|
||||
}
|
||||
|
||||
bool cff2 = (cff.major == 2);
|
||||
// For each glyph, validate the corresponding charstring.
|
||||
for (unsigned i = 1; i < char_strings_index.offsets.size(); ++i) {
|
||||
// Prepare a Buffer object, |char_string|, which contains the charstring
|
||||
|
|
@ -875,9 +980,7 @@ bool ValidateType2CharStringIndex(
|
|||
// Get a local subrs for the glyph.
|
||||
const unsigned glyph_index = i - 1; // index in the map is 0-origin.
|
||||
const CFFIndex *local_subrs_to_use = NULL;
|
||||
if (!SelectLocalSubr(fd_select,
|
||||
local_subrs_per_font,
|
||||
local_subrs,
|
||||
if (!SelectLocalSubr(cff,
|
||||
glyph_index,
|
||||
&local_subrs_to_use)) {
|
||||
return OTS_FAILURE();
|
||||
|
|
@ -891,16 +994,19 @@ bool ValidateType2CharStringIndex(
|
|||
// Check a charstring for the |i|-th glyph.
|
||||
std::stack<int32_t> argument_stack;
|
||||
bool found_endchar = false;
|
||||
bool found_width = false;
|
||||
// CFF2 CharString has no value for width, so we start with true here to
|
||||
// error out if width is found.
|
||||
bool found_width = cff2;
|
||||
size_t num_stems = 0;
|
||||
if (!ExecuteType2CharString(font,
|
||||
0 /* initial call_depth is zero */,
|
||||
global_subrs_index, *local_subrs_to_use,
|
||||
cff_table, &char_string, &argument_stack,
|
||||
&found_endchar, &found_width, &num_stems)) {
|
||||
if (!ExecuteCharString(cff,
|
||||
0 /* initial call_depth is zero */,
|
||||
global_subrs_index, *local_subrs_to_use,
|
||||
cff_table, &char_string, &argument_stack,
|
||||
&found_endchar, &found_width, &num_stems,
|
||||
cff2)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (!found_endchar) {
|
||||
if (!cff2 && !found_endchar) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,9 @@
|
|||
|
||||
namespace ots {
|
||||
|
||||
const size_t kMaxCFF1ArgumentStack = 48;
|
||||
const size_t kMaxCFF2ArgumentStack = 513;
|
||||
|
||||
// Validates all charstrings in |char_strings_index|. Charstring is a small
|
||||
// language for font hinting defined in Adobe Technical Note #5177.
|
||||
// http://www.adobe.com/devnet/font/pdfs/5177.Type2.pdf
|
||||
|
|
@ -34,17 +37,14 @@ namespace ots {
|
|||
// local_subrs: A Local Subrs associated with Top DICT. Can be NULL.
|
||||
// cff_table: A buffer which contains actual byte code of charstring, global
|
||||
// subroutines and local subroutines.
|
||||
bool ValidateType2CharStringIndex(
|
||||
Font *font,
|
||||
const CFFIndex &char_strings_index,
|
||||
bool ValidateCFFCharStrings(
|
||||
OpenTypeCFF& cff,
|
||||
const CFFIndex &global_subrs_index,
|
||||
const std::map<uint16_t, uint8_t> &fd_select,
|
||||
const std::vector<CFFIndex *> &local_subrs_per_font,
|
||||
const CFFIndex *local_subrs,
|
||||
Buffer *cff_table);
|
||||
|
||||
// The list of Operators. See Appendix. A in Adobe Technical Note #5177.
|
||||
enum Type2CharStringOperator {
|
||||
// and https://docs.microsoft.com/en-us/typography/opentype/spec/cff2charstr
|
||||
enum CharStringOperator {
|
||||
kHStem = 1,
|
||||
kVStem = 3,
|
||||
kVMoveTo = 4,
|
||||
|
|
@ -55,6 +55,8 @@ enum Type2CharStringOperator {
|
|||
kCallSubr = 10,
|
||||
kReturn = 11,
|
||||
kEndChar = 14,
|
||||
kVSIndex = 15,
|
||||
kBlend = 16,
|
||||
kHStemHm = 18,
|
||||
kHintMask = 19,
|
||||
kCntrMask = 20,
|
||||
|
|
@ -238,7 +238,7 @@ bool OpenTypeCMAP::ParseFormat4(int platform, int encoding,
|
|||
}
|
||||
uint16_t glyph;
|
||||
std::memcpy(&glyph, data + glyph_id_offset, 2);
|
||||
glyph = ntohs(glyph);
|
||||
glyph = ots_ntohs(glyph);
|
||||
if (glyph >= num_glyphs) {
|
||||
return Error("Range glyph reference too high (%d > %d)", glyph, num_glyphs - 1);
|
||||
}
|
||||
|
|
@ -771,9 +771,10 @@ bool OpenTypeCMAP::Parse(const uint8_t *data, size_t length) {
|
|||
subtable_headers[i].length, num_glyphs)) {
|
||||
return Error("Failed to parse format 4 cmap subtable %d", i);
|
||||
}
|
||||
} else if ((subtable_headers[i].encoding == 3) &&
|
||||
} else if ((subtable_headers[i].encoding == 3 ||
|
||||
subtable_headers[i].encoding == 4) &&
|
||||
(subtable_headers[i].format == 12)) {
|
||||
// parse and output the 0-3-12 table as 3-10-12 table.
|
||||
// parse and output the 0-3-12 or 0-4-12 tables as 3-10-12 table.
|
||||
if (!Parse31012(data + subtable_headers[i].offset,
|
||||
subtable_headers[i].length, num_glyphs)) {
|
||||
return Error("Failed to parse format 12 cmap subtable %d", i);
|
||||
|
|
|
|||
56
gfx/ots/src/cvar.cc
Normal file
56
gfx/ots/src/cvar.cc
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
// Copyright (c) 2018 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "cvar.h"
|
||||
|
||||
#include "fvar.h"
|
||||
#include "variations.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeCVAR
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool OpenTypeCVAR::Parse(const uint8_t* data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
|
||||
uint16_t majorVersion;
|
||||
uint16_t minorVersion;
|
||||
|
||||
if (!table.ReadU16(&majorVersion) ||
|
||||
!table.ReadU16(&minorVersion)) {
|
||||
return Drop("Failed to read table header");
|
||||
}
|
||||
|
||||
if (majorVersion != 1) {
|
||||
return Drop("Unknown table version");
|
||||
}
|
||||
|
||||
OpenTypeFVAR* fvar = static_cast<OpenTypeFVAR*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_FVAR));
|
||||
if (!fvar) {
|
||||
return DropVariations("Required fvar table is missing");
|
||||
}
|
||||
|
||||
if (!ParseVariationData(GetFont(), data + table.offset(), length - table.offset(),
|
||||
fvar->AxisCount(), 0)) {
|
||||
return Drop("Failed to parse variation data");
|
||||
}
|
||||
|
||||
this->m_data = data;
|
||||
this->m_length = length;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeCVAR::Serialize(OTSStream* out) {
|
||||
if (!out->Write(this->m_data, this->m_length)) {
|
||||
return Error("Failed to write cvar table");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
31
gfx/ots/src/cvar.h
Normal file
31
gfx/ots/src/cvar.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright (c) 2018 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_CVAR_H_
|
||||
#define OTS_CVAR_H_
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeCVAR Interface
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class OpenTypeCVAR : public Table {
|
||||
public:
|
||||
explicit OpenTypeCVAR(Font* font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t* data, size_t length);
|
||||
bool Serialize(OTSStream* out);
|
||||
|
||||
private:
|
||||
const uint8_t *m_data;
|
||||
size_t m_length;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_CVAR_H_
|
||||
164
gfx/ots/src/fvar.cc
Normal file
164
gfx/ots/src/fvar.cc
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
// Copyright (c) 2018 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "fvar.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeFVAR
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool OpenTypeFVAR::Parse(const uint8_t* data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
if (!table.ReadU16(&this->majorVersion) ||
|
||||
!table.ReadU16(&this->minorVersion) ||
|
||||
!table.ReadU16(&this->axesArrayOffset) ||
|
||||
!table.ReadU16(&this->reserved) ||
|
||||
!table.ReadU16(&this->axisCount) ||
|
||||
!table.ReadU16(&this->axisSize) ||
|
||||
!table.ReadU16(&this->instanceCount) ||
|
||||
!table.ReadU16(&this->instanceSize)) {
|
||||
return DropVariations("Failed to read table header");
|
||||
}
|
||||
if (this->majorVersion != 1) {
|
||||
return DropVariations("Unknown table version");
|
||||
}
|
||||
if (this->minorVersion > 0) {
|
||||
Warning("Downgrading minor version to 0");
|
||||
this->minorVersion = 0;
|
||||
}
|
||||
if (this->axesArrayOffset > length || this->axesArrayOffset < table.offset()) {
|
||||
return DropVariations("Bad axesArrayOffset");
|
||||
}
|
||||
if (this->reserved != 2) {
|
||||
Warning("Expected reserved=2");
|
||||
this->reserved = 2;
|
||||
}
|
||||
if (this->axisCount == 0) {
|
||||
return DropVariations("No variation axes");
|
||||
}
|
||||
if (this->axisSize != 20) {
|
||||
return DropVariations("Invalid axisSize");
|
||||
}
|
||||
// instanceCount is not validated
|
||||
if (this->instanceSize == this->axisCount * sizeof(Fixed) + 6) {
|
||||
this->instancesHavePostScriptNameID = true;
|
||||
} else if (this->instanceSize == this->axisCount * sizeof(Fixed) + 4) {
|
||||
this->instancesHavePostScriptNameID = false;
|
||||
} else {
|
||||
return DropVariations("Invalid instanceSize");
|
||||
}
|
||||
|
||||
// When we serialize, the axes array will go here, even if it was
|
||||
// originally at a different offset. So we update the axesArrayOffset
|
||||
// field for the header.
|
||||
uint32_t origAxesArrayOffset = this->axesArrayOffset;
|
||||
this->axesArrayOffset = table.offset();
|
||||
|
||||
table.set_offset(origAxesArrayOffset);
|
||||
for (unsigned i = 0; i < this->axisCount; i++) {
|
||||
this->axes.emplace_back();
|
||||
auto& axis = this->axes[i];
|
||||
if (!table.ReadU32(&axis.axisTag) ||
|
||||
!table.ReadS32(&axis.minValue) ||
|
||||
!table.ReadS32(&axis.defaultValue) ||
|
||||
!table.ReadS32(&axis.maxValue) ||
|
||||
!table.ReadU16(&axis.flags) ||
|
||||
!table.ReadU16(&axis.axisNameID)) {
|
||||
return DropVariations("Failed to read axis record");
|
||||
}
|
||||
if (!CheckTag(axis.axisTag)) {
|
||||
return DropVariations("Bad axis tag");
|
||||
}
|
||||
if (!(axis.minValue <= axis.defaultValue && axis.defaultValue <= axis.maxValue)) {
|
||||
return DropVariations("Bad axis value range");
|
||||
}
|
||||
if ((axis.flags & 0xFFFEu) != 0) {
|
||||
Warning("Discarding unknown axis flags");
|
||||
axis.flags &= ~0xFFFEu;
|
||||
}
|
||||
if (axis.axisNameID <= 255 || axis.axisNameID >= 32768) {
|
||||
Warning("Axis nameID out of range");
|
||||
// We don't check that the name actually exists -- assume the client can handle
|
||||
// a missing name when it tries to read the table.
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < this->instanceCount; i++) {
|
||||
this->instances.emplace_back();
|
||||
auto& inst = this->instances[i];
|
||||
if (!table.ReadU16(&inst.subfamilyNameID) ||
|
||||
!table.ReadU16(&inst.flags)) {
|
||||
return DropVariations("Failed to read instance record");
|
||||
}
|
||||
inst.coordinates.reserve(this->axisCount);
|
||||
for (unsigned j = 0; j < this->axisCount; j++) {
|
||||
inst.coordinates.emplace_back();
|
||||
auto& coord = inst.coordinates[j];
|
||||
if (!table.ReadS32(&coord)) {
|
||||
return DropVariations("Failed to read instance coordinates");
|
||||
}
|
||||
}
|
||||
if (this->instancesHavePostScriptNameID) {
|
||||
if (!table.ReadU16(&inst.postScriptNameID)) {
|
||||
return DropVariations("Failed to read instance psname ID");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (table.remaining()) {
|
||||
return Warning("%zu bytes unparsed", table.remaining());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeFVAR::Serialize(OTSStream* out) {
|
||||
if (!out->WriteU16(this->majorVersion) ||
|
||||
!out->WriteU16(this->minorVersion) ||
|
||||
!out->WriteU16(this->axesArrayOffset) ||
|
||||
!out->WriteU16(this->reserved) ||
|
||||
!out->WriteU16(this->axisCount) ||
|
||||
!out->WriteU16(this->axisSize) ||
|
||||
!out->WriteU16(this->instanceCount) ||
|
||||
!out->WriteU16(this->instanceSize)) {
|
||||
return Error("Failed to write table");
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < this->axisCount; i++) {
|
||||
const auto& axis = this->axes[i];
|
||||
if (!out->WriteU32(axis.axisTag) ||
|
||||
!out->WriteS32(axis.minValue) ||
|
||||
!out->WriteS32(axis.defaultValue) ||
|
||||
!out->WriteS32(axis.maxValue) ||
|
||||
!out->WriteU16(axis.flags) ||
|
||||
!out->WriteU16(axis.axisNameID)) {
|
||||
return Error("Failed to write table");
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < this->instanceCount; i++) {
|
||||
const auto& inst = this->instances[i];
|
||||
if (!out->WriteU16(inst.subfamilyNameID) ||
|
||||
!out->WriteU16(inst.flags)) {
|
||||
return Error("Failed to write table");
|
||||
}
|
||||
for (unsigned j = 0; j < this->axisCount; j++) {
|
||||
const auto& coord = inst.coordinates[j];
|
||||
if (!out->WriteS32(coord)) {
|
||||
return Error("Failed to write table");
|
||||
}
|
||||
}
|
||||
if (this->instancesHavePostScriptNameID) {
|
||||
if (!out->WriteU16(inst.postScriptNameID)) {
|
||||
return Error("Failed to write table");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
63
gfx/ots/src/fvar.h
Normal file
63
gfx/ots/src/fvar.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright (c) 2018 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_FVAR_H_
|
||||
#define OTS_FVAR_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeFVAR Interface
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class OpenTypeFVAR : public Table {
|
||||
public:
|
||||
explicit OpenTypeFVAR(Font* font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t* data, size_t length);
|
||||
bool Serialize(OTSStream* out);
|
||||
|
||||
uint16_t AxisCount() const { return axisCount; }
|
||||
|
||||
private:
|
||||
uint16_t majorVersion;
|
||||
uint16_t minorVersion;
|
||||
uint16_t axesArrayOffset;
|
||||
uint16_t reserved;
|
||||
uint16_t axisCount;
|
||||
uint16_t axisSize;
|
||||
uint16_t instanceCount;
|
||||
uint16_t instanceSize;
|
||||
|
||||
typedef int32_t Fixed; /* 16.16 fixed-point value */
|
||||
|
||||
struct VariationAxisRecord {
|
||||
uint32_t axisTag;
|
||||
Fixed minValue;
|
||||
Fixed defaultValue;
|
||||
Fixed maxValue;
|
||||
uint16_t flags;
|
||||
uint16_t axisNameID;
|
||||
};
|
||||
std::vector<VariationAxisRecord> axes;
|
||||
|
||||
struct InstanceRecord {
|
||||
uint16_t subfamilyNameID;
|
||||
uint16_t flags;
|
||||
std::vector<Fixed> coordinates;
|
||||
uint16_t postScriptNameID; // optional
|
||||
};
|
||||
std::vector<InstanceRecord> instances;
|
||||
|
||||
bool instancesHavePostScriptNameID;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_FVAR_H_
|
||||
|
|
@ -11,20 +11,17 @@
|
|||
#include "gsub.h"
|
||||
#include "layout.h"
|
||||
#include "maxp.h"
|
||||
#include "variations.h"
|
||||
|
||||
// GDEF - The Glyph Definition Table
|
||||
// http://www.microsoft.com/typography/otspec/gdef.htm
|
||||
|
||||
namespace {
|
||||
|
||||
// The maximum class value in class definition tables.
|
||||
const uint16_t kMaxClassDefValue = 0xFFFF;
|
||||
// The maximum class value in the glyph class definision table.
|
||||
const uint16_t kMaxGlyphClassDefValue = 4;
|
||||
// The maximum format number of caret value tables.
|
||||
// We don't support format 3 for now. See the comment in
|
||||
// ParseLigCaretListTable() for the reason.
|
||||
const uint16_t kMaxCaretValueFormat = 2;
|
||||
const uint16_t kMaxCaretValueFormat = 3;
|
||||
|
||||
} // namespace
|
||||
|
||||
|
|
@ -165,9 +162,6 @@ bool OpenTypeGDEF::ParseLigCaretListTable(const uint8_t *data, size_t length) {
|
|||
if (!subtable.ReadU16(&caret_format)) {
|
||||
return Error("Can't read caret values table %d in glyph %d", j, i);
|
||||
}
|
||||
// TODO(bashi): We only support caret value format 1 and 2 for now
|
||||
// because there are no fonts which contain caret value format 3
|
||||
// as far as we investigated.
|
||||
if (caret_format == 0 || caret_format > kMaxCaretValueFormat) {
|
||||
return Error("bad caret value format: %u", caret_format);
|
||||
}
|
||||
|
|
@ -176,6 +170,24 @@ bool OpenTypeGDEF::ParseLigCaretListTable(const uint8_t *data, size_t length) {
|
|||
if (!subtable.Skip(2)) {
|
||||
return Error("Bad caret value table structure %d in glyph %d", j, i);
|
||||
}
|
||||
if (caret_format == 3) {
|
||||
uint16_t offset_device = 0;
|
||||
if (!subtable.ReadU16(&offset_device)) {
|
||||
return Error("Can't read device offset for caret value %d "
|
||||
"in glyph %d", j, i);
|
||||
}
|
||||
uint16_t absolute_offset = lig_glyphs[i] + caret_value_offsets[j]
|
||||
+ offset_device;
|
||||
if (offset_device == 0 || absolute_offset >= length) {
|
||||
return Error("Bad device offset for caret value %d in glyph %d: %d",
|
||||
j, i, offset_device);
|
||||
}
|
||||
if (!ots::ParseDeviceTable(GetFont(), data + absolute_offset,
|
||||
length - absolute_offset)) {
|
||||
return Error("Bad device table for caret value %d in glyph %d",
|
||||
j, i, offset_device);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
@ -228,18 +240,15 @@ bool OpenTypeGDEF::Parse(const uint8_t *data, size_t length) {
|
|||
|
||||
Buffer table(data, length);
|
||||
|
||||
uint32_t version = 0;
|
||||
if (!table.ReadU32(&version)) {
|
||||
uint16_t version_major = 0, version_minor = 0;
|
||||
if (!table.ReadU16(&version_major) ||
|
||||
!table.ReadU16(&version_minor)) {
|
||||
return Error("Incomplete table");
|
||||
}
|
||||
if (version < 0x00010000 || version == 0x00010001) {
|
||||
if (version_major != 1 || version_minor == 1) { // there is no v1.1
|
||||
return Error("Bad version");
|
||||
}
|
||||
|
||||
if (version >= 0x00010002) {
|
||||
this->version_2 = true;
|
||||
}
|
||||
|
||||
uint16_t offset_glyph_class_def = 0;
|
||||
uint16_t offset_attach_list = 0;
|
||||
uint16_t offset_lig_caret_list = 0;
|
||||
|
|
@ -251,15 +260,23 @@ bool OpenTypeGDEF::Parse(const uint8_t *data, size_t length) {
|
|||
return Error("Incomplete table");
|
||||
}
|
||||
uint16_t offset_mark_glyph_sets_def = 0;
|
||||
if (this->version_2) {
|
||||
if (version_minor >= 2) {
|
||||
if (!table.ReadU16(&offset_mark_glyph_sets_def)) {
|
||||
return Error("Incomplete table");
|
||||
}
|
||||
}
|
||||
uint32_t item_var_store_offset = 0;
|
||||
if (version_minor >= 3) {
|
||||
if (!table.ReadU32(&item_var_store_offset)) {
|
||||
return Error("Incomplete table");
|
||||
}
|
||||
}
|
||||
|
||||
unsigned gdef_header_end = 4 + 4 * 2;
|
||||
if (this->version_2)
|
||||
if (version_minor >= 2)
|
||||
gdef_header_end += 2;
|
||||
if (version_minor >= 3)
|
||||
gdef_header_end += 4;
|
||||
|
||||
// Parse subtables
|
||||
if (offset_glyph_class_def) {
|
||||
|
|
@ -272,7 +289,6 @@ bool OpenTypeGDEF::Parse(const uint8_t *data, size_t length) {
|
|||
this->m_num_glyphs, kMaxGlyphClassDefValue)) {
|
||||
return Error("Invalid glyph classes");
|
||||
}
|
||||
this->has_glyph_class_def = true;
|
||||
}
|
||||
|
||||
if (offset_attach_list) {
|
||||
|
|
@ -308,7 +324,6 @@ bool OpenTypeGDEF::Parse(const uint8_t *data, size_t length) {
|
|||
this->m_num_glyphs, kMaxClassDefValue)) {
|
||||
return Error("Invalid mark attachment list");
|
||||
}
|
||||
this->has_mark_attachment_class_def = true;
|
||||
}
|
||||
|
||||
if (offset_mark_glyph_sets_def) {
|
||||
|
|
@ -320,8 +335,19 @@ bool OpenTypeGDEF::Parse(const uint8_t *data, size_t length) {
|
|||
length - offset_mark_glyph_sets_def)) {
|
||||
return Error("Invalid mark glyph sets");
|
||||
}
|
||||
this->has_mark_glyph_sets_def = true;
|
||||
}
|
||||
|
||||
if (item_var_store_offset) {
|
||||
if (item_var_store_offset >= length ||
|
||||
item_var_store_offset < gdef_header_end) {
|
||||
return Error("invalid offset to item variation store");
|
||||
}
|
||||
if (!ParseItemVariationStore(GetFont(), data + item_var_store_offset,
|
||||
length - item_var_store_offset)) {
|
||||
return Error("Invalid item variation store");
|
||||
}
|
||||
}
|
||||
|
||||
this->m_data = data;
|
||||
this->m_length = length;
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -13,10 +13,6 @@ class OpenTypeGDEF : public Table {
|
|||
public:
|
||||
explicit OpenTypeGDEF(Font *font, uint32_t tag)
|
||||
: Table(font, tag, tag),
|
||||
version_2(false),
|
||||
has_glyph_class_def(false),
|
||||
has_mark_attachment_class_def(false),
|
||||
has_mark_glyph_sets_def(false),
|
||||
num_mark_glyph_sets(0),
|
||||
m_data(NULL),
|
||||
m_length(0),
|
||||
|
|
@ -26,10 +22,6 @@ class OpenTypeGDEF : public Table {
|
|||
bool Parse(const uint8_t *data, size_t length);
|
||||
bool Serialize(OTSStream *out);
|
||||
|
||||
bool version_2;
|
||||
bool has_glyph_class_def;
|
||||
bool has_mark_attachment_class_def;
|
||||
bool has_mark_glyph_sets_def;
|
||||
uint16_t num_mark_glyph_sets;
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -200,7 +200,19 @@ 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);
|
||||
size_t decompressed_size = this->compHead & FULL_SIZE;
|
||||
if (decompressed_size < length) {
|
||||
return DropGraphite("Decompressed size is less than compressed size");
|
||||
}
|
||||
if (decompressed_size == 0) {
|
||||
return DropGraphite("Decompressed size is set to 0");
|
||||
}
|
||||
// decompressed table must be <= 30MB
|
||||
if (decompressed_size > 30 * 1024 * 1024) {
|
||||
return DropGraphite("Decompressed size exceeds 30MB: %gMB",
|
||||
decompressed_size / (1024.0 * 1024.0));
|
||||
}
|
||||
std::vector<uint8_t> decompressed(decompressed_size);
|
||||
size_t outputSize = 0;
|
||||
bool ret = mozilla::Compression::LZ4::decompressPartial(
|
||||
reinterpret_cast<const char*>(data + table.offset()),
|
||||
|
|
@ -305,7 +317,7 @@ OctaboxMetrics::ParsePart(Buffer& table) {
|
|||
|
||||
unsigned subboxes_len = 0; // count of 1's in this->subbox_bitmap
|
||||
for (uint16_t i = this->subbox_bitmap; i; i >>= 1) {
|
||||
if (i & 1) {
|
||||
if (i & 0b1) {
|
||||
++subboxes_len;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ bool OpenTypeGLOC::Parse(const uint8_t* data, size_t length) {
|
|||
if (this->version >> 16 != 1) {
|
||||
return DropGraphite("Unsupported table version: %u", this->version >> 16);
|
||||
}
|
||||
if (!table.ReadU16(&this->flags) || this->flags > 3) {
|
||||
if (!table.ReadU16(&this->flags) || this->flags > 0b11) {
|
||||
return DropGraphite("Failed to read valid flags");
|
||||
}
|
||||
if (!table.ReadU16(&this->numAttribs)) {
|
||||
|
|
|
|||
|
|
@ -38,6 +38,16 @@ bool OpenTypeGLYF::ParseFlagsForSimpleGlyph(Buffer &glyph,
|
|||
delta += 2;
|
||||
}
|
||||
|
||||
/* MS and Apple specs say this bit is reserved and must be set to zero, but
|
||||
* Apple spec then contradicts itself and says it should be set on the first
|
||||
* contour flag for simple glyphs with overlapping contours:
|
||||
* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6AATIntro.html
|
||||
* (“Overlapping contours” section) */
|
||||
if (flag & (1u << 6) && *flag_index != 0) {
|
||||
return Error("Bad glyph flag (%d), "
|
||||
"bit 6 must be set to zero for flag %d", flag, *flag_index);
|
||||
}
|
||||
|
||||
if (flag & (1u << 3)) { // repeat
|
||||
if (*flag_index + 1 >= num_flags) {
|
||||
return Error("Count too high (%d + 1 >= %d)", *flag_index, num_flags);
|
||||
|
|
@ -57,8 +67,8 @@ bool OpenTypeGLYF::ParseFlagsForSimpleGlyph(Buffer &glyph,
|
|||
}
|
||||
}
|
||||
|
||||
if ((flag & (1u << 6)) || (flag & (1u << 7))) { // reserved flags
|
||||
return Error("Bad glyph flag value (%d), reserved flags must be set to zero", flag);
|
||||
if (flag & (1u << 7)) { // reserved flag
|
||||
return Error("Bad glyph flag (%d), reserved bit 7 must be set to zero", flag);
|
||||
}
|
||||
|
||||
*coordinates_length += delta;
|
||||
|
|
@ -96,8 +106,9 @@ bool OpenTypeGLYF::ParseSimpleGlyph(Buffer &glyph,
|
|||
|
||||
if (this->maxp->version_1 &&
|
||||
this->maxp->max_size_glyf_instructions < bytecode_length) {
|
||||
return Error("Bytecode length is bigger than maxp.maxSizeOfInstructions "
|
||||
"%d: %d", this->maxp->max_size_glyf_instructions, bytecode_length);
|
||||
this->maxp->max_size_glyf_instructions = bytecode_length;
|
||||
Warning("Bytecode length is bigger than maxp.maxSizeOfInstructions %d: %d",
|
||||
this->maxp->max_size_glyf_instructions, bytecode_length);
|
||||
}
|
||||
|
||||
if (!glyph.Skip(bytecode_length)) {
|
||||
|
|
@ -192,9 +203,10 @@ bool OpenTypeGLYF::ParseCompositeGlyph(Buffer &glyph) {
|
|||
|
||||
if (this->maxp->version_1 &&
|
||||
this->maxp->max_size_glyf_instructions < bytecode_length) {
|
||||
return Error("Bytecode length is bigger than maxp.maxSizeOfInstructions "
|
||||
"%d: %d",
|
||||
this->maxp->max_size_glyf_instructions, bytecode_length);
|
||||
this->maxp->max_size_glyf_instructions = bytecode_length;
|
||||
Warning("Bytecode length is bigger than maxp.maxSizeOfInstructions "
|
||||
"%d: %d",
|
||||
this->maxp->max_size_glyf_instructions, bytecode_length);
|
||||
}
|
||||
|
||||
if (!glyph.Skip(bytecode_length)) {
|
||||
|
|
|
|||
|
|
@ -30,12 +30,12 @@ enum GPOS_TYPE {
|
|||
GPOS_TYPE_RESERVED = 10
|
||||
};
|
||||
|
||||
// The size of gpos header.
|
||||
const unsigned kGposHeaderSize = 10;
|
||||
// The size of gpos header, version 1.0.
|
||||
const unsigned kGposHeaderSize_1_0 = 10;
|
||||
// The size of gpos header, version 1.1.
|
||||
const unsigned kGposHeaderSize_1_1 = 14;
|
||||
// The maximum format number for anchor tables.
|
||||
const uint16_t kMaxAnchorFormat = 3;
|
||||
// The maximum number of class value.
|
||||
const uint16_t kMaxClassDefValue = 0xFFFF;
|
||||
|
||||
// Lookup type parsers.
|
||||
bool ParseSingleAdjustment(const ots::Font *font,
|
||||
|
|
@ -393,12 +393,12 @@ bool ParsePairPosFormat2(const ots::Font *font,
|
|||
// Check class definition tables.
|
||||
if (!ots::ParseClassDefTable(font, data + offset_class_def1,
|
||||
length - offset_class_def1,
|
||||
num_glyphs, kMaxClassDefValue)) {
|
||||
num_glyphs, ots::kMaxClassDefValue)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse class definition table 1");
|
||||
}
|
||||
if (!ots::ParseClassDefTable(font, data + offset_class_def2,
|
||||
length - offset_class_def2,
|
||||
num_glyphs, kMaxClassDefValue)) {
|
||||
num_glyphs, ots::kMaxClassDefValue)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse class definition table 2");
|
||||
}
|
||||
|
||||
|
|
@ -749,23 +749,34 @@ bool OpenTypeGPOS::Parse(const uint8_t *data, size_t length) {
|
|||
Font *font = GetFont();
|
||||
Buffer table(data, length);
|
||||
|
||||
uint32_t version = 0;
|
||||
uint16_t version_major = 0, version_minor = 0;
|
||||
uint16_t offset_script_list = 0;
|
||||
uint16_t offset_feature_list = 0;
|
||||
uint16_t offset_lookup_list = 0;
|
||||
if (!table.ReadU32(&version) ||
|
||||
uint32_t offset_feature_variations = 0;
|
||||
if (!table.ReadU16(&version_major) ||
|
||||
!table.ReadU16(&version_minor) ||
|
||||
!table.ReadU16(&offset_script_list) ||
|
||||
!table.ReadU16(&offset_feature_list) ||
|
||||
!table.ReadU16(&offset_lookup_list)) {
|
||||
return Error("Incomplete table");
|
||||
}
|
||||
|
||||
if (version != 0x00010000) {
|
||||
if (version_major != 1 || version_minor > 1) {
|
||||
return Error("Bad version");
|
||||
}
|
||||
|
||||
if (version_minor > 0) {
|
||||
if (!table.ReadU32(&offset_feature_variations)) {
|
||||
return Error("Incomplete table");
|
||||
}
|
||||
}
|
||||
|
||||
const size_t header_size =
|
||||
(version_minor == 0) ? kGposHeaderSize_1_0 : kGposHeaderSize_1_1;
|
||||
|
||||
if (offset_lookup_list) {
|
||||
if (offset_lookup_list < kGposHeaderSize || offset_lookup_list >= length) {
|
||||
if (offset_lookup_list < header_size || offset_lookup_list >= length) {
|
||||
return Error("Bad lookup list offset in table header");
|
||||
}
|
||||
|
||||
|
|
@ -779,7 +790,7 @@ bool OpenTypeGPOS::Parse(const uint8_t *data, size_t length) {
|
|||
|
||||
uint16_t num_features = 0;
|
||||
if (offset_feature_list) {
|
||||
if (offset_feature_list < kGposHeaderSize || offset_feature_list >= length) {
|
||||
if (offset_feature_list < header_size || offset_feature_list >= length) {
|
||||
return Error("Bad feature list offset in table header");
|
||||
}
|
||||
|
||||
|
|
@ -791,7 +802,7 @@ bool OpenTypeGPOS::Parse(const uint8_t *data, size_t length) {
|
|||
}
|
||||
|
||||
if (offset_script_list) {
|
||||
if (offset_script_list < kGposHeaderSize || offset_script_list >= length) {
|
||||
if (offset_script_list < header_size || offset_script_list >= length) {
|
||||
return Error("Bad script list offset in table header");
|
||||
}
|
||||
|
||||
|
|
@ -801,6 +812,18 @@ bool OpenTypeGPOS::Parse(const uint8_t *data, size_t length) {
|
|||
}
|
||||
}
|
||||
|
||||
if (offset_feature_variations) {
|
||||
if (offset_feature_variations < header_size || offset_feature_variations >= length) {
|
||||
return Error("Bad feature variations offset in table header");
|
||||
}
|
||||
|
||||
if (!ParseFeatureVariationsTable(font, data + offset_feature_variations,
|
||||
length - offset_feature_variations,
|
||||
this->num_lookups)) {
|
||||
return Error("Failed to parse feature variations table");
|
||||
}
|
||||
}
|
||||
|
||||
this->m_data = data;
|
||||
this->m_length = length;
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ template<typename ParentType>
|
|||
class TablePart {
|
||||
public:
|
||||
TablePart(ParentType* parent) : parent(parent) { }
|
||||
virtual ~TablePart() { }
|
||||
virtual bool ParsePart(Buffer& table) = 0;
|
||||
virtual bool SerializePart(OTSStream* out) const = 0;
|
||||
protected:
|
||||
|
|
|
|||
|
|
@ -17,8 +17,10 @@
|
|||
|
||||
namespace {
|
||||
|
||||
// The GSUB header size
|
||||
const size_t kGsubHeaderSize = 4 + 3 * 2;
|
||||
// The GSUB header size for table version 1.0
|
||||
const size_t kGsubHeaderSize_1_0 = 4 + 3 * 2;
|
||||
// GSUB header size v1.1
|
||||
const size_t kGsubHeaderSize_1_1 = 4 + 3 * 2 + 4;
|
||||
|
||||
enum GSUB_TYPE {
|
||||
GSUB_TYPE_SINGLE = 1,
|
||||
|
|
@ -580,23 +582,34 @@ bool OpenTypeGSUB::Parse(const uint8_t *data, size_t length) {
|
|||
Font *font = GetFont();
|
||||
Buffer table(data, length);
|
||||
|
||||
uint32_t version = 0;
|
||||
uint16_t version_major = 0, version_minor = 0;
|
||||
uint16_t offset_script_list = 0;
|
||||
uint16_t offset_feature_list = 0;
|
||||
uint16_t offset_lookup_list = 0;
|
||||
if (!table.ReadU32(&version) ||
|
||||
uint32_t offset_feature_variations = 0;
|
||||
if (!table.ReadU16(&version_major) ||
|
||||
!table.ReadU16(&version_minor) ||
|
||||
!table.ReadU16(&offset_script_list) ||
|
||||
!table.ReadU16(&offset_feature_list) ||
|
||||
!table.ReadU16(&offset_lookup_list)) {
|
||||
return Error("Incomplete table");
|
||||
}
|
||||
|
||||
if (version != 0x00010000) {
|
||||
if (version_major != 1 || version_minor > 1) {
|
||||
return Error("Bad version");
|
||||
}
|
||||
|
||||
if (version_minor > 0) {
|
||||
if (!table.ReadU32(&offset_feature_variations)) {
|
||||
return Error("Incomplete table");
|
||||
}
|
||||
}
|
||||
|
||||
const size_t header_size =
|
||||
(version_minor == 0) ? kGsubHeaderSize_1_0 : kGsubHeaderSize_1_1;
|
||||
|
||||
if (offset_lookup_list) {
|
||||
if (offset_lookup_list < kGsubHeaderSize || offset_lookup_list >= length) {
|
||||
if (offset_lookup_list < header_size || offset_lookup_list >= length) {
|
||||
return Error("Bad lookup list offset in table header");
|
||||
}
|
||||
|
||||
|
|
@ -610,7 +623,7 @@ bool OpenTypeGSUB::Parse(const uint8_t *data, size_t length) {
|
|||
|
||||
uint16_t num_features = 0;
|
||||
if (offset_feature_list) {
|
||||
if (offset_feature_list < kGsubHeaderSize || offset_feature_list >= length) {
|
||||
if (offset_feature_list < header_size || offset_feature_list >= length) {
|
||||
return Error("Bad feature list offset in table header");
|
||||
}
|
||||
|
||||
|
|
@ -622,7 +635,7 @@ bool OpenTypeGSUB::Parse(const uint8_t *data, size_t length) {
|
|||
}
|
||||
|
||||
if (offset_script_list) {
|
||||
if (offset_script_list < kGsubHeaderSize || offset_script_list >= length) {
|
||||
if (offset_script_list < header_size || offset_script_list >= length) {
|
||||
return Error("Bad script list offset in table header");
|
||||
}
|
||||
|
||||
|
|
@ -632,6 +645,18 @@ bool OpenTypeGSUB::Parse(const uint8_t *data, size_t length) {
|
|||
}
|
||||
}
|
||||
|
||||
if (offset_feature_variations) {
|
||||
if (offset_feature_variations < header_size || offset_feature_variations >= length) {
|
||||
return Error("Bad feature variations offset in table header");
|
||||
}
|
||||
|
||||
if (!ParseFeatureVariationsTable(font, data + offset_feature_variations,
|
||||
length - offset_feature_variations,
|
||||
this->num_lookups)) {
|
||||
return Error("Failed to parse feature variations table");
|
||||
}
|
||||
}
|
||||
|
||||
this->m_data = data;
|
||||
this->m_length = length;
|
||||
return true;
|
||||
|
|
|
|||
158
gfx/ots/src/gvar.cc
Normal file
158
gfx/ots/src/gvar.cc
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
// Copyright (c) 2018 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "gvar.h"
|
||||
|
||||
#include "fvar.h"
|
||||
#include "maxp.h"
|
||||
#include "variations.h"
|
||||
|
||||
#define TABLE_NAME "gvar"
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeGVAR
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static bool ParseSharedTuples(const Font* font, const uint8_t* data, size_t length,
|
||||
size_t sharedTupleCount, size_t axisCount) {
|
||||
Buffer subtable(data, length);
|
||||
for (unsigned i = 0; i < sharedTupleCount; i++) {
|
||||
for (unsigned j = 0; j < axisCount; j++) {
|
||||
int16_t coordinate;
|
||||
if (!subtable.ReadS16(&coordinate)) {
|
||||
return OTS_FAILURE_MSG("Failed to read shared tuple coordinate");
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ParseGlyphVariationDataArray(const Font* font, const uint8_t* data, size_t length,
|
||||
uint16_t flags, size_t glyphCount, size_t axisCount,
|
||||
size_t sharedTupleCount,
|
||||
const uint8_t* glyphVariationData,
|
||||
size_t glyphVariationDataLength) {
|
||||
Buffer subtable(data, length);
|
||||
|
||||
bool glyphVariationDataOffsetsAreLong = (flags & 0x0001u);
|
||||
uint32_t prevOffset = 0;
|
||||
for (size_t i = 0; i < glyphCount + 1; i++) {
|
||||
uint32_t offset;
|
||||
if (glyphVariationDataOffsetsAreLong) {
|
||||
if (!subtable.ReadU32(&offset)) {
|
||||
return OTS_FAILURE_MSG("Failed to read GlyphVariationData offset");
|
||||
}
|
||||
} else {
|
||||
uint16_t halfOffset;
|
||||
if (!subtable.ReadU16(&halfOffset)) {
|
||||
return OTS_FAILURE_MSG("Failed to read GlyphVariationData offset");
|
||||
}
|
||||
offset = halfOffset * 2;
|
||||
}
|
||||
|
||||
if (i > 0 && offset > prevOffset) {
|
||||
if (prevOffset > glyphVariationDataLength) {
|
||||
return OTS_FAILURE_MSG("Invalid GlyphVariationData offset");
|
||||
}
|
||||
if (!ParseVariationData(font, glyphVariationData + prevOffset,
|
||||
glyphVariationDataLength - prevOffset,
|
||||
axisCount, sharedTupleCount)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse GlyphVariationData");
|
||||
}
|
||||
}
|
||||
prevOffset = offset;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGVAR::Parse(const uint8_t* data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
|
||||
uint16_t majorVersion;
|
||||
uint16_t minorVersion;
|
||||
uint16_t axisCount;
|
||||
uint16_t sharedTupleCount;
|
||||
uint32_t sharedTuplesOffset;
|
||||
uint16_t glyphCount;
|
||||
uint16_t flags;
|
||||
uint32_t glyphVariationDataArrayOffset;
|
||||
|
||||
if (!table.ReadU16(&majorVersion) ||
|
||||
!table.ReadU16(&minorVersion) ||
|
||||
!table.ReadU16(&axisCount) ||
|
||||
!table.ReadU16(&sharedTupleCount) ||
|
||||
!table.ReadU32(&sharedTuplesOffset) ||
|
||||
!table.ReadU16(&glyphCount) ||
|
||||
!table.ReadU16(&flags) ||
|
||||
!table.ReadU32(&glyphVariationDataArrayOffset)) {
|
||||
return DropVariations("Failed to read table header");
|
||||
}
|
||||
if (majorVersion != 1) {
|
||||
return DropVariations("Unknown table version");
|
||||
}
|
||||
|
||||
// check axisCount == fvar->axisCount
|
||||
OpenTypeFVAR* fvar = static_cast<OpenTypeFVAR*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_FVAR));
|
||||
if (!fvar) {
|
||||
return DropVariations("Required fvar table is missing");
|
||||
}
|
||||
if (axisCount != fvar->AxisCount()) {
|
||||
return DropVariations("Axis count mismatch");
|
||||
}
|
||||
|
||||
// check glyphCount == maxp->num_glyphs
|
||||
OpenTypeMAXP* maxp = static_cast<OpenTypeMAXP*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_MAXP));
|
||||
if (!maxp) {
|
||||
return DropVariations("Required maxp table is missing");
|
||||
}
|
||||
if (glyphCount != maxp->num_glyphs) {
|
||||
return DropVariations("Glyph count mismatch");
|
||||
}
|
||||
|
||||
if (sharedTupleCount > 0) {
|
||||
if (sharedTuplesOffset < table.offset() || sharedTuplesOffset > length) {
|
||||
return DropVariations("Invalid sharedTuplesOffset");
|
||||
}
|
||||
if (!ParseSharedTuples(GetFont(),
|
||||
data + sharedTuplesOffset, length - sharedTuplesOffset,
|
||||
sharedTupleCount, axisCount)) {
|
||||
return DropVariations("Failed to parse shared tuples");
|
||||
}
|
||||
}
|
||||
|
||||
if (glyphVariationDataArrayOffset) {
|
||||
if (glyphVariationDataArrayOffset > length) {
|
||||
return DropVariations("Invalid glyphVariationDataArrayOffset");
|
||||
}
|
||||
if (!ParseGlyphVariationDataArray(GetFont(),
|
||||
data + table.offset(), length - table.offset(),
|
||||
flags, glyphCount, axisCount, sharedTupleCount,
|
||||
data + glyphVariationDataArrayOffset,
|
||||
length - glyphVariationDataArrayOffset)) {
|
||||
return DropVariations("Failed to read glyph variation data array");
|
||||
}
|
||||
}
|
||||
|
||||
this->m_data = data;
|
||||
this->m_length = length;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGVAR::Serialize(OTSStream* out) {
|
||||
if (!out->Write(this->m_data, this->m_length)) {
|
||||
return Error("Failed to write gvar table");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#undef TABLE_NAME
|
||||
31
gfx/ots/src/gvar.h
Normal file
31
gfx/ots/src/gvar.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright (c) 2018 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_GVAR_H_
|
||||
#define OTS_GVAR_H_
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeGVAR Interface
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class OpenTypeGVAR : public Table {
|
||||
public:
|
||||
explicit OpenTypeGVAR(Font* font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t* data, size_t length);
|
||||
bool Serialize(OTSStream* out);
|
||||
|
||||
private:
|
||||
const uint8_t *m_data;
|
||||
size_t m_length;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_GVAR_H_
|
||||
85
gfx/ots/src/hvar.cc
Normal file
85
gfx/ots/src/hvar.cc
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
// Copyright (c) 2018 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "hvar.h"
|
||||
|
||||
#include "variations.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeHVAR
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool OpenTypeHVAR::Parse(const uint8_t* data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
|
||||
uint16_t majorVersion;
|
||||
uint16_t minorVersion;
|
||||
uint32_t itemVariationStoreOffset;
|
||||
uint32_t advanceWidthMappingOffset;
|
||||
uint32_t lsbMappingOffset;
|
||||
uint32_t rsbMappingOffset;
|
||||
|
||||
if (!table.ReadU16(&majorVersion) ||
|
||||
!table.ReadU16(&minorVersion) ||
|
||||
!table.ReadU32(&itemVariationStoreOffset) ||
|
||||
!table.ReadU32(&advanceWidthMappingOffset) ||
|
||||
!table.ReadU32(&lsbMappingOffset) ||
|
||||
!table.ReadU32(&rsbMappingOffset)) {
|
||||
return DropVariations("Failed to read table header");
|
||||
}
|
||||
|
||||
if (majorVersion != 1) {
|
||||
return DropVariations("Unknown table version");
|
||||
}
|
||||
|
||||
if (itemVariationStoreOffset > length ||
|
||||
advanceWidthMappingOffset > length ||
|
||||
lsbMappingOffset > length ||
|
||||
rsbMappingOffset > length) {
|
||||
return DropVariations("Invalid subtable offset");
|
||||
}
|
||||
|
||||
if (!ParseItemVariationStore(GetFont(), data + itemVariationStoreOffset,
|
||||
length - itemVariationStoreOffset)) {
|
||||
return DropVariations("Failed to parse item variation store");
|
||||
}
|
||||
|
||||
if (advanceWidthMappingOffset) {
|
||||
if (!ParseDeltaSetIndexMap(GetFont(), data + advanceWidthMappingOffset,
|
||||
length - advanceWidthMappingOffset)) {
|
||||
return DropVariations("Failed to parse advance width mappings");
|
||||
}
|
||||
}
|
||||
|
||||
if (lsbMappingOffset) {
|
||||
if (!ParseDeltaSetIndexMap(GetFont(), data + lsbMappingOffset,
|
||||
length - lsbMappingOffset)) {
|
||||
return DropVariations("Failed to parse LSB mappings");
|
||||
}
|
||||
}
|
||||
|
||||
if (rsbMappingOffset) {
|
||||
if (!ParseDeltaSetIndexMap(GetFont(), data + rsbMappingOffset,
|
||||
length - rsbMappingOffset)) {
|
||||
return DropVariations("Failed to parse RSB mappings");
|
||||
}
|
||||
}
|
||||
|
||||
this->m_data = data;
|
||||
this->m_length = length;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeHVAR::Serialize(OTSStream* out) {
|
||||
if (!out->Write(this->m_data, this->m_length)) {
|
||||
return Error("Failed to write HVAR table");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
31
gfx/ots/src/hvar.h
Normal file
31
gfx/ots/src/hvar.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright (c) 2018 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_HVAR_H_
|
||||
#define OTS_HVAR_H_
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeHVAR Interface
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class OpenTypeHVAR : public Table {
|
||||
public:
|
||||
explicit OpenTypeHVAR(Font* font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t* data, size_t length);
|
||||
bool Serialize(OTSStream* out);
|
||||
|
||||
private:
|
||||
const uint8_t *m_data;
|
||||
size_t m_length;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_HVAR_H_
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
#include "fvar.h"
|
||||
#include "gdef.h"
|
||||
|
||||
// OpenType Layout Common Table Formats
|
||||
|
|
@ -22,14 +23,11 @@ const uint32_t kScriptTableTagDflt = 0x44464c54;
|
|||
const uint16_t kNoRequiredFeatureIndexDefined = 0xFFFF;
|
||||
// The lookup flag bit which indicates existence of MarkFilteringSet.
|
||||
const uint16_t kUseMarkFilteringSetBit = 0x0010;
|
||||
// The lookup flags which require GDEF table.
|
||||
const uint16_t kGdefRequiredFlags = 0x0002 | 0x0004 | 0x0008;
|
||||
// The mask for MarkAttachmentType.
|
||||
const uint16_t kMarkAttachmentTypeMask = 0xFF00;
|
||||
// The maximum type number of format for device tables.
|
||||
const uint16_t kMaxDeltaFormatType = 3;
|
||||
// The maximum number of class value.
|
||||
const uint16_t kMaxClassDefValue = 0xFFFF;
|
||||
// In variation fonts, Device Tables are replaced by VariationIndex tables,
|
||||
// indicated by this flag in the deltaFormat field.
|
||||
const uint16_t kVariationIndex = 0x8000;
|
||||
|
||||
struct ScriptRecord {
|
||||
uint32_t tag;
|
||||
|
|
@ -194,30 +192,7 @@ bool ParseLookupTable(ots::Font *font, const uint8_t *data,
|
|||
return OTS_FAILURE_MSG("Bad lookup type %d", lookup_type);
|
||||
}
|
||||
|
||||
ots::OpenTypeGDEF *gdef = static_cast<ots::OpenTypeGDEF*>(
|
||||
font->GetTypedTable(OTS_TAG_GDEF));
|
||||
|
||||
// Check lookup flags.
|
||||
if ((lookup_flag & kGdefRequiredFlags) &&
|
||||
(!gdef || !gdef->has_glyph_class_def)) {
|
||||
return OTS_FAILURE_MSG("Lookup flags require GDEF table, "
|
||||
"but none was found: %d", lookup_flag);
|
||||
}
|
||||
if ((lookup_flag & kMarkAttachmentTypeMask) &&
|
||||
(!gdef || !gdef->has_mark_attachment_class_def)) {
|
||||
return OTS_FAILURE_MSG("Lookup flags ask for mark attachment, "
|
||||
"but there is no GDEF table or it has no "
|
||||
"mark attachment classes: %d", lookup_flag);
|
||||
}
|
||||
bool use_mark_filtering_set = false;
|
||||
if (lookup_flag & kUseMarkFilteringSetBit) {
|
||||
if (!gdef || !gdef->has_mark_glyph_sets_def) {
|
||||
return OTS_FAILURE_MSG("Lookup flags ask for mark filtering, "
|
||||
"but there is no GDEF table or it has no "
|
||||
"mark filtering sets: %d", lookup_flag);
|
||||
}
|
||||
use_mark_filtering_set = true;
|
||||
}
|
||||
bool use_mark_filtering_set = lookup_flag & kUseMarkFilteringSetBit;
|
||||
|
||||
std::vector<uint16_t> subtables;
|
||||
subtables.reserve(subtable_count);
|
||||
|
|
@ -248,8 +223,12 @@ bool ParseLookupTable(ots::Font *font, const uint8_t *data,
|
|||
if (!subtable.ReadU16(&mark_filtering_set)) {
|
||||
return OTS_FAILURE_MSG("Failed to read mark filtering set");
|
||||
}
|
||||
if (gdef->num_mark_glyph_sets == 0 ||
|
||||
mark_filtering_set >= gdef->num_mark_glyph_sets) {
|
||||
|
||||
ots::OpenTypeGDEF *gdef = static_cast<ots::OpenTypeGDEF*>(
|
||||
font->GetTypedTable(OTS_TAG_GDEF));
|
||||
|
||||
if (gdef && (gdef->num_mark_glyph_sets == 0 ||
|
||||
mark_filtering_set >= gdef->num_mark_glyph_sets)) {
|
||||
return OTS_FAILURE_MSG("Bad mark filtering set %d", mark_filtering_set);
|
||||
}
|
||||
}
|
||||
|
|
@ -664,7 +643,7 @@ bool ParseContextFormat2(const ots::Font *font,
|
|||
}
|
||||
if (!ots::ParseClassDefTable(font, data + offset_class_def,
|
||||
length - offset_class_def,
|
||||
num_glyphs, kMaxClassDefValue)) {
|
||||
num_glyphs, ots::kMaxClassDefValue)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse class definition table in context format 2");
|
||||
}
|
||||
|
||||
|
|
@ -1017,7 +996,7 @@ bool ParseChainContextFormat2(const ots::Font *font,
|
|||
}
|
||||
if (!ots::ParseClassDefTable(font, data + offset_backtrack_class_def,
|
||||
length - offset_backtrack_class_def,
|
||||
num_glyphs, kMaxClassDefValue)) {
|
||||
num_glyphs, ots::kMaxClassDefValue)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse backtrack class defn table in chain context format 2");
|
||||
}
|
||||
}
|
||||
|
|
@ -1028,7 +1007,7 @@ bool ParseChainContextFormat2(const ots::Font *font,
|
|||
}
|
||||
if (!ots::ParseClassDefTable(font, data + offset_input_class_def,
|
||||
length - offset_input_class_def,
|
||||
num_glyphs, kMaxClassDefValue)) {
|
||||
num_glyphs, ots::kMaxClassDefValue)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse input class defn in chain context format 2");
|
||||
}
|
||||
|
||||
|
|
@ -1039,7 +1018,7 @@ bool ParseChainContextFormat2(const ots::Font *font,
|
|||
}
|
||||
if (!ots::ParseClassDefTable(font, data + offset_lookahead_class_def,
|
||||
length - offset_lookahead_class_def,
|
||||
num_glyphs, kMaxClassDefValue)) {
|
||||
num_glyphs, ots::kMaxClassDefValue)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse lookahead class defn in chain context format 2");
|
||||
}
|
||||
}
|
||||
|
|
@ -1403,11 +1382,17 @@ bool ParseDeviceTable(const ots::Font *font,
|
|||
!subtable.ReadU16(&delta_format)) {
|
||||
return OTS_FAILURE_MSG("Failed to read device table header");
|
||||
}
|
||||
if (delta_format == kVariationIndex) {
|
||||
// start_size and end_size are replaced by deltaSetOuterIndex
|
||||
// and deltaSetInnerIndex respectively, but we don't attempt to
|
||||
// check them here, so nothing more to do.
|
||||
return true;
|
||||
}
|
||||
if (start_size > end_size) {
|
||||
return OTS_FAILURE_MSG("bad size range: %u > %u", start_size, end_size);
|
||||
return OTS_FAILURE_MSG("Bad device table size range: %u > %u", start_size, end_size);
|
||||
}
|
||||
if (delta_format == 0 || delta_format > kMaxDeltaFormatType) {
|
||||
return OTS_FAILURE_MSG("bad delta format: %u", delta_format);
|
||||
return OTS_FAILURE_MSG("Bad device table delta format: 0x%x", delta_format);
|
||||
}
|
||||
// The number of delta values per uint16. The device table should contain
|
||||
// at least |num_units| * 2 bytes compressed data.
|
||||
|
|
@ -1519,6 +1504,173 @@ bool ParseExtensionSubtable(const Font *font,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ParseConditionTable(const Font *font,
|
||||
const uint8_t *data, const size_t length,
|
||||
const uint16_t axis_count) {
|
||||
Buffer subtable(data, length);
|
||||
|
||||
uint16_t format = 0;
|
||||
if (!subtable.ReadU16(&format)) {
|
||||
return OTS_FAILURE_MSG("Failed to read condition table format");
|
||||
}
|
||||
|
||||
if (format != 1) {
|
||||
// An unknown format is not an error, but should be ignored per spec.
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t axis_index = 0;
|
||||
int16_t filter_range_min_value = 0;
|
||||
int16_t filter_range_max_value = 0;
|
||||
if (!subtable.ReadU16(&axis_index) ||
|
||||
!subtable.ReadS16(&filter_range_min_value) ||
|
||||
!subtable.ReadS16(&filter_range_max_value)) {
|
||||
return OTS_FAILURE_MSG("Failed to read condition table (format 1)");
|
||||
}
|
||||
|
||||
if (axis_index >= axis_count) {
|
||||
return OTS_FAILURE_MSG("Axis index out of range in condition");
|
||||
}
|
||||
|
||||
// Check min/max values are within range -1.0 .. 1.0 and properly ordered
|
||||
if (filter_range_min_value < -0x4000 || // -1.0 in F2DOT14 format
|
||||
filter_range_max_value > 0x4000 || // +1.0 in F2DOT14 format
|
||||
filter_range_min_value > filter_range_max_value) {
|
||||
return OTS_FAILURE_MSG("Invalid filter range in condition");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseConditionSetTable(const Font *font,
|
||||
const uint8_t *data, const size_t length,
|
||||
const uint16_t axis_count) {
|
||||
Buffer subtable(data, length);
|
||||
|
||||
uint16_t condition_count = 0;
|
||||
if (!subtable.ReadU16(&condition_count)) {
|
||||
return OTS_FAILURE_MSG("Failed to read condition count");
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < condition_count; i++) {
|
||||
uint32_t condition_offset = 0;
|
||||
if (!subtable.ReadU32(&condition_offset)) {
|
||||
return OTS_FAILURE_MSG("Failed to read condition offset");
|
||||
}
|
||||
if (condition_offset < subtable.offset() || condition_offset >= length) {
|
||||
return OTS_FAILURE_MSG("Offset out of range");
|
||||
}
|
||||
if (!ParseConditionTable(font, data + condition_offset, length - condition_offset,
|
||||
axis_count)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse condition table");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseFeatureTableSubstitutionTable(const Font *font,
|
||||
const uint8_t *data, const size_t length,
|
||||
const uint16_t num_lookups) {
|
||||
Buffer subtable(data, length);
|
||||
|
||||
uint16_t version_major = 0;
|
||||
uint16_t version_minor = 0;
|
||||
uint16_t substitution_count = 0;
|
||||
const size_t kFeatureTableSubstitutionHeaderSize = 3 * sizeof(uint16_t);
|
||||
|
||||
if (!subtable.ReadU16(&version_major) ||
|
||||
!subtable.ReadU16(&version_minor) ||
|
||||
!subtable.ReadU16(&substitution_count)) {
|
||||
return OTS_FAILURE_MSG("Failed to read feature table substitution table header");
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < substitution_count; i++) {
|
||||
uint16_t feature_index = 0;
|
||||
uint32_t alternate_feature_table_offset = 0;
|
||||
const size_t kFeatureTableSubstitutionRecordSize = sizeof(uint16_t) + sizeof(uint32_t);
|
||||
|
||||
if (!subtable.ReadU16(&feature_index) ||
|
||||
!subtable.ReadU32(&alternate_feature_table_offset)) {
|
||||
return OTS_FAILURE_MSG("Failed to read feature table substitution record");
|
||||
}
|
||||
|
||||
if (alternate_feature_table_offset < kFeatureTableSubstitutionHeaderSize +
|
||||
kFeatureTableSubstitutionRecordSize * substitution_count ||
|
||||
alternate_feature_table_offset >= length) {
|
||||
return OTS_FAILURE_MSG("Invalid alternate feature table offset");
|
||||
}
|
||||
|
||||
if (!ParseFeatureTable(font, data + alternate_feature_table_offset,
|
||||
length - alternate_feature_table_offset, num_lookups)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse alternate feature table");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseFeatureVariationsTable(const Font *font,
|
||||
const uint8_t *data, const size_t length,
|
||||
const uint16_t num_lookups) {
|
||||
Buffer subtable(data, length);
|
||||
|
||||
uint16_t version_major = 0;
|
||||
uint16_t version_minor = 0;
|
||||
uint32_t feature_variation_record_count = 0;
|
||||
|
||||
if (!subtable.ReadU16(&version_major) ||
|
||||
!subtable.ReadU16(&version_minor) ||
|
||||
!subtable.ReadU32(&feature_variation_record_count)) {
|
||||
return OTS_FAILURE_MSG("Failed to read feature variations table header");
|
||||
}
|
||||
|
||||
OpenTypeFVAR* fvar = static_cast<OpenTypeFVAR*>(font->GetTypedTable(OTS_TAG_FVAR));
|
||||
if (!fvar) {
|
||||
return OTS_FAILURE_MSG("Not a variation font");
|
||||
}
|
||||
const uint16_t axis_count = fvar->AxisCount();
|
||||
|
||||
const size_t kEndOfFeatureVariationRecords =
|
||||
2 * sizeof(uint16_t) + sizeof(uint32_t) +
|
||||
feature_variation_record_count * 2 * sizeof(uint32_t);
|
||||
|
||||
for (uint32_t i = 0; i < feature_variation_record_count; i++) {
|
||||
uint32_t condition_set_offset = 0;
|
||||
uint32_t feature_table_substitution_offset = 0;
|
||||
if (!subtable.ReadU32(&condition_set_offset) ||
|
||||
!subtable.ReadU32(&feature_table_substitution_offset)) {
|
||||
return OTS_FAILURE_MSG("Failed to read feature variation record");
|
||||
}
|
||||
|
||||
if (condition_set_offset) {
|
||||
if (condition_set_offset < kEndOfFeatureVariationRecords ||
|
||||
condition_set_offset >= length) {
|
||||
return OTS_FAILURE_MSG("Condition set offset out of range");
|
||||
}
|
||||
if (!ParseConditionSetTable(font, data + condition_set_offset,
|
||||
length - condition_set_offset,
|
||||
axis_count)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse condition set table");
|
||||
}
|
||||
}
|
||||
|
||||
if (feature_table_substitution_offset) {
|
||||
if (feature_table_substitution_offset < kEndOfFeatureVariationRecords ||
|
||||
feature_table_substitution_offset >= length) {
|
||||
return OTS_FAILURE_MSG("Feature table substitution offset out of range");
|
||||
}
|
||||
if (!ParseFeatureTableSubstitutionTable(font, data + feature_table_substitution_offset,
|
||||
length - feature_table_substitution_offset,
|
||||
num_lookups)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse feature table substitution table");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#undef TABLE_NAME
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
namespace ots {
|
||||
|
||||
// The maximum number of class value.
|
||||
const uint16_t kMaxClassDefValue = 0xFFFF;
|
||||
|
||||
struct LookupSubtableParser {
|
||||
struct TypeParser {
|
||||
|
|
@ -70,6 +72,23 @@ bool ParseExtensionSubtable(const Font *font,
|
|||
const uint8_t *data, const size_t length,
|
||||
const LookupSubtableParser* parser);
|
||||
|
||||
// For feature variations table (in GSUB/GPOS v1.1)
|
||||
bool ParseConditionTable(const Font *font,
|
||||
const uint8_t *data, const size_t length,
|
||||
const uint16_t axis_count);
|
||||
|
||||
bool ParseConditionSetTable(const Font *font,
|
||||
const uint8_t *data, const size_t length,
|
||||
const uint16_t axis_count);
|
||||
|
||||
bool ParseFeatureTableSubstitutionTable(const Font *font,
|
||||
const uint8_t *data, const size_t length,
|
||||
const uint16_t num_lookups);
|
||||
|
||||
bool ParseFeatureVariationsTable(const Font *font,
|
||||
const uint8_t *data, const size_t length,
|
||||
const uint16_t num_lookups);
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_LAYOUT_H_
|
||||
|
|
|
|||
|
|
@ -9,28 +9,29 @@ EXPORTS += [
|
|||
'../include/ots-memory-stream.h',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
# needs to be separate because gpos.cc also defines kMaxClassDefValue
|
||||
'gdef.cc',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'avar.cc',
|
||||
'cff.cc',
|
||||
'cff_type2_charstring.cc',
|
||||
'cff_charstring.cc',
|
||||
'cmap.cc',
|
||||
'cvar.cc',
|
||||
'cvt.cc',
|
||||
'feat.cc',
|
||||
'fpgm.cc',
|
||||
'fvar.cc',
|
||||
'gasp.cc',
|
||||
'gdef.cc',
|
||||
'glat.cc',
|
||||
'gloc.cc',
|
||||
'glyf.cc',
|
||||
'gpos.cc',
|
||||
'gsub.cc',
|
||||
'gvar.cc',
|
||||
'hdmx.cc',
|
||||
'head.cc',
|
||||
'hhea.cc',
|
||||
'hmtx.cc',
|
||||
'hvar.cc',
|
||||
'kern.cc',
|
||||
'layout.cc',
|
||||
'loca.cc',
|
||||
|
|
@ -38,6 +39,7 @@ UNIFIED_SOURCES += [
|
|||
'math.cc',
|
||||
'maxp.cc',
|
||||
'metrics.cc',
|
||||
'mvar.cc',
|
||||
'name.cc',
|
||||
'os2.cc',
|
||||
'ots.cc',
|
||||
|
|
@ -46,10 +48,13 @@ UNIFIED_SOURCES += [
|
|||
'sile.cc',
|
||||
'silf.cc',
|
||||
'sill.cc',
|
||||
'stat.cc',
|
||||
'variations.cc',
|
||||
'vdmx.cc',
|
||||
'vhea.cc',
|
||||
'vmtx.cc',
|
||||
'vorg.cc',
|
||||
'vvar.cc',
|
||||
]
|
||||
|
||||
# We allow warnings for third-party code that can be updated from upstream.
|
||||
|
|
@ -60,6 +65,7 @@ FINAL_LIBRARY = 'gkmedias'
|
|||
DEFINES['PACKAGE_VERSION'] = '"moz"'
|
||||
DEFINES['PACKAGE_BUGREPORT'] = '"http://bugzilla.mozilla.org/"'
|
||||
DEFINES['OTS_GRAPHITE'] = 1
|
||||
DEFINES['OTS_VARIATIONS'] = 1
|
||||
|
||||
USE_LIBS += [
|
||||
'brotli',
|
||||
|
|
|
|||
105
gfx/ots/src/mvar.cc
Normal file
105
gfx/ots/src/mvar.cc
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
// Copyright (c) 2018 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "mvar.h"
|
||||
|
||||
#include "variations.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeMVAR
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool OpenTypeMVAR::Parse(const uint8_t* data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
|
||||
uint16_t majorVersion;
|
||||
uint16_t minorVersion;
|
||||
uint16_t reserved;
|
||||
uint16_t valueRecordSize;
|
||||
uint16_t valueRecordCount;
|
||||
uint16_t itemVariationStoreOffset;
|
||||
|
||||
if (!table.ReadU16(&majorVersion) ||
|
||||
!table.ReadU16(&minorVersion) ||
|
||||
!table.ReadU16(&reserved) ||
|
||||
!table.ReadU16(&valueRecordSize) ||
|
||||
!table.ReadU16(&valueRecordCount) ||
|
||||
!table.ReadU16(&itemVariationStoreOffset)) {
|
||||
return DropVariations("Failed to read table header");
|
||||
}
|
||||
|
||||
if (majorVersion != 1) {
|
||||
return DropVariations("Unknown table version");
|
||||
}
|
||||
|
||||
if (reserved != 0) {
|
||||
Warning("Expected reserved=0");
|
||||
}
|
||||
|
||||
// The spec says that valueRecordSize "must be greater than zero",
|
||||
// but we don't enforce this in the case where valueRecordCount
|
||||
// is zero.
|
||||
// The minimum size for a valueRecord to be valid is 8, for the
|
||||
// three fields currently defined in the record (see below).
|
||||
if (valueRecordSize < 8) {
|
||||
if (valueRecordCount != 0) {
|
||||
return DropVariations("Value record size too small");
|
||||
}
|
||||
}
|
||||
|
||||
if (valueRecordCount == 0) {
|
||||
if (itemVariationStoreOffset != 0) {
|
||||
// The spec says "if valueRecordCount is zero, set to zero",
|
||||
// but having a variation store even when record count is zero
|
||||
// should be harmless -- it just won't be useful for anything.
|
||||
// But we don't need to reject altogether.
|
||||
Warning("Unexpected item variation store");
|
||||
}
|
||||
} else {
|
||||
if (itemVariationStoreOffset < table.offset() || itemVariationStoreOffset > length) {
|
||||
return DropVariations("Invalid item variation store offset");
|
||||
}
|
||||
if (!ParseItemVariationStore(GetFont(), data + itemVariationStoreOffset,
|
||||
length - itemVariationStoreOffset)) {
|
||||
return DropVariations("Failed to parse item variation store");
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t prevTag = 0;
|
||||
size_t offset = table.offset();
|
||||
for (unsigned i = 0; i < valueRecordCount; i++) {
|
||||
uint32_t tag;
|
||||
uint16_t deltaSetOuterIndex, deltaSetInnerIndex;
|
||||
if (!table.ReadU32(&tag) ||
|
||||
!table.ReadU16(&deltaSetOuterIndex) ||
|
||||
!table.ReadU16(&deltaSetInnerIndex)) {
|
||||
return DropVariations("Failed to read value record");
|
||||
}
|
||||
if (tag <= prevTag) {
|
||||
return DropVariations("Invalid or out-of-order value tag");
|
||||
}
|
||||
prevTag = tag;
|
||||
// Adjust offset in case additional fields have been added to the
|
||||
// valueRecord by a new minor version (allowed by spec).
|
||||
offset += valueRecordSize;
|
||||
table.set_offset(offset);
|
||||
}
|
||||
|
||||
this->m_data = data;
|
||||
this->m_length = length;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeMVAR::Serialize(OTSStream* out) {
|
||||
if (!out->Write(this->m_data, this->m_length)) {
|
||||
return Error("Failed to write MVAR table");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
31
gfx/ots/src/mvar.h
Normal file
31
gfx/ots/src/mvar.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright (c) 2018 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_MVAR_H_
|
||||
#define OTS_MVAR_H_
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeMVAR Interface
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class OpenTypeMVAR : public Table {
|
||||
public:
|
||||
explicit OpenTypeMVAR(Font* font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t* data, size_t length);
|
||||
bool Serialize(OTSStream* out);
|
||||
|
||||
private:
|
||||
const uint8_t *m_data;
|
||||
size_t m_length;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_MVAR_H_
|
||||
|
|
@ -169,6 +169,14 @@ bool OpenTypeNAME::Parse(const uint8_t* data, size_t length) {
|
|||
if (tag_end > length) {
|
||||
return Error("bad end of tag %d > %ld for langTagRecord %d", tag_end, length, i);
|
||||
}
|
||||
// Lang tag is BCP 47 tag per the spec, the recommonded BCP 47 max tag
|
||||
// length is 35:
|
||||
// https://tools.ietf.org/html/bcp47#section-4.4.1
|
||||
// We are being too generous and allowing for 100 (multiplied by 2 since
|
||||
// this is UTF-16 string).
|
||||
if (tag_length > 100 * 2) {
|
||||
return Error("Too long language tag for LangTagRecord %d: %d", i, tag_length);
|
||||
}
|
||||
std::string tag(string_base + tag_offset, tag_length);
|
||||
this->lang_tags.push_back(tag);
|
||||
}
|
||||
|
|
@ -203,17 +211,16 @@ bool OpenTypeNAME::Parse(const uint8_t* data, size_t length) {
|
|||
// if not, we'll add our fixed versions here
|
||||
bool mac_name[kStdNameCount] = { 0 };
|
||||
bool win_name[kStdNameCount] = { 0 };
|
||||
for (std::vector<NameRecord>::iterator name_iter = this->names.begin();
|
||||
name_iter != this->names.end(); ++name_iter) {
|
||||
const uint16_t id = name_iter->name_id;
|
||||
for (const auto& name : this->names) {
|
||||
const uint16_t id = name.name_id;
|
||||
if (id >= kStdNameCount || kStdNames[id] == NULL) {
|
||||
continue;
|
||||
}
|
||||
if (name_iter->platform_id == 1) {
|
||||
if (name.platform_id == 1) {
|
||||
mac_name[id] = true;
|
||||
continue;
|
||||
}
|
||||
if (name_iter->platform_id == 3) {
|
||||
if (name.platform_id == 3) {
|
||||
win_name[id] = true;
|
||||
continue;
|
||||
}
|
||||
|
|
@ -266,9 +273,7 @@ bool OpenTypeNAME::Serialize(OTSStream* out) {
|
|||
}
|
||||
|
||||
std::string string_data;
|
||||
for (std::vector<NameRecord>::const_iterator name_iter = this->names.begin();
|
||||
name_iter != this->names.end(); ++name_iter) {
|
||||
const NameRecord& rec = *name_iter;
|
||||
for (const auto& rec : this->names) {
|
||||
if (string_data.size() + rec.text.size() >
|
||||
std::numeric_limits<uint16_t>::max() ||
|
||||
!out->WriteU16(rec.platform_id) ||
|
||||
|
|
@ -286,16 +291,14 @@ bool OpenTypeNAME::Serialize(OTSStream* out) {
|
|||
if (!out->WriteU16(lang_tag_count)) {
|
||||
return Error("Faile to write langTagCount");
|
||||
}
|
||||
for (std::vector<std::string>::const_iterator tag_iter =
|
||||
this->lang_tags.begin();
|
||||
tag_iter != this->lang_tags.end(); ++tag_iter) {
|
||||
if (string_data.size() + tag_iter->size() >
|
||||
for (const auto& tag : this->lang_tags) {
|
||||
if (string_data.size() + tag.size() >
|
||||
std::numeric_limits<uint16_t>::max() ||
|
||||
!out->WriteU16(static_cast<uint16_t>(tag_iter->size())) ||
|
||||
!out->WriteU16(static_cast<uint16_t>(tag.size())) ||
|
||||
!out->WriteU16(static_cast<uint16_t>(string_data.size()))) {
|
||||
return Error("Failed to write langTagRecord");
|
||||
}
|
||||
string_data.append(*tag_iter);
|
||||
string_data.append(tag);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,17 +38,14 @@ bool OpenTypeOS2::Parse(const uint8_t *data, size_t length) {
|
|||
return Error("Unsupported table version: %u", this->table.version);
|
||||
}
|
||||
|
||||
// Follow WPF Font Selection Model's advice.
|
||||
if (1 <= this->table.weight_class && this->table.weight_class <= 9) {
|
||||
Warning("Bad usWeightClass: %u, changing it to %u",
|
||||
this->table.weight_class, this->table.weight_class * 100);
|
||||
this->table.weight_class *= 100;
|
||||
}
|
||||
// Ditto.
|
||||
if (this->table.weight_class > 999) {
|
||||
if (this->table.weight_class < 1) {
|
||||
Warning("Bad usWeightClass: %u, changing it to %d",
|
||||
this->table.weight_class, 999);
|
||||
this->table.weight_class = 999;
|
||||
this->table.weight_class, 1);
|
||||
this->table.weight_class = 1;
|
||||
} else if (this->table.weight_class > 1000) {
|
||||
Warning("Bad usWeightClass: %u, changing it to %d",
|
||||
this->table.weight_class, 1000);
|
||||
this->table.weight_class = 1000;
|
||||
}
|
||||
|
||||
if (this->table.width_class < 1) {
|
||||
|
|
@ -89,7 +86,7 @@ bool OpenTypeOS2::Parse(const uint8_t *data, size_t length) {
|
|||
SET_TO_ZERO("yStrikeoutSize", strikeout_size);
|
||||
#undef SET_TO_ZERO
|
||||
|
||||
static std::string panose_strings[10] = {
|
||||
static const char* panose_strings[10] = {
|
||||
"bFamilyType",
|
||||
"bSerifStyle",
|
||||
"bWeight",
|
||||
|
|
@ -103,7 +100,7 @@ bool OpenTypeOS2::Parse(const uint8_t *data, size_t length) {
|
|||
};
|
||||
for (unsigned i = 0; i < 10; ++i) {
|
||||
if (!table.ReadU8(&this->table.panose[i])) {
|
||||
return Error("Failed to read PANOSE %s", panose_strings[i].c_str());
|
||||
return Error("Failed to read PANOSE %s", panose_strings[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -155,16 +152,17 @@ bool OpenTypeOS2::Parse(const uint8_t *data, size_t length) {
|
|||
if ((this->table.version < 4) &&
|
||||
(this->table.selection & 0x300)) {
|
||||
// bit 8 and 9 must be unset in OS/2 table versions less than 4.
|
||||
return Error("fSelection bits 8 and 9 must be unset for table version %d",
|
||||
this->table.version);
|
||||
Warning("fsSelection bits 8 and 9 must be unset for table version %d",
|
||||
this->table.version);
|
||||
}
|
||||
|
||||
// mask reserved bits. use only 0..9 bits.
|
||||
this->table.selection &= 0x3ff;
|
||||
|
||||
if (this->table.first_char_index > this->table.last_char_index) {
|
||||
return Error("usFirstCharIndex %d > usLastCharIndex %d",
|
||||
this->table.first_char_index, this->table.last_char_index);
|
||||
Warning("usFirstCharIndex %d > usLastCharIndex %d",
|
||||
this->table.first_char_index, this->table.last_char_index);
|
||||
this->table.first_char_index = this->table.last_char_index;
|
||||
}
|
||||
if (this->table.typo_linegap < 0) {
|
||||
Warning("Bad sTypoLineGap, setting it to 0: %d", this->table.typo_linegap);
|
||||
|
|
|
|||
|
|
@ -14,38 +14,46 @@
|
|||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "woff2_dec.h"
|
||||
#include <woff2/decode.h>
|
||||
|
||||
// The OpenType Font File
|
||||
// http://www.microsoft.com/typography/otspec/cmap.htm
|
||||
// http://www.microsoft.com/typography/otspec/otff.htm
|
||||
|
||||
#include "avar.h"
|
||||
#include "cff.h"
|
||||
#include "cmap.h"
|
||||
#include "cvar.h"
|
||||
#include "cvt.h"
|
||||
#include "fpgm.h"
|
||||
#include "fvar.h"
|
||||
#include "gasp.h"
|
||||
#include "gdef.h"
|
||||
#include "glyf.h"
|
||||
#include "gpos.h"
|
||||
#include "gsub.h"
|
||||
#include "gvar.h"
|
||||
#include "hdmx.h"
|
||||
#include "head.h"
|
||||
#include "hhea.h"
|
||||
#include "hmtx.h"
|
||||
#include "hvar.h"
|
||||
#include "kern.h"
|
||||
#include "loca.h"
|
||||
#include "ltsh.h"
|
||||
#include "math_.h"
|
||||
#include "maxp.h"
|
||||
#include "mvar.h"
|
||||
#include "name.h"
|
||||
#include "os2.h"
|
||||
#include "ots.h"
|
||||
#include "post.h"
|
||||
#include "prep.h"
|
||||
#include "stat.h"
|
||||
#include "vdmx.h"
|
||||
#include "vhea.h"
|
||||
#include "vmtx.h"
|
||||
#include "vorg.h"
|
||||
#include "vvar.h"
|
||||
|
||||
// Graphite tables
|
||||
#ifdef OTS_GRAPHITE
|
||||
|
|
@ -62,9 +70,8 @@ namespace ots {
|
|||
struct Arena {
|
||||
public:
|
||||
~Arena() {
|
||||
for (std::vector<uint8_t*>::iterator
|
||||
i = hunks_.begin(); i != hunks_.end(); ++i) {
|
||||
delete[] *i;
|
||||
for (auto& hunk : hunks_) {
|
||||
delete[] hunk;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -78,6 +85,17 @@ struct Arena {
|
|||
std::vector<uint8_t*> hunks_;
|
||||
};
|
||||
|
||||
bool CheckTag(uint32_t tag_value) {
|
||||
for (unsigned i = 0; i < 4; ++i) {
|
||||
const uint32_t check = tag_value & 0xff;
|
||||
if (check < 32 || check > 126) {
|
||||
return false; // non-ASCII character found.
|
||||
}
|
||||
tag_value >>= 8;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}; // namespace ots
|
||||
|
||||
namespace {
|
||||
|
|
@ -91,17 +109,6 @@ namespace {
|
|||
#define OTS_WARNING_MSG_HDR(...) OTS_WARNING_MSG_(header, __VA_ARGS__)
|
||||
|
||||
|
||||
bool CheckTag(uint32_t tag_value) {
|
||||
for (unsigned i = 0; i < 4; ++i) {
|
||||
const uint32_t check = tag_value & 0xff;
|
||||
if (check < 32 || check > 126) {
|
||||
return false; // non-ASCII character found.
|
||||
}
|
||||
tag_value >>= 8;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const struct {
|
||||
uint32_t tag;
|
||||
bool required;
|
||||
|
|
@ -126,6 +133,17 @@ const struct {
|
|||
{ OTS_TAG_LTSH, false },
|
||||
{ OTS_TAG_VORG, false },
|
||||
{ OTS_TAG_KERN, false },
|
||||
// We need to parse fvar table before other tables that may need to know
|
||||
// the number of variation axes (if any)
|
||||
{ OTS_TAG_FVAR, false },
|
||||
{ OTS_TAG_AVAR, false },
|
||||
{ OTS_TAG_CVAR, false },
|
||||
{ OTS_TAG_GVAR, false },
|
||||
{ OTS_TAG_HVAR, false },
|
||||
{ OTS_TAG_MVAR, false },
|
||||
{ OTS_TAG_STAT, false },
|
||||
{ OTS_TAG_VVAR, false },
|
||||
{ OTS_TAG_CFF2, false },
|
||||
// We need to parse GDEF table in advance of parsing GSUB/GPOS tables
|
||||
// because they could refer GDEF table.
|
||||
{ OTS_TAG_GDEF, false },
|
||||
|
|
@ -580,7 +598,7 @@ bool ProcessGeneric(ots::FontFile *header,
|
|||
}
|
||||
|
||||
// all tag names must be built from printable ASCII characters
|
||||
if (!CheckTag(tables[i].tag)) {
|
||||
if (!ots::CheckTag(tables[i].tag)) {
|
||||
OTS_WARNING_MSG_HDR("Invalid table tag: 0x%X", tables[i].tag);
|
||||
}
|
||||
|
||||
|
|
@ -686,7 +704,7 @@ bool ProcessGeneric(ots::FontFile *header,
|
|||
}
|
||||
}
|
||||
|
||||
if (font->GetTable(OTS_TAG_CFF)) {
|
||||
if (font->GetTable(OTS_TAG_CFF) || font->GetTable(OTS_TAG_CFF2)) {
|
||||
// font with PostScript glyph
|
||||
if (font->version != OTS_TAG('O','T','T','O')) {
|
||||
return OTS_FAILURE_MSG_HDR("wrong font version for PostScript glyph data");
|
||||
|
|
@ -862,32 +880,41 @@ bool Font::ParseTable(const TableEntry& table_entry, const uint8_t* data,
|
|||
table = new TablePassthru(this, tag);
|
||||
} else {
|
||||
switch (tag) {
|
||||
case OTS_TAG_AVAR: table = new OpenTypeAVAR(this, tag); break;
|
||||
case OTS_TAG_CFF: table = new OpenTypeCFF(this, tag); break;
|
||||
case OTS_TAG_CFF2: table = new OpenTypeCFF2(this, tag); break;
|
||||
case OTS_TAG_CMAP: table = new OpenTypeCMAP(this, tag); break;
|
||||
case OTS_TAG_CVAR: table = new OpenTypeCVAR(this, tag); break;
|
||||
case OTS_TAG_CVT: table = new OpenTypeCVT(this, tag); break;
|
||||
case OTS_TAG_FPGM: table = new OpenTypeFPGM(this, tag); break;
|
||||
case OTS_TAG_FVAR: table = new OpenTypeFVAR(this, tag); break;
|
||||
case OTS_TAG_GASP: table = new OpenTypeGASP(this, tag); break;
|
||||
case OTS_TAG_GDEF: table = new OpenTypeGDEF(this, tag); break;
|
||||
case OTS_TAG_GLYF: table = new OpenTypeGLYF(this, tag); break;
|
||||
case OTS_TAG_GPOS: table = new OpenTypeGPOS(this, tag); break;
|
||||
case OTS_TAG_GSUB: table = new OpenTypeGSUB(this, tag); break;
|
||||
case OTS_TAG_GVAR: table = new OpenTypeGVAR(this, tag); break;
|
||||
case OTS_TAG_HDMX: table = new OpenTypeHDMX(this, tag); break;
|
||||
case OTS_TAG_HEAD: table = new OpenTypeHEAD(this, tag); break;
|
||||
case OTS_TAG_HHEA: table = new OpenTypeHHEA(this, tag); break;
|
||||
case OTS_TAG_HMTX: table = new OpenTypeHMTX(this, tag); break;
|
||||
case OTS_TAG_HVAR: table = new OpenTypeHVAR(this, tag); break;
|
||||
case OTS_TAG_KERN: table = new OpenTypeKERN(this, tag); break;
|
||||
case OTS_TAG_LOCA: table = new OpenTypeLOCA(this, tag); break;
|
||||
case OTS_TAG_LTSH: table = new OpenTypeLTSH(this, tag); break;
|
||||
case OTS_TAG_MATH: table = new OpenTypeMATH(this, tag); break;
|
||||
case OTS_TAG_MAXP: table = new OpenTypeMAXP(this, tag); break;
|
||||
case OTS_TAG_MVAR: table = new OpenTypeMVAR(this, tag); break;
|
||||
case OTS_TAG_NAME: table = new OpenTypeNAME(this, tag); break;
|
||||
case OTS_TAG_OS2: table = new OpenTypeOS2(this, tag); break;
|
||||
case OTS_TAG_POST: table = new OpenTypePOST(this, tag); break;
|
||||
case OTS_TAG_PREP: table = new OpenTypePREP(this, tag); break;
|
||||
case OTS_TAG_STAT: table = new OpenTypeSTAT(this, tag); break;
|
||||
case OTS_TAG_VDMX: table = new OpenTypeVDMX(this, tag); break;
|
||||
case OTS_TAG_VORG: table = new OpenTypeVORG(this, tag); break;
|
||||
case OTS_TAG_VHEA: table = new OpenTypeVHEA(this, tag); break;
|
||||
case OTS_TAG_VMTX: table = new OpenTypeVMTX(this, tag); break;
|
||||
case OTS_TAG_VORG: table = new OpenTypeVORG(this, tag); break;
|
||||
case OTS_TAG_VVAR: table = new OpenTypeVVAR(this, tag); break;
|
||||
// Graphite tables
|
||||
#ifdef OTS_GRAPHITE
|
||||
case OTS_TAG_FEAT: table = new OpenTypeFEAT(this, tag); break;
|
||||
|
|
@ -953,6 +980,23 @@ void Font::DropGraphite() {
|
|||
dropped_graphite = true;
|
||||
}
|
||||
|
||||
void Font::DropVariations() {
|
||||
file->context->Message(0, "Dropping all Variation tables");
|
||||
for (const std::pair<uint32_t, Table*> entry : m_tables) {
|
||||
if (entry.first == OTS_TAG_AVAR ||
|
||||
entry.first == OTS_TAG_CVAR ||
|
||||
entry.first == OTS_TAG_FVAR ||
|
||||
entry.first == OTS_TAG_GVAR ||
|
||||
entry.first == OTS_TAG_HVAR ||
|
||||
entry.first == OTS_TAG_MVAR ||
|
||||
entry.first == OTS_TAG_STAT ||
|
||||
entry.first == OTS_TAG_VVAR) {
|
||||
entry.second->Drop("Discarding Variations table");
|
||||
}
|
||||
}
|
||||
dropped_variations = true;
|
||||
}
|
||||
|
||||
bool Table::ShouldSerialize() {
|
||||
return m_shouldSerialize;
|
||||
}
|
||||
|
|
@ -960,7 +1004,6 @@ bool Table::ShouldSerialize() {
|
|||
void Table::Message(int level, const char *format, va_list va) {
|
||||
char msg[206] = { OTS_UNTAG(m_tag), ':', ' ' };
|
||||
std::vsnprintf(msg + 6, 200, format, va);
|
||||
// fprintf(stderr, format, va); // font debugging, see TenFourFox issue 317
|
||||
m_font->file->context->Message(level, msg);
|
||||
}
|
||||
|
||||
|
|
@ -1004,6 +1047,16 @@ bool Table::DropGraphite(const char *format, ...) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Table::DropVariations(const char *format, ...) {
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
Message(0, format, va);
|
||||
va_end(va);
|
||||
|
||||
m_font->DropVariations();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TablePassthru::Parse(const uint8_t *data, size_t length) {
|
||||
m_data = data;
|
||||
m_length = length;
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ class Buffer {
|
|||
return OTS_FAILURE();
|
||||
}
|
||||
std::memcpy(value, buffer_ + offset_, sizeof(uint16_t));
|
||||
*value = ntohs(*value);
|
||||
*value = ots_ntohs(*value);
|
||||
offset_ += 2;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -137,7 +137,7 @@ class Buffer {
|
|||
return OTS_FAILURE();
|
||||
}
|
||||
std::memcpy(value, buffer_ + offset_, sizeof(uint32_t));
|
||||
*value = ntohl(*value);
|
||||
*value = ots_ntohl(*value);
|
||||
offset_ += 4;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -184,9 +184,13 @@ template<typename T> T Round2(T value) {
|
|||
return (value + 1) & ~1;
|
||||
}
|
||||
|
||||
// Check that a tag consists entirely of printable ASCII characters
|
||||
bool CheckTag(uint32_t tag_value);
|
||||
|
||||
bool IsValidVersionTag(uint32_t tag);
|
||||
|
||||
#define OTS_TAG_CFF OTS_TAG('C','F','F',' ')
|
||||
#define OTS_TAG_CFF2 OTS_TAG('C','F','F','2')
|
||||
#define OTS_TAG_CMAP OTS_TAG('c','m','a','p')
|
||||
#define OTS_TAG_CVT OTS_TAG('c','v','t',' ')
|
||||
#define OTS_TAG_FEAT OTS_TAG('F','e','a','t')
|
||||
|
|
@ -219,6 +223,15 @@ bool IsValidVersionTag(uint32_t tag);
|
|||
#define OTS_TAG_VMTX OTS_TAG('v','m','t','x')
|
||||
#define OTS_TAG_VORG OTS_TAG('V','O','R','G')
|
||||
|
||||
#define OTS_TAG_AVAR OTS_TAG('a','v','a','r')
|
||||
#define OTS_TAG_CVAR OTS_TAG('c','v','a','r')
|
||||
#define OTS_TAG_FVAR OTS_TAG('f','v','a','r')
|
||||
#define OTS_TAG_GVAR OTS_TAG('g','v','a','r')
|
||||
#define OTS_TAG_HVAR OTS_TAG('H','V','A','R')
|
||||
#define OTS_TAG_MVAR OTS_TAG('M','V','A','R')
|
||||
#define OTS_TAG_VVAR OTS_TAG('V','V','A','R')
|
||||
#define OTS_TAG_STAT OTS_TAG('S','T','A','T')
|
||||
|
||||
struct Font;
|
||||
struct FontFile;
|
||||
struct TableEntry;
|
||||
|
|
@ -252,6 +265,7 @@ class Table {
|
|||
bool Warning(const char *format, ...);
|
||||
bool Drop(const char *format, ...);
|
||||
bool DropGraphite(const char *format, ...);
|
||||
bool DropVariations(const char *format, ...);
|
||||
|
||||
private:
|
||||
void Message(int level, const char *format, va_list va);
|
||||
|
|
@ -286,7 +300,8 @@ struct Font {
|
|||
search_range(0),
|
||||
entry_selector(0),
|
||||
range_shift(0),
|
||||
dropped_graphite(false) {
|
||||
dropped_graphite(false),
|
||||
dropped_variations(false) {
|
||||
}
|
||||
|
||||
bool ParseTable(const TableEntry& tableinfo, const uint8_t* data,
|
||||
|
|
@ -301,6 +316,9 @@ struct Font {
|
|||
// Drop all Graphite tables and don't parse new ones.
|
||||
void DropGraphite();
|
||||
|
||||
// Drop all Variations tables and don't parse new ones.
|
||||
void DropVariations();
|
||||
|
||||
FontFile *file;
|
||||
|
||||
uint32_t version;
|
||||
|
|
@ -309,6 +327,7 @@ struct Font {
|
|||
uint16_t entry_selector;
|
||||
uint16_t range_shift;
|
||||
bool dropped_graphite;
|
||||
bool dropped_variations;
|
||||
|
||||
private:
|
||||
std::map<uint32_t, Table*> m_tables;
|
||||
|
|
|
|||
|
|
@ -9,8 +9,6 @@
|
|||
// post - PostScript
|
||||
// http://www.microsoft.com/typography/otspec/post.htm
|
||||
|
||||
#define TABLE_NAME "post"
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool OpenTypePOST::Parse(const uint8_t *data, size_t length) {
|
||||
|
|
|
|||
|
|
@ -38,7 +38,19 @@ 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);
|
||||
size_t decompressed_size = this->compHead & FULL_SIZE;
|
||||
if (decompressed_size < length) {
|
||||
return DropGraphite("Decompressed size is less than compressed size");
|
||||
}
|
||||
if (decompressed_size == 0) {
|
||||
return DropGraphite("Decompressed size is set to 0");
|
||||
}
|
||||
// decompressed table must be <= 30MB
|
||||
if (decompressed_size > 30 * 1024 * 1024) {
|
||||
return DropGraphite("Decompressed size exceeds 30MB: %gMB",
|
||||
decompressed_size / (1024.0 * 1024.0));
|
||||
}
|
||||
std::vector<uint8_t> decompressed(decompressed_size);
|
||||
size_t outputSize = 0;
|
||||
bool ret = mozilla::Compression::LZ4::decompressPartial(
|
||||
reinterpret_cast<const char*>(data + table.offset()),
|
||||
|
|
@ -165,15 +177,9 @@ bool OpenTypeSILF::SILSub::ParsePart(Buffer& table) {
|
|||
if (!table.ReadU8(&this->attrMirroring)) {
|
||||
return parent->Error("SILSub: Failed to read attrMirroring");
|
||||
}
|
||||
if (parent->version >> 16 < 4 && this->attrMirroring != 0) {
|
||||
parent->Warning("SILSub: Nonzero attrMirroring (reserved before v4)");
|
||||
}
|
||||
if (!table.ReadU8(&this->attrSkipPasses)) {
|
||||
return parent->Error("SILSub: Failed to read attrSkipPasses");
|
||||
}
|
||||
if (parent->version >> 16 < 4 && this->attrSkipPasses != 0) {
|
||||
parent->Warning("SILSub: Nonzero attrSkipPasses (reserved2 before v4)");
|
||||
}
|
||||
|
||||
if (!table.ReadU8(&this->numJLevels)) {
|
||||
return parent->Error("SILSub: Failed to read numJLevels");
|
||||
|
|
@ -642,9 +648,6 @@ SILPass::ParsePart(Buffer& table, const size_t SILSub_init_offset,
|
|||
if (!table.ReadU16(&this->fsmOffset)) {
|
||||
return parent->Error("SILPass: Failed to read fsmOffset");
|
||||
}
|
||||
if (parent->version >> 16 == 2 && this->fsmOffset != 0) {
|
||||
parent->Warning("SILPass: Nonzero fsmOffset (reserved in SILSub v2)");
|
||||
}
|
||||
if (!table.ReadU32(&this->pcCode) ||
|
||||
(parent->version >= 3 && this->pcCode < this->fsmOffset)) {
|
||||
return parent->Error("SILPass: Failed to read pcCode");
|
||||
|
|
@ -770,10 +773,6 @@ SILPass::ParsePart(Buffer& table, const size_t SILSub_init_offset,
|
|||
if (!table.ReadU8(&this->collisionThreshold)) {
|
||||
return parent->Error("SILPass: Failed to read collisionThreshold");
|
||||
}
|
||||
if (parent->version >> 16 < 5 && this->collisionThreshold != 0) {
|
||||
parent->Warning("SILPass: Nonzero collisionThreshold"
|
||||
" (reserved before v5)");
|
||||
}
|
||||
if (!table.ReadU16(&this->pConstraint)) {
|
||||
return parent->Error("SILPass: Failed to read pConstraint");
|
||||
}
|
||||
|
|
|
|||
347
gfx/ots/src/stat.cc
Normal file
347
gfx/ots/src/stat.cc
Normal file
|
|
@ -0,0 +1,347 @@
|
|||
// Copyright (c) 2018 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "stat.h"
|
||||
#include "name.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeSTAT
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool OpenTypeSTAT::ValidateNameId(uint16_t nameid, bool allowPredefined) {
|
||||
OpenTypeNAME* name = static_cast<OpenTypeNAME*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_NAME));
|
||||
|
||||
if (!name || !name->IsValidNameId(nameid)) {
|
||||
Drop("Invalid nameID: %d", nameid);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!allowPredefined && nameid < 26) {
|
||||
Warning("nameID out of range: %d", nameid);
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((nameid >= 26 && nameid <= 255) || nameid >= 32768) {
|
||||
Warning("nameID out of range: %d", nameid);
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSTAT::Parse(const uint8_t* data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
if (!table.ReadU16(&this->majorVersion) ||
|
||||
!table.ReadU16(&this->minorVersion) ||
|
||||
!table.ReadU16(&this->designAxisSize) ||
|
||||
!table.ReadU16(&this->designAxisCount) ||
|
||||
!table.ReadU32(&this->designAxesOffset) ||
|
||||
!table.ReadU16(&this->axisValueCount) ||
|
||||
!table.ReadU32(&this->offsetToAxisValueOffsets) ||
|
||||
!(this->minorVersion < 1 || table.ReadU16(&this->elidedFallbackNameID))) {
|
||||
return Drop("Failed to read table header");
|
||||
}
|
||||
if (this->majorVersion != 1) {
|
||||
return Drop("Unknown table version");
|
||||
}
|
||||
if (this->minorVersion > 2) {
|
||||
Warning("Unknown minor version, downgrading to 2");
|
||||
this->minorVersion = 2;
|
||||
}
|
||||
|
||||
if (this->designAxisSize < sizeof(AxisRecord)) {
|
||||
return Drop("Invalid designAxisSize");
|
||||
}
|
||||
|
||||
size_t headerEnd = table.offset();
|
||||
|
||||
if (this->designAxisCount == 0) {
|
||||
if (this->designAxesOffset != 0) {
|
||||
Warning("Unexpected non-zero designAxesOffset");
|
||||
this->designAxesOffset = 0;
|
||||
}
|
||||
} else {
|
||||
if (this->designAxesOffset < headerEnd ||
|
||||
size_t(this->designAxesOffset) +
|
||||
size_t(this->designAxisCount) * size_t(this->designAxisSize) > length) {
|
||||
return Drop("Invalid designAxesOffset");
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < this->designAxisCount; i++) {
|
||||
table.set_offset(this->designAxesOffset + i * this->designAxisSize);
|
||||
this->designAxes.emplace_back();
|
||||
auto& axis = this->designAxes[i];
|
||||
if (!table.ReadU32(&axis.axisTag) ||
|
||||
!table.ReadU16(&axis.axisNameID) ||
|
||||
!table.ReadU16(&axis.axisOrdering)) {
|
||||
return Drop("Failed to read design axis");
|
||||
}
|
||||
if (!CheckTag(axis.axisTag)) {
|
||||
return Drop("Bad design axis tag");
|
||||
}
|
||||
if (!ValidateNameId(axis.axisNameID, false)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
// - check that all axes defined in fvar are covered by STAT
|
||||
// - check that axisOrdering values are not duplicated (warn only)
|
||||
|
||||
if (this->axisValueCount == 0) {
|
||||
if (this->offsetToAxisValueOffsets != 0) {
|
||||
Warning("Unexpected non-zero offsetToAxisValueOffsets");
|
||||
this->offsetToAxisValueOffsets = 0;
|
||||
}
|
||||
} else {
|
||||
if (this->offsetToAxisValueOffsets < headerEnd ||
|
||||
size_t(this->offsetToAxisValueOffsets) +
|
||||
size_t(this->axisValueCount) * sizeof(uint16_t) > length) {
|
||||
return Drop("Invalid offsetToAxisValueOffsets");
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < this->axisValueCount; i++) {
|
||||
table.set_offset(this->offsetToAxisValueOffsets + i * sizeof(uint16_t));
|
||||
uint16_t axisValueOffset;
|
||||
if (!table.ReadU16(&axisValueOffset)) {
|
||||
return Drop("Failed to read axis value offset");
|
||||
}
|
||||
if (this->offsetToAxisValueOffsets + axisValueOffset > length) {
|
||||
return Drop("Invalid axis value offset");
|
||||
}
|
||||
table.set_offset(this->offsetToAxisValueOffsets + axisValueOffset);
|
||||
uint16_t format;
|
||||
if (!table.ReadU16(&format)) {
|
||||
return Drop("Failed to read axis value format");
|
||||
}
|
||||
this->axisValues.emplace_back(format);
|
||||
auto& axisValue = axisValues[i];
|
||||
switch (format) {
|
||||
case 1:
|
||||
if (!table.ReadU16(&axisValue.format1.axisIndex) ||
|
||||
!table.ReadU16(&axisValue.format1.flags) ||
|
||||
!table.ReadU16(&axisValue.format1.valueNameID) ||
|
||||
!table.ReadS32(&axisValue.format1.value)) {
|
||||
return Drop("Failed to read axis value (format 1)");
|
||||
}
|
||||
if (axisValue.format1.axisIndex >= this->designAxisCount) {
|
||||
return Drop("Axis index out of range");
|
||||
}
|
||||
if ((axisValue.format1.flags & 0xFFFCu) != 0) {
|
||||
Warning("Unexpected axis value flags");
|
||||
axisValue.format1.flags &= ~0xFFFCu;
|
||||
}
|
||||
if (!ValidateNameId(axisValue.format1.valueNameID)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (!table.ReadU16(&axisValue.format2.axisIndex) ||
|
||||
!table.ReadU16(&axisValue.format2.flags) ||
|
||||
!table.ReadU16(&axisValue.format2.valueNameID) ||
|
||||
!table.ReadS32(&axisValue.format2.nominalValue) ||
|
||||
!table.ReadS32(&axisValue.format2.rangeMinValue) ||
|
||||
!table.ReadS32(&axisValue.format2.rangeMaxValue)) {
|
||||
return Drop("Failed to read axis value (format 2)");
|
||||
}
|
||||
if (axisValue.format2.axisIndex >= this->designAxisCount) {
|
||||
return Drop("Axis index out of range");
|
||||
}
|
||||
if ((axisValue.format2.flags & 0xFFFCu) != 0) {
|
||||
Warning("Unexpected axis value flags");
|
||||
axisValue.format1.flags &= ~0xFFFCu;
|
||||
}
|
||||
if (!ValidateNameId(axisValue.format2.valueNameID)) {
|
||||
return true;
|
||||
}
|
||||
if (!(axisValue.format2.rangeMinValue <= axisValue.format2.nominalValue &&
|
||||
axisValue.format2.nominalValue <= axisValue.format2.rangeMaxValue)) {
|
||||
Warning("Bad axis value range or nominal value");
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (!table.ReadU16(&axisValue.format3.axisIndex) ||
|
||||
!table.ReadU16(&axisValue.format3.flags) ||
|
||||
!table.ReadU16(&axisValue.format3.valueNameID) ||
|
||||
!table.ReadS32(&axisValue.format3.value) ||
|
||||
!table.ReadS32(&axisValue.format3.linkedValue)) {
|
||||
return Drop("Failed to read axis value (format 3)");
|
||||
}
|
||||
if (axisValue.format3.axisIndex >= this->designAxisCount) {
|
||||
return Drop("Axis index out of range");
|
||||
}
|
||||
if ((axisValue.format3.flags & 0xFFFCu) != 0) {
|
||||
Warning("Unexpected axis value flags");
|
||||
axisValue.format3.flags &= ~0xFFFCu;
|
||||
}
|
||||
if (!ValidateNameId(axisValue.format3.valueNameID)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (this->minorVersion < 2) {
|
||||
Warning("Invalid table version for format 4 axis values - updating");
|
||||
this->minorVersion = 2;
|
||||
}
|
||||
if (!table.ReadU16(&axisValue.format4.axisCount) ||
|
||||
!table.ReadU16(&axisValue.format4.flags) ||
|
||||
!table.ReadU16(&axisValue.format4.valueNameID)) {
|
||||
return Drop("Failed to read axis value (format 4)");
|
||||
}
|
||||
if (axisValue.format4.axisCount > this->designAxisCount) {
|
||||
return Drop("Axis count out of range");
|
||||
}
|
||||
if ((axisValue.format4.flags & 0xFFFCu) != 0) {
|
||||
Warning("Unexpected axis value flags");
|
||||
axisValue.format4.flags &= ~0xFFFCu;
|
||||
}
|
||||
if (!ValidateNameId(axisValue.format4.valueNameID)) {
|
||||
return true;
|
||||
}
|
||||
for (unsigned j = 0; j < axisValue.format4.axisCount; j++) {
|
||||
axisValue.format4.axisValues.emplace_back();
|
||||
auto& v = axisValue.format4.axisValues[j];
|
||||
if (!table.ReadU16(&v.axisIndex) ||
|
||||
!table.ReadS32(&v.value)) {
|
||||
return Drop("Failed to read axis value");
|
||||
}
|
||||
if (v.axisIndex >= this->designAxisCount) {
|
||||
return Drop("Axis index out of range");
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return Drop("Unknown axis value format");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSTAT::Serialize(OTSStream* out) {
|
||||
off_t tableStart = out->Tell();
|
||||
|
||||
size_t headerSize = 5 * sizeof(uint16_t) + 2 * sizeof(uint32_t);
|
||||
if (this->minorVersion >= 1) {
|
||||
headerSize += sizeof(uint16_t);
|
||||
}
|
||||
|
||||
if (this->designAxisCount == 0) {
|
||||
this->designAxesOffset = 0;
|
||||
} else {
|
||||
this->designAxesOffset = headerSize;
|
||||
}
|
||||
|
||||
this->designAxisSize = sizeof(AxisRecord);
|
||||
|
||||
if (this->axisValueCount == 0) {
|
||||
this->offsetToAxisValueOffsets = 0;
|
||||
} else {
|
||||
if (this->designAxesOffset == 0) {
|
||||
this->offsetToAxisValueOffsets = headerSize;
|
||||
} else {
|
||||
this->offsetToAxisValueOffsets = this->designAxesOffset + this->designAxisCount * this->designAxisSize;
|
||||
}
|
||||
}
|
||||
|
||||
if (!out->WriteU16(this->majorVersion) ||
|
||||
!out->WriteU16(this->minorVersion) ||
|
||||
!out->WriteU16(this->designAxisSize) ||
|
||||
!out->WriteU16(this->designAxisCount) ||
|
||||
!out->WriteU32(this->designAxesOffset) ||
|
||||
!out->WriteU16(this->axisValueCount) ||
|
||||
!out->WriteU32(this->offsetToAxisValueOffsets) ||
|
||||
!(this->minorVersion < 1 || out->WriteU16(this->elidedFallbackNameID))) {
|
||||
return Error("Failed to write table header");
|
||||
}
|
||||
|
||||
if (this->designAxisCount > 0) {
|
||||
if (out->Tell() - tableStart != this->designAxesOffset) {
|
||||
return Error("Error computing designAxesOffset");
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < this->designAxisCount; i++) {
|
||||
const auto& axis = this->designAxes[i];
|
||||
if (!out->WriteU32(axis.axisTag) ||
|
||||
!out->WriteU16(axis.axisNameID) ||
|
||||
!out->WriteU16(axis.axisOrdering)) {
|
||||
return Error("Failed to write design axis");
|
||||
}
|
||||
}
|
||||
|
||||
if (this->axisValueCount > 0) {
|
||||
if (out->Tell() - tableStart != this->offsetToAxisValueOffsets) {
|
||||
return Error("Error computing offsetToAxisValueOffsets");
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t axisValueOffset = this->axisValueCount * sizeof(uint16_t);
|
||||
for (unsigned i = 0; i < this->axisValueCount; i++) {
|
||||
const auto& value = this->axisValues[i];
|
||||
if (!out->WriteU16(axisValueOffset)) {
|
||||
return Error("Failed to write axis value offset");
|
||||
}
|
||||
axisValueOffset += value.Length();
|
||||
}
|
||||
for (unsigned i = 0; i < this->axisValueCount; i++) {
|
||||
const auto& value = this->axisValues[i];
|
||||
if (!out->WriteU16(value.format)) {
|
||||
return Error("Failed to write axis value");
|
||||
}
|
||||
switch (value.format) {
|
||||
case 1:
|
||||
if (!out->WriteU16(value.format1.axisIndex) ||
|
||||
!out->WriteU16(value.format1.flags) ||
|
||||
!out->WriteU16(value.format1.valueNameID) ||
|
||||
!out->WriteS32(value.format1.value)) {
|
||||
return Error("Failed to write axis value");
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (!out->WriteU16(value.format2.axisIndex) ||
|
||||
!out->WriteU16(value.format2.flags) ||
|
||||
!out->WriteU16(value.format2.valueNameID) ||
|
||||
!out->WriteS32(value.format2.nominalValue) ||
|
||||
!out->WriteS32(value.format2.rangeMinValue) ||
|
||||
!out->WriteS32(value.format2.rangeMaxValue)) {
|
||||
return Error("Failed to write axis value");
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (!out->WriteU16(value.format3.axisIndex) ||
|
||||
!out->WriteU16(value.format3.flags) ||
|
||||
!out->WriteU16(value.format3.valueNameID) ||
|
||||
!out->WriteS32(value.format3.value) ||
|
||||
!out->WriteS32(value.format3.linkedValue)) {
|
||||
return Error("Failed to write axis value");
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (!out->WriteU16(value.format4.axisCount) ||
|
||||
!out->WriteU16(value.format4.flags) ||
|
||||
!out->WriteU16(value.format4.valueNameID)) {
|
||||
return Error("Failed to write axis value");
|
||||
}
|
||||
for (unsigned j = 0; j < value.format4.axisValues.size(); j++) {
|
||||
if (!out->WriteU16(value.format4.axisValues[j].axisIndex) ||
|
||||
!out->WriteS32(value.format4.axisValues[j].value)) {
|
||||
return Error("Failed to write axis value");
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return Error("Bad value format");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
155
gfx/ots/src/stat.h
Normal file
155
gfx/ots/src/stat.h
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
// Copyright (c) 2018 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_STAT_H_
|
||||
#define OTS_STAT_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeSTAT Interface
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class OpenTypeSTAT : public Table {
|
||||
public:
|
||||
explicit OpenTypeSTAT(Font* font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t* data, size_t length);
|
||||
bool Serialize(OTSStream* out);
|
||||
|
||||
private:
|
||||
bool ValidateNameId(uint16_t nameid, bool allowPredefined = true);
|
||||
|
||||
uint16_t majorVersion;
|
||||
uint16_t minorVersion;
|
||||
uint16_t designAxisSize;
|
||||
uint16_t designAxisCount;
|
||||
uint32_t designAxesOffset;
|
||||
uint16_t axisValueCount;
|
||||
uint32_t offsetToAxisValueOffsets;
|
||||
uint16_t elidedFallbackNameID;
|
||||
|
||||
struct AxisRecord {
|
||||
uint32_t axisTag;
|
||||
uint16_t axisNameID;
|
||||
uint16_t axisOrdering;
|
||||
};
|
||||
std::vector<AxisRecord> designAxes;
|
||||
|
||||
typedef int32_t Fixed; /* 16.16 fixed-point value */
|
||||
|
||||
struct AxisValueFormat1 {
|
||||
uint16_t axisIndex;
|
||||
uint16_t flags;
|
||||
uint16_t valueNameID;
|
||||
Fixed value;
|
||||
static size_t Length() {
|
||||
return 3 * sizeof(uint16_t) + sizeof(Fixed);
|
||||
}
|
||||
};
|
||||
|
||||
struct AxisValueFormat2 {
|
||||
uint16_t axisIndex;
|
||||
uint16_t flags;
|
||||
uint16_t valueNameID;
|
||||
Fixed nominalValue;
|
||||
Fixed rangeMinValue;
|
||||
Fixed rangeMaxValue;
|
||||
static size_t Length() {
|
||||
return 3 * sizeof(uint16_t) + 3 * sizeof(Fixed);
|
||||
}
|
||||
};
|
||||
|
||||
struct AxisValueFormat3 {
|
||||
uint16_t axisIndex;
|
||||
uint16_t flags;
|
||||
uint16_t valueNameID;
|
||||
Fixed value;
|
||||
Fixed linkedValue;
|
||||
static size_t Length() {
|
||||
return 3 * sizeof(uint16_t) + 2 * sizeof(Fixed);
|
||||
}
|
||||
};
|
||||
|
||||
struct AxisValueFormat4 {
|
||||
uint16_t axisCount;
|
||||
uint16_t flags;
|
||||
uint16_t valueNameID;
|
||||
struct AxisValue {
|
||||
uint16_t axisIndex;
|
||||
Fixed value;
|
||||
};
|
||||
std::vector<AxisValue> axisValues;
|
||||
size_t Length() const {
|
||||
return 3 * sizeof(uint16_t) + axisValues.size() * (sizeof(uint16_t) + sizeof(Fixed));
|
||||
}
|
||||
};
|
||||
|
||||
struct AxisValueRecord {
|
||||
uint16_t format;
|
||||
union {
|
||||
AxisValueFormat1 format1;
|
||||
AxisValueFormat2 format2;
|
||||
AxisValueFormat3 format3;
|
||||
AxisValueFormat4 format4;
|
||||
};
|
||||
explicit AxisValueRecord(uint16_t format_)
|
||||
: format(format_)
|
||||
{
|
||||
if (format == 4) {
|
||||
new (&this->format4) AxisValueFormat4();
|
||||
}
|
||||
}
|
||||
AxisValueRecord(const AxisValueRecord& other_)
|
||||
: format(other_.format)
|
||||
{
|
||||
switch (format) {
|
||||
case 1:
|
||||
format1 = other_.format1;
|
||||
break;
|
||||
case 2:
|
||||
format2 = other_.format2;
|
||||
break;
|
||||
case 3:
|
||||
format3 = other_.format3;
|
||||
break;
|
||||
case 4:
|
||||
new (&this->format4) AxisValueFormat4();
|
||||
format4 = other_.format4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
~AxisValueRecord() {
|
||||
if (format == 4) {
|
||||
this->format4.~AxisValueFormat4();
|
||||
}
|
||||
}
|
||||
uint32_t Length() const {
|
||||
switch (format) {
|
||||
case 1:
|
||||
return sizeof(uint16_t) + format1.Length();
|
||||
case 2:
|
||||
return sizeof(uint16_t) + format2.Length();
|
||||
case 3:
|
||||
return sizeof(uint16_t) + format3.Length();
|
||||
case 4:
|
||||
return sizeof(uint16_t) + format4.Length();
|
||||
default:
|
||||
// can't happen
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<AxisValueRecord> axisValues;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_STAT_H_
|
||||
261
gfx/ots/src/variations.cc
Normal file
261
gfx/ots/src/variations.cc
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
// Copyright (c) 2018 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "layout.h"
|
||||
|
||||
#include "fvar.h"
|
||||
|
||||
// OpenType Variations Common Table Formats
|
||||
|
||||
#define TABLE_NAME "Variations" // XXX: use individual table names
|
||||
|
||||
namespace {
|
||||
|
||||
bool ParseVariationRegionList(const ots::Font* font, const uint8_t* data, const size_t length,
|
||||
uint16_t* regionCount) {
|
||||
ots::Buffer subtable(data, length);
|
||||
|
||||
uint16_t axisCount;
|
||||
|
||||
if (!subtable.ReadU16(&axisCount) ||
|
||||
!subtable.ReadU16(regionCount)) {
|
||||
return OTS_FAILURE_MSG("Failed to read variation region list header");
|
||||
}
|
||||
|
||||
if (*regionCount == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const ots::OpenTypeFVAR* fvar =
|
||||
static_cast<ots::OpenTypeFVAR*>(font->GetTypedTable(OTS_TAG_FVAR));
|
||||
if (!fvar) {
|
||||
return OTS_FAILURE_MSG("Required fvar table is missing");
|
||||
}
|
||||
if (axisCount != fvar->AxisCount()) {
|
||||
return OTS_FAILURE_MSG("Axis count mismatch");
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < *regionCount; i++) {
|
||||
for (unsigned j = 0; j < axisCount; j++) {
|
||||
int16_t startCoord, peakCoord, endCoord;
|
||||
if (!subtable.ReadS16(&startCoord) ||
|
||||
!subtable.ReadS16(&peakCoord) ||
|
||||
!subtable.ReadS16(&endCoord)) {
|
||||
return OTS_FAILURE_MSG("Failed to read region axis coordinates");
|
||||
}
|
||||
if (startCoord > peakCoord || peakCoord > endCoord) {
|
||||
return OTS_FAILURE_MSG("Region axis coordinates out of order");
|
||||
}
|
||||
if (startCoord < -0x4000 || endCoord > 0x4000) {
|
||||
return OTS_FAILURE_MSG("Region axis coordinate out of range");
|
||||
}
|
||||
if ((peakCoord < 0 && endCoord > 0) ||
|
||||
(peakCoord > 0 && startCoord < 0)) {
|
||||
return OTS_FAILURE_MSG("Invalid region axis coordinates");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ParseVariationDataSubtable(const ots::Font* font, const uint8_t* data, const size_t length,
|
||||
const uint16_t regionCount,
|
||||
uint16_t* regionIndexCount) {
|
||||
ots::Buffer subtable(data, length);
|
||||
|
||||
uint16_t itemCount;
|
||||
uint16_t shortDeltaCount;
|
||||
|
||||
if (!subtable.ReadU16(&itemCount) ||
|
||||
!subtable.ReadU16(&shortDeltaCount) ||
|
||||
!subtable.ReadU16(regionIndexCount)) {
|
||||
return OTS_FAILURE_MSG("Failed to read variation data subtable header");
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < *regionIndexCount; i++) {
|
||||
uint16_t regionIndex;
|
||||
if (!subtable.ReadU16(®ionIndex) || regionIndex >= regionCount) {
|
||||
return OTS_FAILURE_MSG("Bad region index");
|
||||
}
|
||||
}
|
||||
|
||||
if (!subtable.Skip(size_t(itemCount) * (size_t(shortDeltaCount) + size_t(*regionIndexCount)))) {
|
||||
return OTS_FAILURE_MSG("Failed to read delta data");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool
|
||||
ParseItemVariationStore(const Font* font,
|
||||
const uint8_t* data, const size_t length,
|
||||
std::vector<uint16_t>* regionIndexCounts) {
|
||||
Buffer subtable(data, length);
|
||||
|
||||
uint16_t format;
|
||||
uint32_t variationRegionListOffset;
|
||||
uint16_t itemVariationDataCount;
|
||||
|
||||
if (!subtable.ReadU16(&format) ||
|
||||
!subtable.ReadU32(&variationRegionListOffset) ||
|
||||
!subtable.ReadU16(&itemVariationDataCount)) {
|
||||
return OTS_FAILURE_MSG("Failed to read item variation store header");
|
||||
}
|
||||
|
||||
if (format != 1) {
|
||||
return OTS_FAILURE_MSG("Unknown item variation store format");
|
||||
}
|
||||
|
||||
if (variationRegionListOffset < subtable.offset() + 4 * itemVariationDataCount ||
|
||||
variationRegionListOffset > length) {
|
||||
return OTS_FAILURE_MSG("Invalid variation region list offset");
|
||||
}
|
||||
|
||||
uint16_t regionCount;
|
||||
if (!ParseVariationRegionList(font,
|
||||
data + variationRegionListOffset,
|
||||
length - variationRegionListOffset,
|
||||
®ionCount)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse variation region list");
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < itemVariationDataCount; i++) {
|
||||
uint32_t offset;
|
||||
if (!subtable.ReadU32(&offset)) {
|
||||
return OTS_FAILURE_MSG("Failed to read variation data subtable offset");
|
||||
}
|
||||
if (offset >= length) {
|
||||
return OTS_FAILURE_MSG("Bad offset to variation data subtable");
|
||||
}
|
||||
uint16_t regionIndexCount = 0;
|
||||
if (!ParseVariationDataSubtable(font, data + offset, length - offset,
|
||||
regionCount,
|
||||
®ionIndexCount)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse variation data subtable");
|
||||
}
|
||||
if (regionIndexCounts) {
|
||||
regionIndexCounts->push_back(regionIndexCount);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseDeltaSetIndexMap(const Font* font, const uint8_t* data, const size_t length) {
|
||||
Buffer subtable(data, length);
|
||||
|
||||
uint16_t entryFormat;
|
||||
uint16_t mapCount;
|
||||
|
||||
if (!subtable.ReadU16(&entryFormat) ||
|
||||
!subtable.ReadU16(&mapCount)) {
|
||||
return OTS_FAILURE_MSG("Failed to read delta set index map header");
|
||||
}
|
||||
|
||||
const uint16_t MAP_ENTRY_SIZE_MASK = 0x0030;
|
||||
|
||||
const uint16_t entrySize = (((entryFormat & MAP_ENTRY_SIZE_MASK) >> 4) + 1);
|
||||
if (!subtable.Skip(entrySize * mapCount)) {
|
||||
return OTS_FAILURE_MSG("Failed to read delta set index map data");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseVariationData(const Font* font, const uint8_t* data, size_t length,
|
||||
size_t axisCount, size_t sharedTupleCount) {
|
||||
Buffer subtable(data, length);
|
||||
|
||||
uint16_t tupleVariationCount;
|
||||
uint16_t dataOffset;
|
||||
if (!subtable.ReadU16(&tupleVariationCount) ||
|
||||
!subtable.ReadU16(&dataOffset)) {
|
||||
return OTS_FAILURE_MSG("Failed to read variation data header");
|
||||
}
|
||||
|
||||
if (dataOffset > length) {
|
||||
return OTS_FAILURE_MSG("Invalid serialized data offset");
|
||||
}
|
||||
|
||||
tupleVariationCount &= 0x0FFF; // mask off flags
|
||||
|
||||
const uint16_t EMBEDDED_PEAK_TUPLE = 0x8000;
|
||||
const uint16_t INTERMEDIATE_REGION = 0x4000;
|
||||
const uint16_t TUPLE_INDEX_MASK = 0x0FFF;
|
||||
|
||||
for (unsigned i = 0; i < tupleVariationCount; i++) {
|
||||
uint16_t variationDataSize;
|
||||
uint16_t tupleIndex;
|
||||
|
||||
if (!subtable.ReadU16(&variationDataSize) ||
|
||||
!subtable.ReadU16(&tupleIndex)) {
|
||||
return OTS_FAILURE_MSG("Failed to read tuple variation header");
|
||||
}
|
||||
|
||||
if (tupleIndex & EMBEDDED_PEAK_TUPLE) {
|
||||
for (unsigned axis = 0; axis < axisCount; axis++) {
|
||||
int16_t coordinate;
|
||||
if (!subtable.ReadS16(&coordinate)) {
|
||||
return OTS_FAILURE_MSG("Failed to read tuple coordinate");
|
||||
}
|
||||
if (coordinate < -0x4000 || coordinate > 0x4000) {
|
||||
return OTS_FAILURE_MSG("Invalid tuple coordinate");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tupleIndex & INTERMEDIATE_REGION) {
|
||||
std::vector<int16_t> startTuple(axisCount);
|
||||
for (unsigned axis = 0; axis < axisCount; axis++) {
|
||||
int16_t coordinate;
|
||||
if (!subtable.ReadS16(&coordinate)) {
|
||||
return OTS_FAILURE_MSG("Failed to read tuple coordinate");
|
||||
}
|
||||
if (coordinate < -0x4000 || coordinate > 0x4000) {
|
||||
return OTS_FAILURE_MSG("Invalid tuple coordinate");
|
||||
}
|
||||
startTuple.push_back(coordinate);
|
||||
}
|
||||
|
||||
std::vector<int16_t> endTuple(axisCount);
|
||||
for (unsigned axis = 0; axis < axisCount; axis++) {
|
||||
int16_t coordinate;
|
||||
if (!subtable.ReadS16(&coordinate)) {
|
||||
return OTS_FAILURE_MSG("Failed to read tuple coordinate");
|
||||
}
|
||||
if (coordinate < -0x4000 || coordinate > 0x4000) {
|
||||
return OTS_FAILURE_MSG("Invalid tuple coordinate");
|
||||
}
|
||||
endTuple.push_back(coordinate);
|
||||
}
|
||||
|
||||
for (unsigned axis = 0; axis < axisCount; axis++) {
|
||||
if (startTuple[axis] > endTuple[axis]) {
|
||||
return OTS_FAILURE_MSG("Invalid intermediate range");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!(tupleIndex & EMBEDDED_PEAK_TUPLE)) {
|
||||
tupleIndex &= TUPLE_INDEX_MASK;
|
||||
if (tupleIndex >= sharedTupleCount) {
|
||||
return OTS_FAILURE_MSG("Tuple index out of range");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: we don't attempt to interpret the serialized data block
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#undef TABLE_NAME
|
||||
26
gfx/ots/src/variations.h
Normal file
26
gfx/ots/src/variations.h
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
// Copyright (c) 2018 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_VARIATIONS_H_
|
||||
#define OTS_VARIATIONS_H_
|
||||
|
||||
#include <vector>
|
||||
#include "ots.h"
|
||||
|
||||
// Utility functions for OpenType variations common table formats.
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ParseItemVariationStore(const Font* font,
|
||||
const uint8_t* data, const size_t length,
|
||||
std::vector<uint16_t>* out_region_index_count = NULL);
|
||||
|
||||
bool ParseDeltaSetIndexMap(const Font* font, const uint8_t* data, const size_t length);
|
||||
|
||||
bool ParseVariationData(const Font* font, const uint8_t* data, size_t length,
|
||||
size_t axisCount, size_t sharedTupleCount);
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_VARIATIONS_H_
|
||||
95
gfx/ots/src/vvar.cc
Normal file
95
gfx/ots/src/vvar.cc
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
// Copyright (c) 2018 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "vvar.h"
|
||||
|
||||
#include "variations.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeVVAR
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool OpenTypeVVAR::Parse(const uint8_t* data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
|
||||
uint16_t majorVersion;
|
||||
uint16_t minorVersion;
|
||||
uint32_t itemVariationStoreOffset;
|
||||
uint32_t advanceHeightMappingOffset;
|
||||
uint32_t tsbMappingOffset;
|
||||
uint32_t bsbMappingOffset;
|
||||
uint32_t vOrgMappingOffset;
|
||||
|
||||
if (!table.ReadU16(&majorVersion) ||
|
||||
!table.ReadU16(&minorVersion) ||
|
||||
!table.ReadU32(&itemVariationStoreOffset) ||
|
||||
!table.ReadU32(&advanceHeightMappingOffset) ||
|
||||
!table.ReadU32(&tsbMappingOffset) ||
|
||||
!table.ReadU32(&bsbMappingOffset) ||
|
||||
!table.ReadU32(&vOrgMappingOffset)) {
|
||||
return DropVariations("Failed to read table header");
|
||||
}
|
||||
|
||||
if (majorVersion != 1) {
|
||||
return DropVariations("Unknown table version");
|
||||
}
|
||||
|
||||
if (itemVariationStoreOffset > length ||
|
||||
advanceHeightMappingOffset > length ||
|
||||
tsbMappingOffset > length ||
|
||||
bsbMappingOffset > length ||
|
||||
vOrgMappingOffset > length) {
|
||||
return DropVariations("Invalid subtable offset");
|
||||
}
|
||||
|
||||
if (!ParseItemVariationStore(GetFont(), data + itemVariationStoreOffset,
|
||||
length - itemVariationStoreOffset)) {
|
||||
return DropVariations("Failed to parse item variation store");
|
||||
}
|
||||
|
||||
if (advanceHeightMappingOffset) {
|
||||
if (!ParseDeltaSetIndexMap(GetFont(), data + advanceHeightMappingOffset,
|
||||
length - advanceHeightMappingOffset)) {
|
||||
return DropVariations("Failed to parse advance height mappings");
|
||||
}
|
||||
}
|
||||
|
||||
if (tsbMappingOffset) {
|
||||
if (!ParseDeltaSetIndexMap(GetFont(), data + tsbMappingOffset,
|
||||
length - tsbMappingOffset)) {
|
||||
return DropVariations("Failed to parse TSB mappings");
|
||||
}
|
||||
}
|
||||
|
||||
if (bsbMappingOffset) {
|
||||
if (!ParseDeltaSetIndexMap(GetFont(), data + bsbMappingOffset,
|
||||
length - bsbMappingOffset)) {
|
||||
return DropVariations("Failed to parse BSB mappings");
|
||||
}
|
||||
}
|
||||
|
||||
if (vOrgMappingOffset) {
|
||||
if (!ParseDeltaSetIndexMap(GetFont(), data + vOrgMappingOffset,
|
||||
length - vOrgMappingOffset)) {
|
||||
return DropVariations("Failed to parse vOrg mappings");
|
||||
}
|
||||
}
|
||||
|
||||
this->m_data = data;
|
||||
this->m_length = length;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeVVAR::Serialize(OTSStream* out) {
|
||||
if (!out->Write(this->m_data, this->m_length)) {
|
||||
return Error("Failed to write VVAR table");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
31
gfx/ots/src/vvar.h
Normal file
31
gfx/ots/src/vvar.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright (c) 2018 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_VVAR_H_
|
||||
#define OTS_VVAR_H_
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeVVAR Interface
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class OpenTypeVVAR : public Table {
|
||||
public:
|
||||
explicit OpenTypeVVAR(Font* font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t* data, size_t length);
|
||||
bool Serialize(OTSStream* out);
|
||||
|
||||
private:
|
||||
const uint8_t *m_data;
|
||||
size_t m_length;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_VVAR_H_
|
||||
1588
gfx/ots/tests/cff_charstring_test.cc
Normal file
1588
gfx/ots/tests/cff_charstring_test.cc
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -533,7 +533,7 @@ TEST_F(LookupListTableTest, TesBadLookupFlag) {
|
|||
// Set IgnoreBaseGlyphs(0x0002) to the lookup flag of LookupTable[0].
|
||||
out.Seek(6);
|
||||
out.WriteU16(0x0002);
|
||||
EXPECT_FALSE(Parse());
|
||||
EXPECT_TRUE(Parse());
|
||||
}
|
||||
|
||||
TEST_F(LookupListTableTest, TesBadSubtableCount) {
|
||||
|
|
|
|||
|
|
@ -180,18 +180,9 @@ public:
|
|||
|
||||
virtual ots::TableAction GetTableAction(uint32_t aTag) override {
|
||||
// Preserve Graphite, color glyph and SVG tables
|
||||
if (
|
||||
#ifdef RELEASE_OR_BETA // For Beta/Release, also allow OT Layout tables through
|
||||
// unchecked, and rely on harfbuzz to handle them safely.
|
||||
aTag == TRUETYPE_TAG('G', 'D', 'E', 'F') ||
|
||||
if (aTag == TRUETYPE_TAG('G', 'D', 'E', 'F') ||
|
||||
aTag == TRUETYPE_TAG('G', 'P', 'O', 'S') ||
|
||||
aTag == TRUETYPE_TAG('G', 'S', 'U', 'B') ||
|
||||
#endif
|
||||
aTag == TRUETYPE_TAG('S', 'i', 'l', 'f') ||
|
||||
aTag == TRUETYPE_TAG('S', 'i', 'l', 'l') ||
|
||||
aTag == TRUETYPE_TAG('G', 'l', 'o', 'c') ||
|
||||
aTag == TRUETYPE_TAG('G', 'l', 'a', 't') ||
|
||||
aTag == TRUETYPE_TAG('F', 'e', 'a', 't') ||
|
||||
aTag == TRUETYPE_TAG('S', 'V', 'G', ' ') ||
|
||||
aTag == TRUETYPE_TAG('C', 'O', 'L', 'R') ||
|
||||
aTag == TRUETYPE_TAG('C', 'P', 'A', 'L')) {
|
||||
|
|
|
|||
|
|
@ -2006,6 +2006,13 @@ CommonStaticResolveRejectImpl(JSContext* cx, HandleValue thisVal, HandleValue ar
|
|||
return promise;
|
||||
}
|
||||
|
||||
MOZ_MUST_USE JSObject*
|
||||
js::PromiseResolve(JSContext* cx, HandleObject constructor, HandleValue value)
|
||||
{
|
||||
RootedValue C(cx, ObjectValue(*constructor));
|
||||
return CommonStaticResolveRejectImpl(cx, C, value, ResolveMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* ES2016, 25.4.4.4, Promise.reject.
|
||||
*/
|
||||
|
|
@ -2739,6 +2746,7 @@ CreatePromisePrototype(JSContext* cx, JSProtoKey key)
|
|||
static const JSFunctionSpec promise_methods[] = {
|
||||
JS_SELF_HOSTED_FN("catch", "Promise_catch", 1, 0),
|
||||
JS_FN("then", Promise_then, 2, 0),
|
||||
JS_SELF_HOSTED_FN("finally", "Promise_finally", 1, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -128,6 +128,14 @@ OriginalPromiseThen(JSContext* cx, Handle<PromiseObject*> promise,
|
|||
HandleValue onFulfilled, HandleValue onRejected,
|
||||
MutableHandleObject dependent, bool createDependent);
|
||||
|
||||
/**
|
||||
* PromiseResolve ( C, x )
|
||||
*
|
||||
* The abstract operation PromiseResolve, given a constructor and a value,
|
||||
* returns a new promise resolved with that value.
|
||||
*/
|
||||
MOZ_MUST_USE JSObject*
|
||||
PromiseResolve(JSContext* cx, HandleObject constructor, HandleValue value);
|
||||
|
||||
MOZ_MUST_USE PromiseObject*
|
||||
CreatePromiseObjectForAsync(JSContext* cx, HandleValue generatorVal);
|
||||
|
|
|
|||
|
|
@ -14,3 +14,72 @@ function Promise_catch(onRejected) {
|
|||
// Steps 1-2.
|
||||
return callContentFunction(this.then, this, undefined, onRejected);
|
||||
}
|
||||
|
||||
// Promise.prototype.finally(onFinally)
|
||||
// See https://tc39.es/proposal-promise-finally/
|
||||
function Promise_finally(onFinally) {
|
||||
// Step 1.
|
||||
var promise = this;
|
||||
|
||||
// Step 2.
|
||||
if (!IsObject(promise))
|
||||
ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "Promise", "finally", "value");
|
||||
|
||||
// Step 3.
|
||||
var C = SpeciesConstructor(promise, GetBuiltinConstructor("Promise"));
|
||||
|
||||
// Step 4.
|
||||
assert(IsConstructor(C), "SpeciesConstructor returns a constructor function");
|
||||
|
||||
// Steps 5-6.
|
||||
var thenFinally, catchFinally;
|
||||
if (!IsCallable(onFinally)) {
|
||||
thenFinally = onFinally;
|
||||
catchFinally = onFinally;
|
||||
} else {
|
||||
// ThenFinally Function.
|
||||
// The parentheses prevent the inferring of a function name.
|
||||
(thenFinally) = function(value) {
|
||||
// Steps 1-2 (implicit).
|
||||
|
||||
// Step 3.
|
||||
var result = onFinally();
|
||||
|
||||
// Steps 4-5 (implicit).
|
||||
|
||||
// Step 6.
|
||||
var promise = PromiseResolve(C, result);
|
||||
|
||||
// Step 7.
|
||||
// FIXME: spec issue - "be equivalent to a function that" is not a defined spec term.
|
||||
// https://github.com/tc39/ecma262/issues/933
|
||||
|
||||
// Step 8.
|
||||
return callContentFunction(promise.then, promise, function() { return value; });
|
||||
};
|
||||
|
||||
// CatchFinally Function.
|
||||
// The parentheses prevent the inferring of a function name.
|
||||
(catchFinally) = function(reason) {
|
||||
// Steps 1-2 (implicit).
|
||||
|
||||
// Step 3.
|
||||
var result = onFinally();
|
||||
|
||||
// Steps 4-5 (implicit).
|
||||
|
||||
// Step 6.
|
||||
var promise = PromiseResolve(C, result);
|
||||
|
||||
// Step 7.
|
||||
// FIXME: spec issue - "be equivalent to a function that" is not a defined spec term.
|
||||
// https://github.com/tc39/ecma262/issues/933
|
||||
|
||||
// Step 8.
|
||||
return callContentFunction(promise.then, promise, function() { throw reason; });
|
||||
};
|
||||
}
|
||||
|
||||
// Step 7.
|
||||
return callContentFunction(promise.then, promise, thenFinally, catchFinally);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3900,10 +3900,10 @@ ConvertRegExpTreeToObject(JSContext* cx, irregexp::RegExpTree* tree)
|
|||
return nullptr;
|
||||
return obj;
|
||||
}
|
||||
if (tree->IsLookahead()) {
|
||||
if (!StringProp(cx, obj, "type", "Lookahead"))
|
||||
if (tree->IsLookaround()) {
|
||||
if (!StringProp(cx, obj, "type", "Lookaround"))
|
||||
return nullptr;
|
||||
irregexp::RegExpLookahead* t = tree->AsLookahead();
|
||||
irregexp::RegExpLookaround* t = tree->AsLookaround();
|
||||
if (!BooleanProp(cx, obj, "is_positive", t->is_positive()))
|
||||
return nullptr;
|
||||
if (!TreeProp(cx, obj, "body", t->body()))
|
||||
|
|
|
|||
|
|
@ -582,7 +582,7 @@ NativeRegExpMacroAssembler::CheckAtStart(Label* on_at_start)
|
|||
}
|
||||
|
||||
void
|
||||
NativeRegExpMacroAssembler::CheckNotAtStart(Label* on_not_at_start)
|
||||
NativeRegExpMacroAssembler::CheckNotAtStart(int cp_offset, Label* on_not_at_start)
|
||||
{
|
||||
JitSpew(SPEW_PREFIX "CheckNotAtStart");
|
||||
|
||||
|
|
@ -673,7 +673,7 @@ NativeRegExpMacroAssembler::CheckGreedyLoop(Label* on_tos_equals_current_positio
|
|||
}
|
||||
|
||||
void
|
||||
NativeRegExpMacroAssembler::CheckNotBackReference(int start_reg, Label* on_no_match)
|
||||
NativeRegExpMacroAssembler::CheckNotBackReference(int start_reg, bool read_backward, Label* on_no_match)
|
||||
{
|
||||
JitSpew(SPEW_PREFIX "CheckNotBackReference(%d)", start_reg);
|
||||
|
||||
|
|
@ -744,8 +744,8 @@ NativeRegExpMacroAssembler::CheckNotBackReference(int start_reg, Label* on_no_ma
|
|||
}
|
||||
|
||||
void
|
||||
NativeRegExpMacroAssembler::CheckNotBackReferenceIgnoreCase(int start_reg, Label* on_no_match,
|
||||
bool unicode)
|
||||
NativeRegExpMacroAssembler::CheckNotBackReferenceIgnoreCase(int start_reg, bool read_backward,
|
||||
Label* on_no_match, bool unicode)
|
||||
{
|
||||
JitSpew(SPEW_PREFIX "CheckNotBackReferenceIgnoreCase(%d, %d)", start_reg, unicode);
|
||||
|
||||
|
|
|
|||
|
|
@ -105,9 +105,10 @@ class MOZ_STACK_CLASS NativeRegExpMacroAssembler final : public RegExpMacroAssem
|
|||
void CheckCharacterGT(char16_t limit, jit::Label* on_greater);
|
||||
void CheckCharacterLT(char16_t limit, jit::Label* on_less);
|
||||
void CheckGreedyLoop(jit::Label* on_tos_equals_current_position);
|
||||
void CheckNotAtStart(jit::Label* on_not_at_start);
|
||||
void CheckNotBackReference(int start_reg, jit::Label* on_no_match);
|
||||
void CheckNotBackReferenceIgnoreCase(int start_reg, jit::Label* on_no_match, bool unicode);
|
||||
void CheckNotAtStart(int cp_offset, jit::Label* on_not_at_start);
|
||||
void CheckNotBackReference(int start_reg, bool read_backward, jit::Label* on_no_match);
|
||||
void CheckNotBackReferenceIgnoreCase(int start_reg, bool read_backward,
|
||||
jit::Label* on_no_match, bool unicode);
|
||||
void CheckNotCharacter(unsigned c, jit::Label* on_not_equal);
|
||||
void CheckNotCharacterAfterAnd(unsigned c, unsigned and_with, jit::Label* on_not_equal);
|
||||
void CheckNotCharacterAfterMinusAnd(char16_t c, char16_t minus, char16_t and_with,
|
||||
|
|
|
|||
|
|
@ -250,16 +250,16 @@ RegExpCapture::CaptureRegisters()
|
|||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// RegExpLookahead
|
||||
// RegExpLookaround
|
||||
|
||||
Interval
|
||||
RegExpLookahead::CaptureRegisters()
|
||||
RegExpLookaround::CaptureRegisters()
|
||||
{
|
||||
return body()->CaptureRegisters();
|
||||
}
|
||||
|
||||
bool
|
||||
RegExpLookahead::IsAnchoredAtStart()
|
||||
RegExpLookaround::IsAnchoredAtStart()
|
||||
{
|
||||
return is_positive() && body()->IsAnchoredAtStart();
|
||||
return is_positive() && type() == LOOKAHEAD && body()->IsAnchoredAtStart();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -360,6 +360,7 @@ class RegExpCapture : public RegExpTree
|
|||
virtual int min_match() { return body_->min_match(); }
|
||||
virtual int max_match() { return body_->max_match(); }
|
||||
RegExpTree* body() { return body_; }
|
||||
void set_body(RegExpTree* body) { body_ = body; }
|
||||
int index() { return index_; }
|
||||
static int StartRegister(int index) { return index * 2; }
|
||||
static int EndRegister(int index) { return index * 2 + 1; }
|
||||
|
|
@ -369,25 +370,29 @@ class RegExpCapture : public RegExpTree
|
|||
int index_;
|
||||
};
|
||||
|
||||
class RegExpLookahead : public RegExpTree
|
||||
class RegExpLookaround : public RegExpTree
|
||||
{
|
||||
public:
|
||||
RegExpLookahead(RegExpTree* body,
|
||||
bool is_positive,
|
||||
int capture_count,
|
||||
int capture_from)
|
||||
enum Type { LOOKAHEAD, LOOKBEHIND };
|
||||
|
||||
RegExpLookaround(RegExpTree* body,
|
||||
bool is_positive,
|
||||
int capture_count,
|
||||
int capture_from,
|
||||
Type type)
|
||||
: body_(body),
|
||||
is_positive_(is_positive),
|
||||
capture_count_(capture_count),
|
||||
capture_from_(capture_from)
|
||||
capture_from_(capture_from),
|
||||
type_(type)
|
||||
{}
|
||||
|
||||
virtual void* Accept(RegExpVisitor* visitor, void* data);
|
||||
virtual RegExpNode* ToNode(RegExpCompiler* compiler,
|
||||
RegExpNode* on_success);
|
||||
virtual RegExpLookahead* AsLookahead();
|
||||
virtual RegExpLookaround* AsLookaround();
|
||||
virtual Interval CaptureRegisters();
|
||||
virtual bool IsLookahead();
|
||||
virtual bool IsLookaround();
|
||||
virtual bool IsAnchoredAtStart();
|
||||
virtual int min_match() { return 0; }
|
||||
virtual int max_match() { return 0; }
|
||||
|
|
@ -395,12 +400,14 @@ class RegExpLookahead : public RegExpTree
|
|||
bool is_positive() { return is_positive_; }
|
||||
int capture_count() { return capture_count_; }
|
||||
int capture_from() { return capture_from_; }
|
||||
Type type() { return type_; }
|
||||
|
||||
private:
|
||||
RegExpTree* body_;
|
||||
bool is_positive_;
|
||||
int capture_count_;
|
||||
int capture_from_;
|
||||
Type type_;
|
||||
};
|
||||
|
||||
typedef InfallibleVector<RegExpCapture*, 1> RegExpCaptureVector;
|
||||
|
|
@ -417,8 +424,14 @@ class RegExpBackReference : public RegExpTree
|
|||
RegExpNode* on_success);
|
||||
virtual RegExpBackReference* AsBackReference();
|
||||
virtual bool IsBackReference();
|
||||
virtual int min_match() { return 0; }
|
||||
virtual int max_match() { return capture_->max_match(); }
|
||||
virtual int min_match() override { return 0; }
|
||||
// The capture may not be completely parsed yet, if the reference occurs
|
||||
// before the capture. In the ordinary case, nothing has been captured yet,
|
||||
// so the back reference must have the length 0. If the back reference is
|
||||
// inside a lookbehind, effectively making it a forward reference, we return
|
||||
virtual int max_match() override {
|
||||
return capture_->body() ? capture_->max_match() : 0;
|
||||
}
|
||||
int index() { return capture_->index(); }
|
||||
RegExpCapture* capture() { return capture_; }
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -82,16 +82,19 @@ V(CHECK_LT, 35, 8) /* bc8 pad8 uc16 addr32 */ \
|
|||
V(CHECK_GT, 36, 8) /* bc8 pad8 uc16 addr32 */ \
|
||||
V(CHECK_NOT_BACK_REF, 37, 8) /* bc8 reg_idx24 addr32 */ \
|
||||
V(CHECK_NOT_BACK_REF_NO_CASE, 38, 8) /* bc8 reg_idx24 addr32 */ \
|
||||
V(CHECK_NOT_REGS_EQUAL, 39, 12) /* bc8 regidx24 reg_idx32 addr32 */ \
|
||||
V(CHECK_REGISTER_LT, 40, 12) /* bc8 reg_idx24 value32 addr32 */ \
|
||||
V(CHECK_REGISTER_GE, 41, 12) /* bc8 reg_idx24 value32 addr32 */ \
|
||||
V(CHECK_REGISTER_EQ_POS, 42, 8) /* bc8 reg_idx24 addr32 */ \
|
||||
V(CHECK_AT_START, 43, 8) /* bc8 pad24 addr32 */ \
|
||||
V(CHECK_NOT_AT_START, 44, 8) /* bc8 pad24 addr32 */ \
|
||||
V(CHECK_GREEDY, 45, 8) /* bc8 pad24 addr32 */ \
|
||||
V(ADVANCE_CP_AND_GOTO, 46, 8) /* bc8 offset24 addr32 */ \
|
||||
V(SET_CURRENT_POSITION_FROM_END, 47, 4) /* bc8 idx24 */ \
|
||||
V(CHECK_NOT_BACK_REF_NO_CASE_UNICODE, 48, 8) /* bc8 reg_idx24 addr32 */
|
||||
V(CHECK_NOT_BACK_REF_BACKWARD, 39, 8) /* bc8 reg_idx24 addr32 */ \
|
||||
V(CHECK_NOT_BACK_REF_NO_CASE_BACKWARD, 40, 8) /* bc8 reg_idx24 addr32 */ \
|
||||
V(CHECK_NOT_REGS_EQUAL, 41, 12) /* bc8 regidx24 reg_idx32 addr32 */ \
|
||||
V(CHECK_REGISTER_LT, 42, 12) /* bc8 reg_idx24 value32 addr32 */ \
|
||||
V(CHECK_REGISTER_GE, 43, 12) /* bc8 reg_idx24 value32 addr32 */ \
|
||||
V(CHECK_REGISTER_EQ_POS, 44, 8) /* bc8 reg_idx24 addr32 */ \
|
||||
V(CHECK_AT_START, 45, 8) /* bc8 pad24 addr32 */ \
|
||||
V(CHECK_NOT_AT_START, 46, 8) /* bc8 pad24 addr32 */ \
|
||||
V(CHECK_GREEDY, 47, 8) /* bc8 pad24 addr32 */ \
|
||||
V(ADVANCE_CP_AND_GOTO, 48, 8) /* bc8 offset24 addr32 */ \
|
||||
V(SET_CURRENT_POSITION_FROM_END, 49, 4) /* bc8 idx24 */ \
|
||||
V(CHECK_NOT_BACK_REF_NO_CASE_UNICODE, 50, 8) /* bc8 reg_idx24 addr32 */ \
|
||||
V(CHECK_NOT_BACK_REF_NO_CASE_BACKWARD_UNICODE, 51, 8) /* bc8 reg_idx24 addr32 */
|
||||
|
||||
#define DECLARE_BYTECODES(name, code, length) \
|
||||
static const int BC_##name = code;
|
||||
|
|
|
|||
|
|
@ -721,6 +721,8 @@ ActionNode::EmptyMatchCheck(int start_register,
|
|||
int
|
||||
TextNode::EatsAtLeast(int still_to_find, int budget, bool not_at_start)
|
||||
{
|
||||
if (read_backward())
|
||||
return 0;
|
||||
int answer = Length();
|
||||
if (answer >= still_to_find)
|
||||
return answer;
|
||||
|
|
@ -736,8 +738,7 @@ TextNode::EatsAtLeast(int still_to_find, int budget, bool not_at_start)
|
|||
int
|
||||
TextNode::GreedyLoopTextLength()
|
||||
{
|
||||
TextElement elm = elements()[elements().length() - 1];
|
||||
return elm.cp_offset() + elm.length();
|
||||
return Length();
|
||||
}
|
||||
|
||||
RegExpNode*
|
||||
|
|
@ -887,6 +888,8 @@ AssertionNode::FillInBMInfo(int offset, int budget, BoyerMooreLookahead* bm, boo
|
|||
int
|
||||
BackReferenceNode::EatsAtLeast(int still_to_find, int budget, bool not_at_start)
|
||||
{
|
||||
if (read_backward())
|
||||
return 0;
|
||||
if (budget <= 0)
|
||||
return 0;
|
||||
return on_success()->EatsAtLeast(still_to_find, budget - 1, not_at_start);
|
||||
|
|
@ -1578,6 +1581,9 @@ class irregexp::RegExpCompiler
|
|||
current_expansion_factor_ = value;
|
||||
}
|
||||
|
||||
bool read_backward() { return read_backward_; }
|
||||
void set_read_backward(bool value) { read_backward_ = value; }
|
||||
|
||||
JSContext* cx() const { return cx_; }
|
||||
LifoAlloc* alloc() const { return alloc_; }
|
||||
|
||||
|
|
@ -1595,6 +1601,7 @@ class irregexp::RegExpCompiler
|
|||
bool unicode_;
|
||||
bool reg_exp_too_big_;
|
||||
int current_expansion_factor_;
|
||||
bool read_backward_;
|
||||
FrequencyCollator frequency_collator_;
|
||||
JSContext* cx_;
|
||||
LifoAlloc* alloc_;
|
||||
|
|
@ -1624,6 +1631,7 @@ RegExpCompiler::RegExpCompiler(JSContext* cx, LifoAlloc* alloc, int capture_coun
|
|||
unicode_(unicode),
|
||||
reg_exp_too_big_(false),
|
||||
current_expansion_factor_(1),
|
||||
read_backward_(false),
|
||||
frequency_collator_(),
|
||||
cx_(cx),
|
||||
alloc_(alloc)
|
||||
|
|
@ -1747,7 +1755,7 @@ irregexp::CompilePattern(JSContext* cx, RegExpShared* shared, RegExpCompileData*
|
|||
// at the start of input.
|
||||
ChoiceNode* first_step_node = alloc.newInfallible<ChoiceNode>(&alloc, 2);
|
||||
RegExpNode* char_class =
|
||||
alloc.newInfallible<TextNode>(alloc.newInfallible<RegExpCharacterClass>('*'), loop_node);
|
||||
alloc.newInfallible<TextNode>(alloc.newInfallible<RegExpCharacterClass>('*'), false, loop_node);
|
||||
first_step_node->AddAlternative(GuardedAlternative(captured_body));
|
||||
first_step_node->AddAlternative(GuardedAlternative(char_class));
|
||||
node = first_step_node;
|
||||
|
|
@ -1850,19 +1858,19 @@ RegExpAtom::ToNode(RegExpCompiler* compiler, RegExpNode* on_success)
|
|||
TextElementVector* elms =
|
||||
compiler->alloc()->newInfallible<TextElementVector>(*compiler->alloc());
|
||||
elms->append(TextElement::Atom(this));
|
||||
return compiler->alloc()->newInfallible<TextNode>(elms, on_success);
|
||||
return compiler->alloc()->newInfallible<TextNode>(elms, compiler->read_backward(), on_success);
|
||||
}
|
||||
|
||||
RegExpNode*
|
||||
RegExpText::ToNode(RegExpCompiler* compiler, RegExpNode* on_success)
|
||||
{
|
||||
return compiler->alloc()->newInfallible<TextNode>(&elements_, on_success);
|
||||
return compiler->alloc()->newInfallible<TextNode>(&elements_, compiler->read_backward(), on_success);
|
||||
}
|
||||
|
||||
RegExpNode*
|
||||
RegExpCharacterClass::ToNode(RegExpCompiler* compiler, RegExpNode* on_success)
|
||||
{
|
||||
return compiler->alloc()->newInfallible<TextNode>(this, on_success);
|
||||
return compiler->alloc()->newInfallible<TextNode>(this, compiler->read_backward(), on_success);
|
||||
}
|
||||
|
||||
RegExpNode*
|
||||
|
|
@ -2003,7 +2011,8 @@ RegExpQuantifier::ToNode(int min,
|
|||
alternation->AddAlternative(GuardedAlternative(body->ToNode(compiler, answer)));
|
||||
}
|
||||
answer = alternation;
|
||||
if (not_at_start) alternation->set_not_at_start();
|
||||
if (not_at_start && !compiler->read_backward())
|
||||
alternation->set_not_at_start();
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
|
@ -2015,8 +2024,9 @@ RegExpQuantifier::ToNode(int min,
|
|||
int reg_ctr = needs_counter
|
||||
? compiler->AllocateRegister()
|
||||
: RegExpCompiler::kNoRegister;
|
||||
LoopChoiceNode* center = alloc->newInfallible<LoopChoiceNode>(alloc, body->min_match() == 0);
|
||||
if (not_at_start)
|
||||
LoopChoiceNode* center = alloc->newInfallible<LoopChoiceNode>(alloc, body->min_match() == 0,
|
||||
compiler->read_backward());
|
||||
if (not_at_start && !compiler->read_backward())
|
||||
center->set_not_at_start();
|
||||
RegExpNode* loop_return = needs_counter
|
||||
? static_cast<RegExpNode*>(ActionNode::IncrementRegister(reg_ctr, center))
|
||||
|
|
@ -2092,7 +2102,7 @@ RegExpAssertion::ToNode(RegExpCompiler* compiler,
|
|||
CharacterRange::AddClassEscape(alloc, 'n', newline_ranges);
|
||||
RegExpCharacterClass* newline_atom = alloc->newInfallible<RegExpCharacterClass>('n');
|
||||
TextNode* newline_matcher =
|
||||
alloc->newInfallible<TextNode>(newline_atom,
|
||||
alloc->newInfallible<TextNode>(newline_atom, false,
|
||||
ActionNode::PositiveSubmatchSuccess(stack_pointer_register,
|
||||
position_register,
|
||||
0, // No captures inside.
|
||||
|
|
@ -2124,6 +2134,7 @@ RegExpBackReference::ToNode(RegExpCompiler* compiler, RegExpNode* on_success)
|
|||
{
|
||||
return compiler->alloc()->newInfallible<BackReferenceNode>(RegExpCapture::StartRegister(index()),
|
||||
RegExpCapture::EndRegister(index()),
|
||||
compiler->read_backward(),
|
||||
on_success);
|
||||
}
|
||||
|
||||
|
|
@ -2134,7 +2145,7 @@ RegExpEmpty::ToNode(RegExpCompiler* compiler, RegExpNode* on_success)
|
|||
}
|
||||
|
||||
RegExpNode*
|
||||
RegExpLookahead::ToNode(RegExpCompiler* compiler, RegExpNode* on_success)
|
||||
RegExpLookaround::ToNode(RegExpCompiler* compiler, RegExpNode* on_success)
|
||||
{
|
||||
int stack_pointer_register = compiler->AllocateRegister();
|
||||
int position_register = compiler->AllocateRegister();
|
||||
|
|
@ -2145,6 +2156,10 @@ RegExpLookahead::ToNode(RegExpCompiler* compiler, RegExpNode* on_success)
|
|||
int register_start =
|
||||
register_of_first_capture + capture_from_ * registers_per_capture;
|
||||
|
||||
RegExpNode* result;
|
||||
bool was_reading_backward = compiler->read_backward();
|
||||
compiler->set_read_backward(type() == LOOKBEHIND);
|
||||
|
||||
if (is_positive()) {
|
||||
RegExpNode* bodyNode =
|
||||
body()->ToNode(compiler,
|
||||
|
|
@ -2153,37 +2168,39 @@ RegExpLookahead::ToNode(RegExpCompiler* compiler, RegExpNode* on_success)
|
|||
register_count,
|
||||
register_start,
|
||||
on_success));
|
||||
return ActionNode::BeginSubmatch(stack_pointer_register,
|
||||
result = ActionNode::BeginSubmatch(stack_pointer_register,
|
||||
position_register,
|
||||
bodyNode);
|
||||
} else {
|
||||
// We use a ChoiceNode for a negative lookahead because it has most of
|
||||
// the characteristics we need. It has the body of the lookahead as its
|
||||
// first alternative and the expression after the lookahead of the second
|
||||
// alternative. If the first alternative succeeds then the
|
||||
// NegativeSubmatchSuccess will unwind the stack including everything the
|
||||
// choice node set up and backtrack. If the first alternative fails then
|
||||
// the second alternative is tried, which is exactly the desired result
|
||||
// for a negative lookahead. The NegativeLookaheadChoiceNode is a special
|
||||
// ChoiceNode that knows to ignore the first exit when calculating quick
|
||||
// checks.
|
||||
LifoAlloc* alloc = compiler->alloc();
|
||||
|
||||
RegExpNode* success =
|
||||
alloc->newInfallible<NegativeSubmatchSuccess>(alloc,
|
||||
stack_pointer_register,
|
||||
position_register,
|
||||
register_count,
|
||||
register_start);
|
||||
GuardedAlternative body_alt(body()->ToNode(compiler, success));
|
||||
|
||||
ChoiceNode* choice_node =
|
||||
alloc->newInfallible<NegativeLookaheadChoiceNode>(alloc, body_alt, GuardedAlternative(on_success));
|
||||
|
||||
result = ActionNode::BeginSubmatch(stack_pointer_register,
|
||||
position_register,
|
||||
bodyNode);
|
||||
choice_node);
|
||||
}
|
||||
|
||||
// We use a ChoiceNode for a negative lookahead because it has most of
|
||||
// the characteristics we need. It has the body of the lookahead as its
|
||||
// first alternative and the expression after the lookahead of the second
|
||||
// alternative. If the first alternative succeeds then the
|
||||
// NegativeSubmatchSuccess will unwind the stack including everything the
|
||||
// choice node set up and backtrack. If the first alternative fails then
|
||||
// the second alternative is tried, which is exactly the desired result
|
||||
// for a negative lookahead. The NegativeLookaheadChoiceNode is a special
|
||||
// ChoiceNode that knows to ignore the first exit when calculating quick
|
||||
// checks.
|
||||
LifoAlloc* alloc = compiler->alloc();
|
||||
|
||||
RegExpNode* success =
|
||||
alloc->newInfallible<NegativeSubmatchSuccess>(alloc,
|
||||
stack_pointer_register,
|
||||
position_register,
|
||||
register_count,
|
||||
register_start);
|
||||
GuardedAlternative body_alt(body()->ToNode(compiler, success));
|
||||
|
||||
ChoiceNode* choice_node =
|
||||
alloc->newInfallible<NegativeLookaheadChoiceNode>(alloc, body_alt, GuardedAlternative(on_success));
|
||||
|
||||
return ActionNode::BeginSubmatch(stack_pointer_register,
|
||||
position_register,
|
||||
choice_node);
|
||||
compiler->set_read_backward(was_reading_backward);
|
||||
return result;
|
||||
}
|
||||
|
||||
RegExpNode*
|
||||
|
|
@ -2198,8 +2215,14 @@ RegExpCapture::ToNode(RegExpTree* body,
|
|||
RegExpCompiler* compiler,
|
||||
RegExpNode* on_success)
|
||||
{
|
||||
MOZ_ASSERT(body);
|
||||
int start_reg = RegExpCapture::StartRegister(index);
|
||||
int end_reg = RegExpCapture::EndRegister(index);
|
||||
if (compiler->read_backward()) {
|
||||
// std::swap(start_reg, end_reg);
|
||||
start_reg = RegExpCapture::EndRegister(index);
|
||||
end_reg = RegExpCapture::StartRegister(index);
|
||||
}
|
||||
RegExpNode* store_end = ActionNode::StorePosition(end_reg, true, on_success);
|
||||
RegExpNode* body_node = body->ToNode(compiler, store_end);
|
||||
return ActionNode::StorePosition(start_reg, true, body_node);
|
||||
|
|
@ -2210,8 +2233,15 @@ RegExpAlternative::ToNode(RegExpCompiler* compiler, RegExpNode* on_success)
|
|||
{
|
||||
const RegExpTreeVector& children = nodes();
|
||||
RegExpNode* current = on_success;
|
||||
for (int i = children.length() - 1; i >= 0; i--)
|
||||
current = children[i]->ToNode(compiler, current);
|
||||
if (compiler->read_backward()) {
|
||||
for (int i = 0; i < children.length(); i++) {
|
||||
current = children[i]->ToNode(compiler, current);
|
||||
}
|
||||
} else {
|
||||
for (int i = children.length() - 1; i >= 0; i--) {
|
||||
current = children[i]->ToNode(compiler, current);
|
||||
}
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
|
|
@ -2764,7 +2794,6 @@ Trace::InvalidateCurrentCharacter()
|
|||
void
|
||||
Trace::AdvanceCurrentPositionInTrace(int by, RegExpCompiler* compiler)
|
||||
{
|
||||
MOZ_ASSERT(by > 0);
|
||||
// We don't have an instruction for shifting the current character register
|
||||
// down or for using a shifted value for anything so lets just forget that
|
||||
// we preloaded any characters into it.
|
||||
|
|
@ -3109,9 +3138,9 @@ AssertionNode::Emit(RegExpCompiler* compiler, Trace* trace)
|
|||
return;
|
||||
}
|
||||
if (trace->at_start() == Trace::UNKNOWN) {
|
||||
assembler->CheckNotAtStart(trace->backtrack());
|
||||
assembler->CheckNotAtStart(trace->cp_offset(), trace->backtrack());
|
||||
Trace at_start_trace = *trace;
|
||||
at_start_trace.set_at_start(true);
|
||||
at_start_trace.set_at_start(Trace::TRUE_VALUE);
|
||||
on_success()->Emit(compiler, &at_start_trace);
|
||||
return;
|
||||
}
|
||||
|
|
@ -3814,9 +3843,10 @@ TextNode::TextEmitPass(RegExpCompiler* compiler,
|
|||
jit::Label* backtrack = trace->backtrack();
|
||||
QuickCheckDetails* quick_check = trace->quick_check_performed();
|
||||
int element_count = elements().length();
|
||||
int backward_offset = read_backward() ? -Length() : 0;
|
||||
for (int i = preloaded ? 0 : element_count - 1; i >= 0; i--) {
|
||||
TextElement elm = elements()[i];
|
||||
int cp_offset = trace->cp_offset() + elm.cp_offset();
|
||||
int cp_offset = trace->cp_offset() + elm.cp_offset() + backward_offset;
|
||||
if (elm.text_type() == TextElement::ATOM) {
|
||||
const CharacterVector& quarks = elm.atom()->data();
|
||||
for (int j = preloaded ? 0 : quarks.length() - 1; j >= 0; j--) {
|
||||
|
|
@ -3844,11 +3874,12 @@ TextNode::TextEmitPass(RegExpCompiler* compiler,
|
|||
break;
|
||||
}
|
||||
if (emit_function != nullptr) {
|
||||
bool bounds_check = *checked_up_to < cp_offset + j || read_backward();
|
||||
bool bound_checked = emit_function(compiler,
|
||||
quarks[j],
|
||||
backtrack,
|
||||
cp_offset + j,
|
||||
*checked_up_to < cp_offset + j,
|
||||
bounds_check,
|
||||
preloaded);
|
||||
if (bound_checked) UpdateBoundsCheck(cp_offset + j, checked_up_to);
|
||||
}
|
||||
|
|
@ -3859,13 +3890,14 @@ TextNode::TextEmitPass(RegExpCompiler* compiler,
|
|||
if (first_element_checked && i == 0) continue;
|
||||
if (DeterminedAlready(quick_check, elm.cp_offset())) continue;
|
||||
RegExpCharacterClass* cc = elm.char_class();
|
||||
bool bounds_check = *checked_up_to < cp_offset || read_backward();
|
||||
EmitCharClass(alloc(),
|
||||
assembler,
|
||||
cc,
|
||||
ascii,
|
||||
backtrack,
|
||||
cp_offset,
|
||||
*checked_up_to < cp_offset,
|
||||
bounds_check,
|
||||
preloaded);
|
||||
UpdateBoundsCheck(cp_offset, checked_up_to);
|
||||
}
|
||||
|
|
@ -3945,8 +3977,11 @@ TextNode::Emit(RegExpCompiler* compiler, Trace* trace)
|
|||
}
|
||||
|
||||
Trace successor_trace(*trace);
|
||||
successor_trace.set_at_start(false);
|
||||
successor_trace.AdvanceCurrentPositionInTrace(Length(), compiler);
|
||||
// If we advance backward, we may end up at the start.
|
||||
successor_trace.AdvanceCurrentPositionInTrace(
|
||||
read_backward() ? -Length() : Length(), compiler);
|
||||
successor_trace.set_at_start(read_backward() ? Trace::UNKNOWN
|
||||
: Trace::FALSE_VALUE);
|
||||
RecursionCheck rc(compiler);
|
||||
on_success()->Emit(compiler, &successor_trace);
|
||||
}
|
||||
|
|
@ -4118,6 +4153,8 @@ ChoiceNode::CalculatePreloadCharacters(RegExpCompiler* compiler, int eats_at_lea
|
|||
RegExpNode*
|
||||
TextNode::GetSuccessorOfOmnivorousTextNode(RegExpCompiler* compiler)
|
||||
{
|
||||
if (read_backward()) return NULL;
|
||||
|
||||
if (elements().length() != 1)
|
||||
return nullptr;
|
||||
|
||||
|
|
@ -4165,7 +4202,7 @@ ChoiceNode::GreedyLoopTextLengthForAlternative(GuardedAlternative* alternative)
|
|||
SeqRegExpNode* seq_node = static_cast<SeqRegExpNode*>(node);
|
||||
node = seq_node->on_success();
|
||||
}
|
||||
return length;
|
||||
return read_backward() ? -length : length;
|
||||
}
|
||||
|
||||
// Creates a list of AlternativeGenerations. If the list has a reasonable
|
||||
|
|
@ -4240,7 +4277,7 @@ ChoiceNode::Emit(RegExpCompiler* compiler, Trace* trace)
|
|||
jit::Label greedy_loop_label;
|
||||
Trace counter_backtrack_trace;
|
||||
counter_backtrack_trace.set_backtrack(&greedy_loop_label);
|
||||
if (not_at_start()) counter_backtrack_trace.set_at_start(false);
|
||||
if (not_at_start()) counter_backtrack_trace.set_at_start(Trace::FALSE_VALUE);
|
||||
|
||||
if (choice_count > 1 && text_length != kNodeIsTooComplexForGreedyLoops) {
|
||||
// Here we have special handling for greedy loops containing only text nodes
|
||||
|
|
@ -4256,7 +4293,7 @@ ChoiceNode::Emit(RegExpCompiler* compiler, Trace* trace)
|
|||
current_trace = &counter_backtrack_trace;
|
||||
jit::Label greedy_match_failed;
|
||||
Trace greedy_match_trace;
|
||||
if (not_at_start()) greedy_match_trace.set_at_start(false);
|
||||
if (not_at_start()) greedy_match_trace.set_at_start(Trace::FALSE_VALUE);
|
||||
greedy_match_trace.set_backtrack(&greedy_match_failed);
|
||||
jit::Label loop_label;
|
||||
macro_assembler->Bind(&loop_label);
|
||||
|
|
@ -4605,11 +4642,14 @@ BackReferenceNode::Emit(RegExpCompiler* compiler, Trace* trace)
|
|||
MOZ_ASSERT(start_reg_ + 1 == end_reg_);
|
||||
if (compiler->ignore_case()) {
|
||||
assembler->CheckNotBackReferenceIgnoreCase(start_reg_,
|
||||
read_backward(),
|
||||
trace->backtrack(),
|
||||
compiler->unicode());
|
||||
} else {
|
||||
assembler->CheckNotBackReference(start_reg_, trace->backtrack());
|
||||
assembler->CheckNotBackReference(start_reg_, read_backward(), trace->backtrack());
|
||||
}
|
||||
// We are going to advance backward, so we may end up at the start.
|
||||
if (read_backward()) trace->set_at_start(Trace::UNKNOWN);
|
||||
on_success()->Emit(compiler, trace);
|
||||
}
|
||||
|
||||
|
|
@ -4977,7 +5017,6 @@ QuickCheckDetails::Clear()
|
|||
void
|
||||
QuickCheckDetails::Advance(int by, bool ascii)
|
||||
{
|
||||
MOZ_ASSERT(by >= 0);
|
||||
if (by >= characters_) {
|
||||
Clear();
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ InterpretCode(JSContext* cx, const uint8_t* byteCode, const CharT* chars, size_t
|
|||
VISIT(Atom) \
|
||||
VISIT(Quantifier) \
|
||||
VISIT(Capture) \
|
||||
VISIT(Lookahead) \
|
||||
VISIT(Lookaround) \
|
||||
VISIT(BackReference) \
|
||||
VISIT(Empty) \
|
||||
VISIT(Text)
|
||||
|
|
@ -763,15 +763,19 @@ class TextNode : public SeqRegExpNode
|
|||
{
|
||||
public:
|
||||
TextNode(TextElementVector* elements,
|
||||
bool read_backward,
|
||||
RegExpNode* on_success)
|
||||
: SeqRegExpNode(on_success),
|
||||
elements_(elements)
|
||||
elements_(elements),
|
||||
read_backward_(read_backward)
|
||||
{}
|
||||
|
||||
TextNode(RegExpCharacterClass* that,
|
||||
bool read_backward,
|
||||
RegExpNode* on_success)
|
||||
: SeqRegExpNode(on_success),
|
||||
elements_(alloc()->newInfallible<TextElementVector>(*alloc()))
|
||||
elements_(alloc()->newInfallible<TextElementVector>(*alloc())),
|
||||
read_backward_(read_backward)
|
||||
{
|
||||
elements_->append(TextElement::CharClass(that));
|
||||
}
|
||||
|
|
@ -784,6 +788,7 @@ class TextNode : public SeqRegExpNode
|
|||
int characters_filled_in,
|
||||
bool not_at_start);
|
||||
TextElementVector& elements() { return *elements_; }
|
||||
bool read_backward() { return read_backward_; }
|
||||
void MakeCaseIndependent(bool is_ascii, bool unicode);
|
||||
virtual int GreedyLoopTextLength();
|
||||
virtual RegExpNode* GetSuccessorOfOmnivorousTextNode(
|
||||
|
|
@ -814,6 +819,7 @@ class TextNode : public SeqRegExpNode
|
|||
int* checked_up_to);
|
||||
int Length();
|
||||
TextElementVector* elements_;
|
||||
bool read_backward_;
|
||||
};
|
||||
|
||||
class AssertionNode : public SeqRegExpNode
|
||||
|
|
@ -882,15 +888,18 @@ class BackReferenceNode : public SeqRegExpNode
|
|||
public:
|
||||
BackReferenceNode(int start_reg,
|
||||
int end_reg,
|
||||
bool read_backward,
|
||||
RegExpNode* on_success)
|
||||
: SeqRegExpNode(on_success),
|
||||
start_reg_(start_reg),
|
||||
end_reg_(end_reg)
|
||||
end_reg_(end_reg),
|
||||
read_backward_(read_backward)
|
||||
{}
|
||||
|
||||
virtual void Accept(NodeVisitor* visitor);
|
||||
int start_register() { return start_reg_; }
|
||||
int end_register() { return end_reg_; }
|
||||
bool read_backward() { return read_backward_; }
|
||||
virtual void Emit(RegExpCompiler* compiler, Trace* trace);
|
||||
virtual int EatsAtLeast(int still_to_find,
|
||||
int recursion_depth,
|
||||
|
|
@ -909,6 +918,7 @@ class BackReferenceNode : public SeqRegExpNode
|
|||
private:
|
||||
int start_reg_;
|
||||
int end_reg_;
|
||||
bool read_backward_;
|
||||
};
|
||||
|
||||
class EndNode : public RegExpNode
|
||||
|
|
@ -1053,6 +1063,7 @@ class ChoiceNode : public RegExpNode
|
|||
void set_being_calculated(bool b) { being_calculated_ = b; }
|
||||
virtual bool try_to_emit_quick_check_for_alternative(int i) { return true; }
|
||||
virtual RegExpNode* FilterASCII(int depth, bool ignore_case, bool unicode);
|
||||
virtual bool read_backward() { return false; }
|
||||
|
||||
protected:
|
||||
int GreedyLoopTextLengthForAlternative(GuardedAlternative* alternative);
|
||||
|
|
@ -1111,11 +1122,13 @@ class NegativeLookaheadChoiceNode : public ChoiceNode
|
|||
class LoopChoiceNode : public ChoiceNode
|
||||
{
|
||||
public:
|
||||
explicit LoopChoiceNode(LifoAlloc* alloc, bool body_can_be_zero_length)
|
||||
explicit LoopChoiceNode(LifoAlloc* alloc, bool body_can_be_zero_length,
|
||||
bool read_backward)
|
||||
: ChoiceNode(alloc, 2),
|
||||
loop_node_(nullptr),
|
||||
continue_node_(nullptr),
|
||||
body_can_be_zero_length_(body_can_be_zero_length)
|
||||
body_can_be_zero_length_(body_can_be_zero_length),
|
||||
read_backward_(read_backward)
|
||||
{}
|
||||
|
||||
void AddLoopAlternative(GuardedAlternative alt);
|
||||
|
|
@ -1133,6 +1146,7 @@ class LoopChoiceNode : public ChoiceNode
|
|||
RegExpNode* loop_node() { return loop_node_; }
|
||||
RegExpNode* continue_node() { return continue_node_; }
|
||||
bool body_can_be_zero_length() { return body_can_be_zero_length_; }
|
||||
virtual bool read_backward() { return read_backward_; }
|
||||
virtual void Accept(NodeVisitor* visitor);
|
||||
virtual RegExpNode* FilterASCII(int depth, bool ignore_case, bool unicode);
|
||||
|
||||
|
|
@ -1147,6 +1161,7 @@ class LoopChoiceNode : public ChoiceNode
|
|||
RegExpNode* loop_node_;
|
||||
RegExpNode* continue_node_;
|
||||
bool body_can_be_zero_length_;
|
||||
bool read_backward_;
|
||||
};
|
||||
|
||||
// Improve the speed that we scan for an initial point where a non-anchored
|
||||
|
|
@ -1422,8 +1437,8 @@ class Trace
|
|||
}
|
||||
|
||||
TriBool at_start() { return at_start_; }
|
||||
void set_at_start(bool at_start) {
|
||||
at_start_ = at_start ? TRUE_VALUE : FALSE_VALUE;
|
||||
void set_at_start(TriBool at_start) {
|
||||
at_start_ = at_start;
|
||||
}
|
||||
jit::Label* backtrack() { return backtrack_; }
|
||||
jit::Label* loop_label() { return loop_label_; }
|
||||
|
|
|
|||
|
|
@ -222,8 +222,8 @@ irregexp::InterpretCode(JSContext* cx, const uint8_t* byteCode, const CharT* cha
|
|||
}
|
||||
break;
|
||||
BYTECODE(LOAD_CURRENT_CHAR) {
|
||||
size_t pos = current + (insn >> BYTECODE_SHIFT);
|
||||
if (pos >= length) {
|
||||
int pos = current + (insn >> BYTECODE_SHIFT);
|
||||
if (pos >= (int)length || pos < 0) {
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
} else {
|
||||
current_char = chars[pos];
|
||||
|
|
@ -238,8 +238,8 @@ irregexp::InterpretCode(JSContext* cx, const uint8_t* byteCode, const CharT* cha
|
|||
break;
|
||||
}
|
||||
BYTECODE(LOAD_2_CURRENT_CHARS) {
|
||||
size_t pos = current + (insn >> BYTECODE_SHIFT);
|
||||
if (pos + 2 > length) {
|
||||
int pos = current + (insn >> BYTECODE_SHIFT);
|
||||
if (pos + 2 > (int)length || pos < 0) {
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
} else {
|
||||
CharT next = chars[pos + 1];
|
||||
|
|
@ -425,6 +425,30 @@ irregexp::InterpretCode(JSContext* cx, const uint8_t* byteCode, const CharT* cha
|
|||
pc += BC_CHECK_NOT_BACK_REF_LENGTH;
|
||||
break;
|
||||
}
|
||||
BYTECODE(CHECK_NOT_BACK_REF_BACKWARD) {
|
||||
int from = registers[insn >> BYTECODE_SHIFT];
|
||||
int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
|
||||
if (from < 0 || len <= 0) {
|
||||
pc += BC_CHECK_NOT_BACK_REF_BACKWARD_LENGTH;
|
||||
break;
|
||||
}
|
||||
if (int(current) - len < 0) {
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
break;
|
||||
} else {
|
||||
int i;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (chars[from + i] != chars[int(current) - len + i]) {
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i < len) break;
|
||||
current -= len;
|
||||
}
|
||||
pc += BC_CHECK_NOT_BACK_REF_BACKWARD_LENGTH;
|
||||
break;
|
||||
}
|
||||
BYTECODE(CHECK_NOT_BACK_REF_NO_CASE) {
|
||||
int from = registers[insn >> BYTECODE_SHIFT];
|
||||
int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
|
||||
|
|
@ -465,6 +489,46 @@ irregexp::InterpretCode(JSContext* cx, const uint8_t* byteCode, const CharT* cha
|
|||
}
|
||||
break;
|
||||
}
|
||||
BYTECODE(CHECK_NOT_BACK_REF_NO_CASE_BACKWARD) {
|
||||
int from = registers[insn >> BYTECODE_SHIFT];
|
||||
int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
|
||||
if (from < 0 || len <= 0) {
|
||||
pc += BC_CHECK_NOT_BACK_REF_NO_CASE_BACKWARD_LENGTH;
|
||||
break;
|
||||
}
|
||||
if (int(current) - len < 0) {
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
break;
|
||||
}
|
||||
if (CaseInsensitiveCompareStrings(chars + from, chars + int(current) - len, len * sizeof(CharT))) {
|
||||
current -= len;
|
||||
pc += BC_CHECK_NOT_BACK_REF_NO_CASE_BACKWARD_LENGTH;
|
||||
} else {
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
BYTECODE(CHECK_NOT_BACK_REF_NO_CASE_BACKWARD_UNICODE) {
|
||||
int from = registers[insn >> BYTECODE_SHIFT];
|
||||
int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
|
||||
if (from < 0 || len <= 0) {
|
||||
pc += BC_CHECK_NOT_BACK_REF_NO_CASE_BACKWARD_LENGTH;
|
||||
break;
|
||||
}
|
||||
if (int(current) - len < 0) {
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
break;
|
||||
}
|
||||
if (CaseInsensitiveCompareUCStrings(chars + from, chars + int(current) - len, len * sizeof(CharT))) {
|
||||
current -= len;
|
||||
pc += BC_CHECK_NOT_BACK_REF_NO_CASE_BACKWARD_LENGTH;
|
||||
} else {
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
BYTECODE(CHECK_AT_START)
|
||||
if (current == 0)
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
|
|
@ -472,7 +536,7 @@ irregexp::InterpretCode(JSContext* cx, const uint8_t* byteCode, const CharT* cha
|
|||
pc += BC_CHECK_AT_START_LENGTH;
|
||||
break;
|
||||
BYTECODE(CHECK_NOT_AT_START)
|
||||
if (current == 0)
|
||||
if (current + (insn >> BYTECODE_SHIFT) == 0)
|
||||
pc += BC_CHECK_NOT_AT_START_LENGTH;
|
||||
else
|
||||
pc = byteCode + Load32Aligned(pc + 4);
|
||||
|
|
|
|||
|
|
@ -226,32 +226,37 @@ InterpretedRegExpMacroAssembler::CheckGreedyLoop(jit::Label* on_tos_equals_curre
|
|||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::CheckNotAtStart(jit::Label* on_not_at_start)
|
||||
InterpretedRegExpMacroAssembler::CheckNotAtStart(int cp_offset, jit::Label* on_not_at_start)
|
||||
{
|
||||
Emit(BC_CHECK_NOT_AT_START, 0);
|
||||
Emit(BC_CHECK_NOT_AT_START, cp_offset);
|
||||
EmitOrLink(on_not_at_start);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::CheckNotBackReference(int start_reg, jit::Label* on_no_match)
|
||||
InterpretedRegExpMacroAssembler::CheckNotBackReference(int start_reg, bool read_backward,
|
||||
jit::Label* on_no_match)
|
||||
{
|
||||
MOZ_ASSERT(start_reg >= 0);
|
||||
MOZ_ASSERT(start_reg <= kMaxRegister);
|
||||
Emit(BC_CHECK_NOT_BACK_REF, start_reg);
|
||||
Emit(read_backward ? BC_CHECK_NOT_BACK_REF_BACKWARD : BC_CHECK_NOT_BACK_REF,
|
||||
start_reg);
|
||||
EmitOrLink(on_no_match);
|
||||
}
|
||||
|
||||
void
|
||||
InterpretedRegExpMacroAssembler::CheckNotBackReferenceIgnoreCase(int start_reg,
|
||||
bool read_backward,
|
||||
jit::Label* on_no_match,
|
||||
bool unicode)
|
||||
{
|
||||
MOZ_ASSERT(start_reg >= 0);
|
||||
MOZ_ASSERT(start_reg <= kMaxRegister);
|
||||
if (unicode)
|
||||
Emit(BC_CHECK_NOT_BACK_REF_NO_CASE_UNICODE, start_reg);
|
||||
Emit(read_backward ? BC_CHECK_NOT_BACK_REF_NO_CASE_BACKWARD_UNICODE : BC_CHECK_NOT_BACK_REF_NO_CASE_UNICODE,
|
||||
start_reg);
|
||||
else
|
||||
Emit(BC_CHECK_NOT_BACK_REF_NO_CASE, start_reg);
|
||||
Emit(read_backward ? BC_CHECK_NOT_BACK_REF_NO_CASE_BACKWARD : BC_CHECK_NOT_BACK_REF_NO_CASE,
|
||||
start_reg);
|
||||
EmitOrLink(on_no_match);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -110,10 +110,10 @@ class MOZ_STACK_CLASS RegExpMacroAssembler
|
|||
virtual void CheckCharacterGT(char16_t limit, jit::Label* on_greater) = 0;
|
||||
virtual void CheckCharacterLT(char16_t limit, jit::Label* on_less) = 0;
|
||||
virtual void CheckGreedyLoop(jit::Label* on_tos_equals_current_position) = 0;
|
||||
virtual void CheckNotAtStart(jit::Label* on_not_at_start) = 0;
|
||||
virtual void CheckNotBackReference(int start_reg, jit::Label* on_no_match) = 0;
|
||||
virtual void CheckNotBackReferenceIgnoreCase(int start_reg, jit::Label* on_no_match,
|
||||
bool unicode) = 0;
|
||||
virtual void CheckNotAtStart(int cp_offset, jit::Label* on_not_at_start) = 0;
|
||||
virtual void CheckNotBackReference(int start_reg, bool read_backward, jit::Label* on_no_match) = 0;
|
||||
virtual void CheckNotBackReferenceIgnoreCase(int start_reg, bool read_backward,
|
||||
jit::Label* on_no_match, bool unicode) = 0;
|
||||
|
||||
// Check the current character for a match with a literal character. If we
|
||||
// fail to match then goto the on_failure label. End of input always
|
||||
|
|
@ -245,9 +245,10 @@ class MOZ_STACK_CLASS InterpretedRegExpMacroAssembler final : public RegExpMacro
|
|||
void CheckCharacterGT(char16_t limit, jit::Label* on_greater);
|
||||
void CheckCharacterLT(char16_t limit, jit::Label* on_less);
|
||||
void CheckGreedyLoop(jit::Label* on_tos_equals_current_position);
|
||||
void CheckNotAtStart(jit::Label* on_not_at_start);
|
||||
void CheckNotBackReference(int start_reg, jit::Label* on_no_match);
|
||||
void CheckNotBackReferenceIgnoreCase(int start_reg, jit::Label* on_no_match, bool unicode);
|
||||
void CheckNotAtStart(int cp_offset, jit::Label* on_not_at_start);
|
||||
void CheckNotBackReference(int start_reg, bool read_backward, jit::Label* on_no_match);
|
||||
void CheckNotBackReferenceIgnoreCase(int start_reg, bool read_backward,
|
||||
jit::Label* on_no_match, bool unicode);
|
||||
void CheckNotCharacter(unsigned c, jit::Label* on_not_equal);
|
||||
void CheckNotCharacterAfterAnd(unsigned c, unsigned and_with, jit::Label* on_not_equal);
|
||||
void CheckNotCharacterAfterMinusAnd(char16_t c, char16_t minus, char16_t and_with,
|
||||
|
|
|
|||
|
|
@ -227,6 +227,7 @@ RegExpParser<CharT>::RegExpParser(frontend::TokenStream& ts, LifoAlloc* alloc,
|
|||
alloc(alloc),
|
||||
captures_(nullptr),
|
||||
next_pos_(chars),
|
||||
captures_started_(0),
|
||||
end_(end),
|
||||
current_(kEndMarker),
|
||||
capture_count_(0),
|
||||
|
|
@ -418,7 +419,8 @@ RangeAtom(LifoAlloc* alloc, char16_t from, char16_t to)
|
|||
static inline RegExpTree*
|
||||
NegativeLookahead(LifoAlloc* alloc, char16_t from, char16_t to)
|
||||
{
|
||||
return alloc->newInfallible<RegExpLookahead>(RangeAtom(alloc, from, to), false, 0, 0);
|
||||
return alloc->newInfallible<RegExpLookaround>(RangeAtom(alloc, from, to), false,
|
||||
0, 0, RegExpLookaround::LOOKAHEAD);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
|
@ -1213,6 +1215,38 @@ RegExpParser<CharT>::ParseBackReferenceIndex(int* index_out)
|
|||
return true;
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
RegExpCapture*
|
||||
RegExpParser<CharT>::GetCapture(int index) {
|
||||
// The index for the capture groups are one-based. Its index in the list is
|
||||
// zero-based.
|
||||
int known_captures =
|
||||
is_scanned_for_captures_ ? capture_count_ : captures_started_;
|
||||
MOZ_ASSERT(index <= known_captures);
|
||||
if (captures_ == NULL) {
|
||||
captures_ = alloc->newInfallible<RegExpCaptureVector>(*alloc);
|
||||
}
|
||||
while ((int)captures_->length() < known_captures) {
|
||||
RegExpCapture* capture = alloc->newInfallible<RegExpCapture>(nullptr, captures_->length() + 1);
|
||||
captures_->append(capture);
|
||||
}
|
||||
return (*captures_)[index - 1];
|
||||
}
|
||||
|
||||
|
||||
template <typename CharT>
|
||||
bool
|
||||
RegExpParser<CharT>::RegExpParserState::IsInsideCaptureGroup(int index) {
|
||||
for (RegExpParserState* s = this; s != NULL; s = s->previous_state()) {
|
||||
if (s->group_type() != CAPTURE) continue;
|
||||
// Return true if we found the matching capture index.
|
||||
if (index == s->capture_index()) return true;
|
||||
// Abort if index is larger than what has been parsed up till this state.
|
||||
if (index > s->capture_index()) return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// QuantifierPrefix ::
|
||||
// { DecimalDigits }
|
||||
// { DecimalDigits , }
|
||||
|
|
@ -1423,24 +1457,24 @@ RegExpTree*
|
|||
RegExpParser<CharT>::ParseDisjunction()
|
||||
{
|
||||
// Used to store current state while parsing subexpressions.
|
||||
RegExpParserState initial_state(alloc, nullptr, INITIAL, 0);
|
||||
RegExpParserState* stored_state = &initial_state;
|
||||
RegExpParserState initial_state(alloc, nullptr, INITIAL, RegExpLookaround::LOOKAHEAD, 0);
|
||||
RegExpParserState* state = &initial_state;
|
||||
// Cache the builder in a local variable for quick access.
|
||||
RegExpBuilder* builder = initial_state.builder();
|
||||
while (true) {
|
||||
switch (current()) {
|
||||
case kEndMarker:
|
||||
if (stored_state->IsSubexpression()) {
|
||||
if (state->IsSubexpression()) {
|
||||
// Inside a parenthesized group when hitting end of input.
|
||||
return ReportError(JSMSG_MISSING_PAREN);
|
||||
}
|
||||
MOZ_ASSERT(INITIAL == stored_state->group_type());
|
||||
MOZ_ASSERT(INITIAL == state->group_type());
|
||||
// Parsing completed successfully.
|
||||
return builder->ToRegExp();
|
||||
case ')': {
|
||||
if (!stored_state->IsSubexpression())
|
||||
if (!state->IsSubexpression())
|
||||
return ReportError(JSMSG_UNMATCHED_RIGHT_PAREN);
|
||||
MOZ_ASSERT(INITIAL != stored_state->group_type());
|
||||
MOZ_ASSERT(INITIAL != state->group_type());
|
||||
|
||||
Advance();
|
||||
// End disjunction parsing and convert builder content to new single
|
||||
|
|
@ -1449,29 +1483,30 @@ RegExpParser<CharT>::ParseDisjunction()
|
|||
|
||||
int end_capture_index = captures_started();
|
||||
|
||||
int capture_index = stored_state->capture_index();
|
||||
SubexpressionType group_type = stored_state->group_type();
|
||||
|
||||
// Restore previous state.
|
||||
stored_state = stored_state->previous_state();
|
||||
builder = stored_state->builder();
|
||||
int capture_index = state->capture_index();
|
||||
SubexpressionType group_type = state->group_type();
|
||||
|
||||
// Build result of subexpression.
|
||||
if (group_type == CAPTURE) {
|
||||
RegExpCapture* capture = alloc->newInfallible<RegExpCapture>(body, capture_index);
|
||||
(*captures_)[capture_index - 1] = capture;
|
||||
RegExpCapture* capture = GetCapture(capture_index);
|
||||
capture->set_body(body);
|
||||
body = capture;
|
||||
} else if (group_type != GROUPING) {
|
||||
MOZ_ASSERT(group_type == POSITIVE_LOOKAHEAD ||
|
||||
group_type == NEGATIVE_LOOKAHEAD);
|
||||
bool is_positive = (group_type == POSITIVE_LOOKAHEAD);
|
||||
body = alloc->newInfallible<RegExpLookahead>(body,
|
||||
MOZ_ASSERT(group_type == POSITIVE_LOOKAROUND ||
|
||||
group_type == NEGATIVE_LOOKAROUND);
|
||||
bool is_positive = (group_type == POSITIVE_LOOKAROUND);
|
||||
body = alloc->newInfallible<RegExpLookaround>(body,
|
||||
is_positive,
|
||||
end_capture_index - capture_index,
|
||||
capture_index);
|
||||
capture_index,
|
||||
state->lookaround_type());
|
||||
}
|
||||
|
||||
// Restore previous state.
|
||||
state = state->previous_state();
|
||||
builder = state->builder();
|
||||
builder->AddAtom(body);
|
||||
if (unicode_ && (group_type == POSITIVE_LOOKAHEAD || group_type == NEGATIVE_LOOKAHEAD))
|
||||
if (unicode_ && (group_type == POSITIVE_LOOKAROUND || group_type == NEGATIVE_LOOKAROUND))
|
||||
continue;
|
||||
// For compatability with JSC and ES3, we allow quantifiers after
|
||||
// lookaheads, and break in all cases.
|
||||
|
|
@ -1519,6 +1554,7 @@ RegExpParser<CharT>::ParseDisjunction()
|
|||
}
|
||||
case '(': {
|
||||
SubexpressionType subexpr_type = CAPTURE;
|
||||
RegExpLookaround::Type lookaround_type = state->lookaround_type();
|
||||
Advance();
|
||||
if (current() == '?') {
|
||||
switch (Next()) {
|
||||
|
|
@ -1526,26 +1562,39 @@ RegExpParser<CharT>::ParseDisjunction()
|
|||
subexpr_type = GROUPING;
|
||||
break;
|
||||
case '=':
|
||||
subexpr_type = POSITIVE_LOOKAHEAD;
|
||||
lookaround_type = RegExpLookaround::LOOKAHEAD;
|
||||
subexpr_type = POSITIVE_LOOKAROUND;
|
||||
break;
|
||||
case '!':
|
||||
subexpr_type = NEGATIVE_LOOKAHEAD;
|
||||
lookaround_type = RegExpLookaround::LOOKAHEAD;
|
||||
subexpr_type = NEGATIVE_LOOKAROUND;
|
||||
break;
|
||||
case '<':
|
||||
Advance();
|
||||
lookaround_type = RegExpLookaround::LOOKBEHIND;
|
||||
if (Next() == '=') {
|
||||
subexpr_type = POSITIVE_LOOKAROUND;
|
||||
break;
|
||||
} else if (Next() == '!') {
|
||||
subexpr_type = NEGATIVE_LOOKAROUND;
|
||||
break;
|
||||
}
|
||||
// We didn't get a positive or negative after '<'.
|
||||
// That's an error.
|
||||
return ReportError(JSMSG_INVALID_GROUP);
|
||||
default:
|
||||
return ReportError(JSMSG_INVALID_GROUP);
|
||||
}
|
||||
Advance(2);
|
||||
} else {
|
||||
if (captures_ == nullptr)
|
||||
captures_ = alloc->newInfallible<RegExpCaptureVector>(*alloc);
|
||||
if (captures_started() >= kMaxCaptures)
|
||||
return ReportError(JSMSG_TOO_MANY_PARENS);
|
||||
captures_->append((RegExpCapture*) nullptr);
|
||||
captures_started_++;
|
||||
}
|
||||
// Store current state and begin new disjunction parsing.
|
||||
stored_state = alloc->newInfallible<RegExpParserState>(alloc, stored_state, subexpr_type,
|
||||
captures_started());
|
||||
builder = stored_state->builder();
|
||||
state = alloc->newInfallible<RegExpParserState>(alloc, state, subexpr_type,
|
||||
lookaround_type, captures_started_);
|
||||
builder = state->builder();
|
||||
continue;
|
||||
}
|
||||
case '[': {
|
||||
|
|
@ -1600,19 +1649,18 @@ RegExpParser<CharT>::ParseDisjunction()
|
|||
case '7': case '8': case '9': {
|
||||
int index = 0;
|
||||
if (ParseBackReferenceIndex(&index)) {
|
||||
RegExpCapture* capture = nullptr;
|
||||
if (captures_ != nullptr && index <= (int) captures_->length()) {
|
||||
capture = (*captures_)[index - 1];
|
||||
if (state->IsInsideCaptureGroup(index)) {
|
||||
// The backreference is inside the capture group it refers to.
|
||||
// Nothing can possibly have been captured yet.
|
||||
builder->AddEmpty();
|
||||
} else {
|
||||
RegExpCapture* capture = GetCapture(index);
|
||||
RegExpTree* atom = alloc->newInfallible<RegExpBackReference>(capture);
|
||||
if (unicode_)
|
||||
builder->AddAtom(UnicodeBackReferenceAtom(alloc, atom));
|
||||
else
|
||||
builder->AddAtom(atom);
|
||||
}
|
||||
if (capture == nullptr) {
|
||||
builder->AddEmpty();
|
||||
break;
|
||||
}
|
||||
RegExpTree* atom = alloc->newInfallible<RegExpBackReference>(capture);
|
||||
if (unicode_)
|
||||
builder->AddAtom(UnicodeBackReferenceAtom(alloc, atom));
|
||||
else
|
||||
builder->AddAtom(atom);
|
||||
break;
|
||||
}
|
||||
if (unicode_)
|
||||
|
|
|
|||
|
|
@ -229,7 +229,7 @@ class RegExpParser
|
|||
bool simple() { return simple_; }
|
||||
bool contains_anchor() { return contains_anchor_; }
|
||||
void set_contains_anchor() { contains_anchor_ = true; }
|
||||
int captures_started() { return captures_ == nullptr ? 0 : captures_->length(); }
|
||||
int captures_started() { return captures_started_; }
|
||||
const CharT* position() { return next_pos_ - 1; }
|
||||
|
||||
static const int kMaxCaptures = 1 << 16;
|
||||
|
|
@ -239,8 +239,8 @@ class RegExpParser
|
|||
enum SubexpressionType {
|
||||
INITIAL,
|
||||
CAPTURE, // All positive values represent captures.
|
||||
POSITIVE_LOOKAHEAD,
|
||||
NEGATIVE_LOOKAHEAD,
|
||||
POSITIVE_LOOKAROUND,
|
||||
NEGATIVE_LOOKAROUND,
|
||||
GROUPING
|
||||
};
|
||||
|
||||
|
|
@ -249,10 +249,12 @@ class RegExpParser
|
|||
RegExpParserState(LifoAlloc* alloc,
|
||||
RegExpParserState* previous_state,
|
||||
SubexpressionType group_type,
|
||||
RegExpLookaround::Type lookaround_type,
|
||||
int disjunction_capture_index)
|
||||
: previous_state_(previous_state),
|
||||
builder_(alloc->newInfallible<RegExpBuilder>(alloc)),
|
||||
group_type_(group_type),
|
||||
lookaround_type_(lookaround_type),
|
||||
disjunction_capture_index_(disjunction_capture_index)
|
||||
{}
|
||||
// Parser state of containing expression, if any.
|
||||
|
|
@ -262,11 +264,16 @@ class RegExpParser
|
|||
RegExpBuilder* builder() { return builder_; }
|
||||
// Type of regexp being parsed (parenthesized group or entire regexp).
|
||||
SubexpressionType group_type() { return group_type_; }
|
||||
// Lookahead or Lookbehind.
|
||||
RegExpLookaround::Type lookaround_type() { return lookaround_type_; }
|
||||
// Index in captures array of first capture in this sub-expression, if any.
|
||||
// Also the capture index of this sub-expression itself, if group_type
|
||||
// is CAPTURE.
|
||||
int capture_index() { return disjunction_capture_index_; }
|
||||
|
||||
// Check whether the parser is inside a capture group with the given index.
|
||||
bool IsInsideCaptureGroup(int index);
|
||||
|
||||
private:
|
||||
// Linked list implementation of stack of states.
|
||||
RegExpParserState* previous_state_;
|
||||
|
|
@ -274,10 +281,15 @@ class RegExpParser
|
|||
RegExpBuilder* builder_;
|
||||
// Stored disjunction type (capture, look-ahead or grouping), if any.
|
||||
SubexpressionType group_type_;
|
||||
// Stored read direction.
|
||||
RegExpLookaround::Type lookaround_type_;
|
||||
// Stored disjunction's capture index (if any).
|
||||
int disjunction_capture_index_;
|
||||
};
|
||||
|
||||
// Return the 1-indexed RegExpCapture object, allocate if necessary.
|
||||
RegExpCapture* GetCapture(int index);
|
||||
|
||||
widechar current() { return current_; }
|
||||
bool has_more() { return has_more_; }
|
||||
bool has_next() { return next_pos_ < end_; }
|
||||
|
|
@ -294,6 +306,7 @@ class RegExpParser
|
|||
const CharT* next_pos_;
|
||||
const CharT* end_;
|
||||
widechar current_;
|
||||
int captures_started_;
|
||||
// The capture count is only valid after we have scanned for captures.
|
||||
int capture_count_;
|
||||
bool has_more_;
|
||||
|
|
|
|||
|
|
@ -2102,6 +2102,21 @@ intrinsic_ModuleNamespaceExports(JSContext* cx, unsigned argc, Value* vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
intrinsic_PromiseResolve(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 2);
|
||||
|
||||
RootedObject constructor(cx, &args[0].toObject());
|
||||
JSObject* promise = js::PromiseResolve(cx, constructor, args[1]);
|
||||
if (!promise)
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*promise);
|
||||
return true;
|
||||
}
|
||||
|
||||
// The self-hosting global isn't initialized with the normal set of builtins.
|
||||
// Instead, individual C++-implemented functions that're required by
|
||||
// self-hosted code are defined as global functions. Accessing these
|
||||
|
|
@ -2498,6 +2513,10 @@ static const JSFunctionSpec intrinsic_functions[] = {
|
|||
JS_FN("AddModuleNamespaceBinding", intrinsic_AddModuleNamespaceBinding, 4, 0),
|
||||
JS_FN("ModuleNamespaceExports", intrinsic_ModuleNamespaceExports, 1, 0),
|
||||
|
||||
JS_FN("IsPromiseObject", intrinsic_IsInstanceOfBuiltin<PromiseObject>, 1, 0),
|
||||
JS_FN("CallPromiseMethodIfWrapped", CallNonGenericSelfhostedMethod<Is<PromiseObject>>, 2, 0),
|
||||
JS_FN("PromiseResolve", intrinsic_PromiseResolve, 2, 0),
|
||||
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -243,7 +243,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681
|
|||
"$`", "$'", Symbol.species])
|
||||
|
||||
gPrototypeProperties['Promise'] =
|
||||
["constructor", "catch", "then", Symbol.toStringTag];
|
||||
["constructor", "catch", "then", "finally", Symbol.toStringTag];
|
||||
gConstructorProperties['Promise'] =
|
||||
constructorProps(["resolve", "reject", "all", "race", Symbol.species]);
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,9 +1,6 @@
|
|||
This is the Brotli data compression library from
|
||||
https://github.com/google/brotli.
|
||||
|
||||
Currently, we import only the Brotli decoder (the /dec/ subdirectory), not the
|
||||
encoder (/enc/ subdirectory).
|
||||
|
||||
Upstream code can be viewed at
|
||||
https://github.com/google/brotli/tree/master/dec
|
||||
|
||||
|
|
@ -14,4 +11,4 @@ The in-tree copy is updated by running
|
|||
sh update.sh
|
||||
from within the modules/brotli directory.
|
||||
|
||||
Current version: [commit 5b4769990dc14a2bd466d2599c946c5652cba4b2].
|
||||
Current version: [commit d6d98957ca8ccb1ef45922e978bb10efca0ea541].
|
||||
|
|
|
|||
|
|
@ -28,18 +28,25 @@
|
|||
/* "code length of 8 is repeated" */
|
||||
#define BROTLI_INITIAL_REPEATED_CODE_LENGTH 8
|
||||
|
||||
/* "Large Window Brotli" */
|
||||
#define BROTLI_LARGE_MAX_DISTANCE_BITS 62U
|
||||
#define BROTLI_LARGE_MIN_WBITS 10
|
||||
#define BROTLI_LARGE_MAX_WBITS 30
|
||||
|
||||
/* Specification: 4. Encoding of distances */
|
||||
#define BROTLI_NUM_DISTANCE_SHORT_CODES 16
|
||||
#define BROTLI_MAX_NPOSTFIX 3
|
||||
#define BROTLI_MAX_NDIRECT 120
|
||||
#define BROTLI_MAX_DISTANCE_BITS 24U
|
||||
/* BROTLI_NUM_DISTANCE_SYMBOLS == 520 */
|
||||
#define BROTLI_NUM_DISTANCE_SYMBOLS (BROTLI_NUM_DISTANCE_SHORT_CODES + \
|
||||
BROTLI_MAX_NDIRECT + \
|
||||
(BROTLI_MAX_DISTANCE_BITS << \
|
||||
(BROTLI_MAX_NPOSTFIX + 1)))
|
||||
/* Distance that is guaranteed to be representable in any stream. */
|
||||
#define BROTLI_DISTANCE_ALPHABET_SIZE(NPOSTFIX, NDIRECT, MAXNBITS) ( \
|
||||
BROTLI_NUM_DISTANCE_SHORT_CODES + (NDIRECT) + \
|
||||
((MAXNBITS) << ((NPOSTFIX) + 1)))
|
||||
/* BROTLI_NUM_DISTANCE_SYMBOLS == 1128 */
|
||||
#define BROTLI_NUM_DISTANCE_SYMBOLS \
|
||||
BROTLI_DISTANCE_ALPHABET_SIZE( \
|
||||
BROTLI_MAX_NDIRECT, BROTLI_MAX_NPOSTFIX, BROTLI_LARGE_MAX_DISTANCE_BITS)
|
||||
#define BROTLI_MAX_DISTANCE 0x3FFFFFC
|
||||
#define BROTLI_MAX_ALLOWED_DISTANCE 0x7FFFFFFC
|
||||
|
||||
/* 7.1. Context modes and context ID lookup for literals */
|
||||
/* "context IDs for literals are in the range of 0..63" */
|
||||
|
|
|
|||
|
|
@ -6,110 +6,171 @@
|
|||
|
||||
/* Lookup table to map the previous two bytes to a context id.
|
||||
|
||||
There are four different context modeling modes defined here:
|
||||
CONTEXT_LSB6: context id is the least significant 6 bits of the last byte,
|
||||
CONTEXT_MSB6: context id is the most significant 6 bits of the last byte,
|
||||
CONTEXT_UTF8: second-order context model tuned for UTF8-encoded text,
|
||||
CONTEXT_SIGNED: second-order context model tuned for signed integers.
|
||||
There are four different context modeling modes defined here:
|
||||
CONTEXT_LSB6: context id is the least significant 6 bits of the last byte,
|
||||
CONTEXT_MSB6: context id is the most significant 6 bits of the last byte,
|
||||
CONTEXT_UTF8: second-order context model tuned for UTF8-encoded text,
|
||||
CONTEXT_SIGNED: second-order context model tuned for signed integers.
|
||||
|
||||
The context id for the UTF8 context model is calculated as follows. If p1
|
||||
and p2 are the previous two bytes, we calculate the context as
|
||||
If |p1| and |p2| are the previous two bytes, and |mode| is current context
|
||||
mode, we calculate the context as:
|
||||
|
||||
context = kContextLookup[p1] | kContextLookup[p2 + 256].
|
||||
context = ContextLut(mode)[p1] | ContextLut(mode)[p2 + 256].
|
||||
|
||||
If the previous two bytes are ASCII characters (i.e. < 128), this will be
|
||||
equivalent to
|
||||
For CONTEXT_UTF8 mode, if the previous two bytes are ASCII characters
|
||||
(i.e. < 128), this will be equivalent to
|
||||
|
||||
context = 4 * context1(p1) + context2(p2),
|
||||
context = 4 * context1(p1) + context2(p2),
|
||||
|
||||
where context1 is based on the previous byte in the following way:
|
||||
where context1 is based on the previous byte in the following way:
|
||||
|
||||
0 : non-ASCII control
|
||||
1 : \t, \n, \r
|
||||
2 : space
|
||||
3 : other punctuation
|
||||
4 : " '
|
||||
5 : %
|
||||
6 : ( < [ {
|
||||
7 : ) > ] }
|
||||
8 : , ; :
|
||||
9 : .
|
||||
10 : =
|
||||
11 : number
|
||||
12 : upper-case vowel
|
||||
13 : upper-case consonant
|
||||
14 : lower-case vowel
|
||||
15 : lower-case consonant
|
||||
0 : non-ASCII control
|
||||
1 : \t, \n, \r
|
||||
2 : space
|
||||
3 : other punctuation
|
||||
4 : " '
|
||||
5 : %
|
||||
6 : ( < [ {
|
||||
7 : ) > ] }
|
||||
8 : , ; :
|
||||
9 : .
|
||||
10 : =
|
||||
11 : number
|
||||
12 : upper-case vowel
|
||||
13 : upper-case consonant
|
||||
14 : lower-case vowel
|
||||
15 : lower-case consonant
|
||||
|
||||
and context2 is based on the second last byte:
|
||||
and context2 is based on the second last byte:
|
||||
|
||||
0 : control, space
|
||||
1 : punctuation
|
||||
2 : upper-case letter, number
|
||||
3 : lower-case letter
|
||||
0 : control, space
|
||||
1 : punctuation
|
||||
2 : upper-case letter, number
|
||||
3 : lower-case letter
|
||||
|
||||
If the last byte is ASCII, and the second last byte is not (in a valid UTF8
|
||||
stream it will be a continuation byte, value between 128 and 191), the
|
||||
context is the same as if the second last byte was an ASCII control or space.
|
||||
If the last byte is ASCII, and the second last byte is not (in a valid UTF8
|
||||
stream it will be a continuation byte, value between 128 and 191), the
|
||||
context is the same as if the second last byte was an ASCII control or space.
|
||||
|
||||
If the last byte is a UTF8 lead byte (value >= 192), then the next byte will
|
||||
be a continuation byte and the context id is 2 or 3 depending on the LSB of
|
||||
the last byte and to a lesser extent on the second last byte if it is ASCII.
|
||||
If the last byte is a UTF8 lead byte (value >= 192), then the next byte will
|
||||
be a continuation byte and the context id is 2 or 3 depending on the LSB of
|
||||
the last byte and to a lesser extent on the second last byte if it is ASCII.
|
||||
|
||||
If the last byte is a UTF8 continuation byte, the second last byte can be:
|
||||
- continuation byte: the next byte is probably ASCII or lead byte (assuming
|
||||
4-byte UTF8 characters are rare) and the context id is 0 or 1.
|
||||
- lead byte (192 - 207): next byte is ASCII or lead byte, context is 0 or 1
|
||||
- lead byte (208 - 255): next byte is continuation byte, context is 2 or 3
|
||||
If the last byte is a UTF8 continuation byte, the second last byte can be:
|
||||
- continuation byte: the next byte is probably ASCII or lead byte (assuming
|
||||
4-byte UTF8 characters are rare) and the context id is 0 or 1.
|
||||
- lead byte (192 - 207): next byte is ASCII or lead byte, context is 0 or 1
|
||||
- lead byte (208 - 255): next byte is continuation byte, context is 2 or 3
|
||||
|
||||
The possible value combinations of the previous two bytes, the range of
|
||||
context ids and the type of the next byte is summarized in the table below:
|
||||
The possible value combinations of the previous two bytes, the range of
|
||||
context ids and the type of the next byte is summarized in the table below:
|
||||
|
||||
|--------\-----------------------------------------------------------------|
|
||||
| \ Last byte |
|
||||
| Second \---------------------------------------------------------------|
|
||||
| last byte \ ASCII | cont. byte | lead byte |
|
||||
| \ (0-127) | (128-191) | (192-) |
|
||||
|=============|===================|=====================|==================|
|
||||
| ASCII | next: ASCII/lead | not valid | next: cont. |
|
||||
| (0-127) | context: 4 - 63 | | context: 2 - 3 |
|
||||
|-------------|-------------------|---------------------|------------------|
|
||||
| cont. byte | next: ASCII/lead | next: ASCII/lead | next: cont. |
|
||||
| (128-191) | context: 4 - 63 | context: 0 - 1 | context: 2 - 3 |
|
||||
|-------------|-------------------|---------------------|------------------|
|
||||
| lead byte | not valid | next: ASCII/lead | not valid |
|
||||
| (192-207) | | context: 0 - 1 | |
|
||||
|-------------|-------------------|---------------------|------------------|
|
||||
| lead byte | not valid | next: cont. | not valid |
|
||||
| (208-) | | context: 2 - 3 | |
|
||||
|-------------|-------------------|---------------------|------------------|
|
||||
|
||||
The context id for the signed context mode is calculated as:
|
||||
|
||||
context = (kContextLookup[512 + p1] << 3) | kContextLookup[512 + p2].
|
||||
|
||||
For any context modeling modes, the context ids can be calculated by |-ing
|
||||
together two lookups from one table using context model dependent offsets:
|
||||
|
||||
context = kContextLookup[offset1 + p1] | kContextLookup[offset2 + p2].
|
||||
|
||||
where offset1 and offset2 are dependent on the context mode.
|
||||
|--------\-----------------------------------------------------------------|
|
||||
| \ Last byte |
|
||||
| Second \---------------------------------------------------------------|
|
||||
| last byte \ ASCII | cont. byte | lead byte |
|
||||
| \ (0-127) | (128-191) | (192-) |
|
||||
|=============|===================|=====================|==================|
|
||||
| ASCII | next: ASCII/lead | not valid | next: cont. |
|
||||
| (0-127) | context: 4 - 63 | | context: 2 - 3 |
|
||||
|-------------|-------------------|---------------------|------------------|
|
||||
| cont. byte | next: ASCII/lead | next: ASCII/lead | next: cont. |
|
||||
| (128-191) | context: 4 - 63 | context: 0 - 1 | context: 2 - 3 |
|
||||
|-------------|-------------------|---------------------|------------------|
|
||||
| lead byte | not valid | next: ASCII/lead | not valid |
|
||||
| (192-207) | | context: 0 - 1 | |
|
||||
|-------------|-------------------|---------------------|------------------|
|
||||
| lead byte | not valid | next: cont. | not valid |
|
||||
| (208-) | | context: 2 - 3 | |
|
||||
|-------------|-------------------|---------------------|------------------|
|
||||
*/
|
||||
|
||||
#ifndef BROTLI_DEC_CONTEXT_H_
|
||||
#define BROTLI_DEC_CONTEXT_H_
|
||||
#ifndef BROTLI_COMMON_CONTEXT_H_
|
||||
#define BROTLI_COMMON_CONTEXT_H_
|
||||
|
||||
#include <brotli/types.h>
|
||||
|
||||
enum ContextType {
|
||||
typedef enum ContextType {
|
||||
CONTEXT_LSB6 = 0,
|
||||
CONTEXT_MSB6 = 1,
|
||||
CONTEXT_UTF8 = 2,
|
||||
CONTEXT_SIGNED = 3
|
||||
};
|
||||
} ContextType;
|
||||
|
||||
/* Common context lookup table for all context modes. */
|
||||
static const uint8_t kContextLookup[1792] = {
|
||||
static const uint8_t kContextLookup[2048] = {
|
||||
/* CONTEXT_LSB6, last byte. */
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
||||
|
||||
/* CONTEXT_LSB6, second last byte, */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
|
||||
/* CONTEXT_MSB6, last byte. */
|
||||
0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3,
|
||||
4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7,
|
||||
8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11,
|
||||
12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15,
|
||||
16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19,
|
||||
20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23,
|
||||
24, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27,
|
||||
28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31,
|
||||
32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35,
|
||||
36, 36, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39, 39,
|
||||
40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42, 42, 43, 43, 43, 43,
|
||||
44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47,
|
||||
48, 48, 48, 48, 49, 49, 49, 49, 50, 50, 50, 50, 51, 51, 51, 51,
|
||||
52, 52, 52, 52, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 55, 55,
|
||||
56, 56, 56, 56, 57, 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59,
|
||||
60, 60, 60, 60, 61, 61, 61, 61, 62, 62, 62, 62, 63, 63, 63, 63,
|
||||
|
||||
/* CONTEXT_MSB6, second last byte, */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
|
||||
/* CONTEXT_UTF8, last byte. */
|
||||
/* ASCII range. */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 0, 4, 0, 0,
|
||||
|
|
@ -130,6 +191,7 @@ static const uint8_t kContextLookup[1792] = {
|
|||
2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
|
||||
2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
|
||||
2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
|
||||
|
||||
/* CONTEXT_UTF8 second last byte. */
|
||||
/* ASCII range. */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
|
|
@ -150,23 +212,7 @@ static const uint8_t kContextLookup[1792] = {
|
|||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
/* CONTEXT_SIGNED, second last byte. */
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7,
|
||||
|
||||
/* CONTEXT_SIGNED, last byte, same as the above values shifted by 3 bits. */
|
||||
0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||
|
|
@ -184,68 +230,32 @@ static const uint8_t kContextLookup[1792] = {
|
|||
40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
|
||||
40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
|
||||
48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 56,
|
||||
/* CONTEXT_LSB6, last byte. */
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
||||
/* CONTEXT_MSB6, last byte. */
|
||||
0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3,
|
||||
4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7,
|
||||
8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11,
|
||||
12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15,
|
||||
16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19,
|
||||
20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23,
|
||||
24, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27,
|
||||
28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31,
|
||||
32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35,
|
||||
36, 36, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39, 39,
|
||||
40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42, 42, 43, 43, 43, 43,
|
||||
44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47,
|
||||
48, 48, 48, 48, 49, 49, 49, 49, 50, 50, 50, 50, 51, 51, 51, 51,
|
||||
52, 52, 52, 52, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 55, 55,
|
||||
56, 56, 56, 56, 57, 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59,
|
||||
60, 60, 60, 60, 61, 61, 61, 61, 62, 62, 62, 62, 63, 63, 63, 63,
|
||||
/* CONTEXT_{M,L}SB6, second last byte, */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
|
||||
/* CONTEXT_SIGNED, second last byte. */
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7,
|
||||
};
|
||||
|
||||
static const int kContextLookupOffsets[8] = {
|
||||
/* CONTEXT_LSB6 */
|
||||
1024, 1536,
|
||||
/* CONTEXT_MSB6 */
|
||||
1280, 1536,
|
||||
/* CONTEXT_UTF8 */
|
||||
0, 256,
|
||||
/* CONTEXT_SIGNED */
|
||||
768, 512,
|
||||
};
|
||||
typedef const uint8_t* ContextLut;
|
||||
|
||||
#endif /* BROTLI_DEC_CONTEXT_H_ */
|
||||
/* typeof(MODE) == ContextType; returns ContextLut */
|
||||
#define BROTLI_CONTEXT_LUT(MODE) (&kContextLookup[(MODE) << 9])
|
||||
|
||||
/* typeof(LUT) == ContextLut */
|
||||
#define BROTLI_CONTEXT(P1, P2, LUT) ((LUT)[P1] | ((LUT) + 256)[P2])
|
||||
|
||||
#endif /* BROTLI_COMMON_CONTEXT_H_ */
|
||||
BIN
modules/brotli/common/dictionary.bin.br
Normal file
BIN
modules/brotli/common/dictionary.bin.br
Normal file
Binary file not shown.
|
|
@ -5883,7 +5883,7 @@ static BrotliDictionary kBrotliDictionary = {
|
|||
122784,
|
||||
|
||||
/* data */
|
||||
#ifdef BROTLI_EXTERNAL_DICTIONARY_DATA
|
||||
#if defined(BROTLI_EXTERNAL_DICTIONARY_DATA)
|
||||
NULL
|
||||
#else
|
||||
kBrotliDictionaryData
|
||||
|
|
|
|||
|
|
@ -27,13 +27,13 @@ typedef struct BrotliDictionary {
|
|||
* Dictionary consists of words with length of [4..24] bytes.
|
||||
* Values at [0..3] and [25..31] indices should not be addressed.
|
||||
*/
|
||||
const uint8_t size_bits_by_length[32];
|
||||
uint8_t size_bits_by_length[32];
|
||||
|
||||
/* assert(offset[i + 1] == offset[i] + (bits[i] ? (i << bits[i]) : 0)) */
|
||||
const uint32_t offsets_by_length[32];
|
||||
uint32_t offsets_by_length[32];
|
||||
|
||||
/* assert(data_size == offsets_by_length[31]) */
|
||||
const size_t data_size;
|
||||
size_t data_size;
|
||||
|
||||
/* Data array is not bound, and should obey to size_bits_by_length values.
|
||||
Specified size matches default (RFC 7932) dictionary. Its size is
|
||||
|
|
@ -41,7 +41,7 @@ typedef struct BrotliDictionary {
|
|||
const uint8_t* data;
|
||||
} BrotliDictionary;
|
||||
|
||||
BROTLI_COMMON_API extern const BrotliDictionary* BrotliGetDictionary(void);
|
||||
BROTLI_COMMON_API const BrotliDictionary* BrotliGetDictionary(void);
|
||||
|
||||
/**
|
||||
* Sets dictionary data.
|
||||
|
|
|
|||
568
modules/brotli/common/platform.h
Normal file
568
modules/brotli/common/platform.h
Normal file
|
|
@ -0,0 +1,568 @@
|
|||
/* Copyright 2016 Google Inc. All Rights Reserved.
|
||||
|
||||
Distributed under MIT license.
|
||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
/* Macros for compiler / platform specific features and build options.
|
||||
|
||||
Build options are:
|
||||
* BROTLI_BUILD_32_BIT disables 64-bit optimizations
|
||||
* BROTLI_BUILD_64_BIT forces to use 64-bit optimizations
|
||||
* BROTLI_BUILD_BIG_ENDIAN forces to use big-endian optimizations
|
||||
* BROTLI_BUILD_ENDIAN_NEUTRAL disables endian-aware optimizations
|
||||
* BROTLI_BUILD_LITTLE_ENDIAN forces to use little-endian optimizations
|
||||
* BROTLI_BUILD_PORTABLE disables dangerous optimizations, like unaligned
|
||||
read and overlapping memcpy; this reduces decompression speed by 5%
|
||||
* BROTLI_BUILD_NO_RBIT disables "rbit" optimization for ARM CPUs
|
||||
* BROTLI_DEBUG dumps file name and line number when decoder detects stream
|
||||
or memory error
|
||||
* BROTLI_ENABLE_LOG enables asserts and dumps various state information
|
||||
*/
|
||||
|
||||
#ifndef BROTLI_COMMON_PLATFORM_H_
|
||||
#define BROTLI_COMMON_PLATFORM_H_
|
||||
|
||||
#include <string.h> /* memcpy */
|
||||
#include <stdlib.h> /* malloc, free */
|
||||
|
||||
#include <brotli/port.h>
|
||||
#include <brotli/types.h>
|
||||
|
||||
#if defined(OS_LINUX) || defined(OS_CYGWIN)
|
||||
#include <endian.h>
|
||||
#elif defined(OS_FREEBSD)
|
||||
#include <machine/endian.h>
|
||||
#elif defined(OS_MACOSX)
|
||||
#include <machine/endian.h>
|
||||
/* Let's try and follow the Linux convention */
|
||||
#define BROTLI_X_BYTE_ORDER BYTE_ORDER
|
||||
#define BROTLI_X_LITTLE_ENDIAN LITTLE_ENDIAN
|
||||
#define BROTLI_X_BIG_ENDIAN BIG_ENDIAN
|
||||
#endif
|
||||
|
||||
#if defined(BROTLI_ENABLE_LOG) || defined(BROTLI_DEBUG)
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
/* The following macros were borrowed from https://github.com/nemequ/hedley
|
||||
* with permission of original author - Evan Nemerson <evan@nemerson.com> */
|
||||
|
||||
/* >>> >>> >>> hedley macros */
|
||||
|
||||
/* Define "BROTLI_PREDICT_TRUE" and "BROTLI_PREDICT_FALSE" macros for capable
|
||||
compilers.
|
||||
|
||||
To apply compiler hint, enclose the branching condition into macros, like this:
|
||||
|
||||
if (BROTLI_PREDICT_TRUE(zero == 0)) {
|
||||
// main execution path
|
||||
} else {
|
||||
// compiler should place this code outside of main execution path
|
||||
}
|
||||
|
||||
OR:
|
||||
|
||||
if (BROTLI_PREDICT_FALSE(something_rare_or_unexpected_happens)) {
|
||||
// compiler should place this code outside of main execution path
|
||||
}
|
||||
|
||||
*/
|
||||
#if BROTLI_GNUC_HAS_BUILTIN(__builtin_expect, 3, 0, 0) || \
|
||||
BROTLI_INTEL_VERSION_CHECK(16, 0, 0) || \
|
||||
BROTLI_SUNPRO_VERSION_CHECK(5, 15, 0) || \
|
||||
BROTLI_ARM_VERSION_CHECK(4, 1, 0) || \
|
||||
BROTLI_IBM_VERSION_CHECK(10, 1, 0) || \
|
||||
BROTLI_TI_VERSION_CHECK(7, 3, 0) || \
|
||||
BROTLI_TINYC_VERSION_CHECK(0, 9, 27)
|
||||
#define BROTLI_PREDICT_TRUE(x) (__builtin_expect(!!(x), 1))
|
||||
#define BROTLI_PREDICT_FALSE(x) (__builtin_expect(x, 0))
|
||||
#else
|
||||
#define BROTLI_PREDICT_FALSE(x) (x)
|
||||
#define BROTLI_PREDICT_TRUE(x) (x)
|
||||
#endif
|
||||
|
||||
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \
|
||||
!defined(__cplusplus)
|
||||
#define BROTLI_RESTRICT restrict
|
||||
#elif BROTLI_GNUC_VERSION_CHECK(3, 1, 0) || \
|
||||
BROTLI_MSVC_VERSION_CHECK(14, 0, 0) || \
|
||||
BROTLI_INTEL_VERSION_CHECK(16, 0, 0) || \
|
||||
BROTLI_ARM_VERSION_CHECK(4, 1, 0) || \
|
||||
BROTLI_IBM_VERSION_CHECK(10, 1, 0) || \
|
||||
BROTLI_PGI_VERSION_CHECK(17, 10, 0) || \
|
||||
BROTLI_TI_VERSION_CHECK(8, 0, 0) || \
|
||||
BROTLI_IAR_VERSION_CHECK(8, 0, 0) || \
|
||||
(BROTLI_SUNPRO_VERSION_CHECK(5, 14, 0) && defined(__cplusplus))
|
||||
#define BROTLI_RESTRICT __restrict
|
||||
#elif BROTLI_SUNPRO_VERSION_CHECK(5, 3, 0) && !defined(__cplusplus)
|
||||
#define BROTLI_RESTRICT _Restrict
|
||||
#else
|
||||
#define BROTLI_RESTRICT
|
||||
#endif
|
||||
|
||||
#if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \
|
||||
(defined(__cplusplus) && (__cplusplus >= 199711L))
|
||||
#define BROTLI_MAYBE_INLINE inline
|
||||
#elif defined(__GNUC_STDC_INLINE__) || defined(__GNUC_GNU_INLINE__) || \
|
||||
BROTLI_ARM_VERSION_CHECK(6, 2, 0)
|
||||
#define BROTLI_MAYBE_INLINE __inline__
|
||||
#elif BROTLI_MSVC_VERSION_CHECK(12, 0, 0) || \
|
||||
BROTLI_ARM_VERSION_CHECK(4, 1, 0) || BROTLI_TI_VERSION_CHECK(8, 0, 0)
|
||||
#define BROTLI_MAYBE_INLINE __inline
|
||||
#else
|
||||
#define BROTLI_MAYBE_INLINE
|
||||
#endif
|
||||
|
||||
#if BROTLI_GNUC_HAS_ATTRIBUTE(always_inline, 4, 0, 0) || \
|
||||
BROTLI_INTEL_VERSION_CHECK(16, 0, 0) || \
|
||||
BROTLI_SUNPRO_VERSION_CHECK(5, 11, 0) || \
|
||||
BROTLI_ARM_VERSION_CHECK(4, 1, 0) || \
|
||||
BROTLI_IBM_VERSION_CHECK(10, 1, 0) || \
|
||||
BROTLI_TI_VERSION_CHECK(8, 0, 0) || \
|
||||
(BROTLI_TI_VERSION_CHECK(7, 3, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__))
|
||||
#define BROTLI_INLINE BROTLI_MAYBE_INLINE __attribute__((__always_inline__))
|
||||
#elif BROTLI_MSVC_VERSION_CHECK(12, 0, 0)
|
||||
#define BROTLI_INLINE BROTLI_MAYBE_INLINE __forceinline
|
||||
#elif BROTLI_TI_VERSION_CHECK(7, 0, 0) && defined(__cplusplus)
|
||||
#define BROTLI_INLINE BROTLI_MAYBE_INLINE _Pragma("FUNC_ALWAYS_INLINE;")
|
||||
#elif BROTLI_IAR_VERSION_CHECK(8, 0, 0)
|
||||
#define BROTLI_INLINE BROTLI_MAYBE_INLINE _Pragma("inline=forced")
|
||||
#else
|
||||
#define BROTLI_INLINE BROTLI_MAYBE_INLINE
|
||||
#endif
|
||||
|
||||
#if BROTLI_GNUC_HAS_ATTRIBUTE(noinline, 4, 0, 0) || \
|
||||
BROTLI_INTEL_VERSION_CHECK(16, 0, 0) || \
|
||||
BROTLI_SUNPRO_VERSION_CHECK(5, 11, 0) || \
|
||||
BROTLI_ARM_VERSION_CHECK(4, 1, 0) || \
|
||||
BROTLI_IBM_VERSION_CHECK(10, 1, 0) || \
|
||||
BROTLI_TI_VERSION_CHECK(8, 0, 0) || \
|
||||
(BROTLI_TI_VERSION_CHECK(7, 3, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__))
|
||||
#define BROTLI_NOINLINE __attribute__((__noinline__))
|
||||
#elif BROTLI_MSVC_VERSION_CHECK(13, 10, 0)
|
||||
#define BROTLI_NOINLINE __declspec(noinline)
|
||||
#elif BROTLI_PGI_VERSION_CHECK(10, 2, 0)
|
||||
#define BROTLI_NOINLINE _Pragma("noinline")
|
||||
#elif BROTLI_TI_VERSION_CHECK(6, 0, 0) && defined(__cplusplus)
|
||||
#define BROTLI_NOINLINE _Pragma("FUNC_CANNOT_INLINE;")
|
||||
#elif BROTLI_IAR_VERSION_CHECK(8, 0, 0)
|
||||
#define BROTLI_NOINLINE _Pragma("inline=never")
|
||||
#else
|
||||
#define BROTLI_NOINLINE
|
||||
#endif
|
||||
|
||||
/* BROTLI_INTERNAL could be defined to override visibility, e.g. for tests. */
|
||||
#if !defined(BROTLI_INTERNAL)
|
||||
#if defined(_WIN32) || defined(__CYGWIN__)
|
||||
#define BROTLI_INTERNAL
|
||||
#elif BROTLI_GNUC_VERSION_CHECK(3, 3, 0) || \
|
||||
BROTLI_TI_VERSION_CHECK(8, 0, 0) || \
|
||||
BROTLI_INTEL_VERSION_CHECK(16, 0, 0) || \
|
||||
BROTLI_ARM_VERSION_CHECK(4, 1, 0) || \
|
||||
BROTLI_IBM_VERSION_CHECK(13, 1, 0) || \
|
||||
BROTLI_SUNPRO_VERSION_CHECK(5, 11, 0) || \
|
||||
(BROTLI_TI_VERSION_CHECK(7, 3, 0) && \
|
||||
defined(__TI_GNU_ATTRIBUTE_SUPPORT__) && defined(__TI_EABI__))
|
||||
#define BROTLI_INTERNAL __attribute__ ((visibility ("hidden")))
|
||||
#else
|
||||
#define BROTLI_INTERNAL
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* <<< <<< <<< end of hedley macros. */
|
||||
|
||||
#if BROTLI_GNUC_HAS_ATTRIBUTE(unused, 2, 7, 0) || \
|
||||
BROTLI_INTEL_VERSION_CHECK(16, 0, 0)
|
||||
#define BROTLI_UNUSED_FUNCTION static BROTLI_INLINE __attribute__ ((unused))
|
||||
#else
|
||||
#define BROTLI_UNUSED_FUNCTION static BROTLI_INLINE
|
||||
#endif
|
||||
|
||||
#if BROTLI_GNUC_HAS_ATTRIBUTE(aligned, 2, 7, 0)
|
||||
#define BROTLI_ALIGNED(N) __attribute__((aligned(N)))
|
||||
#else
|
||||
#define BROTLI_ALIGNED(N)
|
||||
#endif
|
||||
|
||||
#if (defined(__ARM_ARCH) && (__ARM_ARCH == 7)) || \
|
||||
(defined(M_ARM) && (M_ARM == 7))
|
||||
#define BROTLI_TARGET_ARMV7
|
||||
#endif /* ARMv7 */
|
||||
|
||||
#if (defined(__ARM_ARCH) && (__ARM_ARCH == 8)) || \
|
||||
defined(__aarch64__) || defined(__ARM64_ARCH_8__)
|
||||
#define BROTLI_TARGET_ARMV8_ANY
|
||||
|
||||
#if defined(__ARM_32BIT_STATE)
|
||||
#define BROTLI_TARGET_ARMV8_32
|
||||
#elif defined(__ARM_64BIT_STATE)
|
||||
#define BROTLI_TARGET_ARMV8_64
|
||||
#endif
|
||||
|
||||
#endif /* ARMv8 */
|
||||
|
||||
#if defined(__ARM_NEON__) || defined(__ARM_NEON)
|
||||
#define BROTLI_TARGET_NEON
|
||||
#endif
|
||||
|
||||
#if defined(__i386) || defined(_M_IX86)
|
||||
#define BROTLI_TARGET_X86
|
||||
#endif
|
||||
|
||||
#if defined(__x86_64__) || defined(_M_X64)
|
||||
#define BROTLI_TARGET_X64
|
||||
#endif
|
||||
|
||||
#if defined(__PPC64__)
|
||||
#define BROTLI_TARGET_POWERPC64
|
||||
#endif
|
||||
|
||||
#if defined(__riscv) && defined(__riscv_xlen) && __riscv_xlen == 64
|
||||
#define BROTLI_TARGET_RISCV64
|
||||
#endif
|
||||
|
||||
#if defined(BROTLI_BUILD_64_BIT)
|
||||
#define BROTLI_64_BITS 1
|
||||
#elif defined(BROTLI_BUILD_32_BIT)
|
||||
#define BROTLI_64_BITS 0
|
||||
#elif defined(BROTLI_TARGET_X64) || defined(BROTLI_TARGET_ARMV8_64) || \
|
||||
defined(BROTLI_TARGET_POWERPC64) || defined(BROTLI_TARGET_RISCV64)
|
||||
#define BROTLI_64_BITS 1
|
||||
#else
|
||||
#define BROTLI_64_BITS 0
|
||||
#endif
|
||||
|
||||
#if (BROTLI_64_BITS)
|
||||
#define brotli_reg_t uint64_t
|
||||
#else
|
||||
#define brotli_reg_t uint32_t
|
||||
#endif
|
||||
|
||||
#if defined(BROTLI_BUILD_BIG_ENDIAN)
|
||||
#define BROTLI_BIG_ENDIAN 1
|
||||
#elif defined(BROTLI_BUILD_LITTLE_ENDIAN)
|
||||
#define BROTLI_LITTLE_ENDIAN 1
|
||||
#elif defined(BROTLI_BUILD_ENDIAN_NEUTRAL)
|
||||
/* Just break elif chain. */
|
||||
#elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
||||
#define BROTLI_LITTLE_ENDIAN 1
|
||||
#elif defined(_WIN32) || defined(BROTLI_TARGET_X64)
|
||||
/* Win32 & x64 can currently always be assumed to be little endian */
|
||||
#define BROTLI_LITTLE_ENDIAN 1
|
||||
#elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
|
||||
#define BROTLI_BIG_ENDIAN 1
|
||||
#elif defined(BROTLI_X_BYTE_ORDER)
|
||||
#if BROTLI_X_BYTE_ORDER == BROTLI_X_LITTLE_ENDIAN
|
||||
#define BROTLI_LITTLE_ENDIAN 1
|
||||
#elif BROTLI_X_BYTE_ORDER == BROTLI_X_BIG_ENDIAN
|
||||
#define BROTLI_BIG_ENDIAN 1
|
||||
#endif
|
||||
#endif /* BROTLI_X_BYTE_ORDER */
|
||||
|
||||
#if !defined(BROTLI_LITTLE_ENDIAN)
|
||||
#define BROTLI_LITTLE_ENDIAN 0
|
||||
#endif
|
||||
|
||||
#if !defined(BROTLI_BIG_ENDIAN)
|
||||
#define BROTLI_BIG_ENDIAN 0
|
||||
#endif
|
||||
|
||||
#if defined(BROTLI_X_BYTE_ORDER)
|
||||
#undef BROTLI_X_BYTE_ORDER
|
||||
#undef BROTLI_X_LITTLE_ENDIAN
|
||||
#undef BROTLI_X_BIG_ENDIAN
|
||||
#endif
|
||||
|
||||
#if defined(BROTLI_BUILD_PORTABLE)
|
||||
#define BROTLI_ALIGNED_READ (!!1)
|
||||
#elif defined(BROTLI_TARGET_X86) || defined(BROTLI_TARGET_X64) || \
|
||||
defined(BROTLI_TARGET_ARMV7) || defined(BROTLI_TARGET_ARMV8_ANY) || \
|
||||
defined(BROTLI_TARGET_RISCV64)
|
||||
/* Allow unaligned read only for white-listed CPUs. */
|
||||
#define BROTLI_ALIGNED_READ (!!0)
|
||||
#else
|
||||
#define BROTLI_ALIGNED_READ (!!1)
|
||||
#endif
|
||||
|
||||
#if BROTLI_ALIGNED_READ
|
||||
/* Portable unaligned memory access: read / write values via memcpy. */
|
||||
static BROTLI_INLINE uint16_t BrotliUnalignedRead16(const void* p) {
|
||||
uint16_t t;
|
||||
memcpy(&t, p, sizeof t);
|
||||
return t;
|
||||
}
|
||||
static BROTLI_INLINE uint32_t BrotliUnalignedRead32(const void* p) {
|
||||
uint32_t t;
|
||||
memcpy(&t, p, sizeof t);
|
||||
return t;
|
||||
}
|
||||
static BROTLI_INLINE uint64_t BrotliUnalignedRead64(const void* p) {
|
||||
uint64_t t;
|
||||
memcpy(&t, p, sizeof t);
|
||||
return t;
|
||||
}
|
||||
static BROTLI_INLINE void BrotliUnalignedWrite64(void* p, uint64_t v) {
|
||||
memcpy(p, &v, sizeof v);
|
||||
}
|
||||
#else /* BROTLI_ALIGNED_READ */
|
||||
/* Unaligned memory access is allowed: just cast pointer to requested type. */
|
||||
#if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) || \
|
||||
defined(MEMORY_SANITIZER)
|
||||
/* Consider we have an unaligned load/store of 4 bytes from address 0x...05.
|
||||
AddressSanitizer will treat it as a 3-byte access to the range 05:07 and
|
||||
will miss a bug if 08 is the first unaddressable byte.
|
||||
ThreadSanitizer will also treat this as a 3-byte access to 05:07 and will
|
||||
miss a race between this access and some other accesses to 08.
|
||||
MemorySanitizer will correctly propagate the shadow on unaligned stores
|
||||
and correctly report bugs on unaligned loads, but it may not properly
|
||||
update and report the origin of the uninitialized memory.
|
||||
For all three tools, replacing an unaligned access with a tool-specific
|
||||
callback solves the problem. */
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
uint16_t __sanitizer_unaligned_load16(const void* p);
|
||||
uint32_t __sanitizer_unaligned_load32(const void* p);
|
||||
uint64_t __sanitizer_unaligned_load64(const void* p);
|
||||
void __sanitizer_unaligned_store64(void* p, uint64_t v);
|
||||
#if defined(__cplusplus)
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
#define BrotliUnalignedRead16 __sanitizer_unaligned_load16
|
||||
#define BrotliUnalignedRead32 __sanitizer_unaligned_load32
|
||||
#define BrotliUnalignedRead64 __sanitizer_unaligned_load64
|
||||
#define BrotliUnalignedWrite64 __sanitizer_unaligned_store64
|
||||
#else
|
||||
static BROTLI_INLINE uint16_t BrotliUnalignedRead16(const void* p) {
|
||||
return *(const uint16_t*)p;
|
||||
}
|
||||
static BROTLI_INLINE uint32_t BrotliUnalignedRead32(const void* p) {
|
||||
return *(const uint32_t*)p;
|
||||
}
|
||||
#if (BROTLI_64_BITS)
|
||||
static BROTLI_INLINE uint64_t BrotliUnalignedRead64(const void* p) {
|
||||
return *(const uint64_t*)p;
|
||||
}
|
||||
static BROTLI_INLINE void BrotliUnalignedWrite64(void* p, uint64_t v) {
|
||||
*(uint64_t*)p = v;
|
||||
}
|
||||
#else /* BROTLI_64_BITS */
|
||||
/* Avoid emitting LDRD / STRD, which require properly aligned address. */
|
||||
/* If __attribute__(aligned) is available, use that. Otherwise, memcpy. */
|
||||
|
||||
#if BROTLI_GNUC_HAS_ATTRIBUTE(aligned, 2, 7, 0)
|
||||
typedef BROTLI_ALIGNED(1) uint64_t brotli_unaligned_uint64_t;
|
||||
|
||||
static BROTLI_INLINE uint64_t BrotliUnalignedRead64(const void* p) {
|
||||
return (uint64_t) ((brotli_unaligned_uint64_t*) p)[0];
|
||||
}
|
||||
static BROTLI_INLINE void BrotliUnalignedWrite64(void* p, uint64_t v) {
|
||||
brotli_unaligned_uint64_t* dwords = (brotli_unaligned_uint64_t*) p;
|
||||
dwords[0] = (brotli_unaligned_uint64_t) v;
|
||||
}
|
||||
#else /* BROTLI_GNUC_HAS_ATTRIBUTE(aligned, 2, 7, 0) */
|
||||
static BROTLI_INLINE uint64_t BrotliUnalignedRead64(const void* p) {
|
||||
uint64_t v;
|
||||
memcpy(&v, p, sizeof(uint64_t));
|
||||
return v;
|
||||
}
|
||||
|
||||
static BROTLI_INLINE void BrotliUnalignedWrite64(void* p, uint64_t v) {
|
||||
memcpy(p, &v, sizeof(uint64_t));
|
||||
}
|
||||
#endif /* BROTLI_GNUC_HAS_ATTRIBUTE(aligned, 2, 7, 0) */
|
||||
#endif /* BROTLI_64_BITS */
|
||||
#endif /* ASAN / TSAN / MSAN */
|
||||
#endif /* BROTLI_ALIGNED_READ */
|
||||
|
||||
#if BROTLI_LITTLE_ENDIAN
|
||||
/* Straight endianness. Just read / write values. */
|
||||
#define BROTLI_UNALIGNED_LOAD16LE BrotliUnalignedRead16
|
||||
#define BROTLI_UNALIGNED_LOAD32LE BrotliUnalignedRead32
|
||||
#define BROTLI_UNALIGNED_LOAD64LE BrotliUnalignedRead64
|
||||
#define BROTLI_UNALIGNED_STORE64LE BrotliUnalignedWrite64
|
||||
#elif BROTLI_BIG_ENDIAN /* BROTLI_LITTLE_ENDIAN */
|
||||
/* Explain compiler to byte-swap values. */
|
||||
#define BROTLI_BSWAP16_(V) ((uint16_t)( \
|
||||
(((V) & 0xFFU) << 8) | \
|
||||
(((V) >> 8) & 0xFFU)))
|
||||
static BROTLI_INLINE uint16_t BROTLI_UNALIGNED_LOAD16LE(const void* p) {
|
||||
uint16_t value = BrotliUnalignedRead16(p);
|
||||
return BROTLI_BSWAP16_(value);
|
||||
}
|
||||
#define BROTLI_BSWAP32_(V) ( \
|
||||
(((V) & 0xFFU) << 24) | (((V) & 0xFF00U) << 8) | \
|
||||
(((V) >> 8) & 0xFF00U) | (((V) >> 24) & 0xFFU))
|
||||
static BROTLI_INLINE uint32_t BROTLI_UNALIGNED_LOAD32LE(const void* p) {
|
||||
uint32_t value = BrotliUnalignedRead32(p);
|
||||
return BROTLI_BSWAP32_(value);
|
||||
}
|
||||
#define BROTLI_BSWAP64_(V) ( \
|
||||
(((V) & 0xFFU) << 56) | (((V) & 0xFF00U) << 40) | \
|
||||
(((V) & 0xFF0000U) << 24) | (((V) & 0xFF000000U) << 8) | \
|
||||
(((V) >> 8) & 0xFF000000U) | (((V) >> 24) & 0xFF0000U) | \
|
||||
(((V) >> 40) & 0xFF00U) | (((V) >> 56) & 0xFFU))
|
||||
static BROTLI_INLINE uint64_t BROTLI_UNALIGNED_LOAD64LE(const void* p) {
|
||||
uint64_t value = BrotliUnalignedRead64(p);
|
||||
return BROTLI_BSWAP64_(value);
|
||||
}
|
||||
static BROTLI_INLINE void BROTLI_UNALIGNED_STORE64LE(void* p, uint64_t v) {
|
||||
uint64_t value = BROTLI_BSWAP64_(v);
|
||||
BrotliUnalignedWrite64(p, value);
|
||||
}
|
||||
#else /* BROTLI_LITTLE_ENDIAN */
|
||||
/* Read / store values byte-wise; hopefully compiler will understand. */
|
||||
static BROTLI_INLINE uint16_t BROTLI_UNALIGNED_LOAD16LE(const void* p) {
|
||||
const uint8_t* in = (const uint8_t*)p;
|
||||
return (uint16_t)(in[0] | (in[1] << 8));
|
||||
}
|
||||
static BROTLI_INLINE uint32_t BROTLI_UNALIGNED_LOAD32LE(const void* p) {
|
||||
const uint8_t* in = (const uint8_t*)p;
|
||||
uint32_t value = (uint32_t)(in[0]);
|
||||
value |= (uint32_t)(in[1]) << 8;
|
||||
value |= (uint32_t)(in[2]) << 16;
|
||||
value |= (uint32_t)(in[3]) << 24;
|
||||
return value;
|
||||
}
|
||||
static BROTLI_INLINE uint64_t BROTLI_UNALIGNED_LOAD64LE(const void* p) {
|
||||
const uint8_t* in = (const uint8_t*)p;
|
||||
uint64_t value = (uint64_t)(in[0]);
|
||||
value |= (uint64_t)(in[1]) << 8;
|
||||
value |= (uint64_t)(in[2]) << 16;
|
||||
value |= (uint64_t)(in[3]) << 24;
|
||||
value |= (uint64_t)(in[4]) << 32;
|
||||
value |= (uint64_t)(in[5]) << 40;
|
||||
value |= (uint64_t)(in[6]) << 48;
|
||||
value |= (uint64_t)(in[7]) << 56;
|
||||
return value;
|
||||
}
|
||||
static BROTLI_INLINE void BROTLI_UNALIGNED_STORE64LE(void* p, uint64_t v) {
|
||||
uint8_t* out = (uint8_t*)p;
|
||||
out[0] = (uint8_t)v;
|
||||
out[1] = (uint8_t)(v >> 8);
|
||||
out[2] = (uint8_t)(v >> 16);
|
||||
out[3] = (uint8_t)(v >> 24);
|
||||
out[4] = (uint8_t)(v >> 32);
|
||||
out[5] = (uint8_t)(v >> 40);
|
||||
out[6] = (uint8_t)(v >> 48);
|
||||
out[7] = (uint8_t)(v >> 56);
|
||||
}
|
||||
#endif /* BROTLI_LITTLE_ENDIAN */
|
||||
|
||||
/* BROTLI_IS_CONSTANT macros returns true for compile-time constants. */
|
||||
#if BROTLI_GNUC_HAS_BUILTIN(__builtin_constant_p, 3, 0, 1) || \
|
||||
BROTLI_INTEL_VERSION_CHECK(16, 0, 0)
|
||||
#define BROTLI_IS_CONSTANT(x) (!!__builtin_constant_p(x))
|
||||
#else
|
||||
#define BROTLI_IS_CONSTANT(x) (!!0)
|
||||
#endif
|
||||
|
||||
#if defined(BROTLI_TARGET_ARMV7) || defined(BROTLI_TARGET_ARMV8_ANY)
|
||||
#define BROTLI_HAS_UBFX (!!1)
|
||||
#else
|
||||
#define BROTLI_HAS_UBFX (!!0)
|
||||
#endif
|
||||
|
||||
#if defined(BROTLI_ENABLE_LOG)
|
||||
#define BROTLI_DCHECK(x) assert(x)
|
||||
#define BROTLI_LOG(x) printf x
|
||||
#else
|
||||
#define BROTLI_DCHECK(x)
|
||||
#define BROTLI_LOG(x)
|
||||
#endif
|
||||
|
||||
#if defined(BROTLI_DEBUG) || defined(BROTLI_ENABLE_LOG)
|
||||
static BROTLI_INLINE void BrotliDump(const char* f, int l, const char* fn) {
|
||||
fprintf(stderr, "%s:%d (%s)\n", f, l, fn);
|
||||
fflush(stderr);
|
||||
}
|
||||
#define BROTLI_DUMP() BrotliDump(__FILE__, __LINE__, __FUNCTION__)
|
||||
#else
|
||||
#define BROTLI_DUMP() (void)(0)
|
||||
#endif
|
||||
|
||||
/* TODO: add appropriate icc/sunpro/arm/ibm/ti checks. */
|
||||
#if (BROTLI_GNUC_VERSION_CHECK(3, 0, 0) || defined(__llvm__)) && \
|
||||
!defined(BROTLI_BUILD_NO_RBIT)
|
||||
#if defined(BROTLI_TARGET_ARMV7) || defined(BROTLI_TARGET_ARMV8_ANY)
|
||||
/* TODO: detect ARMv6T2 and enable this code for it. */
|
||||
static BROTLI_INLINE brotli_reg_t BrotliRBit(brotli_reg_t input) {
|
||||
brotli_reg_t output;
|
||||
__asm__("rbit %0, %1\n" : "=r"(output) : "r"(input));
|
||||
return output;
|
||||
}
|
||||
#define BROTLI_RBIT(x) BrotliRBit(x)
|
||||
#endif /* armv7 / armv8 */
|
||||
#endif /* gcc || clang */
|
||||
#if !defined(BROTLI_RBIT)
|
||||
static BROTLI_INLINE void BrotliRBit(void) { /* Should break build if used. */ }
|
||||
#endif /* BROTLI_RBIT */
|
||||
|
||||
#define BROTLI_REPEAT(N, X) { \
|
||||
if ((N & 1) != 0) {X;} \
|
||||
if ((N & 2) != 0) {X; X;} \
|
||||
if ((N & 4) != 0) {X; X; X; X;} \
|
||||
}
|
||||
|
||||
#define BROTLI_UNUSED(X) (void)(X)
|
||||
|
||||
#define BROTLI_MIN_MAX(T) \
|
||||
static BROTLI_INLINE T brotli_min_ ## T (T a, T b) { return a < b ? a : b; } \
|
||||
static BROTLI_INLINE T brotli_max_ ## T (T a, T b) { return a > b ? a : b; }
|
||||
BROTLI_MIN_MAX(double) BROTLI_MIN_MAX(float) BROTLI_MIN_MAX(int)
|
||||
BROTLI_MIN_MAX(size_t) BROTLI_MIN_MAX(uint32_t) BROTLI_MIN_MAX(uint8_t)
|
||||
#undef BROTLI_MIN_MAX
|
||||
#define BROTLI_MIN(T, A, B) (brotli_min_ ## T((A), (B)))
|
||||
#define BROTLI_MAX(T, A, B) (brotli_max_ ## T((A), (B)))
|
||||
|
||||
#define BROTLI_SWAP(T, A, I, J) { \
|
||||
T __brotli_swap_tmp = (A)[(I)]; \
|
||||
(A)[(I)] = (A)[(J)]; \
|
||||
(A)[(J)] = __brotli_swap_tmp; \
|
||||
}
|
||||
|
||||
/* Default brotli_alloc_func */
|
||||
static void* BrotliDefaultAllocFunc(void* opaque, size_t size) {
|
||||
BROTLI_UNUSED(opaque);
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
/* Default brotli_free_func */
|
||||
static void BrotliDefaultFreeFunc(void* opaque, void* address) {
|
||||
BROTLI_UNUSED(opaque);
|
||||
free(address);
|
||||
}
|
||||
|
||||
BROTLI_UNUSED_FUNCTION void BrotliSuppressUnusedFunctions(void) {
|
||||
BROTLI_UNUSED(&BrotliSuppressUnusedFunctions);
|
||||
BROTLI_UNUSED(&BrotliUnalignedRead16);
|
||||
BROTLI_UNUSED(&BrotliUnalignedRead32);
|
||||
BROTLI_UNUSED(&BrotliUnalignedRead64);
|
||||
BROTLI_UNUSED(&BrotliUnalignedWrite64);
|
||||
BROTLI_UNUSED(&BROTLI_UNALIGNED_LOAD16LE);
|
||||
BROTLI_UNUSED(&BROTLI_UNALIGNED_LOAD32LE);
|
||||
BROTLI_UNUSED(&BROTLI_UNALIGNED_LOAD64LE);
|
||||
BROTLI_UNUSED(&BROTLI_UNALIGNED_STORE64LE);
|
||||
BROTLI_UNUSED(&BrotliRBit);
|
||||
BROTLI_UNUSED(&brotli_min_double);
|
||||
BROTLI_UNUSED(&brotli_max_double);
|
||||
BROTLI_UNUSED(&brotli_min_float);
|
||||
BROTLI_UNUSED(&brotli_max_float);
|
||||
BROTLI_UNUSED(&brotli_min_int);
|
||||
BROTLI_UNUSED(&brotli_max_int);
|
||||
BROTLI_UNUSED(&brotli_min_size_t);
|
||||
BROTLI_UNUSED(&brotli_max_size_t);
|
||||
BROTLI_UNUSED(&brotli_min_uint32_t);
|
||||
BROTLI_UNUSED(&brotli_max_uint32_t);
|
||||
BROTLI_UNUSED(&brotli_min_uint8_t);
|
||||
BROTLI_UNUSED(&brotli_max_uint8_t);
|
||||
BROTLI_UNUSED(&BrotliDefaultAllocFunc);
|
||||
BROTLI_UNUSED(&BrotliDefaultFreeFunc);
|
||||
#if defined(BROTLI_DEBUG) || defined(BROTLI_ENABLE_LOG)
|
||||
BROTLI_UNUSED(&BrotliDump);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* BROTLI_COMMON_PLATFORM_H_ */
|
||||
235
modules/brotli/common/transform.c
Normal file
235
modules/brotli/common/transform.c
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
/* Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
Distributed under MIT license.
|
||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "./transform.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* RFC 7932 transforms string data */
|
||||
static const char kPrefixSuffix[217] =
|
||||
"\1 \2, \10 of the \4 of \2s \1.\5 and \4 "
|
||||
/* 0x _0 _2 __5 _E _3 _6 _8 _E */
|
||||
"in \1\"\4 to \2\">\1\n\2. \1]\5 for \3 a \6 "
|
||||
/* 2x _3_ _5 _A_ _D_ _F _2 _4 _A _E */
|
||||
"that \1\'\6 with \6 from \4 by \1(\6. T"
|
||||
/* 4x _5_ _7 _E _5 _A _C */
|
||||
"he \4 on \4 as \4 is \4ing \2\n\t\1:\3ed "
|
||||
/* 6x _3 _8 _D _2 _7_ _ _A _C */
|
||||
"\2=\"\4 at \3ly \1,\2=\'\5.com/\7. This \5"
|
||||
/* 8x _0 _ _3 _8 _C _E _ _1 _7 _F */
|
||||
" not \3er \3al \4ful \4ive \5less \4es"
|
||||
/* Ax _5 _9 _D _2 _7 _D */
|
||||
"t \4ize \2\xc2\xa0\4ous \5 the \2e \0";
|
||||
/* Cx _2 _7___ ___ _A _F _5 _8 */
|
||||
|
||||
static const uint16_t kPrefixSuffixMap[50] = {
|
||||
0x00, 0x02, 0x05, 0x0E, 0x13, 0x16, 0x18, 0x1E, 0x23, 0x25,
|
||||
0x2A, 0x2D, 0x2F, 0x32, 0x34, 0x3A, 0x3E, 0x45, 0x47, 0x4E,
|
||||
0x55, 0x5A, 0x5C, 0x63, 0x68, 0x6D, 0x72, 0x77, 0x7A, 0x7C,
|
||||
0x80, 0x83, 0x88, 0x8C, 0x8E, 0x91, 0x97, 0x9F, 0xA5, 0xA9,
|
||||
0xAD, 0xB2, 0xB7, 0xBD, 0xC2, 0xC7, 0xCA, 0xCF, 0xD5, 0xD8
|
||||
};
|
||||
|
||||
/* RFC 7932 transforms */
|
||||
static const uint8_t kTransformsData[] = {
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 49,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 0,
|
||||
0, BROTLI_TRANSFORM_IDENTITY, 0,
|
||||
49, BROTLI_TRANSFORM_OMIT_FIRST_1, 49,
|
||||
49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 0,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 47,
|
||||
0, BROTLI_TRANSFORM_IDENTITY, 49,
|
||||
4, BROTLI_TRANSFORM_IDENTITY, 0,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 3,
|
||||
49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 49,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 6,
|
||||
49, BROTLI_TRANSFORM_OMIT_FIRST_2, 49,
|
||||
49, BROTLI_TRANSFORM_OMIT_LAST_1, 49,
|
||||
1, BROTLI_TRANSFORM_IDENTITY, 0,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 1,
|
||||
0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 0,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 7,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 9,
|
||||
48, BROTLI_TRANSFORM_IDENTITY, 0,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 8,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 5,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 10,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 11,
|
||||
49, BROTLI_TRANSFORM_OMIT_LAST_3, 49,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 13,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 14,
|
||||
49, BROTLI_TRANSFORM_OMIT_FIRST_3, 49,
|
||||
49, BROTLI_TRANSFORM_OMIT_LAST_2, 49,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 15,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 16,
|
||||
0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 49,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 12,
|
||||
5, BROTLI_TRANSFORM_IDENTITY, 49,
|
||||
0, BROTLI_TRANSFORM_IDENTITY, 1,
|
||||
49, BROTLI_TRANSFORM_OMIT_FIRST_4, 49,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 18,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 17,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 19,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 20,
|
||||
49, BROTLI_TRANSFORM_OMIT_FIRST_5, 49,
|
||||
49, BROTLI_TRANSFORM_OMIT_FIRST_6, 49,
|
||||
47, BROTLI_TRANSFORM_IDENTITY, 49,
|
||||
49, BROTLI_TRANSFORM_OMIT_LAST_4, 49,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 22,
|
||||
49, BROTLI_TRANSFORM_UPPERCASE_ALL, 49,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 23,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 24,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 25,
|
||||
49, BROTLI_TRANSFORM_OMIT_LAST_7, 49,
|
||||
49, BROTLI_TRANSFORM_OMIT_LAST_1, 26,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 27,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 28,
|
||||
0, BROTLI_TRANSFORM_IDENTITY, 12,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 29,
|
||||
49, BROTLI_TRANSFORM_OMIT_FIRST_9, 49,
|
||||
49, BROTLI_TRANSFORM_OMIT_FIRST_7, 49,
|
||||
49, BROTLI_TRANSFORM_OMIT_LAST_6, 49,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 21,
|
||||
49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 1,
|
||||
49, BROTLI_TRANSFORM_OMIT_LAST_8, 49,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 31,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 32,
|
||||
47, BROTLI_TRANSFORM_IDENTITY, 3,
|
||||
49, BROTLI_TRANSFORM_OMIT_LAST_5, 49,
|
||||
49, BROTLI_TRANSFORM_OMIT_LAST_9, 49,
|
||||
0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 1,
|
||||
49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 8,
|
||||
5, BROTLI_TRANSFORM_IDENTITY, 21,
|
||||
49, BROTLI_TRANSFORM_UPPERCASE_ALL, 0,
|
||||
49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 10,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 30,
|
||||
0, BROTLI_TRANSFORM_IDENTITY, 5,
|
||||
35, BROTLI_TRANSFORM_IDENTITY, 49,
|
||||
47, BROTLI_TRANSFORM_IDENTITY, 2,
|
||||
49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 17,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 36,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 33,
|
||||
5, BROTLI_TRANSFORM_IDENTITY, 0,
|
||||
49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 21,
|
||||
49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 5,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 37,
|
||||
0, BROTLI_TRANSFORM_IDENTITY, 30,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 38,
|
||||
0, BROTLI_TRANSFORM_UPPERCASE_ALL, 0,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 39,
|
||||
0, BROTLI_TRANSFORM_UPPERCASE_ALL, 49,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 34,
|
||||
49, BROTLI_TRANSFORM_UPPERCASE_ALL, 8,
|
||||
49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 12,
|
||||
0, BROTLI_TRANSFORM_IDENTITY, 21,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 40,
|
||||
0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 12,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 41,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 42,
|
||||
49, BROTLI_TRANSFORM_UPPERCASE_ALL, 17,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 43,
|
||||
0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 5,
|
||||
49, BROTLI_TRANSFORM_UPPERCASE_ALL, 10,
|
||||
0, BROTLI_TRANSFORM_IDENTITY, 34,
|
||||
49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 33,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 44,
|
||||
49, BROTLI_TRANSFORM_UPPERCASE_ALL, 5,
|
||||
45, BROTLI_TRANSFORM_IDENTITY, 49,
|
||||
0, BROTLI_TRANSFORM_IDENTITY, 33,
|
||||
49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 30,
|
||||
49, BROTLI_TRANSFORM_UPPERCASE_ALL, 30,
|
||||
49, BROTLI_TRANSFORM_IDENTITY, 46,
|
||||
49, BROTLI_TRANSFORM_UPPERCASE_ALL, 1,
|
||||
49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 34,
|
||||
0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 33,
|
||||
0, BROTLI_TRANSFORM_UPPERCASE_ALL, 30,
|
||||
0, BROTLI_TRANSFORM_UPPERCASE_ALL, 1,
|
||||
49, BROTLI_TRANSFORM_UPPERCASE_ALL, 33,
|
||||
49, BROTLI_TRANSFORM_UPPERCASE_ALL, 21,
|
||||
49, BROTLI_TRANSFORM_UPPERCASE_ALL, 12,
|
||||
0, BROTLI_TRANSFORM_UPPERCASE_ALL, 5,
|
||||
49, BROTLI_TRANSFORM_UPPERCASE_ALL, 34,
|
||||
0, BROTLI_TRANSFORM_UPPERCASE_ALL, 12,
|
||||
0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 30,
|
||||
0, BROTLI_TRANSFORM_UPPERCASE_ALL, 34,
|
||||
0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 34,
|
||||
};
|
||||
|
||||
static BrotliTransforms kBrotliTransforms = {
|
||||
sizeof(kPrefixSuffix),
|
||||
(const uint8_t*)kPrefixSuffix,
|
||||
kPrefixSuffixMap,
|
||||
sizeof(kTransformsData) / (3 * sizeof(kTransformsData[0])),
|
||||
kTransformsData,
|
||||
{0, 12, 27, 23, 42, 63, 56, 48, 59, 64}
|
||||
};
|
||||
|
||||
const BrotliTransforms* BrotliGetTransforms(void) {
|
||||
return &kBrotliTransforms;
|
||||
}
|
||||
|
||||
static int ToUpperCase(uint8_t* p) {
|
||||
if (p[0] < 0xC0) {
|
||||
if (p[0] >= 'a' && p[0] <= 'z') {
|
||||
p[0] ^= 32;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
/* An overly simplified uppercasing model for UTF-8. */
|
||||
if (p[0] < 0xE0) {
|
||||
p[1] ^= 32;
|
||||
return 2;
|
||||
}
|
||||
/* An arbitrary transform for three byte characters. */
|
||||
p[2] ^= 5;
|
||||
return 3;
|
||||
}
|
||||
|
||||
int BrotliTransformDictionaryWord(uint8_t* dst, const uint8_t* word, int len,
|
||||
const BrotliTransforms* transforms, int transform_idx) {
|
||||
int idx = 0;
|
||||
const uint8_t* prefix = BROTLI_TRANSFORM_PREFIX(transforms, transform_idx);
|
||||
uint8_t type = BROTLI_TRANSFORM_TYPE(transforms, transform_idx);
|
||||
const uint8_t* suffix = BROTLI_TRANSFORM_SUFFIX(transforms, transform_idx);
|
||||
{
|
||||
int prefix_len = *prefix++;
|
||||
while (prefix_len--) { dst[idx++] = *prefix++; }
|
||||
}
|
||||
{
|
||||
const int t = type;
|
||||
int i = 0;
|
||||
if (t <= BROTLI_TRANSFORM_OMIT_LAST_9) {
|
||||
len -= t;
|
||||
} else if (t >= BROTLI_TRANSFORM_OMIT_FIRST_1
|
||||
&& t <= BROTLI_TRANSFORM_OMIT_FIRST_9) {
|
||||
int skip = t - (BROTLI_TRANSFORM_OMIT_FIRST_1 - 1);
|
||||
word += skip;
|
||||
len -= skip;
|
||||
}
|
||||
while (i < len) { dst[idx++] = word[i++]; }
|
||||
if (t == BROTLI_TRANSFORM_UPPERCASE_FIRST) {
|
||||
ToUpperCase(&dst[idx - len]);
|
||||
} else if (t == BROTLI_TRANSFORM_UPPERCASE_ALL) {
|
||||
uint8_t* uppercase = &dst[idx - len];
|
||||
while (len > 0) {
|
||||
int step = ToUpperCase(uppercase);
|
||||
uppercase += step;
|
||||
len -= step;
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
int suffix_len = *suffix++;
|
||||
while (suffix_len--) { dst[idx++] = *suffix++; }
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
80
modules/brotli/common/transform.h
Normal file
80
modules/brotli/common/transform.h
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
/* transforms is a part of ABI, but not API.
|
||||
|
||||
It means that there are some functions that are supposed to be in "common"
|
||||
library, but header itself is not placed into include/brotli. This way,
|
||||
aforementioned functions will be available only to brotli internals.
|
||||
*/
|
||||
|
||||
#ifndef BROTLI_COMMON_TRANSFORM_H_
|
||||
#define BROTLI_COMMON_TRANSFORM_H_
|
||||
|
||||
#include <brotli/port.h>
|
||||
#include <brotli/types.h>
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum BrotliWordTransformType {
|
||||
BROTLI_TRANSFORM_IDENTITY = 0,
|
||||
BROTLI_TRANSFORM_OMIT_LAST_1 = 1,
|
||||
BROTLI_TRANSFORM_OMIT_LAST_2 = 2,
|
||||
BROTLI_TRANSFORM_OMIT_LAST_3 = 3,
|
||||
BROTLI_TRANSFORM_OMIT_LAST_4 = 4,
|
||||
BROTLI_TRANSFORM_OMIT_LAST_5 = 5,
|
||||
BROTLI_TRANSFORM_OMIT_LAST_6 = 6,
|
||||
BROTLI_TRANSFORM_OMIT_LAST_7 = 7,
|
||||
BROTLI_TRANSFORM_OMIT_LAST_8 = 8,
|
||||
BROTLI_TRANSFORM_OMIT_LAST_9 = 9,
|
||||
BROTLI_TRANSFORM_UPPERCASE_FIRST = 10,
|
||||
BROTLI_TRANSFORM_UPPERCASE_ALL = 11,
|
||||
BROTLI_TRANSFORM_OMIT_FIRST_1 = 12,
|
||||
BROTLI_TRANSFORM_OMIT_FIRST_2 = 13,
|
||||
BROTLI_TRANSFORM_OMIT_FIRST_3 = 14,
|
||||
BROTLI_TRANSFORM_OMIT_FIRST_4 = 15,
|
||||
BROTLI_TRANSFORM_OMIT_FIRST_5 = 16,
|
||||
BROTLI_TRANSFORM_OMIT_FIRST_6 = 17,
|
||||
BROTLI_TRANSFORM_OMIT_FIRST_7 = 18,
|
||||
BROTLI_TRANSFORM_OMIT_FIRST_8 = 19,
|
||||
BROTLI_TRANSFORM_OMIT_FIRST_9 = 20,
|
||||
BROTLI_NUM_TRANSFORM_TYPES /* Counts transforms, not a transform itself. */
|
||||
};
|
||||
|
||||
#define BROTLI_TRANSFORMS_MAX_CUT_OFF BROTLI_TRANSFORM_OMIT_LAST_9
|
||||
|
||||
typedef struct BrotliTransforms {
|
||||
uint16_t prefix_suffix_size;
|
||||
/* Last character must be null, so prefix_suffix_size must be at least 1. */
|
||||
const uint8_t* prefix_suffix;
|
||||
const uint16_t* prefix_suffix_map;
|
||||
uint32_t num_transforms;
|
||||
/* Each entry is a [prefix_id, transform, suffix_id] triplet. */
|
||||
const uint8_t* transforms;
|
||||
/* Indices of transforms like ["", BROTLI_TRANSFORM_OMIT_LAST_#, ""].
|
||||
0-th element corresponds to ["", BROTLI_TRANSFORM_IDENTITY, ""].
|
||||
-1, if cut-off transform does not exist. */
|
||||
int16_t cutOffTransforms[BROTLI_TRANSFORMS_MAX_CUT_OFF + 1];
|
||||
} BrotliTransforms;
|
||||
|
||||
/* T is BrotliTransforms*; result is uint8_t. */
|
||||
#define BROTLI_TRANSFORM_PREFIX_ID(T, I) ((T)->transforms[((I) * 3) + 0])
|
||||
#define BROTLI_TRANSFORM_TYPE(T, I) ((T)->transforms[((I) * 3) + 1])
|
||||
#define BROTLI_TRANSFORM_SUFFIX_ID(T, I) ((T)->transforms[((I) * 3) + 2])
|
||||
|
||||
/* T is BrotliTransforms*; result is const uint8_t*. */
|
||||
#define BROTLI_TRANSFORM_PREFIX(T, I) (&(T)->prefix_suffix[ \
|
||||
(T)->prefix_suffix_map[BROTLI_TRANSFORM_PREFIX_ID(T, I)]])
|
||||
#define BROTLI_TRANSFORM_SUFFIX(T, I) (&(T)->prefix_suffix[ \
|
||||
(T)->prefix_suffix_map[BROTLI_TRANSFORM_SUFFIX_ID(T, I)]])
|
||||
|
||||
BROTLI_COMMON_API const BrotliTransforms* BrotliGetTransforms(void);
|
||||
|
||||
BROTLI_COMMON_API int BrotliTransformDictionaryWord(
|
||||
uint8_t* dst, const uint8_t* word, int len,
|
||||
const BrotliTransforms* transforms, int transform_idx);
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* BROTLI_COMMON_TRANSFORM_H_ */
|
||||
|
|
@ -14,6 +14,13 @@
|
|||
BrotliEncoderVersion methods. */
|
||||
|
||||
/* Semantic version, calculated as (MAJOR << 24) | (MINOR << 12) | PATCH */
|
||||
#define BROTLI_VERSION 0x1000001
|
||||
#define BROTLI_VERSION 0x1000007
|
||||
|
||||
/* This macro is used by build system to produce Libtool-friendly soname. See
|
||||
https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html
|
||||
*/
|
||||
|
||||
/* ABI version, calculated as (CURRENT << 24) | (REVISION << 12) | AGE */
|
||||
#define BROTLI_ABI_VERSION 0x1007000
|
||||
|
||||
#endif /* BROTLI_COMMON_VERSION_H_ */
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
#brotli/dec
|
||||
|
||||
include ../shared.mk
|
||||
|
||||
CFLAGS += -Wall
|
||||
|
||||
OBJS = bit_reader.o decode.o dictionary.o huffman.o state.o
|
||||
|
||||
all : $(OBJS)
|
||||
|
||||
clean :
|
||||
rm -f $(OBJS)
|
||||
|
|
@ -8,8 +8,8 @@
|
|||
|
||||
#include "./bit_reader.h"
|
||||
|
||||
#include "../common/platform.h"
|
||||
#include <brotli/types.h>
|
||||
#include "./port.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
|
|
|
|||
|
|
@ -11,16 +11,16 @@
|
|||
|
||||
#include <string.h> /* memcpy */
|
||||
|
||||
#include "../common/platform.h"
|
||||
#include <brotli/types.h>
|
||||
#include "./port.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define BROTLI_SHORT_FILL_BIT_WINDOW_READ (sizeof(reg_t) >> 1)
|
||||
#define BROTLI_SHORT_FILL_BIT_WINDOW_READ (sizeof(brotli_reg_t) >> 1)
|
||||
|
||||
static const uint32_t kBitMask[33] = { 0x0000,
|
||||
static const uint32_t kBitMask[33] = { 0x00000000,
|
||||
0x00000001, 0x00000003, 0x00000007, 0x0000000F,
|
||||
0x0000001F, 0x0000003F, 0x0000007F, 0x000000FF,
|
||||
0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF,
|
||||
|
|
@ -32,24 +32,24 @@ static const uint32_t kBitMask[33] = { 0x0000,
|
|||
};
|
||||
|
||||
static BROTLI_INLINE uint32_t BitMask(uint32_t n) {
|
||||
if (IS_CONSTANT(n) || BROTLI_HAS_UBFX) {
|
||||
if (BROTLI_IS_CONSTANT(n) || BROTLI_HAS_UBFX) {
|
||||
/* Masking with this expression turns to a single
|
||||
"Unsigned Bit Field Extract" UBFX instruction on ARM. */
|
||||
return ~((0xffffffffU) << n);
|
||||
return ~((0xFFFFFFFFu) << n);
|
||||
} else {
|
||||
return kBitMask[n];
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
reg_t val_; /* pre-fetched bits */
|
||||
brotli_reg_t val_; /* pre-fetched bits */
|
||||
uint32_t bit_pos_; /* current bit-reading position in val_ */
|
||||
const uint8_t* next_in; /* the byte we're reading from */
|
||||
size_t avail_in;
|
||||
} BrotliBitReader;
|
||||
|
||||
typedef struct {
|
||||
reg_t val_;
|
||||
brotli_reg_t val_;
|
||||
uint32_t bit_pos_;
|
||||
const uint8_t* next_in;
|
||||
size_t avail_in;
|
||||
|
|
@ -58,8 +58,9 @@ typedef struct {
|
|||
/* Initializes the BrotliBitReader fields. */
|
||||
BROTLI_INTERNAL void BrotliInitBitReader(BrotliBitReader* const br);
|
||||
|
||||
/* Ensures that accumulator is not empty. May consume one byte of input.
|
||||
Returns 0 if data is required but there is no input available.
|
||||
/* Ensures that accumulator is not empty.
|
||||
May consume up to sizeof(brotli_reg_t) - 1 bytes of input.
|
||||
Returns BROTLI_FALSE if data is required but there is no input available.
|
||||
For BROTLI_ALIGNED_READ this function also prepares bit reader for aligned
|
||||
reading. */
|
||||
BROTLI_INTERNAL BROTLI_BOOL BrotliWarmupBitReader(BrotliBitReader* const br);
|
||||
|
|
@ -98,82 +99,27 @@ static BROTLI_INLINE BROTLI_BOOL BrotliCheckInputAmount(
|
|||
return TO_BROTLI_BOOL(br->avail_in >= num);
|
||||
}
|
||||
|
||||
static BROTLI_INLINE uint16_t BrotliLoad16LE(const uint8_t* in) {
|
||||
if (BROTLI_LITTLE_ENDIAN) {
|
||||
return *((const uint16_t*)in);
|
||||
} else if (BROTLI_BIG_ENDIAN) {
|
||||
uint16_t value = *((const uint16_t*)in);
|
||||
return (uint16_t)(((value & 0xFFU) << 8) | ((value & 0xFF00U) >> 8));
|
||||
} else {
|
||||
return (uint16_t)(in[0] | (in[1] << 8));
|
||||
}
|
||||
}
|
||||
|
||||
static BROTLI_INLINE uint32_t BrotliLoad32LE(const uint8_t* in) {
|
||||
if (BROTLI_LITTLE_ENDIAN) {
|
||||
return *((const uint32_t*)in);
|
||||
} else if (BROTLI_BIG_ENDIAN) {
|
||||
uint32_t value = *((const uint32_t*)in);
|
||||
return ((value & 0xFFU) << 24) | ((value & 0xFF00U) << 8) |
|
||||
((value & 0xFF0000U) >> 8) | ((value & 0xFF000000U) >> 24);
|
||||
} else {
|
||||
uint32_t value = (uint32_t)(*(in++));
|
||||
value |= (uint32_t)(*(in++)) << 8;
|
||||
value |= (uint32_t)(*(in++)) << 16;
|
||||
value |= (uint32_t)(*(in++)) << 24;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
#if (BROTLI_64_BITS)
|
||||
static BROTLI_INLINE uint64_t BrotliLoad64LE(const uint8_t* in) {
|
||||
if (BROTLI_LITTLE_ENDIAN) {
|
||||
return *((const uint64_t*)in);
|
||||
} else if (BROTLI_BIG_ENDIAN) {
|
||||
uint64_t value = *((const uint64_t*)in);
|
||||
return
|
||||
((value & 0xFFU) << 56) |
|
||||
((value & 0xFF00U) << 40) |
|
||||
((value & 0xFF0000U) << 24) |
|
||||
((value & 0xFF000000U) << 8) |
|
||||
((value & 0xFF00000000U) >> 8) |
|
||||
((value & 0xFF0000000000U) >> 24) |
|
||||
((value & 0xFF000000000000U) >> 40) |
|
||||
((value & 0xFF00000000000000U) >> 56);
|
||||
} else {
|
||||
uint64_t value = (uint64_t)(*(in++));
|
||||
value |= (uint64_t)(*(in++)) << 8;
|
||||
value |= (uint64_t)(*(in++)) << 16;
|
||||
value |= (uint64_t)(*(in++)) << 24;
|
||||
value |= (uint64_t)(*(in++)) << 32;
|
||||
value |= (uint64_t)(*(in++)) << 40;
|
||||
value |= (uint64_t)(*(in++)) << 48;
|
||||
value |= (uint64_t)(*(in++)) << 56;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Guarantees that there are at least n_bits + 1 bits in accumulator.
|
||||
/* Guarantees that there are at least |n_bits| + 1 bits in accumulator.
|
||||
Precondition: accumulator contains at least 1 bit.
|
||||
n_bits should be in the range [1..24] for regular build. For portable
|
||||
|n_bits| should be in the range [1..24] for regular build. For portable
|
||||
non-64-bit little-endian build only 16 bits are safe to request. */
|
||||
static BROTLI_INLINE void BrotliFillBitWindow(
|
||||
BrotliBitReader* const br, uint32_t n_bits) {
|
||||
#if (BROTLI_64_BITS)
|
||||
if (!BROTLI_ALIGNED_READ && IS_CONSTANT(n_bits) && (n_bits <= 8)) {
|
||||
if (!BROTLI_ALIGNED_READ && BROTLI_IS_CONSTANT(n_bits) && (n_bits <= 8)) {
|
||||
if (br->bit_pos_ >= 56) {
|
||||
br->val_ >>= 56;
|
||||
br->bit_pos_ ^= 56; /* here same as -= 56 because of the if condition */
|
||||
br->val_ |= BrotliLoad64LE(br->next_in) << 8;
|
||||
br->val_ |= BROTLI_UNALIGNED_LOAD64LE(br->next_in) << 8;
|
||||
br->avail_in -= 7;
|
||||
br->next_in += 7;
|
||||
}
|
||||
} else if (!BROTLI_ALIGNED_READ && IS_CONSTANT(n_bits) && (n_bits <= 16)) {
|
||||
} else if (
|
||||
!BROTLI_ALIGNED_READ && BROTLI_IS_CONSTANT(n_bits) && (n_bits <= 16)) {
|
||||
if (br->bit_pos_ >= 48) {
|
||||
br->val_ >>= 48;
|
||||
br->bit_pos_ ^= 48; /* here same as -= 48 because of the if condition */
|
||||
br->val_ |= BrotliLoad64LE(br->next_in) << 16;
|
||||
br->val_ |= BROTLI_UNALIGNED_LOAD64LE(br->next_in) << 16;
|
||||
br->avail_in -= 6;
|
||||
br->next_in += 6;
|
||||
}
|
||||
|
|
@ -181,17 +127,17 @@ static BROTLI_INLINE void BrotliFillBitWindow(
|
|||
if (br->bit_pos_ >= 32) {
|
||||
br->val_ >>= 32;
|
||||
br->bit_pos_ ^= 32; /* here same as -= 32 because of the if condition */
|
||||
br->val_ |= ((uint64_t)BrotliLoad32LE(br->next_in)) << 32;
|
||||
br->val_ |= ((uint64_t)BROTLI_UNALIGNED_LOAD32LE(br->next_in)) << 32;
|
||||
br->avail_in -= BROTLI_SHORT_FILL_BIT_WINDOW_READ;
|
||||
br->next_in += BROTLI_SHORT_FILL_BIT_WINDOW_READ;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (!BROTLI_ALIGNED_READ && IS_CONSTANT(n_bits) && (n_bits <= 8)) {
|
||||
if (!BROTLI_ALIGNED_READ && BROTLI_IS_CONSTANT(n_bits) && (n_bits <= 8)) {
|
||||
if (br->bit_pos_ >= 24) {
|
||||
br->val_ >>= 24;
|
||||
br->bit_pos_ ^= 24; /* here same as -= 24 because of the if condition */
|
||||
br->val_ |= BrotliLoad32LE(br->next_in) << 8;
|
||||
br->val_ |= BROTLI_UNALIGNED_LOAD32LE(br->next_in) << 8;
|
||||
br->avail_in -= 3;
|
||||
br->next_in += 3;
|
||||
}
|
||||
|
|
@ -199,7 +145,7 @@ static BROTLI_INLINE void BrotliFillBitWindow(
|
|||
if (br->bit_pos_ >= 16) {
|
||||
br->val_ >>= 16;
|
||||
br->bit_pos_ ^= 16; /* here same as -= 16 because of the if condition */
|
||||
br->val_ |= ((uint32_t)BrotliLoad16LE(br->next_in)) << 16;
|
||||
br->val_ |= ((uint32_t)BROTLI_UNALIGNED_LOAD16LE(br->next_in)) << 16;
|
||||
br->avail_in -= BROTLI_SHORT_FILL_BIT_WINDOW_READ;
|
||||
br->next_in += BROTLI_SHORT_FILL_BIT_WINDOW_READ;
|
||||
}
|
||||
|
|
@ -213,7 +159,8 @@ static BROTLI_INLINE void BrotliFillBitWindow16(BrotliBitReader* const br) {
|
|||
BrotliFillBitWindow(br, 17);
|
||||
}
|
||||
|
||||
/* Pulls one byte of input to accumulator. */
|
||||
/* Tries to pull one byte of input to accumulator.
|
||||
Returns BROTLI_FALSE if there is no input available. */
|
||||
static BROTLI_INLINE BROTLI_BOOL BrotliPullByte(BrotliBitReader* const br) {
|
||||
if (br->avail_in == 0) {
|
||||
return BROTLI_FALSE;
|
||||
|
|
@ -232,7 +179,8 @@ static BROTLI_INLINE BROTLI_BOOL BrotliPullByte(BrotliBitReader* const br) {
|
|||
|
||||
/* Returns currently available bits.
|
||||
The number of valid bits could be calculated by BrotliGetAvailableBits. */
|
||||
static BROTLI_INLINE reg_t BrotliGetBitsUnmasked(BrotliBitReader* const br) {
|
||||
static BROTLI_INLINE brotli_reg_t BrotliGetBitsUnmasked(
|
||||
BrotliBitReader* const br) {
|
||||
return br->val_ >> br->bit_pos_;
|
||||
}
|
||||
|
||||
|
|
@ -244,15 +192,16 @@ static BROTLI_INLINE uint32_t BrotliGet16BitsUnmasked(
|
|||
return (uint32_t)BrotliGetBitsUnmasked(br);
|
||||
}
|
||||
|
||||
/* Returns the specified number of bits from |br| without advancing bit pos. */
|
||||
/* Returns the specified number of bits from |br| without advancing bit
|
||||
position. */
|
||||
static BROTLI_INLINE uint32_t BrotliGetBits(
|
||||
BrotliBitReader* const br, uint32_t n_bits) {
|
||||
BrotliFillBitWindow(br, n_bits);
|
||||
return (uint32_t)BrotliGetBitsUnmasked(br) & BitMask(n_bits);
|
||||
}
|
||||
|
||||
/* Tries to peek the specified amount of bits. Returns 0, if there is not
|
||||
enough input. */
|
||||
/* Tries to peek the specified amount of bits. Returns BROTLI_FALSE, if there
|
||||
is not enough input. */
|
||||
static BROTLI_INLINE BROTLI_BOOL BrotliSafeGetBits(
|
||||
BrotliBitReader* const br, uint32_t n_bits, uint32_t* val) {
|
||||
while (BrotliGetAvailableBits(br) < n_bits) {
|
||||
|
|
@ -264,7 +213,7 @@ static BROTLI_INLINE BROTLI_BOOL BrotliSafeGetBits(
|
|||
return BROTLI_TRUE;
|
||||
}
|
||||
|
||||
/* Advances the bit pos by n_bits. */
|
||||
/* Advances the bit pos by |n_bits|. */
|
||||
static BROTLI_INLINE void BrotliDropBits(
|
||||
BrotliBitReader* const br, uint32_t n_bits) {
|
||||
br->bit_pos_ += n_bits;
|
||||
|
|
@ -284,12 +233,12 @@ static BROTLI_INLINE void BrotliBitReaderUnload(BrotliBitReader* br) {
|
|||
}
|
||||
|
||||
/* Reads the specified number of bits from |br| and advances the bit pos.
|
||||
Precondition: accumulator MUST contain at least n_bits. */
|
||||
Precondition: accumulator MUST contain at least |n_bits|. */
|
||||
static BROTLI_INLINE void BrotliTakeBits(
|
||||
BrotliBitReader* const br, uint32_t n_bits, uint32_t* val) {
|
||||
*val = (uint32_t)BrotliGetBitsUnmasked(br) & BitMask(n_bits);
|
||||
BROTLI_LOG(("[BrotliReadBits] %d %d %d val: %6x\n",
|
||||
(int)br->avail_in, (int)br->bit_pos_, n_bits, (int)*val));
|
||||
(int)br->avail_in, (int)br->bit_pos_, (int)n_bits, (int)*val));
|
||||
BrotliDropBits(br, n_bits);
|
||||
}
|
||||
|
||||
|
|
@ -313,8 +262,8 @@ static BROTLI_INLINE uint32_t BrotliReadBits(
|
|||
}
|
||||
}
|
||||
|
||||
/* Tries to read the specified amount of bits. Returns 0, if there is not
|
||||
enough input. n_bits MUST be positive. */
|
||||
/* Tries to read the specified amount of bits. Returns BROTLI_FALSE, if there
|
||||
is not enough input. |n_bits| MUST be positive. */
|
||||
static BROTLI_INLINE BROTLI_BOOL BrotliSafeReadBits(
|
||||
BrotliBitReader* const br, uint32_t n_bits, uint32_t* val) {
|
||||
while (BrotliGetAvailableBits(br) < n_bits) {
|
||||
|
|
@ -338,7 +287,7 @@ static BROTLI_INLINE BROTLI_BOOL BrotliJumpToByteBoundary(BrotliBitReader* br) {
|
|||
}
|
||||
|
||||
/* Copies remaining input bytes stored in the bit reader to the output. Value
|
||||
num may not be larger than BrotliGetRemainingBytes. The bit reader must be
|
||||
|num| may not be larger than BrotliGetRemainingBytes. The bit reader must be
|
||||
warmed up again after this. */
|
||||
static BROTLI_INLINE void BrotliCopyBytes(uint8_t* dest,
|
||||
BrotliBitReader* br, size_t num) {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,162 +0,0 @@
|
|||
/* Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
Distributed under MIT license.
|
||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
/* API for Brotli decompression */
|
||||
|
||||
#ifndef BROTLI_DEC_DECODE_H_
|
||||
#define BROTLI_DEC_DECODE_H_
|
||||
|
||||
#include "./types.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct BrotliStateStruct BrotliState;
|
||||
|
||||
typedef enum {
|
||||
/* Decoding error, e.g. corrupt input or memory allocation problem */
|
||||
BROTLI_RESULT_ERROR = 0,
|
||||
/* Decoding successfully completed */
|
||||
BROTLI_RESULT_SUCCESS = 1,
|
||||
/* Partially done; should be called again with more input */
|
||||
BROTLI_RESULT_NEEDS_MORE_INPUT = 2,
|
||||
/* Partially done; should be called again with more output */
|
||||
BROTLI_RESULT_NEEDS_MORE_OUTPUT = 3
|
||||
} BrotliResult;
|
||||
|
||||
#define BROTLI_ERROR_CODES_LIST(BROTLI_ERROR_CODE, SEPARATOR) \
|
||||
BROTLI_ERROR_CODE(_, NO_ERROR, 0) SEPARATOR \
|
||||
/* Same as BrotliResult values */ \
|
||||
BROTLI_ERROR_CODE(_, SUCCESS, 1) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_, NEEDS_MORE_INPUT, 2) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_, NEEDS_MORE_OUTPUT, 3) SEPARATOR \
|
||||
\
|
||||
/* Errors caused by invalid input */ \
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, EXUBERANT_NIBBLE, -1) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, RESERVED, -2) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, EXUBERANT_META_NIBBLE, -3) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, SIMPLE_HUFFMAN_ALPHABET, -4) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, SIMPLE_HUFFMAN_SAME, -5) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, CL_SPACE, -6) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, HUFFMAN_SPACE, -7) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, CONTEXT_MAP_REPEAT, -8) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, BLOCK_LENGTH_1, -9) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, BLOCK_LENGTH_2, -10) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, TRANSFORM, -11) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, DICTIONARY, -12) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, WINDOW_BITS, -13) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, PADDING_1, -14) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, PADDING_2, -15) SEPARATOR \
|
||||
\
|
||||
/* -16..-20 codes are reserved */ \
|
||||
\
|
||||
/* Memory allocation problems */ \
|
||||
BROTLI_ERROR_CODE(_ERROR_ALLOC_, CONTEXT_MODES, -21) SEPARATOR \
|
||||
/* Literal, insert and distance trees together */ \
|
||||
BROTLI_ERROR_CODE(_ERROR_ALLOC_, TREE_GROUPS, -22) SEPARATOR \
|
||||
/* -23..-24 codes are reserved for distinct tree groups */ \
|
||||
BROTLI_ERROR_CODE(_ERROR_ALLOC_, CONTEXT_MAP, -25) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_ALLOC_, RING_BUFFER_1, -26) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_ALLOC_, RING_BUFFER_2, -27) SEPARATOR \
|
||||
/* -28..-29 codes are reserved for dynamic ringbuffer allocation */ \
|
||||
BROTLI_ERROR_CODE(_ERROR_ALLOC_, BLOCK_TYPE_TREES, -30) SEPARATOR \
|
||||
\
|
||||
/* "Impossible" states */ \
|
||||
BROTLI_ERROR_CODE(_ERROR_, UNREACHABLE, -31)
|
||||
|
||||
typedef enum {
|
||||
#define _BROTLI_COMMA ,
|
||||
#define _BROTLI_ERROR_CODE_ENUM_ITEM(PREFIX, NAME, CODE) \
|
||||
BROTLI ## PREFIX ## NAME = CODE
|
||||
BROTLI_ERROR_CODES_LIST(_BROTLI_ERROR_CODE_ENUM_ITEM, _BROTLI_COMMA)
|
||||
#undef _BROTLI_ERROR_CODE_ENUM_ITEM
|
||||
#undef _BROTLI_COMMA
|
||||
} BrotliErrorCode;
|
||||
|
||||
#define BROTLI_LAST_ERROR_CODE BROTLI_ERROR_UNREACHABLE
|
||||
|
||||
/* Creates the instance of BrotliState and initializes it. |alloc_func| and
|
||||
|free_func| MUST be both zero or both non-zero. In the case they are both
|
||||
zero, default memory allocators are used. |opaque| is passed to |alloc_func|
|
||||
and |free_func| when they are called. */
|
||||
BrotliState* BrotliCreateState(
|
||||
brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque);
|
||||
|
||||
/* Deinitializes and frees BrotliState instance. */
|
||||
void BrotliDestroyState(BrotliState* state);
|
||||
|
||||
/* Sets |*decoded_size| to the decompressed size of the given encoded stream.
|
||||
This function only works if the encoded buffer has a single meta block,
|
||||
or if it has two meta-blocks, where the first is uncompressed and the
|
||||
second is empty.
|
||||
Returns 1 on success, 0 on failure. */
|
||||
int BrotliDecompressedSize(size_t encoded_size,
|
||||
const uint8_t* encoded_buffer,
|
||||
size_t* decoded_size);
|
||||
|
||||
/* Decompresses the data in |encoded_buffer| into |decoded_buffer|, and sets
|
||||
|*decoded_size| to the decompressed length. */
|
||||
BrotliResult BrotliDecompressBuffer(size_t encoded_size,
|
||||
const uint8_t* encoded_buffer,
|
||||
size_t* decoded_size,
|
||||
uint8_t* decoded_buffer);
|
||||
|
||||
/* Decompresses the data. Supports partial input and output.
|
||||
|
||||
Must be called with an allocated input buffer in |*next_in| and an allocated
|
||||
output buffer in |*next_out|. The values |*available_in| and |*available_out|
|
||||
must specify the allocated size in |*next_in| and |*next_out| respectively.
|
||||
|
||||
After each call, |*available_in| will be decremented by the amount of input
|
||||
bytes consumed, and the |*next_in| pointer will be incremented by that
|
||||
amount. Similarly, |*available_out| will be decremented by the amount of
|
||||
output bytes written, and the |*next_out| pointer will be incremented by that
|
||||
amount. |total_out|, if it is not a null-pointer, will be set to the number
|
||||
of bytes decompressed since the last state initialization.
|
||||
|
||||
Input is never overconsumed, so |next_in| and |available_in| could be passed
|
||||
to the next consumer after decoding is complete. */
|
||||
BrotliResult BrotliDecompressStream(size_t* available_in,
|
||||
const uint8_t** next_in,
|
||||
size_t* available_out,
|
||||
uint8_t** next_out,
|
||||
size_t* total_out,
|
||||
BrotliState* s);
|
||||
|
||||
/* Fills the new state with a dictionary for LZ77, warming up the ringbuffer,
|
||||
e.g. for custom static dictionaries for data formats.
|
||||
Not to be confused with the built-in transformable dictionary of Brotli.
|
||||
|size| should be less or equal to 2^24 (16MiB), otherwise the dictionary will
|
||||
be ignored. The dictionary must exist in memory until decoding is done and
|
||||
is owned by the caller. To use:
|
||||
1) Allocate and initialize state with BrotliCreateState
|
||||
2) Use BrotliSetCustomDictionary
|
||||
3) Use BrotliDecompressStream
|
||||
4) Clean up and free state with BrotliDestroyState
|
||||
*/
|
||||
void BrotliSetCustomDictionary(
|
||||
size_t size, const uint8_t* dict, BrotliState* s);
|
||||
|
||||
/* Returns 1, if s is in a state where we have not read any input bytes yet,
|
||||
and 0 otherwise */
|
||||
int BrotliStateIsStreamStart(const BrotliState* s);
|
||||
|
||||
/* Returns 1, if s is in a state where we reached the end of the input and
|
||||
produced all of the output, and 0 otherwise. */
|
||||
int BrotliStateIsStreamEnd(const BrotliState* s);
|
||||
|
||||
/* Returns detailed error code after BrotliDecompressStream returns
|
||||
BROTLI_RESULT_ERROR. */
|
||||
BrotliErrorCode BrotliGetErrorCode(const BrotliState* s);
|
||||
|
||||
const char* BrotliErrorString(BrotliErrorCode c);
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* BROTLI_DEC_DECODE_H_ */
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,38 +0,0 @@
|
|||
/* Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
Distributed under MIT license.
|
||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
/* Collection of static dictionary words. */
|
||||
|
||||
#ifndef BROTLI_DEC_DICTIONARY_H_
|
||||
#define BROTLI_DEC_DICTIONARY_H_
|
||||
|
||||
#include "./types.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern const uint8_t kBrotliDictionary[122784];
|
||||
|
||||
static const uint32_t kBrotliDictionaryOffsetsByLength[] = {
|
||||
0, 0, 0, 0, 0, 4096, 9216, 21504, 35840, 44032, 53248, 63488, 74752, 87040,
|
||||
93696, 100864, 104704, 106752, 108928, 113536, 115968, 118528, 119872, 121280,
|
||||
122016
|
||||
};
|
||||
|
||||
static const uint8_t kBrotliDictionarySizeBitsByLength[] = {
|
||||
0, 0, 0, 0, 10, 10, 11, 11, 10, 10, 10, 10, 10,
|
||||
9, 9, 8, 7, 7, 8, 7, 7, 6, 6, 5, 5,
|
||||
};
|
||||
|
||||
static const int kBrotliMinDictionaryWordLength = 4;
|
||||
static const int kBrotliMaxDictionaryWordLength = 24;
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* BROTLI_DEC_DICTIONARY_H_ */
|
||||
|
|
@ -11,8 +11,8 @@
|
|||
#include <string.h> /* memcpy, memset */
|
||||
|
||||
#include "../common/constants.h"
|
||||
#include "../common/platform.h"
|
||||
#include <brotli/types.h>
|
||||
#include "./port.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
|
|
@ -20,9 +20,9 @@ extern "C" {
|
|||
|
||||
#define BROTLI_REVERSE_BITS_MAX 8
|
||||
|
||||
#ifdef BROTLI_RBIT
|
||||
#if defined(BROTLI_RBIT)
|
||||
#define BROTLI_REVERSE_BITS_BASE \
|
||||
((sizeof(reg_t) << 3) - BROTLI_REVERSE_BITS_MAX)
|
||||
((sizeof(brotli_reg_t) << 3) - BROTLI_REVERSE_BITS_MAX)
|
||||
#else
|
||||
#define BROTLI_REVERSE_BITS_BASE 0
|
||||
static uint8_t kReverseBits[1 << BROTLI_REVERSE_BITS_MAX] = {
|
||||
|
|
@ -62,13 +62,13 @@ static uint8_t kReverseBits[1 << BROTLI_REVERSE_BITS_MAX] = {
|
|||
#endif /* BROTLI_RBIT */
|
||||
|
||||
#define BROTLI_REVERSE_BITS_LOWEST \
|
||||
((reg_t)1 << (BROTLI_REVERSE_BITS_MAX - 1 + BROTLI_REVERSE_BITS_BASE))
|
||||
((brotli_reg_t)1 << (BROTLI_REVERSE_BITS_MAX - 1 + BROTLI_REVERSE_BITS_BASE))
|
||||
|
||||
/* Returns reverse(num >> BROTLI_REVERSE_BITS_BASE, BROTLI_REVERSE_BITS_MAX),
|
||||
where reverse(value, len) is the bit-wise reversal of the len least
|
||||
significant bits of value. */
|
||||
static BROTLI_INLINE reg_t BrotliReverseBits(reg_t num) {
|
||||
#ifdef BROTLI_RBIT
|
||||
static BROTLI_INLINE brotli_reg_t BrotliReverseBits(brotli_reg_t num) {
|
||||
#if defined(BROTLI_RBIT)
|
||||
return BROTLI_RBIT(num);
|
||||
#else
|
||||
return kReverseBits[num];
|
||||
|
|
@ -86,9 +86,9 @@ static BROTLI_INLINE void ReplicateValue(HuffmanCode* table,
|
|||
} while (end > 0);
|
||||
}
|
||||
|
||||
/* Returns the table width of the next 2nd level table. count is the histogram
|
||||
of bit lengths for the remaining symbols, len is the code length of the next
|
||||
processed symbol */
|
||||
/* Returns the table width of the next 2nd level table. |count| is the histogram
|
||||
of bit lengths for the remaining symbols, |len| is the code length of the
|
||||
next processed symbol. */
|
||||
static BROTLI_INLINE int NextTableBitSize(const uint16_t* const count,
|
||||
int len, int root_bits) {
|
||||
int left = 1 << (len - root_bits);
|
||||
|
|
@ -104,12 +104,12 @@ static BROTLI_INLINE int NextTableBitSize(const uint16_t* const count,
|
|||
void BrotliBuildCodeLengthsHuffmanTable(HuffmanCode* table,
|
||||
const uint8_t* const code_lengths,
|
||||
uint16_t* count) {
|
||||
HuffmanCode code; /* current table entry */
|
||||
int symbol; /* symbol index in original or sorted table */
|
||||
reg_t key; /* prefix code */
|
||||
reg_t key_step; /* prefix code addend */
|
||||
int step; /* step size to replicate values in current table */
|
||||
int table_size; /* size of current table */
|
||||
HuffmanCode code; /* current table entry */
|
||||
int symbol; /* symbol index in original or sorted table */
|
||||
brotli_reg_t key; /* prefix code */
|
||||
brotli_reg_t key_step; /* prefix code addend */
|
||||
int step; /* step size to replicate values in current table */
|
||||
int table_size; /* size of current table */
|
||||
int sorted[BROTLI_CODE_LENGTH_CODES]; /* symbols sorted by code length */
|
||||
/* offsets in sorted table for each length */
|
||||
int offset[BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH + 1];
|
||||
|
|
@ -118,7 +118,7 @@ void BrotliBuildCodeLengthsHuffmanTable(HuffmanCode* table,
|
|||
BROTLI_DCHECK(BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH <=
|
||||
BROTLI_REVERSE_BITS_MAX);
|
||||
|
||||
/* generate offsets into sorted symbol table by code length */
|
||||
/* Generate offsets into sorted symbol table by code length. */
|
||||
symbol = -1;
|
||||
bits = 1;
|
||||
BROTLI_REPEAT(BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH, {
|
||||
|
|
@ -129,7 +129,7 @@ void BrotliBuildCodeLengthsHuffmanTable(HuffmanCode* table,
|
|||
/* Symbols with code length 0 are placed after all other symbols. */
|
||||
offset[0] = BROTLI_CODE_LENGTH_CODES - 1;
|
||||
|
||||
/* sort symbols by length, by symbol order within each length */
|
||||
/* Sort symbols by length, by symbol order within each length. */
|
||||
symbol = BROTLI_CODE_LENGTH_CODES;
|
||||
do {
|
||||
BROTLI_REPEAT(6, {
|
||||
|
|
@ -142,24 +142,22 @@ void BrotliBuildCodeLengthsHuffmanTable(HuffmanCode* table,
|
|||
|
||||
/* Special case: all symbols but one have 0 code length. */
|
||||
if (offset[0] == 0) {
|
||||
code.bits = 0;
|
||||
code.value = (uint16_t)sorted[0];
|
||||
for (key = 0; key < (reg_t)table_size; ++key) {
|
||||
code = ConstructHuffmanCode(0, (uint16_t)sorted[0]);
|
||||
for (key = 0; key < (brotli_reg_t)table_size; ++key) {
|
||||
table[key] = code;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* fill in table */
|
||||
/* Fill in table. */
|
||||
key = 0;
|
||||
key_step = BROTLI_REVERSE_BITS_LOWEST;
|
||||
symbol = 0;
|
||||
bits = 1;
|
||||
step = 2;
|
||||
do {
|
||||
code.bits = (uint8_t)bits;
|
||||
for (bits_count = count[bits]; bits_count != 0; --bits_count) {
|
||||
code.value = (uint16_t)sorted[symbol++];
|
||||
code = ConstructHuffmanCode((uint8_t)bits, (uint16_t)sorted[symbol++]);
|
||||
ReplicateValue(&table[BrotliReverseBits(key)], step, table_size, code);
|
||||
key += key_step;
|
||||
}
|
||||
|
|
@ -172,18 +170,18 @@ uint32_t BrotliBuildHuffmanTable(HuffmanCode* root_table,
|
|||
int root_bits,
|
||||
const uint16_t* const symbol_lists,
|
||||
uint16_t* count) {
|
||||
HuffmanCode code; /* current table entry */
|
||||
HuffmanCode* table; /* next available space in table */
|
||||
int len; /* current code length */
|
||||
int symbol; /* symbol index in original or sorted table */
|
||||
reg_t key; /* prefix code */
|
||||
reg_t key_step; /* prefix code addend */
|
||||
reg_t sub_key; /* 2nd level table prefix code */
|
||||
reg_t sub_key_step; /* 2nd level table prefix code addend */
|
||||
int step; /* step size to replicate values in current table */
|
||||
int table_bits; /* key length of current table */
|
||||
int table_size; /* size of current table */
|
||||
int total_size; /* sum of root table size and 2nd level table sizes */
|
||||
HuffmanCode code; /* current table entry */
|
||||
HuffmanCode* table; /* next available space in table */
|
||||
int len; /* current code length */
|
||||
int symbol; /* symbol index in original or sorted table */
|
||||
brotli_reg_t key; /* prefix code */
|
||||
brotli_reg_t key_step; /* prefix code addend */
|
||||
brotli_reg_t sub_key; /* 2nd level table prefix code */
|
||||
brotli_reg_t sub_key_step; /* 2nd level table prefix code addend */
|
||||
int step; /* step size to replicate values in current table */
|
||||
int table_bits; /* key length of current table */
|
||||
int table_size; /* size of current table */
|
||||
int total_size; /* sum of root table size and 2nd level table sizes */
|
||||
int max_length = -1;
|
||||
int bits;
|
||||
int bits_count;
|
||||
|
|
@ -200,9 +198,8 @@ uint32_t BrotliBuildHuffmanTable(HuffmanCode* root_table,
|
|||
table_size = 1 << table_bits;
|
||||
total_size = table_size;
|
||||
|
||||
/* fill in root table */
|
||||
/* let's reduce the table size to a smaller size if possible, and */
|
||||
/* create the repetitions by memcpy if possible in the coming loop */
|
||||
/* Fill in the root table. Reduce the table size to if possible,
|
||||
and create the repetitions by memcpy. */
|
||||
if (table_bits > max_length) {
|
||||
table_bits = max_length;
|
||||
table_size = 1 << table_bits;
|
||||
|
|
@ -212,11 +209,10 @@ uint32_t BrotliBuildHuffmanTable(HuffmanCode* root_table,
|
|||
bits = 1;
|
||||
step = 2;
|
||||
do {
|
||||
code.bits = (uint8_t)bits;
|
||||
symbol = bits - (BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1);
|
||||
for (bits_count = count[bits]; bits_count != 0; --bits_count) {
|
||||
symbol = symbol_lists[symbol];
|
||||
code.value = (uint16_t)symbol;
|
||||
code = ConstructHuffmanCode((uint8_t)bits, (uint16_t)symbol);
|
||||
ReplicateValue(&table[BrotliReverseBits(key)], step, table_size, code);
|
||||
key += key_step;
|
||||
}
|
||||
|
|
@ -224,15 +220,14 @@ uint32_t BrotliBuildHuffmanTable(HuffmanCode* root_table,
|
|||
key_step >>= 1;
|
||||
} while (++bits <= table_bits);
|
||||
|
||||
/* if root_bits != table_bits we only created one fraction of the */
|
||||
/* table, and we need to replicate it now. */
|
||||
/* If root_bits != table_bits then replicate to fill the remaining slots. */
|
||||
while (total_size != table_size) {
|
||||
memcpy(&table[table_size], &table[0],
|
||||
(size_t)table_size * sizeof(table[0]));
|
||||
table_size <<= 1;
|
||||
}
|
||||
|
||||
/* fill in 2nd level tables and add pointers to root table */
|
||||
/* Fill in 2nd level tables and add pointers to root table. */
|
||||
key_step = BROTLI_REVERSE_BITS_LOWEST >> (root_bits - 1);
|
||||
sub_key = (BROTLI_REVERSE_BITS_LOWEST << 1);
|
||||
sub_key_step = BROTLI_REVERSE_BITS_LOWEST;
|
||||
|
|
@ -246,14 +241,13 @@ uint32_t BrotliBuildHuffmanTable(HuffmanCode* root_table,
|
|||
total_size += table_size;
|
||||
sub_key = BrotliReverseBits(key);
|
||||
key += key_step;
|
||||
root_table[sub_key].bits = (uint8_t)(table_bits + root_bits);
|
||||
root_table[sub_key].value =
|
||||
(uint16_t)(((size_t)(table - root_table)) - sub_key);
|
||||
root_table[sub_key] = ConstructHuffmanCode(
|
||||
(uint8_t)(table_bits + root_bits),
|
||||
(uint16_t)(((size_t)(table - root_table)) - sub_key));
|
||||
sub_key = 0;
|
||||
}
|
||||
code.bits = (uint8_t)(len - root_bits);
|
||||
symbol = symbol_lists[symbol];
|
||||
code.value = (uint16_t)symbol;
|
||||
code = ConstructHuffmanCode((uint8_t)(len - root_bits), (uint16_t)symbol);
|
||||
ReplicateValue(
|
||||
&table[BrotliReverseBits(sub_key)], step, table_size, code);
|
||||
sub_key += sub_key_step;
|
||||
|
|
@ -272,35 +266,28 @@ uint32_t BrotliBuildSimpleHuffmanTable(HuffmanCode* table,
|
|||
const uint32_t goal_size = 1U << root_bits;
|
||||
switch (num_symbols) {
|
||||
case 0:
|
||||
table[0].bits = 0;
|
||||
table[0].value = val[0];
|
||||
table[0] = ConstructHuffmanCode(0, val[0]);
|
||||
break;
|
||||
case 1:
|
||||
table[0].bits = 1;
|
||||
table[1].bits = 1;
|
||||
if (val[1] > val[0]) {
|
||||
table[0].value = val[0];
|
||||
table[1].value = val[1];
|
||||
table[0] = ConstructHuffmanCode(1, val[0]);
|
||||
table[1] = ConstructHuffmanCode(1, val[1]);
|
||||
} else {
|
||||
table[0].value = val[1];
|
||||
table[1].value = val[0];
|
||||
table[0] = ConstructHuffmanCode(1, val[1]);
|
||||
table[1] = ConstructHuffmanCode(1, val[0]);
|
||||
}
|
||||
table_size = 2;
|
||||
break;
|
||||
case 2:
|
||||
table[0].bits = 1;
|
||||
table[0].value = val[0];
|
||||
table[2].bits = 1;
|
||||
table[2].value = val[0];
|
||||
table[0] = ConstructHuffmanCode(1, val[0]);
|
||||
table[2] = ConstructHuffmanCode(1, val[0]);
|
||||
if (val[2] > val[1]) {
|
||||
table[1].value = val[1];
|
||||
table[3].value = val[2];
|
||||
table[1] = ConstructHuffmanCode(2, val[1]);
|
||||
table[3] = ConstructHuffmanCode(2, val[2]);
|
||||
} else {
|
||||
table[1].value = val[2];
|
||||
table[3].value = val[1];
|
||||
table[1] = ConstructHuffmanCode(2, val[2]);
|
||||
table[3] = ConstructHuffmanCode(2, val[1]);
|
||||
}
|
||||
table[1].bits = 2;
|
||||
table[3].bits = 2;
|
||||
table_size = 4;
|
||||
break;
|
||||
case 3: {
|
||||
|
|
@ -314,33 +301,27 @@ uint32_t BrotliBuildSimpleHuffmanTable(HuffmanCode* table,
|
|||
}
|
||||
}
|
||||
}
|
||||
for (i = 0; i < 4; ++i) {
|
||||
table[i].bits = 2;
|
||||
}
|
||||
table[0].value = val[0];
|
||||
table[2].value = val[1];
|
||||
table[1].value = val[2];
|
||||
table[3].value = val[3];
|
||||
table[0] = ConstructHuffmanCode(2, val[0]);
|
||||
table[2] = ConstructHuffmanCode(2, val[1]);
|
||||
table[1] = ConstructHuffmanCode(2, val[2]);
|
||||
table[3] = ConstructHuffmanCode(2, val[3]);
|
||||
table_size = 4;
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
int i;
|
||||
if (val[3] < val[2]) {
|
||||
uint16_t t = val[3];
|
||||
val[3] = val[2];
|
||||
val[2] = t;
|
||||
}
|
||||
for (i = 0; i < 7; ++i) {
|
||||
table[i].value = val[0];
|
||||
table[i].bits = (uint8_t)(1 + (i & 1));
|
||||
}
|
||||
table[1].value = val[1];
|
||||
table[3].value = val[2];
|
||||
table[5].value = val[1];
|
||||
table[7].value = val[3];
|
||||
table[3].bits = 3;
|
||||
table[7].bits = 3;
|
||||
table[0] = ConstructHuffmanCode(1, val[0]);
|
||||
table[1] = ConstructHuffmanCode(2, val[1]);
|
||||
table[2] = ConstructHuffmanCode(1, val[0]);
|
||||
table[3] = ConstructHuffmanCode(3, val[2]);
|
||||
table[4] = ConstructHuffmanCode(1, val[0]);
|
||||
table[5] = ConstructHuffmanCode(2, val[1]);
|
||||
table[6] = ConstructHuffmanCode(1, val[0]);
|
||||
table[7] = ConstructHuffmanCode(3, val[3]);
|
||||
table_size = 8;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@
|
|||
#ifndef BROTLI_DEC_HUFFMAN_H_
|
||||
#define BROTLI_DEC_HUFFMAN_H_
|
||||
|
||||
#include "../common/platform.h"
|
||||
#include <brotli/types.h>
|
||||
#include "./port.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
|
|
@ -19,10 +19,11 @@ extern "C" {
|
|||
#define BROTLI_HUFFMAN_MAX_CODE_LENGTH 15
|
||||
|
||||
/* Maximum possible Huffman table size for an alphabet size of (index * 32),
|
||||
* max code length 15 and root table bits 8. */
|
||||
max code length 15 and root table bits 8. */
|
||||
static const uint16_t kMaxHuffmanTableSize[] = {
|
||||
256, 402, 436, 468, 500, 534, 566, 598, 630, 662, 694, 726, 758, 790, 822,
|
||||
854, 886, 920, 952, 984, 1016, 1048, 1080};
|
||||
854, 886, 920, 952, 984, 1016, 1048, 1080, 1112, 1144, 1176, 1208, 1240, 1272,
|
||||
1304, 1336, 1368, 1400, 1432, 1464, 1496, 1528};
|
||||
/* BROTLI_NUM_BLOCK_LEN_SYMBOLS == 26 */
|
||||
#define BROTLI_HUFFMAN_MAX_SIZE_26 396
|
||||
/* BROTLI_MAX_BLOCK_TYPE_SYMBOLS == 258 */
|
||||
|
|
@ -32,32 +33,90 @@ static const uint16_t kMaxHuffmanTableSize[] = {
|
|||
|
||||
#define BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH 5
|
||||
|
||||
#if ((defined(BROTLI_TARGET_ARMV7) || defined(BROTLI_TARGET_ARMV8_32)) && \
|
||||
BROTLI_GNUC_HAS_ATTRIBUTE(aligned, 2, 7, 0))
|
||||
#define BROTLI_HUFFMAN_CODE_FAST_LOAD
|
||||
#endif
|
||||
|
||||
#if !defined(BROTLI_HUFFMAN_CODE_FAST_LOAD)
|
||||
/* Do not create this struct directly - use the ConstructHuffmanCode
|
||||
* constructor below! */
|
||||
typedef struct {
|
||||
uint8_t bits; /* number of bits used for this symbol */
|
||||
uint16_t value; /* symbol value or table offset */
|
||||
} HuffmanCode;
|
||||
|
||||
static BROTLI_INLINE HuffmanCode ConstructHuffmanCode(const uint8_t bits,
|
||||
const uint16_t value) {
|
||||
HuffmanCode h;
|
||||
h.bits = bits;
|
||||
h.value = value;
|
||||
return h;
|
||||
}
|
||||
|
||||
/* Please use the following macros to optimize HuffmanCode accesses in hot
|
||||
* paths.
|
||||
*
|
||||
* For example, assuming |table| contains a HuffmanCode pointer:
|
||||
*
|
||||
* BROTLI_HC_MARK_TABLE_FOR_FAST_LOAD(table);
|
||||
* BROTLI_HC_ADJUST_TABLE_INDEX(table, index_into_table);
|
||||
* *bits = BROTLI_HC_GET_BITS(table);
|
||||
* *value = BROTLI_HC_GET_VALUE(table);
|
||||
* BROTLI_HC_ADJUST_TABLE_INDEX(table, offset);
|
||||
* *bits2 = BROTLI_HC_GET_BITS(table);
|
||||
* *value2 = BROTLI_HC_GET_VALUE(table);
|
||||
*
|
||||
*/
|
||||
|
||||
#define BROTLI_HC_MARK_TABLE_FOR_FAST_LOAD(H)
|
||||
#define BROTLI_HC_ADJUST_TABLE_INDEX(H, V) H += (V)
|
||||
|
||||
/* These must be given a HuffmanCode pointer! */
|
||||
#define BROTLI_HC_FAST_LOAD_BITS(H) (H->bits)
|
||||
#define BROTLI_HC_FAST_LOAD_VALUE(H) (H->value)
|
||||
|
||||
#else /* BROTLI_HUFFMAN_CODE_FAST_LOAD */
|
||||
|
||||
typedef BROTLI_ALIGNED(4) uint32_t HuffmanCode;
|
||||
|
||||
static BROTLI_INLINE HuffmanCode ConstructHuffmanCode(const uint8_t bits,
|
||||
const uint16_t value) {
|
||||
return ((value & 0xFFFF) << 16) | (bits & 0xFF);
|
||||
}
|
||||
|
||||
#define BROTLI_HC_MARK_TABLE_FOR_FAST_LOAD(H) uint32_t __fastload_##H = (*H)
|
||||
#define BROTLI_HC_ADJUST_TABLE_INDEX(H, V) H += (V); __fastload_##H = (*H)
|
||||
|
||||
/* These must be given a HuffmanCode pointer! */
|
||||
#define BROTLI_HC_FAST_LOAD_BITS(H) ((__fastload_##H) & 0xFF)
|
||||
#define BROTLI_HC_FAST_LOAD_VALUE(H) ((__fastload_##H) >> 16)
|
||||
#endif /* BROTLI_HUFFMAN_CODE_FAST_LOAD */
|
||||
|
||||
/* Builds Huffman lookup table assuming code lengths are in symbol order. */
|
||||
BROTLI_INTERNAL void BrotliBuildCodeLengthsHuffmanTable(HuffmanCode* root_table,
|
||||
const uint8_t* const code_lengths, uint16_t* count);
|
||||
|
||||
/* Builds Huffman lookup table assuming code lengths are in symbol order. */
|
||||
/* Returns size of resulting table. */
|
||||
/* Builds Huffman lookup table assuming code lengths are in symbol order.
|
||||
Returns size of resulting table. */
|
||||
BROTLI_INTERNAL uint32_t BrotliBuildHuffmanTable(HuffmanCode* root_table,
|
||||
int root_bits, const uint16_t* const symbol_lists, uint16_t* count_arg);
|
||||
|
||||
/* Builds a simple Huffman table. The num_symbols parameter is to be */
|
||||
/* interpreted as follows: 0 means 1 symbol, 1 means 2 symbols, 2 means 3 */
|
||||
/* symbols, 3 means 4 symbols with lengths 2,2,2,2, 4 means 4 symbols with */
|
||||
/* lengths 1,2,3,3. */
|
||||
/* Builds a simple Huffman table. The |num_symbols| parameter is to be
|
||||
interpreted as follows: 0 means 1 symbol, 1 means 2 symbols,
|
||||
2 means 3 symbols, 3 means 4 symbols with lengths [2, 2, 2, 2],
|
||||
4 means 4 symbols with lengths [1, 2, 3, 3]. */
|
||||
BROTLI_INTERNAL uint32_t BrotliBuildSimpleHuffmanTable(HuffmanCode* table,
|
||||
int root_bits, uint16_t* symbols, uint32_t num_symbols);
|
||||
|
||||
/* Contains a collection of Huffman trees with the same alphabet size. */
|
||||
/* max_symbol is needed due to simple codes since log2(alphabet_size) could be
|
||||
greater than log2(max_symbol). */
|
||||
typedef struct {
|
||||
HuffmanCode** htrees;
|
||||
HuffmanCode* codes;
|
||||
uint16_t alphabet_size;
|
||||
uint16_t max_symbol;
|
||||
uint16_t num_htrees;
|
||||
} HuffmanTreeGroup;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,168 +0,0 @@
|
|||
/* Copyright 2015 Google Inc. All Rights Reserved.
|
||||
|
||||
Distributed under MIT license.
|
||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
/* Macros for compiler / platform specific features and build options.
|
||||
|
||||
Build options are:
|
||||
* BROTLI_BUILD_32_BIT disables 64-bit optimizations
|
||||
* BROTLI_BUILD_64_BIT forces to use 64-bit optimizations
|
||||
* BROTLI_BUILD_BIG_ENDIAN forces to use big-endian optimizations
|
||||
* BROTLI_BUILD_ENDIAN_NEUTRAL disables endian-aware optimizations
|
||||
* BROTLI_BUILD_LITTLE_ENDIAN forces to use little-endian optimizations
|
||||
* BROTLI_BUILD_MODERN_COMPILER forces to use modern compilers built-ins,
|
||||
features and attributes
|
||||
* BROTLI_BUILD_PORTABLE disables dangerous optimizations, like unaligned
|
||||
read and overlapping memcpy; this reduces decompression speed by 5%
|
||||
* BROTLI_BUILD_NO_RBIT disables "rbit" optimization for ARM CPUs
|
||||
* BROTLI_DEBUG dumps file name and line number when decoder detects stream
|
||||
or memory error
|
||||
* BROTLI_ENABLE_LOG enables asserts and dumps various state information
|
||||
*/
|
||||
|
||||
#ifndef BROTLI_DEC_PORT_H_
|
||||
#define BROTLI_DEC_PORT_H_
|
||||
|
||||
#if defined(BROTLI_ENABLE_LOG) || defined(BROTLI_DEBUG)
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#include <brotli/port.h>
|
||||
|
||||
#if defined(__arm__) || defined(__thumb__) || \
|
||||
defined(_M_ARM) || defined(_M_ARMT) || defined(__ARM64_ARCH_8__)
|
||||
#define BROTLI_TARGET_ARM
|
||||
#if (defined(__ARM_ARCH) && (__ARM_ARCH == 7)) || \
|
||||
(defined(M_ARM) && (M_ARM == 7))
|
||||
#define BROTLI_TARGET_ARMV7
|
||||
#endif /* ARMv7 */
|
||||
#if defined(__aarch64__) || defined(__ARM64_ARCH_8__)
|
||||
#define BROTLI_TARGET_ARMV8
|
||||
#endif /* ARMv8 */
|
||||
#endif /* ARM */
|
||||
|
||||
#if defined(__i386) || defined(_M_IX86)
|
||||
#define BROTLI_TARGET_X86
|
||||
#endif
|
||||
|
||||
#if defined(__x86_64__) || defined(_M_X64)
|
||||
#define BROTLI_TARGET_X64
|
||||
#endif
|
||||
|
||||
#if defined(__PPC64__)
|
||||
#define BROTLI_TARGET_POWERPC64
|
||||
#endif
|
||||
|
||||
#ifdef BROTLI_BUILD_PORTABLE
|
||||
#define BROTLI_ALIGNED_READ (!!1)
|
||||
#elif defined(BROTLI_TARGET_X86) || defined(BROTLI_TARGET_X64) || \
|
||||
defined(BROTLI_TARGET_ARMV7) || defined(BROTLI_TARGET_ARMV8)
|
||||
/* Allow unaligned read only for white-listed CPUs. */
|
||||
#define BROTLI_ALIGNED_READ (!!0)
|
||||
#else
|
||||
#define BROTLI_ALIGNED_READ (!!1)
|
||||
#endif
|
||||
|
||||
/* IS_CONSTANT macros returns true for compile-time constant expressions. */
|
||||
#if BROTLI_MODERN_COMPILER || __has_builtin(__builtin_constant_p)
|
||||
#define IS_CONSTANT(x) (!!__builtin_constant_p(x))
|
||||
#else
|
||||
#define IS_CONSTANT(x) (!!0)
|
||||
#endif
|
||||
|
||||
#ifdef BROTLI_ENABLE_LOG
|
||||
#define BROTLI_DCHECK(x) assert(x)
|
||||
#define BROTLI_LOG(x) printf x
|
||||
#else
|
||||
#define BROTLI_DCHECK(x)
|
||||
#define BROTLI_LOG(x)
|
||||
#endif
|
||||
|
||||
#if defined(BROTLI_DEBUG) || defined(BROTLI_ENABLE_LOG)
|
||||
static BROTLI_INLINE void BrotliDump(const char* f, int l, const char* fn) {
|
||||
fprintf(stderr, "%s:%d (%s)\n", f, l, fn);
|
||||
fflush(stderr);
|
||||
}
|
||||
#define BROTLI_DUMP() BrotliDump(__FILE__, __LINE__, __FUNCTION__)
|
||||
#else
|
||||
#define BROTLI_DUMP() (void)(0)
|
||||
#endif
|
||||
|
||||
#if defined(BROTLI_BUILD_64_BIT)
|
||||
#define BROTLI_64_BITS 1
|
||||
#elif defined(BROTLI_BUILD_32_BIT)
|
||||
#define BROTLI_64_BITS 0
|
||||
#elif defined(BROTLI_TARGET_X64) || defined(BROTLI_TARGET_ARMV8) || \
|
||||
defined(BROTLI_TARGET_POWERPC64)
|
||||
#define BROTLI_64_BITS 1
|
||||
#else
|
||||
#define BROTLI_64_BITS 0
|
||||
#endif
|
||||
|
||||
#if (BROTLI_64_BITS)
|
||||
#define reg_t uint64_t
|
||||
#else
|
||||
#define reg_t uint32_t
|
||||
#endif
|
||||
|
||||
#if defined(BROTLI_BUILD_BIG_ENDIAN)
|
||||
#define BROTLI_LITTLE_ENDIAN 0
|
||||
#define BROTLI_BIG_ENDIAN 1
|
||||
#elif defined(BROTLI_BUILD_LITTLE_ENDIAN)
|
||||
#define BROTLI_LITTLE_ENDIAN 1
|
||||
#define BROTLI_BIG_ENDIAN 0
|
||||
#elif defined(BROTLI_BUILD_ENDIAN_NEUTRAL)
|
||||
#define BROTLI_LITTLE_ENDIAN 0
|
||||
#define BROTLI_BIG_ENDIAN 0
|
||||
#elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
||||
#define BROTLI_LITTLE_ENDIAN 1
|
||||
#define BROTLI_BIG_ENDIAN 0
|
||||
#elif defined(_WIN32)
|
||||
/* Win32 can currently always be assumed to be little endian */
|
||||
#define BROTLI_LITTLE_ENDIAN 1
|
||||
#define BROTLI_BIG_ENDIAN 0
|
||||
#else
|
||||
#if (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
|
||||
#define BROTLI_BIG_ENDIAN 1
|
||||
#else
|
||||
#define BROTLI_BIG_ENDIAN 0
|
||||
#endif
|
||||
#define BROTLI_LITTLE_ENDIAN 0
|
||||
#endif
|
||||
|
||||
#define BROTLI_REPEAT(N, X) { \
|
||||
if ((N & 1) != 0) {X;} \
|
||||
if ((N & 2) != 0) {X; X;} \
|
||||
if ((N & 4) != 0) {X; X; X; X;} \
|
||||
}
|
||||
|
||||
#if (BROTLI_MODERN_COMPILER || defined(__llvm__)) && \
|
||||
!defined(BROTLI_BUILD_NO_RBIT)
|
||||
#if defined(BROTLI_TARGET_ARMV7) || defined(BROTLI_TARGET_ARMV8)
|
||||
/* TODO: detect ARMv6T2 and enable this code for it. */
|
||||
static BROTLI_INLINE reg_t BrotliRBit(reg_t input) {
|
||||
reg_t output;
|
||||
__asm__("rbit %0, %1\n" : "=r"(output) : "r"(input));
|
||||
return output;
|
||||
}
|
||||
#define BROTLI_RBIT(x) BrotliRBit(x)
|
||||
#endif /* armv7 */
|
||||
#endif /* gcc || clang */
|
||||
|
||||
#if defined(BROTLI_TARGET_ARM)
|
||||
#define BROTLI_HAS_UBFX (!!1)
|
||||
#else
|
||||
#define BROTLI_HAS_UBFX (!!0)
|
||||
#endif
|
||||
|
||||
#define BROTLI_ALLOC(S, L) S->alloc_func(S->memory_manager_opaque, L)
|
||||
|
||||
#define BROTLI_FREE(S, X) { \
|
||||
S->free_func(S->memory_manager_opaque, X); \
|
||||
X = NULL; \
|
||||
}
|
||||
|
||||
#endif /* BROTLI_DEC_PORT_H_ */
|
||||
|
|
@ -5,8 +5,7 @@
|
|||
*/
|
||||
|
||||
/* Lookup tables to map prefix codes to value ranges. This is used during
|
||||
decoding of the block lengths, literal insertion lengths and copy lengths.
|
||||
*/
|
||||
decoding of the block lengths, literal insertion lengths and copy lengths. */
|
||||
|
||||
#ifndef BROTLI_DEC_PREFIX_H_
|
||||
#define BROTLI_DEC_PREFIX_H_
|
||||
|
|
@ -14,8 +13,8 @@
|
|||
#include "../common/constants.h"
|
||||
#include <brotli/types.h>
|
||||
|
||||
/* Represents the range of values belonging to a prefix code: */
|
||||
/* [offset, offset + 2^nbits) */
|
||||
/* Represents the range of values belonging to a prefix code:
|
||||
[offset, offset + 2^nbits) */
|
||||
struct PrefixCodeRange {
|
||||
uint16_t offset;
|
||||
uint8_t nbits;
|
||||
|
|
|
|||
|
|
@ -15,25 +15,11 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
static void* DefaultAllocFunc(void* opaque, size_t size) {
|
||||
BROTLI_UNUSED(opaque);
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
static void DefaultFreeFunc(void* opaque, void* address) {
|
||||
BROTLI_UNUSED(opaque);
|
||||
free(address);
|
||||
}
|
||||
|
||||
void BrotliDecoderStateInit(BrotliDecoderState* s) {
|
||||
BrotliDecoderStateInitWithCustomAllocators(s, 0, 0, 0);
|
||||
}
|
||||
|
||||
void BrotliDecoderStateInitWithCustomAllocators(BrotliDecoderState* s,
|
||||
BROTLI_BOOL BrotliDecoderStateInit(BrotliDecoderState* s,
|
||||
brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque) {
|
||||
if (!alloc_func) {
|
||||
s->alloc_func = DefaultAllocFunc;
|
||||
s->free_func = DefaultFreeFunc;
|
||||
s->alloc_func = BrotliDefaultAllocFunc;
|
||||
s->free_func = BrotliDefaultFreeFunc;
|
||||
s->memory_manager_opaque = 0;
|
||||
} else {
|
||||
s->alloc_func = alloc_func;
|
||||
|
|
@ -45,6 +31,7 @@ void BrotliDecoderStateInitWithCustomAllocators(BrotliDecoderState* s,
|
|||
|
||||
BrotliInitBitReader(&s->br);
|
||||
s->state = BROTLI_STATE_UNINITED;
|
||||
s->large_window = 0;
|
||||
s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_NONE;
|
||||
s->substate_tree_group = BROTLI_STATE_TREE_GROUP_NONE;
|
||||
s->substate_context_map = BROTLI_STATE_CONTEXT_MAP_NONE;
|
||||
|
|
@ -53,8 +40,6 @@ void BrotliDecoderStateInitWithCustomAllocators(BrotliDecoderState* s,
|
|||
s->substate_decode_uint8 = BROTLI_STATE_DECODE_UINT8_NONE;
|
||||
s->substate_read_block_length = BROTLI_STATE_READ_BLOCK_LENGTH_NONE;
|
||||
|
||||
s->dictionary = BrotliGetDictionary();
|
||||
|
||||
s->buffer_length = 0;
|
||||
s->loop_counter = 0;
|
||||
s->pos = 0;
|
||||
|
|
@ -103,13 +88,18 @@ void BrotliDecoderStateInitWithCustomAllocators(BrotliDecoderState* s,
|
|||
s->symbol_lists = &s->symbols_lists_array[BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1];
|
||||
|
||||
s->mtf_upper_bound = 63;
|
||||
|
||||
s->dictionary = BrotliGetDictionary();
|
||||
s->transforms = BrotliGetTransforms();
|
||||
|
||||
return BROTLI_TRUE;
|
||||
}
|
||||
|
||||
void BrotliDecoderStateMetablockBegin(BrotliDecoderState* s) {
|
||||
s->meta_block_remaining_len = 0;
|
||||
s->block_length[0] = 1U << 28;
|
||||
s->block_length[1] = 1U << 28;
|
||||
s->block_length[2] = 1U << 28;
|
||||
s->block_length[0] = 1U << 24;
|
||||
s->block_length[1] = 1U << 24;
|
||||
s->block_length[2] = 1U << 24;
|
||||
s->num_block_types[0] = 1;
|
||||
s->num_block_types[1] = 1;
|
||||
s->num_block_types[2] = 1;
|
||||
|
|
@ -126,8 +116,7 @@ void BrotliDecoderStateMetablockBegin(BrotliDecoderState* s) {
|
|||
s->literal_htree = NULL;
|
||||
s->dist_context_map_slice = NULL;
|
||||
s->dist_htree_index = 0;
|
||||
s->context_lookup1 = NULL;
|
||||
s->context_lookup2 = NULL;
|
||||
s->context_lookup = NULL;
|
||||
s->literal_hgroup.codes = NULL;
|
||||
s->literal_hgroup.htrees = NULL;
|
||||
s->insert_copy_hgroup.codes = NULL;
|
||||
|
|
@ -137,30 +126,33 @@ void BrotliDecoderStateMetablockBegin(BrotliDecoderState* s) {
|
|||
}
|
||||
|
||||
void BrotliDecoderStateCleanupAfterMetablock(BrotliDecoderState* s) {
|
||||
BROTLI_FREE(s, s->context_modes);
|
||||
BROTLI_FREE(s, s->context_map);
|
||||
BROTLI_FREE(s, s->dist_context_map);
|
||||
BROTLI_FREE(s, s->literal_hgroup.htrees);
|
||||
BROTLI_FREE(s, s->insert_copy_hgroup.htrees);
|
||||
BROTLI_FREE(s, s->distance_hgroup.htrees);
|
||||
BROTLI_DECODER_FREE(s, s->context_modes);
|
||||
BROTLI_DECODER_FREE(s, s->context_map);
|
||||
BROTLI_DECODER_FREE(s, s->dist_context_map);
|
||||
BROTLI_DECODER_FREE(s, s->literal_hgroup.htrees);
|
||||
BROTLI_DECODER_FREE(s, s->insert_copy_hgroup.htrees);
|
||||
BROTLI_DECODER_FREE(s, s->distance_hgroup.htrees);
|
||||
}
|
||||
|
||||
void BrotliDecoderStateCleanup(BrotliDecoderState* s) {
|
||||
BrotliDecoderStateCleanupAfterMetablock(s);
|
||||
|
||||
BROTLI_FREE(s, s->ringbuffer);
|
||||
BROTLI_FREE(s, s->block_type_trees);
|
||||
BROTLI_DECODER_FREE(s, s->ringbuffer);
|
||||
BROTLI_DECODER_FREE(s, s->block_type_trees);
|
||||
}
|
||||
|
||||
BROTLI_BOOL BrotliDecoderHuffmanTreeGroupInit(BrotliDecoderState* s,
|
||||
HuffmanTreeGroup* group, uint32_t alphabet_size, uint32_t ntrees) {
|
||||
HuffmanTreeGroup* group, uint32_t alphabet_size, uint32_t max_symbol,
|
||||
uint32_t ntrees) {
|
||||
/* Pack two allocations into one */
|
||||
const size_t max_table_size = kMaxHuffmanTableSize[(alphabet_size + 31) >> 5];
|
||||
const size_t code_size = sizeof(HuffmanCode) * ntrees * max_table_size;
|
||||
const size_t htree_size = sizeof(HuffmanCode*) * ntrees;
|
||||
/* Pointer alignment is, hopefully, wider than sizeof(HuffmanCode). */
|
||||
HuffmanCode** p = (HuffmanCode**)BROTLI_ALLOC(s, code_size + htree_size);
|
||||
HuffmanCode** p = (HuffmanCode**)BROTLI_DECODER_ALLOC(s,
|
||||
code_size + htree_size);
|
||||
group->alphabet_size = (uint16_t)alphabet_size;
|
||||
group->max_symbol = (uint16_t)max_symbol;
|
||||
group->num_htrees = (uint16_t)ntrees;
|
||||
group->htrees = p;
|
||||
group->codes = (HuffmanCode*)(&p[ntrees]);
|
||||
|
|
|
|||
|
|
@ -11,10 +11,11 @@
|
|||
|
||||
#include "../common/constants.h"
|
||||
#include "../common/dictionary.h"
|
||||
#include "../common/platform.h"
|
||||
#include "../common/transform.h"
|
||||
#include <brotli/types.h>
|
||||
#include "./bit_reader.h"
|
||||
#include "./huffman.h"
|
||||
#include "./port.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
|
|
@ -22,6 +23,8 @@ extern "C" {
|
|||
|
||||
typedef enum {
|
||||
BROTLI_STATE_UNINITED,
|
||||
BROTLI_STATE_LARGE_WINDOW_BITS,
|
||||
BROTLI_STATE_INITIALIZE,
|
||||
BROTLI_STATE_METABLOCK_BEGIN,
|
||||
BROTLI_STATE_METABLOCK_HEADER,
|
||||
BROTLI_STATE_METABLOCK_HEADER_2,
|
||||
|
|
@ -126,23 +129,22 @@ struct BrotliDecoderStateStruct {
|
|||
uint8_t* ringbuffer;
|
||||
uint8_t* ringbuffer_end;
|
||||
HuffmanCode* htree_command;
|
||||
const uint8_t* context_lookup1;
|
||||
const uint8_t* context_lookup2;
|
||||
const uint8_t* context_lookup;
|
||||
uint8_t* context_map_slice;
|
||||
uint8_t* dist_context_map_slice;
|
||||
|
||||
/* This ring buffer holds a few past copy distances that will be used by */
|
||||
/* some special distance codes. */
|
||||
/* This ring buffer holds a few past copy distances that will be used by
|
||||
some special distance codes. */
|
||||
HuffmanTreeGroup literal_hgroup;
|
||||
HuffmanTreeGroup insert_copy_hgroup;
|
||||
HuffmanTreeGroup distance_hgroup;
|
||||
HuffmanCode* block_type_trees;
|
||||
HuffmanCode* block_len_trees;
|
||||
/* This is true if the literal context map histogram type always matches the
|
||||
block type. It is then not needed to keep the context (faster decoding). */
|
||||
block type. It is then not needed to keep the context (faster decoding). */
|
||||
int trivial_literal_context;
|
||||
/* Distance context is actual after command is decoded and before distance
|
||||
is computed. After distance computation it is used as a temporary variable. */
|
||||
/* Distance context is actual after command is decoded and before distance is
|
||||
computed. After distance computation it is used as a temporary variable. */
|
||||
int distance_context;
|
||||
int meta_block_remaining_len;
|
||||
uint32_t block_length_index;
|
||||
|
|
@ -162,11 +164,11 @@ struct BrotliDecoderStateStruct {
|
|||
int copy_length;
|
||||
int distance_code;
|
||||
|
||||
/* For partial write operations */
|
||||
size_t rb_roundtrips; /* How many times we went around the ring-buffer */
|
||||
size_t partial_pos_out; /* How much output to the user in total */
|
||||
/* For partial write operations. */
|
||||
size_t rb_roundtrips; /* how many times we went around the ring-buffer */
|
||||
size_t partial_pos_out; /* how much output to the user in total */
|
||||
|
||||
/* For ReadHuffmanCode */
|
||||
/* For ReadHuffmanCode. */
|
||||
uint32_t symbol;
|
||||
uint32_t repeat;
|
||||
uint32_t space;
|
||||
|
|
@ -180,25 +182,26 @@ struct BrotliDecoderStateStruct {
|
|||
/* Tails of symbol chains. */
|
||||
int next_symbol[32];
|
||||
uint8_t code_length_code_lengths[BROTLI_CODE_LENGTH_CODES];
|
||||
/* Population counts for the code lengths */
|
||||
/* Population counts for the code lengths. */
|
||||
uint16_t code_length_histo[16];
|
||||
|
||||
/* For HuffmanTreeGroupDecode */
|
||||
/* For HuffmanTreeGroupDecode. */
|
||||
int htree_index;
|
||||
HuffmanCode* next;
|
||||
|
||||
/* For DecodeContextMap */
|
||||
/* For DecodeContextMap. */
|
||||
uint32_t context_index;
|
||||
uint32_t max_run_length_prefix;
|
||||
uint32_t code;
|
||||
HuffmanCode context_map_table[BROTLI_HUFFMAN_MAX_SIZE_272];
|
||||
|
||||
/* For InverseMoveToFrontTransform */
|
||||
/* For InverseMoveToFrontTransform. */
|
||||
uint32_t mtf_upper_bound;
|
||||
uint32_t mtf[64 + 1];
|
||||
|
||||
/* less used attributes are in the end of this struct */
|
||||
/* States inside function calls */
|
||||
/* Less used attributes are at the end of this struct. */
|
||||
|
||||
/* States inside function calls. */
|
||||
BrotliRunningMetablockHeaderState substate_metablock_header;
|
||||
BrotliRunningTreeGroupState substate_tree_group;
|
||||
BrotliRunningContextMapState substate_context_map;
|
||||
|
|
@ -212,6 +215,7 @@ struct BrotliDecoderStateStruct {
|
|||
unsigned int is_metadata : 1;
|
||||
unsigned int should_wrap_ringbuffer : 1;
|
||||
unsigned int canny_ringbuffer_allocation : 1;
|
||||
unsigned int large_window : 1;
|
||||
unsigned int size_nibbles : 8;
|
||||
uint32_t window_bits;
|
||||
|
||||
|
|
@ -220,7 +224,9 @@ struct BrotliDecoderStateStruct {
|
|||
uint32_t num_literal_htrees;
|
||||
uint8_t* context_map;
|
||||
uint8_t* context_modes;
|
||||
|
||||
const BrotliDictionary* dictionary;
|
||||
const BrotliTransforms* transforms;
|
||||
|
||||
uint32_t trivial_literal_contexts[8]; /* 256 bits */
|
||||
};
|
||||
|
|
@ -228,17 +234,22 @@ struct BrotliDecoderStateStruct {
|
|||
typedef struct BrotliDecoderStateStruct BrotliDecoderStateInternal;
|
||||
#define BrotliDecoderState BrotliDecoderStateInternal
|
||||
|
||||
BROTLI_INTERNAL void BrotliDecoderStateInit(BrotliDecoderState* s);
|
||||
BROTLI_INTERNAL void BrotliDecoderStateInitWithCustomAllocators(
|
||||
BrotliDecoderState* s, brotli_alloc_func alloc_func,
|
||||
brotli_free_func free_func, void* opaque);
|
||||
BROTLI_INTERNAL BROTLI_BOOL BrotliDecoderStateInit(BrotliDecoderState* s,
|
||||
brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque);
|
||||
BROTLI_INTERNAL void BrotliDecoderStateCleanup(BrotliDecoderState* s);
|
||||
BROTLI_INTERNAL void BrotliDecoderStateMetablockBegin(BrotliDecoderState* s);
|
||||
BROTLI_INTERNAL void BrotliDecoderStateCleanupAfterMetablock(
|
||||
BrotliDecoderState* s);
|
||||
BROTLI_INTERNAL BROTLI_BOOL BrotliDecoderHuffmanTreeGroupInit(
|
||||
BrotliDecoderState* s, HuffmanTreeGroup* group, uint32_t alphabet_size,
|
||||
uint32_t ntrees);
|
||||
uint32_t max_symbol, uint32_t ntrees);
|
||||
|
||||
#define BROTLI_DECODER_ALLOC(S, L) S->alloc_func(S->memory_manager_opaque, L)
|
||||
|
||||
#define BROTLI_DECODER_FREE(S, X) { \
|
||||
S->free_func(S->memory_manager_opaque, X); \
|
||||
X = NULL; \
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} /* extern "C" */
|
||||
|
|
|
|||
|
|
@ -1,300 +0,0 @@
|
|||
/* Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
Distributed under MIT license.
|
||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
/* Transformations on dictionary words. */
|
||||
|
||||
#ifndef BROTLI_DEC_TRANSFORM_H_
|
||||
#define BROTLI_DEC_TRANSFORM_H_
|
||||
|
||||
#include <brotli/types.h>
|
||||
#include "./port.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum WordTransformType {
|
||||
kIdentity = 0,
|
||||
kOmitLast1 = 1,
|
||||
kOmitLast2 = 2,
|
||||
kOmitLast3 = 3,
|
||||
kOmitLast4 = 4,
|
||||
kOmitLast5 = 5,
|
||||
kOmitLast6 = 6,
|
||||
kOmitLast7 = 7,
|
||||
kOmitLast8 = 8,
|
||||
kOmitLast9 = 9,
|
||||
kUppercaseFirst = 10,
|
||||
kUppercaseAll = 11,
|
||||
kOmitFirst1 = 12,
|
||||
kOmitFirst2 = 13,
|
||||
kOmitFirst3 = 14,
|
||||
kOmitFirst4 = 15,
|
||||
kOmitFirst5 = 16,
|
||||
kOmitFirst6 = 17,
|
||||
kOmitFirst7 = 18,
|
||||
kOmitFirst8 = 19,
|
||||
kOmitFirst9 = 20
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
const uint8_t prefix_id;
|
||||
const uint8_t transform;
|
||||
const uint8_t suffix_id;
|
||||
} Transform;
|
||||
|
||||
static const char kPrefixSuffix[208] =
|
||||
"\0 \0, \0 of the \0 of \0s \0.\0 and \0 in \0\"\0 to \0\">\0\n\0. \0]\0"
|
||||
" for \0 a \0 that \0\'\0 with \0 from \0 by \0(\0. The \0 on \0 as \0"
|
||||
" is \0ing \0\n\t\0:\0ed \0=\"\0 at \0ly \0,\0=\'\0.com/\0. This \0"
|
||||
" not \0er \0al \0ful \0ive \0less \0est \0ize \0\xc2\xa0\0ous ";
|
||||
|
||||
enum {
|
||||
/* EMPTY = ""
|
||||
SP = " "
|
||||
DQUOT = "\""
|
||||
SQUOT = "'"
|
||||
CLOSEBR = "]"
|
||||
OPEN = "("
|
||||
SLASH = "/"
|
||||
NBSP = non-breaking space "\0xc2\xa0"
|
||||
*/
|
||||
kPFix_EMPTY = 0,
|
||||
kPFix_SP = 1,
|
||||
kPFix_COMMASP = 3,
|
||||
kPFix_SPofSPtheSP = 6,
|
||||
kPFix_SPtheSP = 9,
|
||||
kPFix_eSP = 12,
|
||||
kPFix_SPofSP = 15,
|
||||
kPFix_sSP = 20,
|
||||
kPFix_DOT = 23,
|
||||
kPFix_SPandSP = 25,
|
||||
kPFix_SPinSP = 31,
|
||||
kPFix_DQUOT = 36,
|
||||
kPFix_SPtoSP = 38,
|
||||
kPFix_DQUOTGT = 43,
|
||||
kPFix_NEWLINE = 46,
|
||||
kPFix_DOTSP = 48,
|
||||
kPFix_CLOSEBR = 51,
|
||||
kPFix_SPforSP = 53,
|
||||
kPFix_SPaSP = 59,
|
||||
kPFix_SPthatSP = 63,
|
||||
kPFix_SQUOT = 70,
|
||||
kPFix_SPwithSP = 72,
|
||||
kPFix_SPfromSP = 79,
|
||||
kPFix_SPbySP = 86,
|
||||
kPFix_OPEN = 91,
|
||||
kPFix_DOTSPTheSP = 93,
|
||||
kPFix_SPonSP = 100,
|
||||
kPFix_SPasSP = 105,
|
||||
kPFix_SPisSP = 110,
|
||||
kPFix_ingSP = 115,
|
||||
kPFix_NEWLINETAB = 120,
|
||||
kPFix_COLON = 123,
|
||||
kPFix_edSP = 125,
|
||||
kPFix_EQDQUOT = 129,
|
||||
kPFix_SPatSP = 132,
|
||||
kPFix_lySP = 137,
|
||||
kPFix_COMMA = 141,
|
||||
kPFix_EQSQUOT = 143,
|
||||
kPFix_DOTcomSLASH = 146,
|
||||
kPFix_DOTSPThisSP = 152,
|
||||
kPFix_SPnotSP = 160,
|
||||
kPFix_erSP = 166,
|
||||
kPFix_alSP = 170,
|
||||
kPFix_fulSP = 174,
|
||||
kPFix_iveSP = 179,
|
||||
kPFix_lessSP = 184,
|
||||
kPFix_estSP = 190,
|
||||
kPFix_izeSP = 195,
|
||||
kPFix_NBSP = 200,
|
||||
kPFix_ousSP = 203
|
||||
};
|
||||
|
||||
static const Transform kTransforms[] = {
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_EMPTY },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_SP },
|
||||
{ kPFix_SP, kIdentity, kPFix_SP },
|
||||
{ kPFix_EMPTY, kOmitFirst1, kPFix_EMPTY },
|
||||
{ kPFix_EMPTY, kUppercaseFirst, kPFix_SP },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_SPtheSP },
|
||||
{ kPFix_SP, kIdentity, kPFix_EMPTY },
|
||||
{ kPFix_sSP, kIdentity, kPFix_SP },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_SPofSP },
|
||||
{ kPFix_EMPTY, kUppercaseFirst, kPFix_EMPTY },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_SPandSP },
|
||||
{ kPFix_EMPTY, kOmitFirst2, kPFix_EMPTY },
|
||||
{ kPFix_EMPTY, kOmitLast1, kPFix_EMPTY },
|
||||
{ kPFix_COMMASP, kIdentity, kPFix_SP },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_COMMASP },
|
||||
{ kPFix_SP, kUppercaseFirst, kPFix_SP },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_SPinSP },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_SPtoSP },
|
||||
{ kPFix_eSP, kIdentity, kPFix_SP },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_DQUOT },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_DOT },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_DQUOTGT },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_NEWLINE },
|
||||
{ kPFix_EMPTY, kOmitLast3, kPFix_EMPTY },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_CLOSEBR },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_SPforSP },
|
||||
{ kPFix_EMPTY, kOmitFirst3, kPFix_EMPTY },
|
||||
{ kPFix_EMPTY, kOmitLast2, kPFix_EMPTY },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_SPaSP },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_SPthatSP },
|
||||
{ kPFix_SP, kUppercaseFirst, kPFix_EMPTY },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_DOTSP },
|
||||
{ kPFix_DOT, kIdentity, kPFix_EMPTY },
|
||||
{ kPFix_SP, kIdentity, kPFix_COMMASP },
|
||||
{ kPFix_EMPTY, kOmitFirst4, kPFix_EMPTY },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_SPwithSP },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_SQUOT },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_SPfromSP },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_SPbySP },
|
||||
{ kPFix_EMPTY, kOmitFirst5, kPFix_EMPTY },
|
||||
{ kPFix_EMPTY, kOmitFirst6, kPFix_EMPTY },
|
||||
{ kPFix_SPtheSP, kIdentity, kPFix_EMPTY },
|
||||
{ kPFix_EMPTY, kOmitLast4, kPFix_EMPTY },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_DOTSPTheSP },
|
||||
{ kPFix_EMPTY, kUppercaseAll, kPFix_EMPTY },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_SPonSP },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_SPasSP },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_SPisSP },
|
||||
{ kPFix_EMPTY, kOmitLast7, kPFix_EMPTY },
|
||||
{ kPFix_EMPTY, kOmitLast1, kPFix_ingSP },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_NEWLINETAB },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_COLON },
|
||||
{ kPFix_SP, kIdentity, kPFix_DOTSP },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_edSP },
|
||||
{ kPFix_EMPTY, kOmitFirst9, kPFix_EMPTY },
|
||||
{ kPFix_EMPTY, kOmitFirst7, kPFix_EMPTY },
|
||||
{ kPFix_EMPTY, kOmitLast6, kPFix_EMPTY },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_OPEN },
|
||||
{ kPFix_EMPTY, kUppercaseFirst, kPFix_COMMASP },
|
||||
{ kPFix_EMPTY, kOmitLast8, kPFix_EMPTY },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_SPatSP },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_lySP },
|
||||
{ kPFix_SPtheSP, kIdentity, kPFix_SPofSP },
|
||||
{ kPFix_EMPTY, kOmitLast5, kPFix_EMPTY },
|
||||
{ kPFix_EMPTY, kOmitLast9, kPFix_EMPTY },
|
||||
{ kPFix_SP, kUppercaseFirst, kPFix_COMMASP },
|
||||
{ kPFix_EMPTY, kUppercaseFirst, kPFix_DQUOT },
|
||||
{ kPFix_DOT, kIdentity, kPFix_OPEN },
|
||||
{ kPFix_EMPTY, kUppercaseAll, kPFix_SP },
|
||||
{ kPFix_EMPTY, kUppercaseFirst, kPFix_DQUOTGT },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_EQDQUOT },
|
||||
{ kPFix_SP, kIdentity, kPFix_DOT },
|
||||
{ kPFix_DOTcomSLASH, kIdentity, kPFix_EMPTY },
|
||||
{ kPFix_SPtheSP, kIdentity, kPFix_SPofSPtheSP },
|
||||
{ kPFix_EMPTY, kUppercaseFirst, kPFix_SQUOT },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_DOTSPThisSP },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_COMMA },
|
||||
{ kPFix_DOT, kIdentity, kPFix_SP },
|
||||
{ kPFix_EMPTY, kUppercaseFirst, kPFix_OPEN },
|
||||
{ kPFix_EMPTY, kUppercaseFirst, kPFix_DOT },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_SPnotSP },
|
||||
{ kPFix_SP, kIdentity, kPFix_EQDQUOT },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_erSP },
|
||||
{ kPFix_SP, kUppercaseAll, kPFix_SP },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_alSP },
|
||||
{ kPFix_SP, kUppercaseAll, kPFix_EMPTY },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_EQSQUOT },
|
||||
{ kPFix_EMPTY, kUppercaseAll, kPFix_DQUOT },
|
||||
{ kPFix_EMPTY, kUppercaseFirst, kPFix_DOTSP },
|
||||
{ kPFix_SP, kIdentity, kPFix_OPEN },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_fulSP },
|
||||
{ kPFix_SP, kUppercaseFirst, kPFix_DOTSP },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_iveSP },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_lessSP },
|
||||
{ kPFix_EMPTY, kUppercaseAll, kPFix_SQUOT },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_estSP },
|
||||
{ kPFix_SP, kUppercaseFirst, kPFix_DOT },
|
||||
{ kPFix_EMPTY, kUppercaseAll, kPFix_DQUOTGT },
|
||||
{ kPFix_SP, kIdentity, kPFix_EQSQUOT },
|
||||
{ kPFix_EMPTY, kUppercaseFirst, kPFix_COMMA },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_izeSP },
|
||||
{ kPFix_EMPTY, kUppercaseAll, kPFix_DOT },
|
||||
{ kPFix_NBSP, kIdentity, kPFix_EMPTY },
|
||||
{ kPFix_SP, kIdentity, kPFix_COMMA },
|
||||
{ kPFix_EMPTY, kUppercaseFirst, kPFix_EQDQUOT },
|
||||
{ kPFix_EMPTY, kUppercaseAll, kPFix_EQDQUOT },
|
||||
{ kPFix_EMPTY, kIdentity, kPFix_ousSP },
|
||||
{ kPFix_EMPTY, kUppercaseAll, kPFix_COMMASP },
|
||||
{ kPFix_EMPTY, kUppercaseFirst, kPFix_EQSQUOT },
|
||||
{ kPFix_SP, kUppercaseFirst, kPFix_COMMA },
|
||||
{ kPFix_SP, kUppercaseAll, kPFix_EQDQUOT },
|
||||
{ kPFix_SP, kUppercaseAll, kPFix_COMMASP },
|
||||
{ kPFix_EMPTY, kUppercaseAll, kPFix_COMMA },
|
||||
{ kPFix_EMPTY, kUppercaseAll, kPFix_OPEN },
|
||||
{ kPFix_EMPTY, kUppercaseAll, kPFix_DOTSP },
|
||||
{ kPFix_SP, kUppercaseAll, kPFix_DOT },
|
||||
{ kPFix_EMPTY, kUppercaseAll, kPFix_EQSQUOT },
|
||||
{ kPFix_SP, kUppercaseAll, kPFix_DOTSP },
|
||||
{ kPFix_SP, kUppercaseFirst, kPFix_EQDQUOT },
|
||||
{ kPFix_SP, kUppercaseAll, kPFix_EQSQUOT },
|
||||
{ kPFix_SP, kUppercaseFirst, kPFix_EQSQUOT },
|
||||
};
|
||||
|
||||
static const int kNumTransforms = sizeof(kTransforms) / sizeof(kTransforms[0]);
|
||||
|
||||
static int ToUpperCase(uint8_t* p) {
|
||||
if (p[0] < 0xc0) {
|
||||
if (p[0] >= 'a' && p[0] <= 'z') {
|
||||
p[0] ^= 32;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
/* An overly simplified uppercasing model for UTF-8. */
|
||||
if (p[0] < 0xe0) {
|
||||
p[1] ^= 32;
|
||||
return 2;
|
||||
}
|
||||
/* An arbitrary transform for three byte characters. */
|
||||
p[2] ^= 5;
|
||||
return 3;
|
||||
}
|
||||
|
||||
static BROTLI_NOINLINE int TransformDictionaryWord(
|
||||
uint8_t* dst, const uint8_t* word, int len, int transform) {
|
||||
int idx = 0;
|
||||
{
|
||||
const char* prefix = &kPrefixSuffix[kTransforms[transform].prefix_id];
|
||||
while (*prefix) { dst[idx++] = (uint8_t)*prefix++; }
|
||||
}
|
||||
{
|
||||
const int t = kTransforms[transform].transform;
|
||||
int i = 0;
|
||||
int skip = t - (kOmitFirst1 - 1);
|
||||
if (skip > 0) {
|
||||
word += skip;
|
||||
len -= skip;
|
||||
} else if (t <= kOmitLast9) {
|
||||
len -= t;
|
||||
}
|
||||
while (i < len) { dst[idx++] = word[i++]; }
|
||||
if (t == kUppercaseFirst) {
|
||||
ToUpperCase(&dst[idx - len]);
|
||||
} else if (t == kUppercaseAll) {
|
||||
uint8_t* uppercase = &dst[idx - len];
|
||||
while (len > 0) {
|
||||
int step = ToUpperCase(uppercase);
|
||||
uppercase += step;
|
||||
len -= step;
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
const char* suffix = &kPrefixSuffix[kTransforms[transform].suffix_id];
|
||||
while (*suffix) { dst[idx++] = (uint8_t)*suffix++; }
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* BROTLI_DEC_TRANSFORM_H_ */
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
/* Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
Distributed under MIT license.
|
||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
/* Common types */
|
||||
|
||||
#ifndef BROTLI_DEC_TYPES_H_
|
||||
#define BROTLI_DEC_TYPES_H_
|
||||
|
||||
#include <stddef.h> /* for size_t */
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1600)
|
||||
typedef __int8 int8_t;
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef __int16 int16_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef __int32 int32_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
typedef __int64 int64_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif /* defined(_MSC_VER) && (_MSC_VER < 1600) */
|
||||
|
||||
/* Allocating function pointer. Function MUST return 0 in the case of failure.
|
||||
Otherwise it MUST return a valid pointer to a memory region of at least
|
||||
size length. Neither items nor size are allowed to be 0.
|
||||
opaque argument is a pointer provided by client and could be used to bind
|
||||
function to specific object (memory pool). */
|
||||
typedef void* (*brotli_alloc_func)(void* opaque, size_t size);
|
||||
|
||||
/* Deallocating function pointer. Function SHOULD be no-op in the case the
|
||||
address is 0. */
|
||||
typedef void (*brotli_free_func)(void* opaque, void* address);
|
||||
|
||||
#endif /* BROTLI_DEC_TYPES_H_ */
|
||||
|
|
@ -10,11 +10,11 @@
|
|||
|
||||
#include "../common/constants.h"
|
||||
#include "../common/dictionary.h"
|
||||
#include "../common/platform.h"
|
||||
#include <brotli/types.h>
|
||||
#include "./command.h"
|
||||
#include "./dictionary_hash.h"
|
||||
#include "./memory.h"
|
||||
#include "./port.h"
|
||||
#include "./quality.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
|
|
@ -49,6 +49,7 @@ static BROTLI_INLINE size_t ComputeDistanceCode(size_t distance,
|
|||
#define CAT(a, b) a ## b
|
||||
#define FN(X) EXPAND_CAT(X, HASHER())
|
||||
#define EXPORT_FN(X) EXPAND_CAT(X, EXPAND_CAT(PREFIX(), HASHER()))
|
||||
|
||||
#define PREFIX() N
|
||||
|
||||
#define HASHER() H2
|
||||
|
|
@ -96,29 +97,38 @@ static BROTLI_INLINE size_t ComputeDistanceCode(size_t distance,
|
|||
#include "./backward_references_inc.h"
|
||||
#undef HASHER
|
||||
|
||||
#define HASHER() H35
|
||||
/* NOLINTNEXTLINE(build/include) */
|
||||
#include "./backward_references_inc.h"
|
||||
#undef HASHER
|
||||
|
||||
#define HASHER() H55
|
||||
/* NOLINTNEXTLINE(build/include) */
|
||||
#include "./backward_references_inc.h"
|
||||
#undef HASHER
|
||||
|
||||
#define HASHER() H65
|
||||
/* NOLINTNEXTLINE(build/include) */
|
||||
#include "./backward_references_inc.h"
|
||||
#undef HASHER
|
||||
|
||||
#undef PREFIX
|
||||
|
||||
#undef EXPORT_FN
|
||||
#undef FN
|
||||
#undef CAT
|
||||
#undef EXPAND_CAT
|
||||
|
||||
void BrotliCreateBackwardReferences(const BrotliDictionary* dictionary,
|
||||
size_t num_bytes,
|
||||
size_t position,
|
||||
const uint8_t* ringbuffer,
|
||||
size_t ringbuffer_mask,
|
||||
const BrotliEncoderParams* params,
|
||||
HasherHandle hasher,
|
||||
int* dist_cache,
|
||||
size_t* last_insert_len,
|
||||
Command* commands,
|
||||
size_t* num_commands,
|
||||
size_t* num_literals) {
|
||||
void BrotliCreateBackwardReferences(
|
||||
size_t num_bytes, size_t position, const uint8_t* ringbuffer,
|
||||
size_t ringbuffer_mask, const BrotliEncoderParams* params,
|
||||
HasherHandle hasher, int* dist_cache, size_t* last_insert_len,
|
||||
Command* commands, size_t* num_commands, size_t* num_literals) {
|
||||
switch (params->hasher.type) {
|
||||
#define CASE_(N) \
|
||||
case N: \
|
||||
CreateBackwardReferencesNH ## N(dictionary, \
|
||||
kStaticDictionaryHash, num_bytes, position, ringbuffer, \
|
||||
CreateBackwardReferencesNH ## N( \
|
||||
num_bytes, position, ringbuffer, \
|
||||
ringbuffer_mask, params, hasher, dist_cache, \
|
||||
last_insert_len, commands, num_commands, num_literals); \
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@
|
|||
|
||||
#include "../common/constants.h"
|
||||
#include "../common/dictionary.h"
|
||||
#include "../common/platform.h"
|
||||
#include <brotli/types.h>
|
||||
#include "./command.h"
|
||||
#include "./hash.h"
|
||||
#include "./port.h"
|
||||
#include "./quality.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
|
|
@ -26,11 +26,10 @@ extern "C" {
|
|||
CreateBackwardReferences calls, and must be incremented by the amount written
|
||||
by this call. */
|
||||
BROTLI_INTERNAL void BrotliCreateBackwardReferences(
|
||||
const BrotliDictionary* dictionary, size_t num_bytes, size_t position,
|
||||
const uint8_t* ringbuffer, size_t ringbuffer_mask,
|
||||
const BrotliEncoderParams* params, HasherHandle hasher, int* dist_cache,
|
||||
size_t* last_insert_len, Command* commands, size_t* num_commands,
|
||||
size_t* num_literals);
|
||||
size_t num_bytes, size_t position, const uint8_t* ringbuffer,
|
||||
size_t ringbuffer_mask, const BrotliEncoderParams* params,
|
||||
HasherHandle hasher, int* dist_cache, size_t* last_insert_len,
|
||||
Command* commands, size_t* num_commands, size_t* num_literals);
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} /* extern "C" */
|
||||
|
|
|
|||
|
|
@ -11,13 +11,14 @@
|
|||
#include <string.h> /* memcpy, memset */
|
||||
|
||||
#include "../common/constants.h"
|
||||
#include "../common/platform.h"
|
||||
#include <brotli/types.h>
|
||||
#include "./command.h"
|
||||
#include "./fast_log.h"
|
||||
#include "./find_match_length.h"
|
||||
#include "./literal_cost.h"
|
||||
#include "./memory.h"
|
||||
#include "./port.h"
|
||||
#include "./params.h"
|
||||
#include "./prefix.h"
|
||||
#include "./quality.h"
|
||||
|
||||
|
|
@ -25,6 +26,8 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define BROTLI_MAX_EFFECTIVE_DISTANCE_ALPHABET_SIZE 544
|
||||
|
||||
static const float kInfinity = 1.7e38f; /* ~= 2 ^ 127 */
|
||||
|
||||
static const uint32_t kDistanceCacheIndex[] = {
|
||||
|
|
@ -39,40 +42,41 @@ void BrotliInitZopfliNodes(ZopfliNode* array, size_t length) {
|
|||
size_t i;
|
||||
stub.length = 1;
|
||||
stub.distance = 0;
|
||||
stub.insert_length = 0;
|
||||
stub.dcode_insert_length = 0;
|
||||
stub.u.cost = kInfinity;
|
||||
for (i = 0; i < length; ++i) array[i] = stub;
|
||||
}
|
||||
|
||||
static BROTLI_INLINE uint32_t ZopfliNodeCopyLength(const ZopfliNode* self) {
|
||||
return self->length & 0xffffff;
|
||||
return self->length & 0x1FFFFFF;
|
||||
}
|
||||
|
||||
static BROTLI_INLINE uint32_t ZopfliNodeLengthCode(const ZopfliNode* self) {
|
||||
const uint32_t modifier = self->length >> 24;
|
||||
const uint32_t modifier = self->length >> 25;
|
||||
return ZopfliNodeCopyLength(self) + 9u - modifier;
|
||||
}
|
||||
|
||||
static BROTLI_INLINE uint32_t ZopfliNodeCopyDistance(const ZopfliNode* self) {
|
||||
return self->distance & 0x1ffffff;
|
||||
return self->distance;
|
||||
}
|
||||
|
||||
static BROTLI_INLINE uint32_t ZopfliNodeDistanceCode(const ZopfliNode* self) {
|
||||
const uint32_t short_code = self->distance >> 25;
|
||||
const uint32_t short_code = self->dcode_insert_length >> 27;
|
||||
return short_code == 0 ?
|
||||
ZopfliNodeCopyDistance(self) + BROTLI_NUM_DISTANCE_SHORT_CODES - 1 :
|
||||
short_code - 1;
|
||||
}
|
||||
|
||||
static BROTLI_INLINE uint32_t ZopfliNodeCommandLength(const ZopfliNode* self) {
|
||||
return ZopfliNodeCopyLength(self) + self->insert_length;
|
||||
return ZopfliNodeCopyLength(self) + (self->dcode_insert_length & 0x7FFFFFF);
|
||||
}
|
||||
|
||||
/* Histogram based cost model for zopflification. */
|
||||
typedef struct ZopfliCostModel {
|
||||
/* The insert and copy length symbols. */
|
||||
float cost_cmd_[BROTLI_NUM_COMMAND_SYMBOLS];
|
||||
float cost_dist_[BROTLI_NUM_DISTANCE_SYMBOLS];
|
||||
float* cost_dist_;
|
||||
uint32_t distance_histogram_size;
|
||||
/* Cumulative costs of literals per position in the stream. */
|
||||
float* literal_costs_;
|
||||
float min_cost_cmd_;
|
||||
|
|
@ -80,28 +84,45 @@ typedef struct ZopfliCostModel {
|
|||
} ZopfliCostModel;
|
||||
|
||||
static void InitZopfliCostModel(
|
||||
MemoryManager* m, ZopfliCostModel* self, size_t num_bytes) {
|
||||
MemoryManager* m, ZopfliCostModel* self, const BrotliDistanceParams* dist,
|
||||
size_t num_bytes) {
|
||||
uint32_t distance_histogram_size = dist->alphabet_size;
|
||||
if (distance_histogram_size > BROTLI_MAX_EFFECTIVE_DISTANCE_ALPHABET_SIZE) {
|
||||
distance_histogram_size = BROTLI_MAX_EFFECTIVE_DISTANCE_ALPHABET_SIZE;
|
||||
}
|
||||
self->num_bytes_ = num_bytes;
|
||||
self->literal_costs_ = BROTLI_ALLOC(m, float, num_bytes + 2);
|
||||
self->cost_dist_ = BROTLI_ALLOC(m, float, dist->alphabet_size);
|
||||
self->distance_histogram_size = distance_histogram_size;
|
||||
if (BROTLI_IS_OOM(m)) return;
|
||||
}
|
||||
|
||||
static void CleanupZopfliCostModel(MemoryManager* m, ZopfliCostModel* self) {
|
||||
BROTLI_FREE(m, self->literal_costs_);
|
||||
BROTLI_FREE(m, self->cost_dist_);
|
||||
}
|
||||
|
||||
static void SetCost(const uint32_t* histogram, size_t histogram_size,
|
||||
float* cost) {
|
||||
BROTLI_BOOL literal_histogram, float* cost) {
|
||||
size_t sum = 0;
|
||||
size_t missing_symbol_sum;
|
||||
float log2sum;
|
||||
float missing_symbol_cost;
|
||||
size_t i;
|
||||
for (i = 0; i < histogram_size; i++) {
|
||||
sum += histogram[i];
|
||||
}
|
||||
log2sum = (float)FastLog2(sum);
|
||||
missing_symbol_sum = sum;
|
||||
if (!literal_histogram) {
|
||||
for (i = 0; i < histogram_size; i++) {
|
||||
if (histogram[i] == 0) missing_symbol_sum++;
|
||||
}
|
||||
}
|
||||
missing_symbol_cost = (float)FastLog2(missing_symbol_sum) + 2;
|
||||
for (i = 0; i < histogram_size; i++) {
|
||||
if (histogram[i] == 0) {
|
||||
cost[i] = log2sum + 2;
|
||||
cost[i] = missing_symbol_cost;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -122,7 +143,7 @@ static void ZopfliCostModelSetFromCommands(ZopfliCostModel* self,
|
|||
size_t last_insert_len) {
|
||||
uint32_t histogram_literal[BROTLI_NUM_LITERAL_SYMBOLS];
|
||||
uint32_t histogram_cmd[BROTLI_NUM_COMMAND_SYMBOLS];
|
||||
uint32_t histogram_dist[BROTLI_NUM_DISTANCE_SYMBOLS];
|
||||
uint32_t histogram_dist[BROTLI_MAX_EFFECTIVE_DISTANCE_ALPHABET_SIZE];
|
||||
float cost_literal[BROTLI_NUM_LITERAL_SYMBOLS];
|
||||
size_t pos = position - last_insert_len;
|
||||
float min_cost_cmd = kInfinity;
|
||||
|
|
@ -136,7 +157,7 @@ static void ZopfliCostModelSetFromCommands(ZopfliCostModel* self,
|
|||
for (i = 0; i < num_commands; i++) {
|
||||
size_t inslength = commands[i].insert_len_;
|
||||
size_t copylength = CommandCopyLen(&commands[i]);
|
||||
size_t distcode = commands[i].dist_prefix_;
|
||||
size_t distcode = commands[i].dist_prefix_ & 0x3FF;
|
||||
size_t cmdcode = commands[i].cmd_prefix_;
|
||||
size_t j;
|
||||
|
||||
|
|
@ -150,9 +171,12 @@ static void ZopfliCostModelSetFromCommands(ZopfliCostModel* self,
|
|||
pos += inslength + copylength;
|
||||
}
|
||||
|
||||
SetCost(histogram_literal, BROTLI_NUM_LITERAL_SYMBOLS, cost_literal);
|
||||
SetCost(histogram_cmd, BROTLI_NUM_COMMAND_SYMBOLS, cost_cmd);
|
||||
SetCost(histogram_dist, BROTLI_NUM_DISTANCE_SYMBOLS, self->cost_dist_);
|
||||
SetCost(histogram_literal, BROTLI_NUM_LITERAL_SYMBOLS, BROTLI_TRUE,
|
||||
cost_literal);
|
||||
SetCost(histogram_cmd, BROTLI_NUM_COMMAND_SYMBOLS, BROTLI_FALSE,
|
||||
cost_cmd);
|
||||
SetCost(histogram_dist, self->distance_histogram_size, BROTLI_FALSE,
|
||||
self->cost_dist_);
|
||||
|
||||
for (i = 0; i < BROTLI_NUM_COMMAND_SYMBOLS; ++i) {
|
||||
min_cost_cmd = BROTLI_MIN(float, min_cost_cmd, cost_cmd[i]);
|
||||
|
|
@ -161,11 +185,14 @@ static void ZopfliCostModelSetFromCommands(ZopfliCostModel* self,
|
|||
|
||||
{
|
||||
float* literal_costs = self->literal_costs_;
|
||||
float literal_carry = 0.0;
|
||||
size_t num_bytes = self->num_bytes_;
|
||||
literal_costs[0] = 0.0;
|
||||
for (i = 0; i < num_bytes; ++i) {
|
||||
literal_costs[i + 1] = literal_costs[i] +
|
||||
literal_carry +=
|
||||
cost_literal[ringbuffer[(position + i) & ringbuffer_mask]];
|
||||
literal_costs[i + 1] = literal_costs[i] + literal_carry;
|
||||
literal_carry -= literal_costs[i + 1] - literal_costs[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -175,6 +202,7 @@ static void ZopfliCostModelSetFromLiteralCosts(ZopfliCostModel* self,
|
|||
const uint8_t* ringbuffer,
|
||||
size_t ringbuffer_mask) {
|
||||
float* literal_costs = self->literal_costs_;
|
||||
float literal_carry = 0.0;
|
||||
float* cost_dist = self->cost_dist_;
|
||||
float* cost_cmd = self->cost_cmd_;
|
||||
size_t num_bytes = self->num_bytes_;
|
||||
|
|
@ -183,12 +211,14 @@ static void ZopfliCostModelSetFromLiteralCosts(ZopfliCostModel* self,
|
|||
ringbuffer, &literal_costs[1]);
|
||||
literal_costs[0] = 0.0;
|
||||
for (i = 0; i < num_bytes; ++i) {
|
||||
literal_costs[i + 1] += literal_costs[i];
|
||||
literal_carry += literal_costs[i + 1];
|
||||
literal_costs[i + 1] = literal_costs[i] + literal_carry;
|
||||
literal_carry -= literal_costs[i + 1] - literal_costs[i];
|
||||
}
|
||||
for (i = 0; i < BROTLI_NUM_COMMAND_SYMBOLS; ++i) {
|
||||
cost_cmd[i] = (float)FastLog2(11 + (uint32_t)i);
|
||||
}
|
||||
for (i = 0; i < BROTLI_NUM_DISTANCE_SYMBOLS; ++i) {
|
||||
for (i = 0; i < self->distance_histogram_size; ++i) {
|
||||
cost_dist[i] = (float)FastLog2(20 + (uint32_t)i);
|
||||
}
|
||||
self->min_cost_cmd_ = (float)FastLog2(11);
|
||||
|
|
@ -221,9 +251,10 @@ static BROTLI_INLINE void UpdateZopfliNode(ZopfliNode* nodes, size_t pos,
|
|||
size_t start_pos, size_t len, size_t len_code, size_t dist,
|
||||
size_t short_code, float cost) {
|
||||
ZopfliNode* next = &nodes[pos + len];
|
||||
next->length = (uint32_t)(len | ((len + 9u - len_code) << 24));
|
||||
next->distance = (uint32_t)(dist | (short_code << 25));
|
||||
next->insert_length = (uint32_t)(pos - start_pos);
|
||||
next->length = (uint32_t)(len | ((len + 9u - len_code) << 25));
|
||||
next->distance = (uint32_t)dist;
|
||||
next->dcode_insert_length = (uint32_t)(
|
||||
(short_code << 27) | (pos - start_pos));
|
||||
next->u.cost = cost;
|
||||
}
|
||||
|
||||
|
|
@ -299,21 +330,21 @@ static size_t ComputeMinimumCopyLength(const float start_cost,
|
|||
REQUIRES: nodes[0..pos] satisfies that "ZopfliNode array invariant". */
|
||||
static uint32_t ComputeDistanceShortcut(const size_t block_start,
|
||||
const size_t pos,
|
||||
const size_t max_backward,
|
||||
const size_t max_backward_limit,
|
||||
const size_t gap,
|
||||
const ZopfliNode* nodes) {
|
||||
const size_t clen = ZopfliNodeCopyLength(&nodes[pos]);
|
||||
const size_t ilen = nodes[pos].insert_length;
|
||||
const size_t ilen = nodes[pos].dcode_insert_length & 0x7FFFFFF;
|
||||
const size_t dist = ZopfliNodeCopyDistance(&nodes[pos]);
|
||||
/* Since |block_start + pos| is the end position of the command, the copy part
|
||||
starts from |block_start + pos - clen|. Distances that are greater than
|
||||
this or greater than |max_backward| are static dictionary references, and
|
||||
do not update the last distances. Also distance code 0 (last distance)
|
||||
does not update the last distances. */
|
||||
this or greater than |max_backward_limit| + |gap| are static dictionary
|
||||
references, and do not update the last distances.
|
||||
Also distance code 0 (last distance) does not update the last distances. */
|
||||
if (pos == 0) {
|
||||
return 0;
|
||||
} else if (dist + clen <= block_start + pos + gap &&
|
||||
dist <= max_backward + gap &&
|
||||
dist <= max_backward_limit + gap &&
|
||||
ZopfliNodeDistanceCode(&nodes[pos]) > 0) {
|
||||
return (uint32_t)pos;
|
||||
} else {
|
||||
|
|
@ -335,7 +366,7 @@ static void ComputeDistanceCache(const size_t pos,
|
|||
int idx = 0;
|
||||
size_t p = nodes[pos].u.shortcut;
|
||||
while (idx < 4 && p > 0) {
|
||||
const size_t ilen = nodes[p].insert_length;
|
||||
const size_t ilen = nodes[p].dcode_insert_length & 0x7FFFFFF;
|
||||
const size_t clen = ZopfliNodeCopyLength(&nodes[p]);
|
||||
const size_t dist = ZopfliNodeCopyDistance(&nodes[p]);
|
||||
dist_cache[idx++] = (int)dist;
|
||||
|
|
@ -423,9 +454,11 @@ static size_t UpdateNodes(
|
|||
break;
|
||||
}
|
||||
if (BROTLI_PREDICT_FALSE(backward > max_distance + gap)) {
|
||||
/* Word dictionary -> ignore. */
|
||||
continue;
|
||||
}
|
||||
if (backward <= max_distance) {
|
||||
/* Regular backward reference. */
|
||||
if (prev_ix >= cur_ix) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -482,10 +515,12 @@ static size_t UpdateNodes(
|
|||
uint32_t distnumextra;
|
||||
float dist_cost;
|
||||
size_t max_match_len;
|
||||
PrefixEncodeCopyDistance(dist_code, 0, 0, &dist_symbol, &distextra);
|
||||
distnumextra = distextra >> 24;
|
||||
PrefixEncodeCopyDistance(
|
||||
dist_code, params->dist.num_direct_distance_codes,
|
||||
params->dist.distance_postfix_bits, &dist_symbol, &distextra);
|
||||
distnumextra = dist_symbol >> 10;
|
||||
dist_cost = base_cost + (float)distnumextra +
|
||||
ZopfliCostModelGetDistanceCost(model, dist_symbol);
|
||||
ZopfliCostModelGetDistanceCost(model, dist_symbol & 0x3FF);
|
||||
|
||||
/* Try all copy lengths up until the maximum copy length corresponding
|
||||
to this distance. If the distance refers to the static dictionary, or
|
||||
|
|
@ -517,7 +552,8 @@ static size_t ComputeShortestPathFromNodes(size_t num_bytes,
|
|||
ZopfliNode* nodes) {
|
||||
size_t index = num_bytes;
|
||||
size_t num_commands = 0;
|
||||
while (nodes[index].insert_length == 0 && nodes[index].length == 1) --index;
|
||||
while ((nodes[index].dcode_insert_length & 0x7FFFFFF) == 0 &&
|
||||
nodes[index].length == 1) --index;
|
||||
nodes[index].u.next = BROTLI_UINT32_MAX;
|
||||
while (index != 0) {
|
||||
size_t len = ZopfliNodeCommandLength(&nodes[index]);
|
||||
|
|
@ -530,23 +566,18 @@ static size_t ComputeShortestPathFromNodes(size_t num_bytes,
|
|||
|
||||
/* REQUIRES: nodes != NULL and len(nodes) >= num_bytes + 1 */
|
||||
void BrotliZopfliCreateCommands(const size_t num_bytes,
|
||||
const size_t block_start,
|
||||
const size_t max_backward_limit,
|
||||
const ZopfliNode* nodes,
|
||||
int* dist_cache,
|
||||
size_t* last_insert_len,
|
||||
const BrotliEncoderParams* params,
|
||||
Command* commands,
|
||||
size_t* num_literals) {
|
||||
const size_t block_start, const ZopfliNode* nodes, int* dist_cache,
|
||||
size_t* last_insert_len, const BrotliEncoderParams* params,
|
||||
Command* commands, size_t* num_literals) {
|
||||
const size_t max_backward_limit = BROTLI_MAX_BACKWARD_LIMIT(params->lgwin);
|
||||
size_t pos = 0;
|
||||
uint32_t offset = nodes[0].u.next;
|
||||
size_t i;
|
||||
size_t gap = 0;
|
||||
BROTLI_UNUSED(params);
|
||||
for (i = 0; offset != BROTLI_UINT32_MAX; i++) {
|
||||
const ZopfliNode* next = &nodes[pos + offset];
|
||||
size_t copy_length = ZopfliNodeCopyLength(next);
|
||||
size_t insert_length = next->insert_length;
|
||||
size_t insert_length = next->dcode_insert_length & 0x7FFFFFF;
|
||||
pos += insert_length;
|
||||
offset = next->u.next;
|
||||
if (i == 0) {
|
||||
|
|
@ -560,8 +591,7 @@ void BrotliZopfliCreateCommands(const size_t num_bytes,
|
|||
BROTLI_MIN(size_t, block_start + pos, max_backward_limit);
|
||||
BROTLI_BOOL is_dictionary = TO_BROTLI_BOOL(distance > max_distance + gap);
|
||||
size_t dist_code = ZopfliNodeDistanceCode(next);
|
||||
|
||||
InitCommand(&commands[i], insert_length,
|
||||
InitCommand(&commands[i], ¶ms->dist, insert_length,
|
||||
copy_length, (int)len_code - (int)copy_length, dist_code);
|
||||
|
||||
if (!is_dictionary && dist_code > 0) {
|
||||
|
|
@ -578,18 +608,12 @@ void BrotliZopfliCreateCommands(const size_t num_bytes,
|
|||
*last_insert_len += num_bytes - pos;
|
||||
}
|
||||
|
||||
static size_t ZopfliIterate(size_t num_bytes,
|
||||
size_t position,
|
||||
const uint8_t* ringbuffer,
|
||||
size_t ringbuffer_mask,
|
||||
const BrotliEncoderParams* params,
|
||||
const size_t max_backward_limit,
|
||||
const size_t gap,
|
||||
const int* dist_cache,
|
||||
const ZopfliCostModel* model,
|
||||
const uint32_t* num_matches,
|
||||
const BackwardMatch* matches,
|
||||
ZopfliNode* nodes) {
|
||||
static size_t ZopfliIterate(size_t num_bytes, size_t position,
|
||||
const uint8_t* ringbuffer, size_t ringbuffer_mask,
|
||||
const BrotliEncoderParams* params, const size_t gap, const int* dist_cache,
|
||||
const ZopfliCostModel* model, const uint32_t* num_matches,
|
||||
const BackwardMatch* matches, ZopfliNode* nodes) {
|
||||
const size_t max_backward_limit = BROTLI_MAX_BACKWARD_LIMIT(params->lgwin);
|
||||
const size_t max_zopfli_len = MaxZopfliLen(params);
|
||||
StartPosQueue queue;
|
||||
size_t cur_match_pos = 0;
|
||||
|
|
@ -613,8 +637,8 @@ static size_t ZopfliIterate(size_t num_bytes,
|
|||
while (skip) {
|
||||
i++;
|
||||
if (i + 3 >= num_bytes) break;
|
||||
EvaluateNode(position, i, max_backward_limit, gap, dist_cache, model,
|
||||
&queue, nodes);
|
||||
EvaluateNode(position, i, max_backward_limit, gap,
|
||||
dist_cache, model, &queue, nodes);
|
||||
cur_match_pos += num_matches[i];
|
||||
skip--;
|
||||
}
|
||||
|
|
@ -624,28 +648,23 @@ static size_t ZopfliIterate(size_t num_bytes,
|
|||
}
|
||||
|
||||
/* REQUIRES: nodes != NULL and len(nodes) >= num_bytes + 1 */
|
||||
size_t BrotliZopfliComputeShortestPath(MemoryManager* m,
|
||||
const BrotliDictionary* dictionary,
|
||||
size_t num_bytes,
|
||||
size_t position,
|
||||
const uint8_t* ringbuffer,
|
||||
size_t ringbuffer_mask,
|
||||
const BrotliEncoderParams* params,
|
||||
const size_t max_backward_limit,
|
||||
const int* dist_cache,
|
||||
HasherHandle hasher,
|
||||
ZopfliNode* nodes) {
|
||||
size_t BrotliZopfliComputeShortestPath(MemoryManager* m, size_t num_bytes,
|
||||
size_t position, const uint8_t* ringbuffer, size_t ringbuffer_mask,
|
||||
const BrotliEncoderParams* params,
|
||||
const int* dist_cache, HasherHandle hasher, ZopfliNode* nodes) {
|
||||
const size_t max_backward_limit = BROTLI_MAX_BACKWARD_LIMIT(params->lgwin);
|
||||
const size_t max_zopfli_len = MaxZopfliLen(params);
|
||||
ZopfliCostModel model;
|
||||
StartPosQueue queue;
|
||||
BackwardMatch matches[MAX_NUM_MATCHES_H10];
|
||||
BackwardMatch matches[2 * (MAX_NUM_MATCHES_H10 + 64)];
|
||||
const size_t store_end = num_bytes >= StoreLookaheadH10() ?
|
||||
position + num_bytes - StoreLookaheadH10() + 1 : position;
|
||||
size_t i;
|
||||
size_t gap = 0;
|
||||
size_t lz_matches_offset = 0;
|
||||
nodes[0].length = 0;
|
||||
nodes[0].u.cost = 0;
|
||||
InitZopfliCostModel(m, &model, num_bytes);
|
||||
InitZopfliCostModel(m, &model, ¶ms->dist, num_bytes);
|
||||
if (BROTLI_IS_OOM(m)) return 0;
|
||||
ZopfliCostModelSetFromLiteralCosts(
|
||||
&model, position, ringbuffer, ringbuffer_mask);
|
||||
|
|
@ -653,10 +672,12 @@ size_t BrotliZopfliComputeShortestPath(MemoryManager* m,
|
|||
for (i = 0; i + HashTypeLengthH10() - 1 < num_bytes; i++) {
|
||||
const size_t pos = position + i;
|
||||
const size_t max_distance = BROTLI_MIN(size_t, pos, max_backward_limit);
|
||||
size_t num_matches = FindAllMatchesH10(hasher, dictionary, ringbuffer,
|
||||
ringbuffer_mask, pos, num_bytes - i, max_distance, gap, params,
|
||||
matches);
|
||||
size_t skip;
|
||||
size_t num_matches;
|
||||
num_matches = FindAllMatchesH10(hasher,
|
||||
¶ms->dictionary,
|
||||
ringbuffer, ringbuffer_mask, pos, num_bytes - i, max_distance,
|
||||
gap, params, &matches[lz_matches_offset]);
|
||||
if (num_matches > 0 &&
|
||||
BackwardMatchLength(&matches[num_matches - 1]) > max_zopfli_len) {
|
||||
matches[0] = matches[num_matches - 1];
|
||||
|
|
@ -677,8 +698,8 @@ size_t BrotliZopfliComputeShortestPath(MemoryManager* m,
|
|||
while (skip) {
|
||||
i++;
|
||||
if (i + HashTypeLengthH10() - 1 >= num_bytes) break;
|
||||
EvaluateNode(position, i, max_backward_limit, gap, dist_cache, &model,
|
||||
&queue, nodes);
|
||||
EvaluateNode(position, i, max_backward_limit, gap,
|
||||
dist_cache, &model, &queue, nodes);
|
||||
skip--;
|
||||
}
|
||||
}
|
||||
|
|
@ -687,32 +708,29 @@ size_t BrotliZopfliComputeShortestPath(MemoryManager* m,
|
|||
return ComputeShortestPathFromNodes(num_bytes, nodes);
|
||||
}
|
||||
|
||||
void BrotliCreateZopfliBackwardReferences(
|
||||
MemoryManager* m, const BrotliDictionary* dictionary, size_t num_bytes,
|
||||
void BrotliCreateZopfliBackwardReferences(MemoryManager* m, size_t num_bytes,
|
||||
size_t position, const uint8_t* ringbuffer, size_t ringbuffer_mask,
|
||||
const BrotliEncoderParams* params, HasherHandle hasher, int* dist_cache,
|
||||
size_t* last_insert_len, Command* commands, size_t* num_commands,
|
||||
size_t* num_literals) {
|
||||
const size_t max_backward_limit = BROTLI_MAX_BACKWARD_LIMIT(params->lgwin);
|
||||
const BrotliEncoderParams* params,
|
||||
HasherHandle hasher, int* dist_cache, size_t* last_insert_len,
|
||||
Command* commands, size_t* num_commands, size_t* num_literals) {
|
||||
ZopfliNode* nodes;
|
||||
nodes = BROTLI_ALLOC(m, ZopfliNode, num_bytes + 1);
|
||||
if (BROTLI_IS_OOM(m)) return;
|
||||
BrotliInitZopfliNodes(nodes, num_bytes + 1);
|
||||
*num_commands += BrotliZopfliComputeShortestPath(m, dictionary, num_bytes,
|
||||
position, ringbuffer, ringbuffer_mask, params, max_backward_limit,
|
||||
*num_commands += BrotliZopfliComputeShortestPath(m, num_bytes,
|
||||
position, ringbuffer, ringbuffer_mask, params,
|
||||
dist_cache, hasher, nodes);
|
||||
if (BROTLI_IS_OOM(m)) return;
|
||||
BrotliZopfliCreateCommands(num_bytes, position, max_backward_limit, nodes,
|
||||
dist_cache, last_insert_len, params, commands, num_literals);
|
||||
BrotliZopfliCreateCommands(num_bytes, position, nodes, dist_cache,
|
||||
last_insert_len, params, commands, num_literals);
|
||||
BROTLI_FREE(m, nodes);
|
||||
}
|
||||
|
||||
void BrotliCreateHqZopfliBackwardReferences(
|
||||
MemoryManager* m, const BrotliDictionary* dictionary, size_t num_bytes,
|
||||
void BrotliCreateHqZopfliBackwardReferences(MemoryManager* m, size_t num_bytes,
|
||||
size_t position, const uint8_t* ringbuffer, size_t ringbuffer_mask,
|
||||
const BrotliEncoderParams* params, HasherHandle hasher, int* dist_cache,
|
||||
size_t* last_insert_len, Command* commands, size_t* num_commands,
|
||||
size_t* num_literals) {
|
||||
const BrotliEncoderParams* params,
|
||||
HasherHandle hasher, int* dist_cache, size_t* last_insert_len,
|
||||
Command* commands, size_t* num_commands, size_t* num_literals) {
|
||||
const size_t max_backward_limit = BROTLI_MAX_BACKWARD_LIMIT(params->lgwin);
|
||||
uint32_t* num_matches = BROTLI_ALLOC(m, uint32_t, num_bytes);
|
||||
size_t matches_size = 4 * num_bytes;
|
||||
|
|
@ -728,6 +746,7 @@ void BrotliCreateHqZopfliBackwardReferences(
|
|||
ZopfliNode* nodes;
|
||||
BackwardMatch* matches = BROTLI_ALLOC(m, BackwardMatch, matches_size);
|
||||
size_t gap = 0;
|
||||
size_t shadow_matches = 0;
|
||||
if (BROTLI_IS_OOM(m)) return;
|
||||
for (i = 0; i + HashTypeLengthH10() - 1 < num_bytes; ++i) {
|
||||
const size_t pos = position + i;
|
||||
|
|
@ -738,14 +757,16 @@ void BrotliCreateHqZopfliBackwardReferences(
|
|||
size_t j;
|
||||
/* Ensure that we have enough free slots. */
|
||||
BROTLI_ENSURE_CAPACITY(m, BackwardMatch, matches, matches_size,
|
||||
cur_match_pos + MAX_NUM_MATCHES_H10);
|
||||
cur_match_pos + MAX_NUM_MATCHES_H10 + shadow_matches);
|
||||
if (BROTLI_IS_OOM(m)) return;
|
||||
num_found_matches = FindAllMatchesH10(hasher, dictionary, ringbuffer,
|
||||
ringbuffer_mask, pos, max_length, max_distance, gap, params,
|
||||
&matches[cur_match_pos]);
|
||||
num_found_matches = FindAllMatchesH10(hasher,
|
||||
¶ms->dictionary,
|
||||
ringbuffer, ringbuffer_mask, pos, max_length,
|
||||
max_distance, gap, params,
|
||||
&matches[cur_match_pos + shadow_matches]);
|
||||
cur_match_end = cur_match_pos + num_found_matches;
|
||||
for (j = cur_match_pos; j + 1 < cur_match_end; ++j) {
|
||||
assert(BackwardMatchLength(&matches[j]) <=
|
||||
BROTLI_DCHECK(BackwardMatchLength(&matches[j]) <=
|
||||
BackwardMatchLength(&matches[j + 1]));
|
||||
}
|
||||
num_matches[i] = (uint32_t)num_found_matches;
|
||||
|
|
@ -771,7 +792,7 @@ void BrotliCreateHqZopfliBackwardReferences(
|
|||
orig_num_commands = *num_commands;
|
||||
nodes = BROTLI_ALLOC(m, ZopfliNode, num_bytes + 1);
|
||||
if (BROTLI_IS_OOM(m)) return;
|
||||
InitZopfliCostModel(m, &model, num_bytes);
|
||||
InitZopfliCostModel(m, &model, ¶ms->dist, num_bytes);
|
||||
if (BROTLI_IS_OOM(m)) return;
|
||||
for (i = 0; i < 2; i++) {
|
||||
BrotliInitZopfliNodes(nodes, num_bytes + 1);
|
||||
|
|
@ -788,10 +809,10 @@ void BrotliCreateHqZopfliBackwardReferences(
|
|||
*last_insert_len = orig_last_insert_len;
|
||||
memcpy(dist_cache, orig_dist_cache, 4 * sizeof(dist_cache[0]));
|
||||
*num_commands += ZopfliIterate(num_bytes, position, ringbuffer,
|
||||
ringbuffer_mask, params, max_backward_limit, gap, dist_cache,
|
||||
&model, num_matches, matches, nodes);
|
||||
BrotliZopfliCreateCommands(num_bytes, position, max_backward_limit,
|
||||
nodes, dist_cache, last_insert_len, params, commands, num_literals);
|
||||
ringbuffer_mask, params, gap, dist_cache, &model, num_matches, matches,
|
||||
nodes);
|
||||
BrotliZopfliCreateCommands(num_bytes, position, nodes, dist_cache,
|
||||
last_insert_len, params, commands, num_literals);
|
||||
}
|
||||
CleanupZopfliCostModel(m, &model);
|
||||
BROTLI_FREE(m, nodes);
|
||||
|
|
|
|||
|
|
@ -11,41 +11,38 @@
|
|||
|
||||
#include "../common/constants.h"
|
||||
#include "../common/dictionary.h"
|
||||
#include "../common/platform.h"
|
||||
#include <brotli/types.h>
|
||||
#include "./command.h"
|
||||
#include "./hash.h"
|
||||
#include "./memory.h"
|
||||
#include "./port.h"
|
||||
#include "./quality.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
BROTLI_INTERNAL void BrotliCreateZopfliBackwardReferences(
|
||||
MemoryManager* m, const BrotliDictionary* dictionary, size_t num_bytes,
|
||||
size_t position, const uint8_t* ringbuffer, size_t ringbuffer_mask,
|
||||
const BrotliEncoderParams* params, HasherHandle hasher, int* dist_cache,
|
||||
size_t* last_insert_len, Command* commands, size_t* num_commands,
|
||||
size_t* num_literals);
|
||||
BROTLI_INTERNAL void BrotliCreateZopfliBackwardReferences(MemoryManager* m,
|
||||
size_t num_bytes, size_t position, const uint8_t* ringbuffer,
|
||||
size_t ringbuffer_mask, const BrotliEncoderParams* params,
|
||||
HasherHandle hasher, int* dist_cache, size_t* last_insert_len,
|
||||
Command* commands, size_t* num_commands, size_t* num_literals);
|
||||
|
||||
BROTLI_INTERNAL void BrotliCreateHqZopfliBackwardReferences(
|
||||
MemoryManager* m, const BrotliDictionary* dictionary, size_t num_bytes,
|
||||
size_t position, const uint8_t* ringbuffer, size_t ringbuffer_mask,
|
||||
const BrotliEncoderParams* params, HasherHandle hasher, int* dist_cache,
|
||||
size_t* last_insert_len, Command* commands, size_t* num_commands,
|
||||
size_t* num_literals);
|
||||
BROTLI_INTERNAL void BrotliCreateHqZopfliBackwardReferences(MemoryManager* m,
|
||||
size_t num_bytes, size_t position, const uint8_t* ringbuffer,
|
||||
size_t ringbuffer_mask, const BrotliEncoderParams* params,
|
||||
HasherHandle hasher, int* dist_cache, size_t* last_insert_len,
|
||||
Command* commands, size_t* num_commands, size_t* num_literals);
|
||||
|
||||
typedef struct ZopfliNode {
|
||||
/* best length to get up to this byte (not including this byte itself)
|
||||
highest 8 bit is used to reconstruct the length code */
|
||||
/* Best length to get up to this byte (not including this byte itself)
|
||||
highest 7 bit is used to reconstruct the length code. */
|
||||
uint32_t length;
|
||||
/* distance associated with the length
|
||||
highest 7 bit contains distance short code + 1 (or zero if no short code)
|
||||
*/
|
||||
/* Distance associated with the length. */
|
||||
uint32_t distance;
|
||||
/* number of literal inserts before this copy */
|
||||
uint32_t insert_length;
|
||||
/* Number of literal inserts before this copy; highest 5 bits contain
|
||||
distance short code + 1 (or zero if no short code). */
|
||||
uint32_t dcode_insert_length;
|
||||
|
||||
/* This union holds information used by dynamic-programming. During forward
|
||||
pass |cost| it used to store the goal function. When node is processed its
|
||||
|
|
@ -78,14 +75,13 @@ BROTLI_INTERNAL void BrotliInitZopfliNodes(ZopfliNode* array, size_t length);
|
|||
(2) nodes[i].command_length() <= i and
|
||||
(3) nodes[i - nodes[i].command_length()].cost < kInfinity */
|
||||
BROTLI_INTERNAL size_t BrotliZopfliComputeShortestPath(
|
||||
MemoryManager* m, const BrotliDictionary* dictionary, size_t num_bytes,
|
||||
MemoryManager* m, size_t num_bytes,
|
||||
size_t position, const uint8_t* ringbuffer, size_t ringbuffer_mask,
|
||||
const BrotliEncoderParams* params, const size_t max_backward_limit,
|
||||
const BrotliEncoderParams* params,
|
||||
const int* dist_cache, HasherHandle hasher, ZopfliNode* nodes);
|
||||
|
||||
BROTLI_INTERNAL void BrotliZopfliCreateCommands(
|
||||
const size_t num_bytes, const size_t block_start,
|
||||
const size_t max_backward_limit, const ZopfliNode* nodes,
|
||||
const size_t num_bytes, const size_t block_start, const ZopfliNode* nodes,
|
||||
int* dist_cache, size_t* last_insert_len, const BrotliEncoderParams* params,
|
||||
Command* commands, size_t* num_literals);
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue