Compare commits
40
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6b9d9cbea | ||
|
|
b863bd2edc | ||
|
|
a8a397fbce | ||
|
|
a89597b5db | ||
|
|
074d194776 | ||
|
|
76d62cfc38 | ||
|
|
eefaee9064 | ||
|
|
1864bbec2a | ||
|
|
5771487106 | ||
|
|
887fface56 | ||
|
|
4782e8dfa2 | ||
|
|
bd061ac043 | ||
|
|
31a64578e2 | ||
|
|
85dc118aac | ||
|
|
2f3cf3c8cf | ||
|
|
241f06bc53 | ||
|
|
ecb827c5c4 | ||
|
|
fa5aba4c0f | ||
|
|
b6d909ad17 | ||
|
|
6bc5748b00 | ||
|
|
3abb8e28ad | ||
|
|
18df01eb54 | ||
|
|
8aa9355535 | ||
|
|
511ac54cab | ||
|
|
6d2e301d90 | ||
|
|
2d8013526a | ||
|
|
d86f49ba59 | ||
|
|
870fd86e13 | ||
|
|
13f505539c | ||
|
|
5262e01a4e | ||
|
|
4aeaea9195 | ||
|
|
5ef801fdc5 | ||
|
|
0ba1d49ae5 | ||
|
|
e8a6ee355f | ||
|
|
4694c60964 | ||
|
|
74ebcea409 | ||
|
|
c732425e1a | ||
|
|
51468e998c | ||
|
|
77746f1d90 | ||
|
|
cb0208163f |
No files matched your search
@@ -161,6 +161,8 @@ def old_configure_options(*options):
|
||||
'--enable-b2g-camera',
|
||||
'--enable-b2g-ril',
|
||||
'--enable-bundled-fonts',
|
||||
'--enable-chrome-compression',
|
||||
'--enable-chrome-format',
|
||||
'--enable-clang-plugin',
|
||||
'--enable-content-sandbox',
|
||||
'--enable-cookies',
|
||||
@@ -262,6 +264,7 @@ def old_configure_options(*options):
|
||||
'--with-app-name',
|
||||
'--with-arch',
|
||||
'--with-branding',
|
||||
'--with-chrome-name',
|
||||
'--with-crashreporter-enable-percent',
|
||||
'--with-cross-lib',
|
||||
'--with-debug-label',
|
||||
|
||||
@@ -86,6 +86,7 @@ included_inclnames_to_ignore = set([
|
||||
'unicode/udat.h', # ICU
|
||||
'unicode/udatpg.h', # ICU
|
||||
'unicode/uenum.h', # ICU
|
||||
'unicode/uniset.h', # ICU
|
||||
'unicode/unorm.h', # ICU
|
||||
'unicode/unum.h', # ICU
|
||||
'unicode/unumsys.h', # ICU
|
||||
|
||||
+1
-1
@@ -216,7 +216,7 @@ endif # NO_PROFILE_GUIDED_OPTIMIZE
|
||||
|
||||
MAKE_JARS_FLAGS = \
|
||||
-t $(topsrcdir) \
|
||||
-f $(MOZ_JAR_MAKER_FILE_FORMAT) \
|
||||
-f $(MOZ_CHROME_FILE_FORMAT) \
|
||||
$(NULL)
|
||||
|
||||
ifdef USE_EXTENSION_MANIFEST
|
||||
|
||||
@@ -526,7 +526,6 @@ hardware/lights.h
|
||||
hardware/power.h
|
||||
hardware_legacy/power.h
|
||||
hardware_legacy/uevent.h
|
||||
hardware_legacy/vibrator.h
|
||||
#endif
|
||||
HIToolbox/HIToolbox.h
|
||||
hlink.h
|
||||
|
||||
@@ -22,7 +22,7 @@ REF_MANIFEST = $(if $(USE_EXTENSION_MANIFEST),chrome.manifest,test.manifest)
|
||||
|
||||
check-%::
|
||||
if test -d $(FINAL_TARGET); then rm -rf $(FINAL_TARGET); fi;
|
||||
$(MAKE) realchrome MOZ_JAR_MAKER_FILE_FORMAT=$*
|
||||
$(MAKE) realchrome MOZ_CHROME_FILE_FORMAT=$*
|
||||
@echo 'Comparing manifests...'
|
||||
@if ! sort $(MY_MANIFEST) | diff --text -U 0 $(srcdir)/../$(REF_MANIFEST).$* - ; then \
|
||||
echo 'TEST-UNEXPECTED-FAIL | config/tests/$(REF_MANIFEST).$* | differing content in manifest!' ; \
|
||||
|
||||
@@ -94,7 +94,7 @@ exports.menuitems = [
|
||||
},
|
||||
key: {
|
||||
id: "browserToolbox",
|
||||
modifiers: "accel,alt,shift",
|
||||
modifiers: isMac ? "accel,alt" : "accel,shift",
|
||||
keytext: true
|
||||
}
|
||||
},
|
||||
|
||||
+1
-192
@@ -97,11 +97,6 @@
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
static bool sVibratorEnabled = false;
|
||||
static uint32_t sMaxVibrateMS = 0;
|
||||
static uint32_t sMaxVibrateListLen = 0;
|
||||
static const char* kVibrationPermissionType = "vibration";
|
||||
|
||||
static void
|
||||
AddPermission(nsIPrincipal* aPrincipal, const char* aType, uint32_t aPermission,
|
||||
uint32_t aExpireType, int64_t aExpireTime)
|
||||
@@ -152,12 +147,7 @@ GetPermission(nsIPrincipal* aPrincipal, const char* aType)
|
||||
void
|
||||
Navigator::Init()
|
||||
{
|
||||
Preferences::AddBoolVarCache(&sVibratorEnabled,
|
||||
"dom.vibrator.enabled", true);
|
||||
Preferences::AddUintVarCache(&sMaxVibrateMS,
|
||||
"dom.vibrator.max_vibrate_ms", 10000);
|
||||
Preferences::AddUintVarCache(&sMaxVibrateListLen,
|
||||
"dom.vibrator.max_vibrate_list_len", 128);
|
||||
// Add any Preferences::Add*VarCache(&sPref, "pref", default) here if needed.
|
||||
}
|
||||
|
||||
Navigator::Navigator(nsPIDOMWindowInner* aWindow)
|
||||
@@ -705,82 +695,6 @@ Navigator::RefreshMIMEArray()
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class VibrateWindowListener : public nsIDOMEventListener
|
||||
{
|
||||
public:
|
||||
VibrateWindowListener(nsPIDOMWindowInner* aWindow, nsIDocument* aDocument)
|
||||
{
|
||||
mWindow = do_GetWeakReference(aWindow);
|
||||
mDocument = do_GetWeakReference(aDocument);
|
||||
|
||||
NS_NAMED_LITERAL_STRING(visibilitychange, "visibilitychange");
|
||||
aDocument->AddSystemEventListener(visibilitychange,
|
||||
this, /* listener */
|
||||
true, /* use capture */
|
||||
false /* wants untrusted */);
|
||||
}
|
||||
|
||||
void RemoveListener();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIDOMEVENTLISTENER
|
||||
|
||||
private:
|
||||
virtual ~VibrateWindowListener()
|
||||
{
|
||||
}
|
||||
|
||||
nsWeakPtr mWindow;
|
||||
nsWeakPtr mDocument;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(VibrateWindowListener, nsIDOMEventListener)
|
||||
|
||||
StaticRefPtr<VibrateWindowListener> gVibrateWindowListener;
|
||||
|
||||
static bool
|
||||
MayVibrate(nsIDocument* doc) {
|
||||
// Hidden documents cannot start or stop a vibration.
|
||||
return (doc && !doc->Hidden());
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
VibrateWindowListener::HandleEvent(nsIDOMEvent* aEvent)
|
||||
{
|
||||
nsCOMPtr<nsIDocument> doc =
|
||||
do_QueryInterface(aEvent->InternalDOMEvent()->GetTarget());
|
||||
|
||||
if (!MayVibrate(doc)) {
|
||||
// It's important that we call CancelVibrate(), not Vibrate() with an
|
||||
// empty list, because Vibrate() will fail if we're no longer focused, but
|
||||
// CancelVibrate() will succeed, so long as nobody else has started a new
|
||||
// vibration pattern.
|
||||
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mWindow);
|
||||
hal::CancelVibrate(window);
|
||||
RemoveListener();
|
||||
gVibrateWindowListener = nullptr;
|
||||
// Careful: The line above might have deleted |this|!
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
VibrateWindowListener::RemoveListener()
|
||||
{
|
||||
nsCOMPtr<EventTarget> target = do_QueryReferent(mDocument);
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
NS_NAMED_LITERAL_STRING(visibilitychange, "visibilitychange");
|
||||
target->RemoveSystemEventListener(visibilitychange, this,
|
||||
true /* use capture */);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void
|
||||
Navigator::AddIdleObserver(MozIdleObserver& aIdleObserver, ErrorResult& aRv)
|
||||
{
|
||||
@@ -809,111 +723,6 @@ Navigator::RemoveIdleObserver(MozIdleObserver& aIdleObserver, ErrorResult& aRv)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Navigator::SetVibrationPermission(bool aPermitted, bool aPersistent)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsTArray<uint32_t> pattern;
|
||||
pattern.SwapElements(mRequestedVibrationPattern);
|
||||
|
||||
if (!mWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = mWindow->GetExtantDoc();
|
||||
|
||||
if (!MayVibrate(doc)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (aPermitted) {
|
||||
// Add a listener to cancel the vibration if the document becomes hidden,
|
||||
// and remove the old visibility listener, if there was one.
|
||||
if (!gVibrateWindowListener) {
|
||||
// If gVibrateWindowListener is null, this is the first time we've vibrated,
|
||||
// and we need to register a listener to clear gVibrateWindowListener on
|
||||
// shutdown.
|
||||
ClearOnShutdown(&gVibrateWindowListener);
|
||||
} else {
|
||||
gVibrateWindowListener->RemoveListener();
|
||||
}
|
||||
gVibrateWindowListener = new VibrateWindowListener(mWindow, doc);
|
||||
hal::Vibrate(pattern, mWindow);
|
||||
}
|
||||
|
||||
if (aPersistent) {
|
||||
AddPermission(doc->NodePrincipal(), kVibrationPermissionType,
|
||||
aPermitted ? nsIPermissionManager::ALLOW_ACTION :
|
||||
nsIPermissionManager::DENY_ACTION,
|
||||
nsIPermissionManager::EXPIRE_SESSION, 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Navigator::Vibrate(uint32_t aDuration)
|
||||
{
|
||||
AutoTArray<uint32_t, 1> pattern;
|
||||
pattern.AppendElement(aDuration);
|
||||
return Vibrate(pattern);
|
||||
}
|
||||
|
||||
bool
|
||||
Navigator::Vibrate(const nsTArray<uint32_t>& aPattern)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!mWindow) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = mWindow->GetExtantDoc();
|
||||
|
||||
if (!MayVibrate(doc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsTArray<uint32_t> pattern(aPattern);
|
||||
|
||||
if (pattern.Length() > sMaxVibrateListLen) {
|
||||
pattern.SetLength(sMaxVibrateListLen);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < pattern.Length(); ++i) {
|
||||
pattern[i] = std::min(sMaxVibrateMS, pattern[i]);
|
||||
}
|
||||
|
||||
// The spec says we check sVibratorEnabled after we've done the sanity
|
||||
// checking on the pattern.
|
||||
if (!sVibratorEnabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
mRequestedVibrationPattern.SwapElements(pattern);
|
||||
uint32_t permission = GetPermission(mWindow, kVibrationPermissionType);
|
||||
|
||||
if (permission == nsIPermissionManager::ALLOW_ACTION ||
|
||||
mRequestedVibrationPattern.IsEmpty() ||
|
||||
(mRequestedVibrationPattern.Length() == 1 &&
|
||||
mRequestedVibrationPattern[0] == 0)) {
|
||||
// Always allow cancelling vibration and respect session permissions.
|
||||
SetVibrationPermission(true /* permitted */, false /* persistent */);
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
if (!obs || permission == nsIPermissionManager::DENY_ACTION) {
|
||||
// Abort without observer service or on denied session permission.
|
||||
SetVibrationPermission(false /* permitted */, false /* persistent */);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Request user permission.
|
||||
obs->NotifyObservers(ToSupports(this), "Vibration:Request", nullptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
// Pointer Events interface
|
||||
//*****************************************************************************
|
||||
|
||||
@@ -149,9 +149,6 @@ public:
|
||||
// NavigatorBinding::ClearCachedUserAgentValue(this);
|
||||
void ClearUserAgentCache();
|
||||
|
||||
bool Vibrate(uint32_t aDuration);
|
||||
bool Vibrate(const nsTArray<uint32_t>& aDuration);
|
||||
void SetVibrationPermission(bool aPermitted, bool aPersistent);
|
||||
uint32_t MaxTouchPoints();
|
||||
void GetAppCodeName(nsString& aAppCodeName, ErrorResult& aRv)
|
||||
{
|
||||
|
||||
@@ -1384,6 +1384,8 @@ nsTreeSanitizer::SanitizeChildren(nsINode* aRoot)
|
||||
nsAutoString styleText;
|
||||
nsContentUtils::GetNodeTextContent(node, false, styleText);
|
||||
|
||||
RemoveAllAttributesFromDescendants(elt);
|
||||
|
||||
nsAutoString sanitizedStyle;
|
||||
nsCOMPtr<nsIURI> baseURI = node->GetBaseURI();
|
||||
if (SanitizeStyleSheet(styleText,
|
||||
@@ -1479,6 +1481,17 @@ nsTreeSanitizer::RemoveAllAttributes(nsIContent* aElement)
|
||||
}
|
||||
}
|
||||
|
||||
void nsTreeSanitizer::RemoveAllAttributesFromDescendants(mozilla::dom::Element* aElement) {
|
||||
nsIContent* node = aElement->GetFirstChild();
|
||||
while (node) {
|
||||
if (node->IsElement()) {
|
||||
mozilla::dom::Element* elt = node->AsElement();
|
||||
RemoveAllAttributes(elt);
|
||||
}
|
||||
node = node->GetNextNode(aElement);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsTreeSanitizer::InitializeStatics()
|
||||
{
|
||||
|
||||
@@ -183,6 +183,12 @@ class MOZ_STACK_CLASS nsTreeSanitizer {
|
||||
*/
|
||||
void RemoveAllAttributes(nsIContent* aElement);
|
||||
|
||||
/**
|
||||
* Removes all attributes from the descendants of an element but not from
|
||||
* the element itself.
|
||||
*/
|
||||
void RemoveAllAttributesFromDescendants(mozilla::dom::Element* aElement);
|
||||
|
||||
/**
|
||||
* The whitelist of HTML elements.
|
||||
*/
|
||||
|
||||
+37
-30
@@ -599,38 +599,31 @@ ScriptLoader::CreateModuleScript(ModuleLoadRequest* aRequest)
|
||||
return rv;
|
||||
}
|
||||
|
||||
static bool
|
||||
CreateTypeError(JSContext* aCx, ModuleScript* aScript, const nsString& aMessage,
|
||||
JS::MutableHandle<JS::Value> aError)
|
||||
{
|
||||
JS::Rooted<JSObject*> module(aCx, aScript->ModuleRecord());
|
||||
JS::Rooted<JSScript*> script(aCx, JS::GetModuleScript(aCx, module));
|
||||
JS::Rooted<JSString*> filename(aCx);
|
||||
filename = JS_NewStringCopyZ(aCx, JS_GetScriptFilename(script));
|
||||
if (!filename) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::Rooted<JSString*> message(aCx, JS_NewUCStringCopyZ(aCx, aMessage.get()));
|
||||
if (!message) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return JS::CreateError(aCx, JSEXN_TYPEERR, nullptr, filename, 0, 0, nullptr,
|
||||
message, aError);
|
||||
}
|
||||
|
||||
static nsresult
|
||||
HandleResolveFailure(JSContext* aCx, ModuleScript* aScript,
|
||||
const nsAString& aSpecifier)
|
||||
const nsAString& aSpecifier,
|
||||
uint32_t aLineNumber, uint32_t aColumnNumber)
|
||||
{
|
||||
// TODO: How can we get the line number of the failed import?
|
||||
nsAutoCString url;
|
||||
aScript->BaseURL()->GetAsciiSpec(url);
|
||||
|
||||
JS::Rooted<JSString*> filename(aCx);
|
||||
filename = JS_NewStringCopyZ(aCx, url.get());
|
||||
if (!filename) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
nsAutoString message(NS_LITERAL_STRING("Error resolving module specifier: "));
|
||||
message.Append(aSpecifier);
|
||||
|
||||
JS::Rooted<JSString*> string(aCx, JS_NewUCStringCopyZ(aCx, message.get()));
|
||||
if (!string) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> error(aCx);
|
||||
if (!CreateTypeError(aCx, aScript, message, &error)) {
|
||||
if (!JS::CreateError(aCx, JSEXN_TYPEERR, nullptr, filename, aLineNumber,
|
||||
aColumnNumber, nullptr, string, &error)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
@@ -685,28 +678,37 @@ ResolveRequestedModules(ModuleLoadRequest* aRequest, nsCOMArray<nsIURI>* aUrlsOu
|
||||
|
||||
JSContext* cx = jsapi.cx();
|
||||
JS::Rooted<JSObject*> moduleRecord(cx, ms->ModuleRecord());
|
||||
JS::Rooted<JSObject*> specifiers(cx, JS::GetRequestedModules(cx, moduleRecord));
|
||||
JS::Rooted<JSObject*> requestedModules(cx);
|
||||
requestedModules = JS::GetRequestedModules(cx, moduleRecord);
|
||||
MOZ_ASSERT(requestedModules);
|
||||
|
||||
uint32_t length;
|
||||
if (!JS_GetArrayLength(cx, specifiers, &length)) {
|
||||
if (!JS_GetArrayLength(cx, requestedModules, &length)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> val(cx);
|
||||
JS::Rooted<JS::Value> element(cx);
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
if (!JS_GetElement(cx, specifiers, i, &val)) {
|
||||
if (!JS_GetElement(cx, requestedModules, i, &element)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
JS::Rooted<JSString*> str(cx, JS::GetRequestedModuleSpecifier(cx, element));
|
||||
MOZ_ASSERT(str);
|
||||
|
||||
nsAutoJSString specifier;
|
||||
if (!specifier.init(cx, val)) {
|
||||
if (!specifier.init(cx, str)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Let url be the result of resolving a module specifier given module script and requested.
|
||||
nsCOMPtr<nsIURI> uri = ResolveModuleSpecifier(ms, specifier);
|
||||
if (!uri) {
|
||||
nsresult rv = HandleResolveFailure(cx, ms, specifier);
|
||||
uint32_t lineNumber = 0;
|
||||
uint32_t columnNumber = 0;
|
||||
JS::GetRequestedModuleSourcePos(cx, element, &lineNumber, &columnNumber);
|
||||
|
||||
nsresult rv = HandleResolveFailure(cx, ms, specifier, lineNumber, columnNumber);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@@ -806,6 +808,11 @@ HostResolveImportedModule(JSContext* aCx, JS::Handle<JSObject*> aModule,
|
||||
auto script = static_cast<ModuleScript*>(value.toPrivate());
|
||||
MOZ_ASSERT(script->ModuleRecord() == aModule);
|
||||
|
||||
// FIXME: in-line module scripts apparently pass a null specifier?
|
||||
// At least make sure that doesn't crash the browser.
|
||||
if (!aSpecifier)
|
||||
return nullptr;
|
||||
|
||||
// Let url be the result of resolving a module specifier given referencing
|
||||
// module script and specifier.
|
||||
nsAutoJSString string;
|
||||
|
||||
@@ -122,7 +122,6 @@ run-if = e10s
|
||||
[test_storagePermissionsReject.html]
|
||||
[test_storagePermissionsRejectForeign.html]
|
||||
[test_stylesheetPI.html]
|
||||
[test_vibrator.html]
|
||||
[test_WebKitCSSMatrix.html]
|
||||
[test_windowedhistoryframes.html]
|
||||
[test_windowProperties.html]
|
||||
@@ -1,93 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for Vibrator</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Although we can't test that the vibrator works properly, we can test that
|
||||
navigator.vibrate throws an exception where appropriate. -->
|
||||
|
||||
<script class="testbody" type="text/javascript;version=1.7">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
var result;
|
||||
function expectFailure(param) {
|
||||
result = navigator.vibrate(param);
|
||||
is(result, false, 'vibrate(' + param + ') should have failed.');
|
||||
}
|
||||
|
||||
function expectSuccess(param) {
|
||||
result = navigator.vibrate(param);
|
||||
is(result, true, 'vibrate(' + param + ') must succeed.');
|
||||
}
|
||||
|
||||
function tests(aEnabled) {
|
||||
// Some edge cases that the bindings should handle for us.
|
||||
expectSuccess(null);
|
||||
expectSuccess(undefined);
|
||||
// -1 will be converted to the highest unsigned long then clamped.
|
||||
expectSuccess(-1);
|
||||
expectSuccess('a');
|
||||
// -1 will be converted to the highest unsigned long then clamped.
|
||||
expectSuccess([100, -1]);
|
||||
expectSuccess([100, 'a']);
|
||||
|
||||
var maxVibrateMs = SpecialPowers.getIntPref('dom.vibrator.max_vibrate_ms');
|
||||
var maxVibrateListLen = SpecialPowers.getIntPref('dom.vibrator.max_vibrate_list_len');
|
||||
|
||||
// If we pass a vibration pattern with a value higher than max_vibrate_ms or a
|
||||
// pattern longer than max_vibrate_list_len, the call should succeed but the
|
||||
// pattern should be modified to match the restrictions.
|
||||
|
||||
// Values will be clamped to dom.vibrator.max_vibrate_ms.
|
||||
expectSuccess(maxVibrateMs + 1);
|
||||
expectSuccess([maxVibrateMs + 1]);
|
||||
|
||||
var arr = [];
|
||||
for (var i = 0; i < maxVibrateListLen + 1; i++) {
|
||||
arr[i] = 0;
|
||||
}
|
||||
// The array will be truncated to have a length equal to dom.vibrator.max_vibrate_list_len.
|
||||
expectSuccess(arr);
|
||||
|
||||
|
||||
expectSuccess(0);
|
||||
expectSuccess([]);
|
||||
expectSuccess('1000');
|
||||
expectSuccess(1000);
|
||||
expectSuccess(1000.1);
|
||||
expectSuccess([0, 0, 0]);
|
||||
expectSuccess(['1000', 1000]);
|
||||
expectSuccess([1000, 1000]);
|
||||
expectSuccess([1000, 1000.1]);
|
||||
|
||||
// The following loop shouldn't cause us to crash. See bug 701716.
|
||||
for (var i = 0; i < 10000; i++) {
|
||||
navigator.vibrate([100, 100]);
|
||||
}
|
||||
ok(true, "Didn't crash after issuing a lot of vibrate() calls.");
|
||||
if(!aEnabled)
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SpecialPowers.pushPermissions([
|
||||
{type: 'vibration', allow: true, context: document}
|
||||
], function() {
|
||||
// Test with the vibrator pref enabled.
|
||||
SpecialPowers.pushPrefEnv({"set": [['dom.vibrator.enabled', true]]}, function() {
|
||||
tests(true);
|
||||
SpecialPowers.pushPrefEnv({"set": [['dom.vibrator.enabled', false]]}, tests(false));
|
||||
});
|
||||
// Everything should be the same when the vibrator is disabled -- in
|
||||
// particular, a disabled vibrator shouldn't eat failures we'd otherwise
|
||||
// observe.
|
||||
}
|
||||
);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -13,9 +13,9 @@
|
||||
* http://www.w3.org/TR/beacon/#sec-beacon-method
|
||||
* https://html.spec.whatwg.org/#navigatorconcurrenthardware
|
||||
*
|
||||
* © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
|
||||
* Opera Software ASA. You are granted a license to use, reproduce
|
||||
* and create derivative works of this document.
|
||||
* © Copyright 2004-2020 Apple Computer, Inc., Mozilla Foundation,
|
||||
* Opera Software ASA and Moonchild Productions. You are granted a license to use,
|
||||
* reproduce and create derivative works of this document.
|
||||
*/
|
||||
|
||||
// http://www.whatwg.org/specs/web-apps/current-work/#the-navigator-object
|
||||
@@ -124,14 +124,6 @@ interface NavigatorGeolocation {
|
||||
};
|
||||
Navigator implements NavigatorGeolocation;
|
||||
|
||||
// http://www.w3.org/TR/vibration/#vibration-interface
|
||||
partial interface Navigator {
|
||||
// We don't support sequences in unions yet
|
||||
//boolean vibrate ((unsigned long or sequence<unsigned long>) pattern);
|
||||
boolean vibrate(unsigned long duration);
|
||||
boolean vibrate(sequence<unsigned long> pattern);
|
||||
};
|
||||
|
||||
// http://www.w3.org/TR/pointerevents/#extensions-to-the-navigator-interface
|
||||
partial interface Navigator {
|
||||
[Pref="dom.w3c_pointer_events.enabled"]
|
||||
@@ -140,17 +132,6 @@ partial interface Navigator {
|
||||
|
||||
// Mozilla-specific extensions
|
||||
|
||||
// Chrome-only interface for Vibration API permission handling.
|
||||
partial interface Navigator {
|
||||
/* Set permission state to device vibration.
|
||||
* @param permitted permission state (true for allowing vibration)
|
||||
* @param persistent make the permission session-persistent
|
||||
*/
|
||||
[ChromeOnly]
|
||||
void setVibrationPermission(boolean permitted,
|
||||
optional boolean persistent = true);
|
||||
};
|
||||
|
||||
callback interface MozIdleObserver {
|
||||
// Time is in seconds and is read only when idle observers are added
|
||||
// and removed.
|
||||
|
||||
+30
-16
@@ -212,7 +212,7 @@ VerifyRGBXCorners(uint8_t* aData, const IntSize &aSize, const int32_t aStride, S
|
||||
#endif
|
||||
|
||||
static sk_sp<SkImage>
|
||||
GetSkImageForSurface(SourceSurface* aSurface)
|
||||
GetSkImageForSurface(SourceSurface* aSurface, Maybe<MutexAutoLock>* aLock)
|
||||
{
|
||||
if (!aSurface) {
|
||||
gfxDebug() << "Creating null Skia image from null SourceSurface";
|
||||
@@ -220,7 +220,7 @@ GetSkImageForSurface(SourceSurface* aSurface)
|
||||
}
|
||||
|
||||
if (aSurface->GetType() == SurfaceType::SKIA) {
|
||||
return static_cast<SourceSurfaceSkia*>(aSurface)->GetImage();
|
||||
return static_cast<SourceSurfaceSkia*>(aSurface)->GetImage(aLock);
|
||||
}
|
||||
|
||||
DataSourceSurface* surf = aSurface->GetDataSurface().take();
|
||||
@@ -390,9 +390,9 @@ ExtractAlphaBitmap(sk_sp<SkImage> aImage, SkBitmap* aResultBitmap)
|
||||
}
|
||||
|
||||
static sk_sp<SkImage>
|
||||
ExtractAlphaForSurface(SourceSurface* aSurface)
|
||||
ExtractAlphaForSurface(SourceSurface* aSurface, Maybe<MutexAutoLock>& aLock)
|
||||
{
|
||||
sk_sp<SkImage> image = GetSkImageForSurface(aSurface);
|
||||
sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &aLock);
|
||||
if (!image) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -411,7 +411,11 @@ ExtractAlphaForSurface(SourceSurface* aSurface)
|
||||
}
|
||||
|
||||
static void
|
||||
SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, Float aAlpha = 1.0, Point aOffset = Point(0, 0))
|
||||
SetPaintPattern(SkPaint& aPaint,
|
||||
const Pattern& aPattern,
|
||||
Maybe<MutexAutoLock>& aLock,
|
||||
Float aAlpha = 1.0,
|
||||
Point aOffset = Point(0, 0))
|
||||
{
|
||||
switch (aPattern.GetType()) {
|
||||
case PatternType::COLOR: {
|
||||
@@ -473,7 +477,7 @@ SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, Float aAlpha = 1.0, Po
|
||||
}
|
||||
case PatternType::SURFACE: {
|
||||
const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern);
|
||||
sk_sp<SkImage> image = GetSkImageForSurface(pat.mSurface);
|
||||
sk_sp<SkImage> image = GetSkImageForSurface(pat.mSurface, &aLock);
|
||||
if (!image) {
|
||||
aPaint.setColor(SK_ColorTRANSPARENT);
|
||||
break;
|
||||
@@ -525,7 +529,7 @@ struct AutoPaintSetup {
|
||||
: mNeedsRestore(false), mAlpha(1.0)
|
||||
{
|
||||
Init(aCanvas, aOptions, aMaskBounds, false);
|
||||
SetPaintPattern(mPaint, aPattern, mAlpha, aOffset);
|
||||
SetPaintPattern(mPaint, aPattern, mLock, mAlpha, aOffset);
|
||||
}
|
||||
|
||||
AutoPaintSetup(SkCanvas *aCanvas, const DrawOptions& aOptions, const Rect* aMaskBounds = nullptr, bool aForceGroup = false)
|
||||
@@ -579,6 +583,7 @@ struct AutoPaintSetup {
|
||||
SkPaint mPaint;
|
||||
bool mNeedsRestore;
|
||||
SkCanvas* mCanvas;
|
||||
Maybe<MutexAutoLock> mLock;
|
||||
Float mAlpha;
|
||||
};
|
||||
|
||||
@@ -601,7 +606,8 @@ DrawTargetSkia::DrawSurface(SourceSurface *aSurface,
|
||||
|
||||
MarkChanged();
|
||||
|
||||
sk_sp<SkImage> image = GetSkImageForSurface(aSurface);
|
||||
Maybe<MutexAutoLock> lock;
|
||||
sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
@@ -654,7 +660,8 @@ DrawTargetSkia::DrawSurfaceWithShadow(SourceSurface *aSurface,
|
||||
|
||||
MarkChanged();
|
||||
|
||||
sk_sp<SkImage> image = GetSkImageForSurface(aSurface);
|
||||
Maybe<MutexAutoLock> lock;
|
||||
sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
@@ -1407,8 +1414,9 @@ DrawTargetSkia::Mask(const Pattern &aSource,
|
||||
MarkChanged();
|
||||
AutoPaintSetup paint(mCanvas.get(), aOptions, aSource);
|
||||
|
||||
Maybe<MutexAutoLock> lock;
|
||||
SkPaint maskPaint;
|
||||
SetPaintPattern(maskPaint, aMask);
|
||||
SetPaintPattern(maskPaint, aMask, lock);
|
||||
|
||||
SkLayerRasterizer::Builder builder;
|
||||
builder.addLayer(maskPaint);
|
||||
@@ -1427,7 +1435,8 @@ DrawTargetSkia::MaskSurface(const Pattern &aSource,
|
||||
MarkChanged();
|
||||
AutoPaintSetup paint(mCanvas.get(), aOptions, aSource, nullptr, -aOffset);
|
||||
|
||||
sk_sp<SkImage> alphaMask = ExtractAlphaForSurface(aMask);
|
||||
Maybe<MutexAutoLock> lock;
|
||||
sk_sp<SkImage> alphaMask = ExtractAlphaForSurface(aMask, lock);
|
||||
if (!alphaMask) {
|
||||
gfxDebug() << *this << ": MaskSurface() failed to extract alpha for mask";
|
||||
return;
|
||||
@@ -1456,7 +1465,8 @@ DrawTarget::Draw3DTransformedSurface(SourceSurface* aSurface, const Matrix4x4& a
|
||||
fullMat.PostTranslate(-xformBounds.x, -xformBounds.y, 0);
|
||||
|
||||
// Read in the source data.
|
||||
sk_sp<SkImage> srcImage = GetSkImageForSurface(aSurface);
|
||||
Maybe<MutexAutoLock> lock;
|
||||
sk_sp<SkImage> srcImage = GetSkImageForSurface(aSurface, &lock);
|
||||
if (!srcImage) {
|
||||
return true;
|
||||
}
|
||||
@@ -1515,7 +1525,8 @@ DrawTargetSkia::Draw3DTransformedSurface(SourceSurface* aSurface, const Matrix4x
|
||||
|
||||
MarkChanged();
|
||||
|
||||
sk_sp<SkImage> image = GetSkImageForSurface(aSurface);
|
||||
Maybe<MutexAutoLock> lock;
|
||||
sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
|
||||
if (!image) {
|
||||
return true;
|
||||
}
|
||||
@@ -1599,7 +1610,8 @@ already_AddRefed<SourceSurface>
|
||||
DrawTargetSkia::OptimizeGPUSourceSurface(SourceSurface *aSurface) const
|
||||
{
|
||||
// Check if the underlying SkImage already has an associated GrTexture.
|
||||
sk_sp<SkImage> image = GetSkImageForSurface(aSurface);
|
||||
Maybe<MutexAutoLock> lock;
|
||||
sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
|
||||
if (!image || image->isTextureBacked()) {
|
||||
RefPtr<SourceSurface> surface(aSurface);
|
||||
return surface.forget();
|
||||
@@ -1716,7 +1728,8 @@ DrawTargetSkia::CopySurface(SourceSurface *aSurface,
|
||||
{
|
||||
MarkChanged();
|
||||
|
||||
sk_sp<SkImage> image = GetSkImageForSurface(aSurface);
|
||||
Maybe<MutexAutoLock> lock;
|
||||
sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
@@ -2061,7 +2074,8 @@ DrawTargetSkia::PopLayer()
|
||||
paint.setColor(SK_ColorTRANSPARENT);
|
||||
}
|
||||
|
||||
sk_sp<SkImage> alphaMask = ExtractAlphaForSurface(layer.mMask);
|
||||
Maybe<MutexAutoLock> lock;
|
||||
sk_sp<SkImage> alphaMask = ExtractAlphaForSurface(layer.mMask, lock);
|
||||
if (!alphaMask) {
|
||||
gfxDebug() << *this << ": PopLayer() failed to extract alpha for mask";
|
||||
} else {
|
||||
|
||||
@@ -40,6 +40,25 @@ SourceSurfaceSkia::GetFormat() const
|
||||
return mFormat;
|
||||
}
|
||||
|
||||
sk_sp<SkImage>
|
||||
SourceSurfaceSkia::GetImage(Maybe<MutexAutoLock>* aLock) {
|
||||
// If we were provided a lock object, we can let the caller access
|
||||
// a shared SkImage and we know it won't go away while the lock is held.
|
||||
if (aLock) {
|
||||
// We are locked, so now we can check mDrawTarget.
|
||||
// If it's null, then we're not shared and we can unlock eagerly.
|
||||
if (!mDrawTarget) {
|
||||
aLock->reset();
|
||||
}
|
||||
} else {
|
||||
// Otherwise we need to call DrawTargetWillChange to ensure we have our
|
||||
// own copy of the SkImage.
|
||||
DrawTargetWillChange();
|
||||
}
|
||||
sk_sp<SkImage> image = mImage;
|
||||
return image;
|
||||
}
|
||||
|
||||
static sk_sp<SkData>
|
||||
MakeSkData(unsigned char* aData, const IntSize& aSize, int32_t aStride)
|
||||
{
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
#include <vector>
|
||||
#include "skia/include/core/SkCanvas.h"
|
||||
#include "skia/include/core/SkImage.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
|
||||
typedef mozilla::MutexAutoLock MutexAutoLock;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@@ -28,7 +32,7 @@ public:
|
||||
virtual IntSize GetSize() const;
|
||||
virtual SurfaceFormat GetFormat() const;
|
||||
|
||||
sk_sp<SkImage>& GetImage() { return mImage; }
|
||||
sk_sp<SkImage> GetImage(Maybe<MutexAutoLock>* aLock);
|
||||
|
||||
bool InitFromData(unsigned char* aData,
|
||||
const IntSize &aSize,
|
||||
|
||||
-79
@@ -94,87 +94,8 @@ WindowIsActive(nsPIDOMWindowInner* aWindow)
|
||||
return !document->Hidden();
|
||||
}
|
||||
|
||||
StaticAutoPtr<WindowIdentifier::IDArrayType> gLastIDToVibrate;
|
||||
|
||||
void InitLastIDToVibrate()
|
||||
{
|
||||
gLastIDToVibrate = new WindowIdentifier::IDArrayType();
|
||||
ClearOnShutdown(&gLastIDToVibrate);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void
|
||||
Vibrate(const nsTArray<uint32_t>& pattern, nsPIDOMWindowInner* window)
|
||||
{
|
||||
Vibrate(pattern, WindowIdentifier(window));
|
||||
}
|
||||
|
||||
void
|
||||
Vibrate(const nsTArray<uint32_t>& pattern, const WindowIdentifier &id)
|
||||
{
|
||||
AssertMainThread();
|
||||
|
||||
// Only active windows may start vibrations. If |id| hasn't gone
|
||||
// through the IPC layer -- that is, if our caller is the outside
|
||||
// world, not hal_proxy -- check whether the window is active. If
|
||||
// |id| has gone through IPC, don't check the window's visibility;
|
||||
// only the window corresponding to the bottommost process has its
|
||||
// visibility state set correctly.
|
||||
if (!id.HasTraveledThroughIPC() && !WindowIsActive(id.GetWindow())) {
|
||||
HAL_LOG("Vibrate: Window is inactive, dropping vibrate.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!InSandbox()) {
|
||||
if (!gLastIDToVibrate) {
|
||||
InitLastIDToVibrate();
|
||||
}
|
||||
*gLastIDToVibrate = id.AsArray();
|
||||
}
|
||||
|
||||
// Don't forward our ID if we are not in the sandbox, because hal_impl
|
||||
// doesn't need it, and we don't want it to be tempted to read it. The
|
||||
// empty identifier will assert if it's used.
|
||||
PROXY_IF_SANDBOXED(Vibrate(pattern, InSandbox() ? id : WindowIdentifier()));
|
||||
}
|
||||
|
||||
void
|
||||
CancelVibrate(nsPIDOMWindowInner* window)
|
||||
{
|
||||
CancelVibrate(WindowIdentifier(window));
|
||||
}
|
||||
|
||||
void
|
||||
CancelVibrate(const WindowIdentifier &id)
|
||||
{
|
||||
AssertMainThread();
|
||||
|
||||
// Although only active windows may start vibrations, a window may
|
||||
// cancel its own vibration even if it's no longer active.
|
||||
//
|
||||
// After a window is marked as inactive, it sends a CancelVibrate
|
||||
// request. We want this request to cancel a playing vibration
|
||||
// started by that window, so we certainly don't want to reject the
|
||||
// cancellation request because the window is now inactive.
|
||||
//
|
||||
// But it could be the case that, after this window became inactive,
|
||||
// some other window came along and started a vibration. We don't
|
||||
// want this window's cancellation request to cancel that window's
|
||||
// actively-playing vibration!
|
||||
//
|
||||
// To solve this problem, we keep track of the id of the last window
|
||||
// to start a vibration, and only accepts cancellation requests from
|
||||
// the same window. All other cancellation requests are ignored.
|
||||
|
||||
if (InSandbox() || (gLastIDToVibrate && *gLastIDToVibrate == id.AsArray())) {
|
||||
// Don't forward our ID if we are not in the sandbox, because hal_impl
|
||||
// doesn't need it, and we don't want it to be tempted to read it. The
|
||||
// empty identifier will assert if it's used.
|
||||
PROXY_IF_SANDBOXED(CancelVibrate(InSandbox() ? id : WindowIdentifier()));
|
||||
}
|
||||
}
|
||||
|
||||
template <class InfoType>
|
||||
class ObserversManager
|
||||
{
|
||||
|
||||
@@ -50,41 +50,6 @@ typedef Observer<SystemTimezoneChangeInformation> SystemTimezoneChangeObserver;
|
||||
|
||||
namespace MOZ_HAL_NAMESPACE {
|
||||
|
||||
/**
|
||||
* Turn the default vibrator device on/off per the pattern specified
|
||||
* by |pattern|. Each element in the pattern is the number of
|
||||
* milliseconds to turn the vibrator on or off. The first element in
|
||||
* |pattern| is an "on" element, the next is "off", and so on.
|
||||
*
|
||||
* If |pattern| is empty, any in-progress vibration is canceled.
|
||||
*
|
||||
* Only an active window within an active tab may call Vibrate; calls
|
||||
* from inactive windows and windows on inactive tabs do nothing.
|
||||
*
|
||||
* If you're calling hal::Vibrate from the outside world, pass an
|
||||
* nsIDOMWindow* in place of the WindowIdentifier parameter.
|
||||
* The method with WindowIdentifier will be called automatically.
|
||||
*/
|
||||
void Vibrate(const nsTArray<uint32_t>& pattern,
|
||||
nsPIDOMWindowInner* aWindow);
|
||||
void Vibrate(const nsTArray<uint32_t>& pattern,
|
||||
const hal::WindowIdentifier &id);
|
||||
|
||||
/**
|
||||
* Cancel a vibration started by the content window identified by
|
||||
* WindowIdentifier.
|
||||
*
|
||||
* If the window was the last window to start a vibration, the
|
||||
* cancellation request will go through even if the window is not
|
||||
* active.
|
||||
*
|
||||
* As with hal::Vibrate(), if you're calling hal::CancelVibrate from the outside
|
||||
* world, pass an nsIDOMWindow*. The method with WindowIdentifier will be called
|
||||
* automatically.
|
||||
*/
|
||||
void CancelVibrate(nsPIDOMWindowInner* aWindow);
|
||||
void CancelVibrate(const hal::WindowIdentifier &id);
|
||||
|
||||
/**
|
||||
* Determine whether the device's screen is currently enabled.
|
||||
*/
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "Hal.h"
|
||||
|
||||
using mozilla::hal::WindowIdentifier;
|
||||
|
||||
namespace mozilla {
|
||||
namespace hal_impl {
|
||||
|
||||
void
|
||||
Vibrate(const nsTArray<uint32_t>& pattern, const hal::WindowIdentifier &)
|
||||
{}
|
||||
|
||||
void
|
||||
CancelVibrate(const hal::WindowIdentifier &)
|
||||
{}
|
||||
|
||||
} // namespace hal_impl
|
||||
} // namespace mozilla
|
||||
@@ -29,7 +29,6 @@ if CONFIG['OS_TARGET'] == 'Linux':
|
||||
'fallback/FallbackAlarm.cpp',
|
||||
'fallback/FallbackScreenConfiguration.cpp',
|
||||
'fallback/FallbackSensor.cpp',
|
||||
'fallback/FallbackVibration.cpp',
|
||||
'linux/LinuxMemory.cpp',
|
||||
'linux/LinuxPower.cpp',
|
||||
]
|
||||
@@ -39,7 +38,6 @@ elif CONFIG['OS_TARGET'] == 'WINNT':
|
||||
'fallback/FallbackMemory.cpp',
|
||||
'fallback/FallbackPower.cpp',
|
||||
'fallback/FallbackScreenConfiguration.cpp',
|
||||
'fallback/FallbackVibration.cpp',
|
||||
'windows/WindowsSensor.cpp',
|
||||
]
|
||||
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
|
||||
@@ -48,7 +46,6 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
|
||||
'fallback/FallbackMemory.cpp',
|
||||
'fallback/FallbackPower.cpp',
|
||||
'fallback/FallbackScreenConfiguration.cpp',
|
||||
'fallback/FallbackVibration.cpp',
|
||||
]
|
||||
elif CONFIG['OS_TARGET'] in ('OpenBSD', 'NetBSD', 'FreeBSD', 'DragonFly'):
|
||||
UNIFIED_SOURCES += [
|
||||
@@ -57,7 +54,6 @@ elif CONFIG['OS_TARGET'] in ('OpenBSD', 'NetBSD', 'FreeBSD', 'DragonFly'):
|
||||
'fallback/FallbackPower.cpp',
|
||||
'fallback/FallbackScreenConfiguration.cpp',
|
||||
'fallback/FallbackSensor.cpp',
|
||||
'fallback/FallbackVibration.cpp',
|
||||
]
|
||||
else:
|
||||
UNIFIED_SOURCES += [
|
||||
@@ -66,7 +62,6 @@ else:
|
||||
'fallback/FallbackPower.cpp',
|
||||
'fallback/FallbackScreenConfiguration.cpp',
|
||||
'fallback/FallbackSensor.cpp',
|
||||
'fallback/FallbackVibration.cpp',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
|
||||
@@ -69,9 +69,6 @@ child:
|
||||
async NotifySystemTimezoneChange(SystemTimezoneChangeInformation aSystemTimezoneChangeInfo);
|
||||
|
||||
parent:
|
||||
async Vibrate(uint32_t[] pattern, uint64_t[] id, PBrowser browser);
|
||||
async CancelVibrate(uint64_t[] id, PBrowser browser);
|
||||
|
||||
async EnableNetworkNotifications();
|
||||
async DisableNetworkNotifications();
|
||||
sync GetCurrentNetworkInformation()
|
||||
|
||||
@@ -45,28 +45,6 @@ Hal()
|
||||
return sHal;
|
||||
}
|
||||
|
||||
void
|
||||
Vibrate(const nsTArray<uint32_t>& pattern, const WindowIdentifier &id)
|
||||
{
|
||||
HAL_LOG("Vibrate: Sending to parent process.");
|
||||
|
||||
AutoTArray<uint32_t, 8> p(pattern);
|
||||
|
||||
WindowIdentifier newID(id);
|
||||
newID.AppendProcessID();
|
||||
Hal()->SendVibrate(p, newID.AsArray(), TabChild::GetFrom(newID.GetWindow()));
|
||||
}
|
||||
|
||||
void
|
||||
CancelVibrate(const WindowIdentifier &id)
|
||||
{
|
||||
HAL_LOG("CancelVibrate: Sending to parent process.");
|
||||
|
||||
WindowIdentifier newID(id);
|
||||
newID.AppendProcessID();
|
||||
Hal()->SendCancelVibrate(newID.AsArray(), TabChild::GetFrom(newID.GetWindow()));
|
||||
}
|
||||
|
||||
void
|
||||
EnableNetworkNotifications()
|
||||
{
|
||||
@@ -379,36 +357,6 @@ public:
|
||||
hal::UnregisterSystemTimezoneChangeObserver(this);
|
||||
}
|
||||
|
||||
virtual bool
|
||||
RecvVibrate(InfallibleTArray<unsigned int>&& pattern,
|
||||
InfallibleTArray<uint64_t>&& id,
|
||||
PBrowserParent *browserParent) override
|
||||
{
|
||||
// We give all content vibration permission.
|
||||
// TabParent *tabParent = TabParent::GetFrom(browserParent);
|
||||
/* xxxkhuey wtf
|
||||
nsCOMPtr<nsIDOMWindow> window =
|
||||
do_QueryInterface(tabParent->GetBrowserDOMWindow());
|
||||
*/
|
||||
WindowIdentifier newID(id, nullptr);
|
||||
hal::Vibrate(pattern, newID);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool
|
||||
RecvCancelVibrate(InfallibleTArray<uint64_t> &&id,
|
||||
PBrowserParent *browserParent) override
|
||||
{
|
||||
//TabParent *tabParent = TabParent::GetFrom(browserParent);
|
||||
/* XXXkhuey wtf
|
||||
nsCOMPtr<nsIDOMWindow> window =
|
||||
tabParent->GetBrowserDOMWindow();
|
||||
*/
|
||||
WindowIdentifier newID(id, nullptr);
|
||||
hal::CancelVibrate(newID);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool
|
||||
RecvEnableNetworkNotifications() override {
|
||||
// We give all content access to this network-status information.
|
||||
|
||||
+1
-1
@@ -778,7 +778,7 @@ struct JSClass {
|
||||
// application.
|
||||
#define JSCLASS_GLOBAL_APPLICATION_SLOTS 5
|
||||
#define JSCLASS_GLOBAL_SLOT_COUNT \
|
||||
(JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 45)
|
||||
(JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 46)
|
||||
#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
|
||||
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
|
||||
#define JSCLASS_GLOBAL_FLAGS \
|
||||
|
||||
@@ -363,7 +363,7 @@ function InnerModuleInstantiation(module, stack, index)
|
||||
// Step 9
|
||||
let requestedModules = module.requestedModules;
|
||||
for (let i = 0; i < requestedModules.length; i++) {
|
||||
let required = requestedModules[i];
|
||||
let required = requestedModules[i].moduleSpecifier;
|
||||
let requiredModule = CallModuleResolveHook(module, required, MODULE_STATUS_UNINSTANTIATED);
|
||||
|
||||
index = InnerModuleInstantiation(requiredModule, stack, index);
|
||||
@@ -591,7 +591,7 @@ function InnerModuleEvaluation(module, stack, index)
|
||||
// Step 10
|
||||
let requestedModules = module.requestedModules;
|
||||
for (let i = 0; i < requestedModules.length; i++) {
|
||||
let required = requestedModules[i];
|
||||
let required = requestedModules[i].moduleSpecifier;
|
||||
let requiredModule =
|
||||
CallModuleResolveHook(module, required, MODULE_STATUS_INSTANTIATED);
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ GlobalObject::initImportEntryProto(JSContext* cx, Handle<GlobalObject*> global)
|
||||
if (!DefinePropertiesAndFunctions(cx, proto, protoAccessors, nullptr))
|
||||
return false;
|
||||
|
||||
global->setReservedSlot(IMPORT_ENTRY_PROTO, ObjectValue(*proto));
|
||||
global->initReservedSlot(IMPORT_ENTRY_PROTO, ObjectValue(*proto));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -203,7 +203,7 @@ GlobalObject::initExportEntryProto(JSContext* cx, Handle<GlobalObject*> global)
|
||||
if (!DefinePropertiesAndFunctions(cx, proto, protoAccessors, nullptr))
|
||||
return false;
|
||||
|
||||
global->setReservedSlot(EXPORT_ENTRY_PROTO, ObjectValue(*proto));
|
||||
global->initReservedSlot(EXPORT_ENTRY_PROTO, ObjectValue(*proto));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -240,6 +240,71 @@ ExportEntryObject::create(ExclusiveContext* cx,
|
||||
return self;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// RequestedModuleObject
|
||||
|
||||
/* static */ const Class
|
||||
RequestedModuleObject::class_ = {
|
||||
"RequestedModule",
|
||||
JSCLASS_HAS_RESERVED_SLOTS(RequestedModuleObject::SlotCount) |
|
||||
JSCLASS_IS_ANONYMOUS
|
||||
};
|
||||
|
||||
DEFINE_GETTER_FUNCTIONS(RequestedModuleObject, moduleSpecifier, ModuleSpecifierSlot)
|
||||
DEFINE_GETTER_FUNCTIONS(RequestedModuleObject, lineNumber, LineNumberSlot)
|
||||
DEFINE_GETTER_FUNCTIONS(RequestedModuleObject, columnNumber, ColumnNumberSlot)
|
||||
|
||||
DEFINE_ATOM_ACCESSOR_METHOD(RequestedModuleObject, moduleSpecifier)
|
||||
DEFINE_UINT32_ACCESSOR_METHOD(RequestedModuleObject, lineNumber)
|
||||
DEFINE_UINT32_ACCESSOR_METHOD(RequestedModuleObject, columnNumber)
|
||||
|
||||
/* static */ bool
|
||||
RequestedModuleObject::isInstance(HandleValue value)
|
||||
{
|
||||
return value.isObject() && value.toObject().is<RequestedModuleObject>();
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
GlobalObject::initRequestedModuleProto(JSContext* cx, Handle<GlobalObject*> global)
|
||||
{
|
||||
static const JSPropertySpec protoAccessors[] = {
|
||||
JS_PSG("moduleSpecifier", RequestedModuleObject_moduleSpecifierGetter, 0),
|
||||
JS_PSG("lineNumber", RequestedModuleObject_lineNumberGetter, 0),
|
||||
JS_PSG("columnNumber", RequestedModuleObject_columnNumberGetter, 0),
|
||||
JS_PS_END
|
||||
};
|
||||
|
||||
RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
|
||||
if (!proto)
|
||||
return false;
|
||||
|
||||
if (!DefinePropertiesAndFunctions(cx, proto, protoAccessors, nullptr))
|
||||
return false;
|
||||
|
||||
global->initReservedSlot(REQUESTED_MODULE_PROTO, ObjectValue(*proto));
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ RequestedModuleObject*
|
||||
RequestedModuleObject::create(JSContext* cx,
|
||||
HandleAtom moduleSpecifier,
|
||||
uint32_t lineNumber,
|
||||
uint32_t columnNumber)
|
||||
{
|
||||
MOZ_ASSERT(lineNumber > 0);
|
||||
|
||||
RootedObject proto(cx, cx->global()->getRequestedModulePrototype());
|
||||
RootedObject obj(cx, NewObjectWithGivenProto(cx, &class_, proto));
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
|
||||
RootedRequestedModuleObject self(cx, &obj->as<RequestedModuleObject>());
|
||||
self->initReservedSlot(ModuleSpecifierSlot, StringValue(moduleSpecifier));
|
||||
self->initReservedSlot(LineNumberSlot, Int32Value(lineNumber));
|
||||
self->initReservedSlot(ColumnNumberSlot, Int32Value(columnNumber));
|
||||
return self;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// IndirectBindingMap
|
||||
|
||||
@@ -1074,7 +1139,8 @@ ModuleBuilder::ModuleBuilder(ExclusiveContext* cx, HandleModuleObject module,
|
||||
: cx_(cx),
|
||||
module_(cx, module),
|
||||
tokenStream_(tokenStream),
|
||||
requestedModules_(cx, AtomVector(cx)),
|
||||
requestedModuleSpecifiers_(cx, AtomSet(cx)),
|
||||
requestedModules_(cx, RequestedModuleVector(cx)),
|
||||
importedBoundNames_(cx, AtomVector(cx)),
|
||||
importEntries_(cx, ImportEntryVector(cx)),
|
||||
exportEntries_(cx, ExportEntryVector(cx)),
|
||||
@@ -1083,6 +1149,12 @@ ModuleBuilder::ModuleBuilder(ExclusiveContext* cx, HandleModuleObject module,
|
||||
starExportEntries_(cx, ExportEntryVector(cx))
|
||||
{}
|
||||
|
||||
bool
|
||||
ModuleBuilder::init()
|
||||
{
|
||||
return requestedModuleSpecifiers_.init();
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleBuilder::buildTables()
|
||||
{
|
||||
@@ -1130,24 +1202,23 @@ ModuleBuilder::buildTables()
|
||||
bool
|
||||
ModuleBuilder::initModule()
|
||||
{
|
||||
RootedArrayObject requestedModules(cx_, createArray<JSAtom*>(requestedModules_));
|
||||
RootedArrayObject requestedModules(cx_, createArray(requestedModules_));
|
||||
if (!requestedModules)
|
||||
return false;
|
||||
|
||||
RootedArrayObject importEntries(cx_, createArray<ImportEntryObject*>(importEntries_));
|
||||
RootedArrayObject importEntries(cx_, createArray(importEntries_));
|
||||
if (!importEntries)
|
||||
return false;
|
||||
|
||||
RootedArrayObject localExportEntries(cx_, createArray<ExportEntryObject*>(localExportEntries_));
|
||||
RootedArrayObject localExportEntries(cx_, createArray(localExportEntries_));
|
||||
if (!localExportEntries)
|
||||
return false;
|
||||
|
||||
RootedArrayObject indirectExportEntries(cx_);
|
||||
indirectExportEntries = createArray<ExportEntryObject*>(indirectExportEntries_);
|
||||
RootedArrayObject indirectExportEntries(cx_, createArray(indirectExportEntries_));
|
||||
if (!indirectExportEntries)
|
||||
return false;
|
||||
|
||||
RootedArrayObject starExportEntries(cx_, createArray<ExportEntryObject*>(starExportEntries_));
|
||||
RootedArrayObject starExportEntries(cx_, createArray(starExportEntries_));
|
||||
if (!starExportEntries)
|
||||
return false;
|
||||
|
||||
@@ -1169,7 +1240,7 @@ ModuleBuilder::processImport(frontend::ParseNode* pn)
|
||||
MOZ_ASSERT(pn->pn_right->isKind(PNK_STRING));
|
||||
|
||||
RootedAtom module(cx_, pn->pn_right->pn_atom);
|
||||
if (!maybeAppendRequestedModule(module))
|
||||
if (!maybeAppendRequestedModule(module, pn->pn_right))
|
||||
return false;
|
||||
|
||||
for (ParseNode* spec = pn->pn_left->pn_head; spec; spec = spec->pn_next) {
|
||||
@@ -1277,7 +1348,7 @@ ModuleBuilder::processExportFrom(frontend::ParseNode* pn)
|
||||
MOZ_ASSERT(pn->pn_right->isKind(PNK_STRING));
|
||||
|
||||
RootedAtom module(cx_, pn->pn_right->pn_atom);
|
||||
if (!maybeAppendRequestedModule(module))
|
||||
if (!maybeAppendRequestedModule(module, pn->pn_right))
|
||||
return false;
|
||||
|
||||
for (ParseNode* spec = pn->pn_left->pn_head; spec; spec = spec->pn_next) {
|
||||
@@ -1346,29 +1417,26 @@ ModuleBuilder::appendExportFromEntry(HandleAtom exportName, HandleAtom moduleReq
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleBuilder::maybeAppendRequestedModule(HandleAtom module)
|
||||
ModuleBuilder::maybeAppendRequestedModule(HandleAtom specifier, ParseNode* node)
|
||||
{
|
||||
for (auto m : requestedModules_) {
|
||||
if (m == module)
|
||||
return true;
|
||||
if (requestedModuleSpecifiers_.has(specifier)) {
|
||||
return true;
|
||||
}
|
||||
return requestedModules_.append(module);
|
||||
}
|
||||
|
||||
static Value
|
||||
MakeElementValue(JSString *string)
|
||||
{
|
||||
return StringValue(string);
|
||||
}
|
||||
|
||||
static Value
|
||||
MakeElementValue(JSObject *object)
|
||||
{
|
||||
return ObjectValue(*object);
|
||||
uint32_t line;
|
||||
uint32_t column;
|
||||
tokenStream_.srcCoords.lineNumAndColumnIndex(node->pn_pos.begin, &line, &column);
|
||||
JSContext* cx = reinterpret_cast<JSContext*>(cx_);
|
||||
RootedRequestedModuleObject req(cx, RequestedModuleObject::create(cx, specifier, line, column));
|
||||
if (!req) {
|
||||
return false;
|
||||
}
|
||||
return FreezeObject(cx, req) &&
|
||||
requestedModules_.append(req) &&
|
||||
requestedModuleSpecifiers_.put(specifier);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ArrayObject* ModuleBuilder::createArray(const GCVector<T>& vector)
|
||||
ArrayObject* ModuleBuilder::createArray(const JS::Rooted<GCVector<T>>& vector)
|
||||
{
|
||||
uint32_t length = vector.length();
|
||||
RootedArrayObject array(cx_, NewDenseFullyAllocatedArray(cx_, length));
|
||||
@@ -1377,7 +1445,7 @@ ArrayObject* ModuleBuilder::createArray(const GCVector<T>& vector)
|
||||
|
||||
array->setDenseInitializedLength(length);
|
||||
for (uint32_t i = 0; i < length; i++)
|
||||
array->initDenseElement(i, MakeElementValue(vector[i]));
|
||||
array->initDenseElement(i, ObjectValue(*vector[i]));
|
||||
|
||||
return array;
|
||||
}
|
||||
@@ -100,6 +100,32 @@ class ExportEntryObject : public NativeObject
|
||||
typedef Rooted<ExportEntryObject*> RootedExportEntryObject;
|
||||
typedef Handle<ExportEntryObject*> HandleExportEntryObject;
|
||||
|
||||
class RequestedModuleObject : public NativeObject
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
ModuleSpecifierSlot = 0,
|
||||
LineNumberSlot,
|
||||
ColumnNumberSlot,
|
||||
SlotCount
|
||||
};
|
||||
|
||||
static const Class class_;
|
||||
static JSObject* initClass(JSContext* cx, HandleObject obj);
|
||||
static bool isInstance(HandleValue value);
|
||||
static RequestedModuleObject* create(JSContext* cx,
|
||||
HandleAtom moduleSpecifier,
|
||||
uint32_t lineNumber,
|
||||
uint32_t columnNumber);
|
||||
JSAtom* moduleSpecifier() const;
|
||||
uint32_t lineNumber() const;
|
||||
uint32_t columnNumber() const;
|
||||
};
|
||||
|
||||
typedef Rooted<RequestedModuleObject*> RootedRequestedModuleObject;
|
||||
typedef Handle<RequestedModuleObject*> HandleRequestedModuleObject;
|
||||
|
||||
class IndirectBindingMap
|
||||
{
|
||||
public:
|
||||
@@ -318,6 +344,8 @@ class MOZ_STACK_CLASS ModuleBuilder
|
||||
explicit ModuleBuilder(ExclusiveContext* cx, HandleModuleObject module,
|
||||
const frontend::TokenStream& tokenStream);
|
||||
|
||||
bool init();
|
||||
|
||||
bool processImport(frontend::ParseNode* pn);
|
||||
bool processExport(frontend::ParseNode* pn);
|
||||
bool processExportFrom(frontend::ParseNode* pn);
|
||||
@@ -334,15 +362,20 @@ class MOZ_STACK_CLASS ModuleBuilder
|
||||
|
||||
private:
|
||||
using AtomVector = GCVector<JSAtom*>;
|
||||
using RootedAtomVector = JS::Rooted<AtomVector>;
|
||||
using ImportEntryVector = GCVector<ImportEntryObject*>;
|
||||
using RequestedModuleVector = GCVector<RequestedModuleObject*>;
|
||||
using AtomSet = JS::GCHashSet<JSAtom*>;
|
||||
using RootedAtomVector = JS::Rooted<AtomVector>;
|
||||
using RootedImportEntryVector = JS::Rooted<ImportEntryVector>;
|
||||
using RootedExportEntryVector = JS::Rooted<ExportEntryVector>;
|
||||
using RootedRequestedModuleVector = JS::Rooted<RequestedModuleVector>;
|
||||
using RootedAtomSet = JS::Rooted<AtomSet>;
|
||||
|
||||
ExclusiveContext* cx_;
|
||||
RootedModuleObject module_;
|
||||
const frontend::TokenStream& tokenStream_;
|
||||
RootedAtomVector requestedModules_;
|
||||
RootedAtomSet requestedModuleSpecifiers_;
|
||||
RootedRequestedModuleVector requestedModules_;
|
||||
RootedAtomVector importedBoundNames_;
|
||||
RootedImportEntryVector importEntries_;
|
||||
RootedExportEntryVector exportEntries_;
|
||||
@@ -357,10 +390,10 @@ class MOZ_STACK_CLASS ModuleBuilder
|
||||
bool appendExportFromEntry(HandleAtom exportName, HandleAtom moduleRequest,
|
||||
HandleAtom importName, frontend::ParseNode* node);
|
||||
|
||||
bool maybeAppendRequestedModule(HandleAtom module);
|
||||
bool maybeAppendRequestedModule(HandleAtom specifier, frontend::ParseNode* node);
|
||||
|
||||
template <typename T>
|
||||
ArrayObject* createArray(const GCVector<T>& vector);
|
||||
ArrayObject* createArray(const JS::Rooted<GCVector<T>>& vector);
|
||||
};
|
||||
|
||||
} // namespace js
|
||||
|
||||
@@ -3741,6 +3741,9 @@ reflect_parse(JSContext* cx, uint32_t argc, Value* vp)
|
||||
return false;
|
||||
|
||||
ModuleBuilder builder(cx, module, parser.tokenStream);
|
||||
if (!builder.init())
|
||||
return false;
|
||||
|
||||
ModuleSharedContext modulesc(cx, module, &cx->global()->emptyGlobalScope(), builder);
|
||||
pn = parser.moduleBody(&modulesc);
|
||||
if (!pn)
|
||||
|
||||
@@ -405,6 +405,9 @@ BytecodeCompiler::compileModule()
|
||||
module->init(script);
|
||||
|
||||
ModuleBuilder builder(cx, module, parser->tokenStream);
|
||||
if (!builder.init())
|
||||
return nullptr;
|
||||
|
||||
ModuleSharedContext modulesc(cx, module, enclosingScope, builder);
|
||||
ParseNode* pn = parser->moduleBody(&modulesc);
|
||||
if (!pn)
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "js/HeapAPI.h"
|
||||
#include "js/RootingAPI.h"
|
||||
#include "js/TracingAPI.h"
|
||||
#include "js/TraceKind.h"
|
||||
|
||||
struct JSRuntime;
|
||||
|
||||
@@ -272,6 +273,23 @@ struct Cell
|
||||
|
||||
static MOZ_ALWAYS_INLINE bool needWriteBarrierPre(JS::Zone* zone);
|
||||
|
||||
template <class T>
|
||||
inline bool is() const {
|
||||
return getTraceKind() == JS::MapTypeToTraceKind<T>::kind;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline T* as() {
|
||||
MOZ_ASSERT(this->is<T>());
|
||||
return static_cast<T*>(this);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline const T* as() const {
|
||||
MOZ_ASSERT(this->is<T>());
|
||||
return static_cast<const T*>(this);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
inline bool isAligned() const;
|
||||
void dump(FILE* fp) const;
|
||||
|
||||
@@ -46,6 +46,7 @@ class RegExpObject;
|
||||
class SavedFrame;
|
||||
class Scope;
|
||||
class EnvironmentObject;
|
||||
class RequestedModuleObject;
|
||||
class ScriptSourceObject;
|
||||
class Shape;
|
||||
class SharedArrayBufferObject;
|
||||
@@ -84,6 +85,7 @@ class JitCode;
|
||||
D(js::PlainObject*) \
|
||||
D(js::PropertyName*) \
|
||||
D(js::RegExpObject*) \
|
||||
D(js::RequestedModuleObject*) \
|
||||
D(js::SavedFrame*) \
|
||||
D(js::Scope*) \
|
||||
D(js::ScriptSourceObject*) \
|
||||
|
||||
@@ -5,7 +5,7 @@ function testRequestedModules(source, expected) {
|
||||
var actual = module.requestedModules;
|
||||
assertEq(actual.length, expected.length);
|
||||
for (var i = 0; i < actual.length; i++) {
|
||||
assertEq(actual[i], expected[i]);
|
||||
assertEq(actual[i].moduleSpecifier, expected[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+17
-17
@@ -11,7 +11,7 @@ FINAL_LIBRARY = "js"
|
||||
# Includes should be relative to parent path
|
||||
LOCAL_INCLUDES += ["!..", ".."]
|
||||
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'AliasAnalysis.cpp',
|
||||
'AliasAnalysisShared.cpp',
|
||||
'AlignmentMaskAnalysis.cpp',
|
||||
@@ -83,14 +83,14 @@ SOURCES += [
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_DEBUG']:
|
||||
SOURCES += ['Disassembler.cpp']
|
||||
UNIFIED_SOURCES += ['Disassembler.cpp']
|
||||
|
||||
if not CONFIG['ENABLE_ION']:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'none/Trampoline-none.cpp'
|
||||
]
|
||||
elif CONFIG['JS_CODEGEN_X86'] or CONFIG['JS_CODEGEN_X64']:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'x86-shared/Architecture-x86-shared.cpp',
|
||||
'x86-shared/Assembler-x86-shared.cpp',
|
||||
'x86-shared/AssemblerBuffer-x86-shared.cpp',
|
||||
@@ -101,11 +101,11 @@ elif CONFIG['JS_CODEGEN_X86'] or CONFIG['JS_CODEGEN_X64']:
|
||||
'x86-shared/MacroAssembler-x86-shared.cpp',
|
||||
'x86-shared/MoveEmitter-x86-shared.cpp',
|
||||
]
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'x86-shared/Disassembler-x86-shared.cpp', # using namespace js::jit::X86Encoding;
|
||||
]
|
||||
if CONFIG['JS_CODEGEN_X64']:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'x64/Assembler-x64.cpp',
|
||||
'x64/Bailouts-x64.cpp',
|
||||
'x64/BaselineCompiler-x64.cpp',
|
||||
@@ -117,7 +117,7 @@ elif CONFIG['JS_CODEGEN_X86'] or CONFIG['JS_CODEGEN_X64']:
|
||||
'x64/Trampoline-x64.cpp',
|
||||
]
|
||||
else:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'x86/Assembler-x86.cpp',
|
||||
'x86/Bailouts-x86.cpp',
|
||||
'x86/BaselineCompiler-x86.cpp',
|
||||
@@ -129,7 +129,7 @@ elif CONFIG['JS_CODEGEN_X86'] or CONFIG['JS_CODEGEN_X64']:
|
||||
'x86/Trampoline-x86.cpp',
|
||||
]
|
||||
elif CONFIG['JS_CODEGEN_ARM']:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'arm/Architecture-arm.cpp',
|
||||
'arm/Assembler-arm.cpp',
|
||||
'arm/Bailouts-arm.cpp',
|
||||
@@ -145,16 +145,16 @@ elif CONFIG['JS_CODEGEN_ARM']:
|
||||
'arm/Trampoline-arm.cpp',
|
||||
]
|
||||
if CONFIG['JS_SIMULATOR_ARM']:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'arm/Simulator-arm.cpp'
|
||||
]
|
||||
elif CONFIG['OS_ARCH'] == 'Darwin':
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'arm/llvm-compiler-rt/arm/aeabi_idivmod.S',
|
||||
'arm/llvm-compiler-rt/arm/aeabi_uidivmod.S',
|
||||
]
|
||||
elif CONFIG['JS_CODEGEN_ARM64']:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'arm64/Architecture-arm64.cpp',
|
||||
'arm64/Assembler-arm64.cpp',
|
||||
'arm64/Bailouts-arm64.cpp',
|
||||
@@ -177,14 +177,14 @@ elif CONFIG['JS_CODEGEN_ARM64']:
|
||||
'arm64/vixl/Utils-vixl.cpp'
|
||||
]
|
||||
if CONFIG['JS_SIMULATOR_ARM64']:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'arm64/vixl/Debugger-vixl.cpp',
|
||||
'arm64/vixl/Logic-vixl.cpp',
|
||||
'arm64/vixl/MozSimulator-vixl.cpp',
|
||||
'arm64/vixl/Simulator-vixl.cpp'
|
||||
]
|
||||
elif CONFIG['JS_CODEGEN_MIPS32'] or CONFIG['JS_CODEGEN_MIPS64']:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'mips-shared/Architecture-mips-shared.cpp',
|
||||
'mips-shared/Assembler-mips-shared.cpp',
|
||||
'mips-shared/Bailouts-mips-shared.cpp',
|
||||
@@ -196,7 +196,7 @@ elif CONFIG['JS_CODEGEN_MIPS32'] or CONFIG['JS_CODEGEN_MIPS64']:
|
||||
'mips-shared/MoveEmitter-mips-shared.cpp',
|
||||
]
|
||||
if CONFIG['JS_CODEGEN_MIPS32']:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'mips32/Architecture-mips32.cpp',
|
||||
'mips32/Assembler-mips32.cpp',
|
||||
'mips32/Bailouts-mips32.cpp',
|
||||
@@ -210,11 +210,11 @@ elif CONFIG['JS_CODEGEN_MIPS32'] or CONFIG['JS_CODEGEN_MIPS64']:
|
||||
'mips32/Trampoline-mips32.cpp',
|
||||
]
|
||||
if CONFIG['JS_SIMULATOR_MIPS32']:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'mips32/Simulator-mips32.cpp'
|
||||
]
|
||||
elif CONFIG['JS_CODEGEN_MIPS64']:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'mips64/Architecture-mips64.cpp',
|
||||
'mips64/Assembler-mips64.cpp',
|
||||
'mips64/Bailouts-mips64.cpp',
|
||||
@@ -228,6 +228,6 @@ elif CONFIG['JS_CODEGEN_MIPS32'] or CONFIG['JS_CODEGEN_MIPS64']:
|
||||
'mips64/Trampoline-mips64.cpp',
|
||||
]
|
||||
if CONFIG['JS_SIMULATOR_MIPS64']:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'mips64/Simulator-mips64.cpp'
|
||||
]
|
||||
@@ -3,6 +3,8 @@
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
FILES_PER_UNIFIED_FILE = 6
|
||||
|
||||
# Also set in shell/moz.build
|
||||
DEFINES['ENABLE_SHARED_ARRAY_BUFFER'] = True
|
||||
|
||||
|
||||
+19
-4
@@ -4729,13 +4729,28 @@ JS::GetRequestedModules(JSContext* cx, JS::HandleObject moduleArg)
|
||||
return &moduleArg->as<ModuleObject>().requestedModules();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSScript*)
|
||||
JS::GetModuleScript(JSContext* cx, JS::HandleObject moduleArg)
|
||||
JS_PUBLIC_API(JSString*)
|
||||
JS::GetRequestedModuleSpecifier(JSContext* cx, JS::HandleValue value)
|
||||
{
|
||||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, moduleArg);
|
||||
return moduleArg->as<ModuleObject>().script();
|
||||
assertSameCompartment(cx, value);
|
||||
JSObject* obj = &value.toObject();
|
||||
return obj->as<RequestedModuleObject>().moduleSpecifier();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS::GetRequestedModuleSourcePos(JSContext* cx, JS::HandleValue value,
|
||||
uint32_t* lineNumber, uint32_t* columnNumber)
|
||||
{
|
||||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, value);
|
||||
MOZ_ASSERT(lineNumber);
|
||||
MOZ_ASSERT(columnNumber);
|
||||
auto& requested = value.toObject().as<RequestedModuleObject>();
|
||||
*lineNumber = requested.lineNumber();
|
||||
*columnNumber = requested.columnNumber();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSObject*)
|
||||
|
||||
+17
-8
@@ -4386,18 +4386,27 @@ ModuleEvaluate(JSContext* cx, JS::HandleObject moduleRecord);
|
||||
* Get a list of the module specifiers used by a source text module
|
||||
* record to request importation of modules.
|
||||
*
|
||||
* The result is a JavaScript array of string values. To extract the individual
|
||||
* values use only JS_GetArrayLength and JS_GetElement with indices 0 to
|
||||
* length - 1.
|
||||
* The result is a JavaScript array of object values. To extract the individual
|
||||
* values use only JS_GetArrayLength and JS_GetElement with indices 0 to length
|
||||
* - 1.
|
||||
*
|
||||
* The element values are objects with the following properties:
|
||||
* - moduleSpecifier: the module specifier string
|
||||
* - lineNumber: the line number of the import in the source text
|
||||
* - columnNumber: the column number of the import in the source text
|
||||
*
|
||||
* These property values can be extracted with GetRequestedModuleSpecifier() and
|
||||
* GetRequestedModuleSourcePos()
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSObject*)
|
||||
GetRequestedModules(JSContext* cx, JS::HandleObject moduleRecord);
|
||||
|
||||
/*
|
||||
* Get the script associated with a module.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSScript*)
|
||||
GetModuleScript(JSContext* cx, JS::HandleObject moduleRecord);
|
||||
extern JS_PUBLIC_API(JSString*)
|
||||
GetRequestedModuleSpecifier(JSContext* cx, JS::HandleValue requestedModuleObject);
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
GetRequestedModuleSourcePos(JSContext* cx, JS::HandleValue requestedModuleObject,
|
||||
uint32_t* lineNumber, uint32_t* columnNumber);
|
||||
|
||||
} /* namespace JS */
|
||||
|
||||
|
||||
+18
-9
@@ -131,22 +131,18 @@ if not CONFIG['JS_DISABLE_SHELL']:
|
||||
if CONFIG['MOZ_VTUNE']:
|
||||
DIRS += ['vtune']
|
||||
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'jsalloc.cpp',
|
||||
'jsapi.cpp',
|
||||
'jsarray.cpp',
|
||||
'jsatom.cpp',
|
||||
'jsbool.cpp',
|
||||
'jscntxt.cpp',
|
||||
'jscompartment.cpp',
|
||||
'jsdate.cpp',
|
||||
'jsdtoa.cpp',
|
||||
'jsexn.cpp',
|
||||
'jsfriendapi.cpp',
|
||||
'jsfun.cpp',
|
||||
'jsgc.cpp',
|
||||
'jsiter.cpp',
|
||||
'jsmath.cpp',
|
||||
'jsnativestack.cpp',
|
||||
'jsnum.cpp',
|
||||
'jsobj.cpp',
|
||||
@@ -156,16 +152,24 @@ SOURCES += [
|
||||
'jspropertytree.cpp',
|
||||
'jsscript.cpp',
|
||||
'jsstr.cpp',
|
||||
'jsutil.cpp',
|
||||
'jsweakmap.cpp',
|
||||
]
|
||||
|
||||
# Can't be built unified
|
||||
SOURCES += [
|
||||
'jsarray.cpp',
|
||||
'jsatom.cpp',
|
||||
'jsdtoa.cpp',
|
||||
'jsmath.cpp',
|
||||
'jsutil.cpp',
|
||||
]
|
||||
|
||||
# Suppress warnings in third-party code.
|
||||
# We are keeping this in the main moz.build because it is file specific
|
||||
if CONFIG['CLANG_CXX'] or CONFIG['GNU_CXX']:
|
||||
SOURCES['jsdtoa.cpp'].flags += ['-Wno-implicit-fallthrough']
|
||||
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'vm/ArgumentsObject.cpp',
|
||||
'vm/ArrayBufferObject.cpp',
|
||||
'vm/AsyncFunction.cpp',
|
||||
@@ -185,7 +189,6 @@ SOURCES += [
|
||||
'vm/GlobalObject.cpp',
|
||||
'vm/HelperThreads.cpp',
|
||||
'vm/Id.cpp',
|
||||
'vm/Initialization.cpp',
|
||||
'vm/Interpreter.cpp',
|
||||
'vm/JSONParser.cpp',
|
||||
'vm/MemoryMetrics.cpp',
|
||||
@@ -227,12 +230,18 @@ SOURCES += [
|
||||
'vm/Xdr.cpp',
|
||||
]
|
||||
|
||||
# Can't be built unified
|
||||
SOURCES += [
|
||||
'vm/Initialization.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['JS_POSIX_NSPR']:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'vm/PosixNSPR.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['ENABLE_TRACE_LOGGING']:
|
||||
# Can't be built unified
|
||||
SOURCES += [
|
||||
'vm/TraceLogging.cpp',
|
||||
'vm/TraceLoggingGraph.cpp',
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This file forward-defines Irregexp classes that need to be visible
|
||||
// to the rest of Spidermonkey and re-exports them into js::irregexp.
|
||||
|
||||
#ifndef regexp_RegExpTypes_h
|
||||
#define regexp_RegExpTypes_h
|
||||
|
||||
namespace js {
|
||||
class MatchPairs;
|
||||
}
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
struct InputOutputData {
|
||||
const void* inputStart;
|
||||
const void* inputEnd;
|
||||
|
||||
// Index into inputStart (in chars) at which to begin matching.
|
||||
size_t startIndex;
|
||||
|
||||
js::MatchPairs* matches;
|
||||
|
||||
template <typename CharT>
|
||||
InputOutputData(const CharT* inputStart, const CharT* inputEnd,
|
||||
size_t startIndex, js::MatchPairs* matches)
|
||||
: inputStart(inputStart),
|
||||
inputEnd(inputEnd),
|
||||
startIndex(startIndex),
|
||||
matches(matches)
|
||||
{}
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
|
||||
namespace js {
|
||||
namespace irregexp {
|
||||
|
||||
using InputOutputData = v8::internal::InputOutputData;
|
||||
|
||||
} // namespace irregexp
|
||||
} // namespace js
|
||||
|
||||
#endif // regexp_RegExpTypes_h
|
||||
@@ -1,3 +1,2 @@
|
||||
This code was most recently imported from the following version of V8:
|
||||
|
||||
https://github.com/v8/v8/tree/2599d3cc208a3a4873be517285220abd8416c3d7/src/regexp
|
||||
Imported using import-irregexp.py from:
|
||||
https://github.com/v8/v8/tree/560f2d8bb3f3a72d78e1a7d7654235d53fdcc83c/src/regexp
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2019 the V8 project authors. All rights reserved.
|
||||
// Copyright 2020 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
@@ -7,18 +7,19 @@
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "unicode/uchar.h"
|
||||
#include "unicode/uniset.h"
|
||||
#include "regexp/special-case.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
// The following code generates BuildSpecialAddSet() and BuildIgnoreSet()
|
||||
// functions into "src/regexp/special-case.cc".
|
||||
// See more details in http://shorturl.at/adfO5
|
||||
void PrintSet(std::ofstream& out, const char* func_name,
|
||||
static const uc32 kSurrogateStart = 0xd800;
|
||||
static const uc32 kSurrogateEnd = 0xdfff;
|
||||
static const uc32 kNonBmpStart = 0x10000;
|
||||
|
||||
// The following code generates "src/regexp/special-case.cc".
|
||||
void PrintSet(std::ofstream& out, const char* name,
|
||||
const icu::UnicodeSet& set) {
|
||||
out << "icu::UnicodeSet " << func_name << "() {\n"
|
||||
out << "icu::UnicodeSet Build" << name << "() {\n"
|
||||
<< " icu::UnicodeSet set;\n";
|
||||
for (int32_t i = 0; i < set.getRangeCount(); i++) {
|
||||
if (set.getRangeStart(i) == set.getRangeEnd(i)) {
|
||||
@@ -30,73 +31,113 @@ void PrintSet(std::ofstream& out, const char* func_name,
|
||||
}
|
||||
out << " set.freeze();\n"
|
||||
<< " return set;\n"
|
||||
<< "}\n";
|
||||
<< "}\n\n";
|
||||
|
||||
out << "struct " << name << "Data {\n"
|
||||
<< " " << name << "Data() : set(Build" << name << "()) {}\n"
|
||||
<< " const icu::UnicodeSet set;\n"
|
||||
<< "};\n\n";
|
||||
|
||||
out << "//static\n"
|
||||
<< "const icu::UnicodeSet& RegExpCaseFolding::" << name << "() {\n"
|
||||
<< " static base::LazyInstance<" << name << "Data>::type set =\n"
|
||||
<< " LAZY_INSTANCE_INITIALIZER;\n"
|
||||
<< " return set.Pointer()->set;\n"
|
||||
<< "}\n\n";
|
||||
}
|
||||
|
||||
void PrintSpecial(std::ofstream& out) {
|
||||
icu::UnicodeSet current;
|
||||
icu::UnicodeSet processed(0xd800, 0xdbff); // Ignore surrogate range.
|
||||
icu::UnicodeSet special_add;
|
||||
icu::UnicodeSet ignore;
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
icu::UnicodeSet upper("[\\p{Lu}]", status);
|
||||
CHECK(U_SUCCESS(status));
|
||||
// Iterate through all chars in BMP except ASCII and Surrogate.
|
||||
for (UChar32 i = 0x80; i < 0x010000; i++) {
|
||||
// Ignore those characters which is already processed.
|
||||
if (!processed.contains(i)) {
|
||||
current.set(i, i);
|
||||
current.closeOver(USET_CASE_INSENSITIVE);
|
||||
|
||||
// Remember we already processed current.
|
||||
processed.addAll(current);
|
||||
// Iterate through all chars in BMP except surrogates.
|
||||
for (UChar32 i = 0; i < kNonBmpStart; i++) {
|
||||
if (i >= kSurrogateStart && i <= kSurrogateEnd) {
|
||||
continue; // Ignore surrogate range
|
||||
}
|
||||
current.set(i, i);
|
||||
current.closeOver(USET_CASE_INSENSITIVE);
|
||||
|
||||
// All uppercase characters in current.
|
||||
icu::UnicodeSet keep_upper(current);
|
||||
keep_upper.retainAll(upper);
|
||||
|
||||
// Check if we have more than one uppercase character in current.
|
||||
// If there are more than one uppercase character, then it is a special
|
||||
// set which need to be added into either "Special Add" set or "Ignore"
|
||||
// set.
|
||||
int32_t number_of_upper = 0;
|
||||
for (int32_t i = 0; i < keep_upper.getRangeCount() && i <= 1; i++) {
|
||||
number_of_upper +=
|
||||
keep_upper.getRangeEnd(i) - keep_upper.getRangeStart(i) + 1;
|
||||
// Check to see if all characters in the case-folding equivalence
|
||||
// class as defined by UnicodeSet::closeOver all map to the same
|
||||
// canonical value.
|
||||
UChar32 canonical = RegExpCaseFolding::Canonicalize(i);
|
||||
bool class_has_matching_canonical_char = false;
|
||||
bool class_has_non_matching_canonical_char = false;
|
||||
for (int32_t j = 0; j < current.getRangeCount(); j++) {
|
||||
for (UChar32 c = current.getRangeStart(j); c <= current.getRangeEnd(j);
|
||||
c++) {
|
||||
if (c == i) {
|
||||
continue;
|
||||
}
|
||||
UChar32 other_canonical = RegExpCaseFolding::Canonicalize(c);
|
||||
if (canonical == other_canonical) {
|
||||
class_has_matching_canonical_char = true;
|
||||
} else {
|
||||
class_has_non_matching_canonical_char = true;
|
||||
}
|
||||
}
|
||||
if (number_of_upper > 1) {
|
||||
// Add all non uppercase characters (could be Ll or Mn) to special add
|
||||
// set.
|
||||
current.removeAll(upper);
|
||||
special_add.addAll(current);
|
||||
|
||||
// Add the uppercase characters of non uppercase character to
|
||||
// special add set.
|
||||
CHECK_GT(current.getRangeCount(), 0);
|
||||
UChar32 main_upper = u_toupper(current.getRangeStart(0));
|
||||
special_add.add(main_upper);
|
||||
|
||||
// Add all uppercase except the main upper to ignore set.
|
||||
keep_upper.remove(main_upper);
|
||||
ignore.addAll(keep_upper);
|
||||
}
|
||||
// If any other character in i's equivalence class has a
|
||||
// different canonical value, then i needs special handling. If
|
||||
// no other character shares a canonical value with i, we can
|
||||
// ignore i when adding alternatives for case-independent
|
||||
// comparison. If at least one other character shares a
|
||||
// canonical value, then i needs special handling.
|
||||
if (class_has_non_matching_canonical_char) {
|
||||
if (class_has_matching_canonical_char) {
|
||||
special_add.add(i);
|
||||
} else {
|
||||
ignore.add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove any ASCII
|
||||
special_add.remove(0x0000, 0x007f);
|
||||
PrintSet(out, "BuildIgnoreSet", ignore);
|
||||
PrintSet(out, "BuildSpecialAddSet", special_add);
|
||||
// Verify that no Unicode equivalence class contains two non-trivial
|
||||
// JS equivalence classes. Every character in SpecialAddSet has the
|
||||
// same canonical value as every other non-IgnoreSet character in
|
||||
// its Unicode equivalence class. Therefore, if we call closeOver on
|
||||
// a set containing no IgnoreSet characters, the only characters
|
||||
// that must be removed from the result are in IgnoreSet. This fact
|
||||
// is used in CharacterRange::AddCaseEquivalents.
|
||||
for (int32_t i = 0; i < special_add.getRangeCount(); i++) {
|
||||
for (UChar32 c = special_add.getRangeStart(i);
|
||||
c <= special_add.getRangeEnd(i); c++) {
|
||||
UChar32 canonical = RegExpCaseFolding::Canonicalize(c);
|
||||
current.set(c, c);
|
||||
current.closeOver(USET_CASE_INSENSITIVE);
|
||||
current.removeAll(ignore);
|
||||
for (int32_t j = 0; j < current.getRangeCount(); j++) {
|
||||
for (UChar32 c2 = current.getRangeStart(j);
|
||||
c2 <= current.getRangeEnd(j); c2++) {
|
||||
CHECK_EQ(canonical, RegExpCaseFolding::Canonicalize(c2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PrintSet(out, "IgnoreSet", ignore);
|
||||
PrintSet(out, "SpecialAddSet", special_add);
|
||||
}
|
||||
|
||||
void WriteHeader(const char* header_filename) {
|
||||
std::ofstream out(header_filename);
|
||||
out << std::hex << std::setfill('0') << std::setw(4);
|
||||
|
||||
out << "// Automatically generated by regexp/gen-regexp-special-case.cc\n"
|
||||
<< "// The following functions are used to build icu::UnicodeSet\n"
|
||||
<< "// for specical cases different between Unicode and ECMA262.\n"
|
||||
out << "// Copyright 2020 the V8 project authors. All rights reserved.\n"
|
||||
<< "// Use of this source code is governed by a BSD-style license that\n"
|
||||
<< "// can be found in the LICENSE file.\n\n"
|
||||
<< "// Automatically generated by regexp/gen-regexp-special-case.cc\n\n"
|
||||
<< "// The following functions are used to build UnicodeSets\n"
|
||||
<< "// for special cases where the case-folding algorithm used by\n"
|
||||
<< "// UnicodeSet::closeOver(USET_CASE_INSENSITIVE) does not match\n"
|
||||
<< "// the algorithm defined in ECMAScript 2020 21.2.2.8.2 (Runtime\n"
|
||||
<< "// Semantics: Canonicalize) step 3.\n\n"
|
||||
<< "#ifdef V8_INTL_SUPPORT\n"
|
||||
<< "#include \"src/base/lazy-instance.h\"\n\n"
|
||||
<< "#include \"src/regexp/special-case.h\"\n\n"
|
||||
<< "#include \"unicode/uniset.h\"\n"
|
||||
<< "namespace v8 {\n"
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
# This script handles all the mechanical steps of importing irregexp from v8:
|
||||
#
|
||||
# 1. Acquire the source: either from github, or optionally from a local copy of v8.
|
||||
# 2. Copy the contents of v8/src/regexp into js/src/regexp
|
||||
# - Exclude files that we have chosen not to import.
|
||||
# 3. While doing so, update #includes:
|
||||
# - Change "src/regexp/*" to "regexp/*".
|
||||
# - Remove other v8-specific headers completely.
|
||||
# 4. Add '#include "regexp/regexp-shim.h" in the necessary places.
|
||||
# 5. Update the VERSION file to include the correct git hash.
|
||||
#
|
||||
# Usage:
|
||||
# cd path/to/js/src/regexp
|
||||
# ./import-irregexp.py --path path/to/v8/src/regexp
|
||||
#
|
||||
# Alternatively, without the --path argument, import-irregexp.py will
|
||||
# clone v8 from github into a temporary directory.
|
||||
#
|
||||
# After running this script, changes to the shim code may be necessary
|
||||
# to account for changes in upstream irregexp.
|
||||
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def get_hash(path):
|
||||
# Get the hash for the current git revision
|
||||
cwd = os.getcwd()
|
||||
os.chdir(path)
|
||||
command = ['git', 'rev-parse', 'HEAD']
|
||||
result = subprocess.check_output(command, encoding='utf-8')
|
||||
os.chdir(cwd)
|
||||
return result.rstrip()
|
||||
|
||||
|
||||
def copy_and_update_includes(src_path, dst_path):
|
||||
# List of header files that need to include the shim header
|
||||
need_shim = ['property-sequences.h',
|
||||
'regexp-ast.h',
|
||||
'regexp-bytecode-peephole.h',
|
||||
'regexp-bytecodes.h',
|
||||
'regexp-dotprinter.h',
|
||||
'regexp.h',
|
||||
'regexp-macro-assembler.h',
|
||||
'regexp-stack.h',
|
||||
'special-case.h']
|
||||
|
||||
src = open(str(src_path), 'r')
|
||||
dst = open(str(dst_path), 'w')
|
||||
|
||||
# 1. Rewrite includes of V8 regexp headers:
|
||||
regexp_include = re.compile('#include "src/regexp')
|
||||
regexp_include_new = '#include "regexp'
|
||||
|
||||
# 2. Remove includes of other V8 headers
|
||||
other_include = re.compile('#include "src/')
|
||||
|
||||
# 3. If needed, add '#include "regexp/regexp-shim.h"'.
|
||||
# Note: We get a little fancy to ensure that header files are
|
||||
# in alphabetic order. `need_to_add_shim` is true if we still
|
||||
# have to add the shim header in this file. `adding_shim_now`
|
||||
# is true if we have found a '#include "src/*' and we are just
|
||||
# waiting to find something alphabetically smaller (or an empty
|
||||
# line) so that we can insert the shim header in the right place.
|
||||
need_to_add_shim = src_path.name in need_shim
|
||||
adding_shim_now = False
|
||||
|
||||
for line in src:
|
||||
if adding_shim_now:
|
||||
if (line == '\n' or line > '#include "src/regexp/regexp-shim.h"'):
|
||||
dst.write('#include "regexp/regexp-shim.h"\n')
|
||||
need_to_add_shim = False
|
||||
adding_shim_now = False
|
||||
|
||||
if regexp_include.search(line):
|
||||
dst.write(re.sub(regexp_include, regexp_include_new, line))
|
||||
elif other_include.search(line):
|
||||
if need_to_add_shim:
|
||||
adding_shim_now = True
|
||||
else:
|
||||
dst.write(line)
|
||||
|
||||
|
||||
def import_from(srcdir, dstdir):
|
||||
excluded = ['OWNERS',
|
||||
'regexp.cc',
|
||||
'regexp-utils.cc',
|
||||
'regexp-utils.h',
|
||||
'regexp-macro-assembler-arch.h']
|
||||
|
||||
for file in srcdir.iterdir():
|
||||
if file.is_dir():
|
||||
continue
|
||||
if str(file.name) in excluded:
|
||||
continue
|
||||
copy_and_update_includes(file, dstdir / file.name)
|
||||
|
||||
# Update VERSION file
|
||||
hash = get_hash(srcdir)
|
||||
version_file = open(str(dstdir / 'VERSION'), 'w')
|
||||
version_file.write('Imported using import-irregexp.py from:\n')
|
||||
version_file.write('https://github.com/v8/v8/tree/%s/src/regexp\n' % hash)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
import tempfile
|
||||
|
||||
# This script should be run from js/src/regexp to work correctly.
|
||||
current_path = Path(os.getcwd())
|
||||
expected_path = 'js/src/regexp'
|
||||
if not current_path.match(expected_path):
|
||||
raise RuntimeError('%s must be run from %s' % (sys.argv[0],
|
||||
expected_path))
|
||||
|
||||
parser = argparse.ArgumentParser(description='Import irregexp from v8')
|
||||
parser.add_argument('-p', '--path', help='path to v8/src/regexp')
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.path:
|
||||
src_path = Path(args.path)
|
||||
|
||||
if not (src_path / 'regexp.h').exists():
|
||||
print('Usage:\n import-irregexp.py --path <path/to/v8/src/regexp>')
|
||||
sys.exit(1)
|
||||
import_from(src_path, current_path)
|
||||
sys.exit(0)
|
||||
|
||||
with tempfile.TemporaryDirectory() as tempdir:
|
||||
v8_git = 'https://github.com/v8/v8.git'
|
||||
clone = 'git clone --depth 1 %s %s' % (v8_git, tempdir)
|
||||
os.system(clone)
|
||||
src_path = Path(tempdir) / 'src/regexp'
|
||||
import_from(src_path, current_path)
|
||||
@@ -22,10 +22,16 @@ SOURCES += [
|
||||
'regexp-interpreter.cc',
|
||||
'regexp-macro-assembler-tracer.cc',
|
||||
'regexp-macro-assembler.cc',
|
||||
'regexp-native-macro-assembler.cc',
|
||||
'regexp-parser.cc',
|
||||
'regexp-shim.cc',
|
||||
'regexp-stack.cc',
|
||||
'util/unicode.cc'
|
||||
]
|
||||
|
||||
if CONFIG['ENABLE_INTL_API']:
|
||||
CXXFLAGS += ['-DV8_INTL_SUPPORT']
|
||||
SOURCES += ['property-sequences.cc']
|
||||
SOURCES += [
|
||||
'property-sequences.cc',
|
||||
'special-case.cc'
|
||||
]
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
|
||||
#include "regexp/regexp-shim.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#ifndef V8_REGEXP_REGEXP_AST_H_
|
||||
#define V8_REGEXP_REGEXP_AST_H_
|
||||
|
||||
#include "regexp/regexp-shim.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@@ -457,7 +458,11 @@ class RegExpQuantifier final : public RegExpTree {
|
||||
class RegExpCapture final : public RegExpTree {
|
||||
public:
|
||||
explicit RegExpCapture(int index)
|
||||
: body_(nullptr), index_(index), name_(nullptr) {}
|
||||
: body_(nullptr),
|
||||
index_(index),
|
||||
min_match_(0),
|
||||
max_match_(0),
|
||||
name_(nullptr) {}
|
||||
void* Accept(RegExpVisitor* visitor, void* data) override;
|
||||
RegExpNode* ToNode(RegExpCompiler* compiler, RegExpNode* on_success) override;
|
||||
static RegExpNode* ToNode(RegExpTree* body, int index,
|
||||
@@ -467,10 +472,14 @@ class RegExpCapture final : public RegExpTree {
|
||||
bool IsAnchoredAtEnd() override;
|
||||
Interval CaptureRegisters() override;
|
||||
bool IsCapture() override;
|
||||
int min_match() override { return body_->min_match(); }
|
||||
int max_match() override { return body_->max_match(); }
|
||||
int min_match() override { return min_match_; }
|
||||
int max_match() override { return max_match_; }
|
||||
RegExpTree* body() { return body_; }
|
||||
void set_body(RegExpTree* body) { body_ = body; }
|
||||
void set_body(RegExpTree* body) {
|
||||
body_ = body;
|
||||
min_match_ = body->min_match();
|
||||
max_match_ = body->max_match();
|
||||
}
|
||||
int index() const { return index_; }
|
||||
const ZoneVector<uc16>* name() const { return name_; }
|
||||
void set_name(const ZoneVector<uc16>* name) { name_ = name; }
|
||||
@@ -480,12 +489,17 @@ class RegExpCapture final : public RegExpTree {
|
||||
private:
|
||||
RegExpTree* body_;
|
||||
int index_;
|
||||
int min_match_;
|
||||
int max_match_;
|
||||
const ZoneVector<uc16>* name_;
|
||||
};
|
||||
|
||||
class RegExpGroup final : public RegExpTree {
|
||||
public:
|
||||
explicit RegExpGroup(RegExpTree* body) : body_(body) {}
|
||||
explicit RegExpGroup(RegExpTree* body)
|
||||
: body_(body),
|
||||
min_match_(body->min_match()),
|
||||
max_match_(body->max_match()) {}
|
||||
void* Accept(RegExpVisitor* visitor, void* data) override;
|
||||
RegExpNode* ToNode(RegExpCompiler* compiler,
|
||||
RegExpNode* on_success) override {
|
||||
@@ -495,13 +509,15 @@ class RegExpGroup final : public RegExpTree {
|
||||
bool IsAnchoredAtStart() override { return body_->IsAnchoredAtStart(); }
|
||||
bool IsAnchoredAtEnd() override { return body_->IsAnchoredAtEnd(); }
|
||||
bool IsGroup() override;
|
||||
int min_match() override { return body_->min_match(); }
|
||||
int max_match() override { return body_->max_match(); }
|
||||
int min_match() override { return min_match_; }
|
||||
int max_match() override { return max_match_; }
|
||||
Interval CaptureRegisters() override { return body_->CaptureRegisters(); }
|
||||
RegExpTree* body() { return body_; }
|
||||
|
||||
private:
|
||||
RegExpTree* body_;
|
||||
int min_match_;
|
||||
int max_match_;
|
||||
};
|
||||
|
||||
class RegExpLookaround final : public RegExpTree {
|
||||
|
||||
@@ -327,13 +327,11 @@ void RegExpBytecodeGenerator::CheckNotBackReference(int start_reg,
|
||||
}
|
||||
|
||||
void RegExpBytecodeGenerator::CheckNotBackReferenceIgnoreCase(
|
||||
int start_reg, bool read_backward, bool unicode, Label* on_not_equal) {
|
||||
int start_reg, bool read_backward, Label* on_not_equal) {
|
||||
DCHECK_LE(0, start_reg);
|
||||
DCHECK_GE(kMaxRegister, start_reg);
|
||||
Emit(read_backward ? (unicode ? BC_CHECK_NOT_BACK_REF_NO_CASE_UNICODE_BACKWARD
|
||||
: BC_CHECK_NOT_BACK_REF_NO_CASE_BACKWARD)
|
||||
: (unicode ? BC_CHECK_NOT_BACK_REF_NO_CASE_UNICODE
|
||||
: BC_CHECK_NOT_BACK_REF_NO_CASE),
|
||||
Emit(read_backward ? BC_CHECK_NOT_BACK_REF_NO_CASE_BACKWARD
|
||||
: BC_CHECK_NOT_BACK_REF_NO_CASE,
|
||||
start_reg);
|
||||
EmitOrLink(on_not_equal);
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ class V8_EXPORT_PRIVATE RegExpBytecodeGenerator : public RegExpMacroAssembler {
|
||||
virtual void CheckNotBackReference(int start_reg, bool read_backward,
|
||||
Label* on_no_match);
|
||||
virtual void CheckNotBackReferenceIgnoreCase(int start_reg,
|
||||
bool read_backward, bool unicode,
|
||||
bool read_backward,
|
||||
Label* on_no_match);
|
||||
virtual void IfRegisterLT(int register_index, int comparand, Label* if_lt);
|
||||
virtual void IfRegisterGE(int register_index, int comparand, Label* if_ge);
|
||||
|
||||
@@ -428,7 +428,6 @@ BytecodeArgumentMapping BytecodeSequenceNode::ArgumentMapping(
|
||||
size_t index) const {
|
||||
DCHECK(IsSequence());
|
||||
DCHECK(argument_mapping_ != nullptr);
|
||||
DCHECK_GE(index, 0);
|
||||
DCHECK_LT(index, argument_mapping_->size());
|
||||
|
||||
return argument_mapping_->at(index);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#ifndef V8_REGEXP_REGEXP_BYTECODE_PEEPHOLE_H_
|
||||
#define V8_REGEXP_REGEXP_BYTECODE_PEEPHOLE_H_
|
||||
|
||||
#include "regexp/regexp-shim.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#ifndef V8_REGEXP_REGEXP_BYTECODES_H_
|
||||
#define V8_REGEXP_REGEXP_BYTECODES_H_
|
||||
|
||||
#include "regexp/regexp-shim.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@@ -99,12 +100,12 @@ STATIC_ASSERT(1 << BYTECODE_SHIFT > BYTECODE_MASK);
|
||||
V(CHECK_BIT_IN_TABLE, 34, 24) /* bc8 pad24 addr32 bits128 */ \
|
||||
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_BACK_REF_NO_CASE_UNICODE, 39, 8) \
|
||||
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_BACK_REF_NO_CASE_UNICODE, 39, 8) /* UNUSED */ \
|
||||
V(CHECK_NOT_BACK_REF_BACKWARD, 40, 8) /* bc8 reg_idx24 addr32 */ \
|
||||
V(CHECK_NOT_BACK_REF_NO_CASE_BACKWARD, 41, 8) /* bc8 reg_idx24 addr32 */ \
|
||||
V(CHECK_NOT_BACK_REF_NO_CASE_UNICODE_BACKWARD, 42, 8) \
|
||||
V(CHECK_NOT_BACK_REF_NO_CASE_UNICODE_BACKWARD, 42, 8) /* UNUSED */ \
|
||||
V(CHECK_NOT_REGS_EQUAL, 43, 12) /* bc8 regidx24 reg_idx32 addr32 */ \
|
||||
V(CHECK_REGISTER_LT, 44, 12) /* bc8 reg_idx24 value32 addr32 */ \
|
||||
V(CHECK_REGISTER_GE, 45, 12) /* bc8 reg_idx24 value32 addr32 */ \
|
||||
|
||||
@@ -1137,39 +1137,6 @@ Vector<const int> CharacterRange::GetWordBounds() {
|
||||
return Vector<const int>(kWordRanges, kWordRangeCount - 1);
|
||||
}
|
||||
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
struct IgnoreSet {
|
||||
IgnoreSet() : set(BuildIgnoreSet()) {}
|
||||
const icu::UnicodeSet set;
|
||||
};
|
||||
|
||||
struct SpecialAddSet {
|
||||
SpecialAddSet() : set(BuildSpecialAddSet()) {}
|
||||
const icu::UnicodeSet set;
|
||||
};
|
||||
|
||||
icu::UnicodeSet BuildAsciiAToZSet() {
|
||||
icu::UnicodeSet set('a', 'z');
|
||||
set.add('A', 'Z');
|
||||
set.freeze();
|
||||
return set;
|
||||
}
|
||||
|
||||
struct AsciiAToZSet {
|
||||
AsciiAToZSet() : set(BuildAsciiAToZSet()) {}
|
||||
const icu::UnicodeSet set;
|
||||
};
|
||||
|
||||
static base::LazyInstance<IgnoreSet>::type ignore_set =
|
||||
LAZY_INSTANCE_INITIALIZER;
|
||||
|
||||
static base::LazyInstance<SpecialAddSet>::type special_add_set =
|
||||
LAZY_INSTANCE_INITIALIZER;
|
||||
|
||||
static base::LazyInstance<AsciiAToZSet>::type ascii_a_to_z_set =
|
||||
LAZY_INSTANCE_INITIALIZER;
|
||||
#endif // V8_INTL_SUPPORT
|
||||
|
||||
// static
|
||||
void CharacterRange::AddCaseEquivalents(Isolate* isolate, Zone* zone,
|
||||
ZoneList<CharacterRange>* ranges,
|
||||
@@ -1192,75 +1159,22 @@ void CharacterRange::AddCaseEquivalents(Isolate* isolate, Zone* zone,
|
||||
others.add(from, to);
|
||||
}
|
||||
|
||||
// Set of characters already added to ranges that do not need to be added
|
||||
// again.
|
||||
// Compute the set of additional characters that should be added,
|
||||
// using UnicodeSet::closeOver. ECMA 262 defines slightly different
|
||||
// case-folding rules than Unicode, so some characters that are
|
||||
// added by closeOver do not match anything other than themselves in
|
||||
// JS. For example, 'ſ' (U+017F LATIN SMALL LETTER LONG S) is the
|
||||
// same case-insensitive character as 's' or 'S' according to
|
||||
// Unicode, but does not match any other character in JS. To handle
|
||||
// this case, we add such characters to the IgnoreSet and filter
|
||||
// them out. We filter twice: once before calling closeOver (to
|
||||
// prevent 'ſ' from adding 's'), and once after calling closeOver
|
||||
// (to prevent 's' from adding 'ſ'). See regexp/special-case.h for
|
||||
// more information.
|
||||
icu::UnicodeSet already_added(others);
|
||||
|
||||
// Set of characters in ranges that are in the 52 ASCII characters [a-zA-Z].
|
||||
icu::UnicodeSet in_ascii_a_to_z(others);
|
||||
in_ascii_a_to_z.retainAll(ascii_a_to_z_set.Pointer()->set);
|
||||
|
||||
// Remove all chars in [a-zA-Z] from others.
|
||||
others.removeAll(in_ascii_a_to_z);
|
||||
|
||||
// Set of characters in ranges that are overlapping with special add set.
|
||||
icu::UnicodeSet in_special_add(others);
|
||||
in_special_add.retainAll(special_add_set.Pointer()->set);
|
||||
|
||||
others.removeAll(in_special_add);
|
||||
|
||||
// Ignore all chars in ignore set.
|
||||
others.removeAll(ignore_set.Pointer()->set);
|
||||
|
||||
// For most of the chars in ranges that is still in others, find the case
|
||||
// equivlant set by calling closeOver(USET_CASE_INSENSITIVE).
|
||||
others.removeAll(RegExpCaseFolding::IgnoreSet());
|
||||
others.closeOver(USET_CASE_INSENSITIVE);
|
||||
|
||||
// Because closeOver(USET_CASE_INSENSITIVE) may add ASCII [a-zA-Z] to others,
|
||||
// but ECMA262 "i" mode won't consider that, remove them from others.
|
||||
// Ex: U+017F add 'S' and 's' to others.
|
||||
others.removeAll(ascii_a_to_z_set.Pointer()->set);
|
||||
|
||||
// Special handling for in_ascii_a_to_z.
|
||||
for (int32_t i = 0; i < in_ascii_a_to_z.getRangeCount(); i++) {
|
||||
UChar32 start = in_ascii_a_to_z.getRangeStart(i);
|
||||
UChar32 end = in_ascii_a_to_z.getRangeEnd(i);
|
||||
// Check if it is uppercase A-Z by checking bit 6.
|
||||
if (start & 0x0020) {
|
||||
// Add the lowercases
|
||||
others.add(start & 0x005F, end & 0x005F);
|
||||
} else {
|
||||
// Add the uppercases
|
||||
others.add(start | 0x0020, end | 0x0020);
|
||||
}
|
||||
}
|
||||
|
||||
// Special handling for chars in "Special Add" set.
|
||||
for (int32_t i = 0; i < in_special_add.getRangeCount(); i++) {
|
||||
UChar32 end = in_special_add.getRangeEnd(i);
|
||||
for (UChar32 ch = in_special_add.getRangeStart(i); ch <= end; ch++) {
|
||||
// Add the uppercase of this character if itself is not an uppercase
|
||||
// character.
|
||||
// Note: The if condiction cannot be u_islower(ch) because ch could be
|
||||
// neither uppercase nor lowercase but Mn.
|
||||
if (!u_isupper(ch)) {
|
||||
others.add(u_toupper(ch));
|
||||
}
|
||||
icu::UnicodeSet candidates(ch, ch);
|
||||
candidates.closeOver(USET_CASE_INSENSITIVE);
|
||||
for (int32_t j = 0; j < candidates.getRangeCount(); j++) {
|
||||
UChar32 end2 = candidates.getRangeEnd(j);
|
||||
for (UChar32 ch2 = candidates.getRangeStart(j); ch2 <= end2; ch2++) {
|
||||
// Add character that is not uppercase to others.
|
||||
if (!u_isupper(ch2)) {
|
||||
others.add(ch2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all characters which already in the ranges.
|
||||
others.removeAll(RegExpCaseFolding::IgnoreSet());
|
||||
others.removeAll(already_added);
|
||||
|
||||
// Add others to the ranges
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
#include "regexp/regexp-compiler.h"
|
||||
|
||||
#include "regexp/regexp-macro-assembler-arch.h"
|
||||
#include "regexp/regexp-macro-assembler-tracer.h"
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
#include "regexp/special-case.h"
|
||||
#endif // V8_INTL_SUPPORT
|
||||
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
#include "unicode/locid.h"
|
||||
@@ -237,20 +239,15 @@ RegExpCompiler::RegExpCompiler(Isolate* isolate, Zone* zone, int capture_count,
|
||||
RegExpCompiler::CompilationResult RegExpCompiler::Assemble(
|
||||
Isolate* isolate, RegExpMacroAssembler* macro_assembler, RegExpNode* start,
|
||||
int capture_count, Handle<String> pattern) {
|
||||
#ifdef DEBUG
|
||||
if (FLAG_trace_regexp_assembler)
|
||||
macro_assembler_ = new RegExpMacroAssemblerTracer(isolate, macro_assembler);
|
||||
else
|
||||
#endif
|
||||
macro_assembler_ = macro_assembler;
|
||||
macro_assembler_ = macro_assembler;
|
||||
|
||||
std::vector<RegExpNode*> work_list;
|
||||
ZoneVector<RegExpNode*> work_list(zone());
|
||||
work_list_ = &work_list;
|
||||
Label fail;
|
||||
macro_assembler_->PushBacktrack(&fail);
|
||||
Trace new_trace;
|
||||
start->Emit(this, &new_trace);
|
||||
macro_assembler_->Bind(&fail);
|
||||
macro_assembler_->BindJumpTarget(&fail);
|
||||
macro_assembler_->Fail();
|
||||
while (!work_list.empty()) {
|
||||
RegExpNode* node = work_list.back();
|
||||
@@ -264,14 +261,9 @@ RegExpCompiler::CompilationResult RegExpCompiler::Assemble(
|
||||
}
|
||||
|
||||
Handle<HeapObject> code = macro_assembler_->GetCode(pattern);
|
||||
isolate->IncreaseTotalRegexpCodeGenerated(code->Size());
|
||||
isolate->IncreaseTotalRegexpCodeGenerated(code);
|
||||
work_list_ = nullptr;
|
||||
|
||||
#ifdef DEBUG
|
||||
if (FLAG_trace_regexp_assembler) {
|
||||
delete macro_assembler_;
|
||||
}
|
||||
#endif
|
||||
return {*code, next_register_};
|
||||
}
|
||||
|
||||
@@ -557,7 +549,7 @@ void Trace::Flush(RegExpCompiler* compiler, RegExpNode* successor) {
|
||||
}
|
||||
|
||||
// On backtrack we need to restore state.
|
||||
assembler->Bind(&undo);
|
||||
assembler->BindJumpTarget(&undo);
|
||||
RestoreAffectedRegisters(assembler, max_register, registers_to_pop,
|
||||
registers_to_clear);
|
||||
if (backtrack() == nullptr) {
|
||||
@@ -720,32 +712,34 @@ static int GetCaseIndependentLetters(Isolate* isolate, uc16 character,
|
||||
unibrow::uchar* letters,
|
||||
int letter_length) {
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
// Special case for U+017F which has upper case in ASCII range.
|
||||
if (character == 0x017f) {
|
||||
if (RegExpCaseFolding::IgnoreSet().contains(character)) {
|
||||
letters[0] = character;
|
||||
return 1;
|
||||
}
|
||||
bool in_special_add_set =
|
||||
RegExpCaseFolding::SpecialAddSet().contains(character);
|
||||
|
||||
icu::UnicodeSet set;
|
||||
set.add(character);
|
||||
set = set.closeOver(USET_CASE_INSENSITIVE);
|
||||
|
||||
UChar32 canon = 0;
|
||||
if (in_special_add_set) {
|
||||
canon = RegExpCaseFolding::Canonicalize(character);
|
||||
}
|
||||
|
||||
int32_t range_count = set.getRangeCount();
|
||||
int items = 0;
|
||||
for (int32_t i = 0; i < range_count; i++) {
|
||||
UChar32 start = set.getRangeStart(i);
|
||||
UChar32 end = set.getRangeEnd(i);
|
||||
CHECK(end - start + items <= letter_length);
|
||||
// Only add to the output if character is not in ASCII range
|
||||
// or the case equivalent character is in ASCII range.
|
||||
// #sec-runtime-semantics-canonicalize-ch
|
||||
// 3.g If the numeric value of ch ≥ 128 and the numeric value of cu < 128,
|
||||
// return ch.
|
||||
if (!((start >= 128) && (character < 128))) {
|
||||
// No range have start and end span across code point 128.
|
||||
DCHECK((start >= 128) == (end >= 128));
|
||||
for (UChar32 cu = start; cu <= end; cu++) {
|
||||
if (one_byte_subject && cu > String::kMaxOneByteCharCode) break;
|
||||
letters[items++] = (unibrow::uchar)(cu);
|
||||
for (UChar32 cu = start; cu <= end; cu++) {
|
||||
if (one_byte_subject && cu > String::kMaxOneByteCharCode) break;
|
||||
if (in_special_add_set && RegExpCaseFolding::Canonicalize(cu) != canon) {
|
||||
continue;
|
||||
}
|
||||
letters[items++] = (unibrow::uchar)(cu);
|
||||
}
|
||||
}
|
||||
return items;
|
||||
@@ -852,10 +846,6 @@ static bool ShortCutEmitCharacterPair(RegExpMacroAssembler* macro_assembler,
|
||||
return false;
|
||||
}
|
||||
|
||||
using EmitCharacterFunction = bool(Isolate* isolate, RegExpCompiler* compiler,
|
||||
uc16 c, Label* on_failure, int cp_offset,
|
||||
bool check, bool preloaded);
|
||||
|
||||
// Only emits letters (things that have case). Only used for case independent
|
||||
// matches.
|
||||
static inline bool EmitAtomLetter(Isolate* isolate, RegExpCompiler* compiler,
|
||||
@@ -1843,13 +1833,13 @@ RegExpNode* TextNode::FilterOneByte(int depth) {
|
||||
if (elm.text_type() == TextElement::ATOM) {
|
||||
Vector<const uc16> quarks = elm.atom()->data();
|
||||
for (int j = 0; j < quarks.length(); j++) {
|
||||
uint16_t c = quarks[j];
|
||||
uc16 c = quarks[j];
|
||||
if (elm.atom()->ignore_case()) {
|
||||
c = unibrow::Latin1::TryConvertToLatin1(c);
|
||||
}
|
||||
if (c > unibrow::Latin1::kMaxChar) return set_replacement(nullptr);
|
||||
// Replace quark in case we converted to Latin-1.
|
||||
uint16_t* writable_quarks = const_cast<uint16_t*>(quarks.begin());
|
||||
uc16* writable_quarks = const_cast<uc16*>(quarks.begin());
|
||||
writable_quarks[j] = c;
|
||||
}
|
||||
} else {
|
||||
@@ -2304,7 +2294,6 @@ void TextNode::TextEmitPass(RegExpCompiler* compiler, TextEmitPassType pass,
|
||||
for (int j = preloaded ? 0 : quarks.length() - 1; j >= 0; j--) {
|
||||
if (first_element_checked && i == 0 && j == 0) continue;
|
||||
if (DeterminedAlready(quick_check, elm.cp_offset() + j)) continue;
|
||||
EmitCharacterFunction* emit_function = nullptr;
|
||||
uc16 quark = quarks[j];
|
||||
if (elm.atom()->ignore_case()) {
|
||||
// Everywhere else we assume that a non-Latin-1 character cannot match
|
||||
@@ -2312,6 +2301,9 @@ void TextNode::TextEmitPass(RegExpCompiler* compiler, TextEmitPassType pass,
|
||||
// invalid by using the Latin1 equivalent instead.
|
||||
quark = unibrow::Latin1::TryConvertToLatin1(quark);
|
||||
}
|
||||
bool needs_bounds_check =
|
||||
*checked_up_to < cp_offset + j || read_backward();
|
||||
bool bounds_checked = false;
|
||||
switch (pass) {
|
||||
case NON_LATIN1_MATCH:
|
||||
DCHECK(one_byte);
|
||||
@@ -2321,24 +2313,24 @@ void TextNode::TextEmitPass(RegExpCompiler* compiler, TextEmitPassType pass,
|
||||
}
|
||||
break;
|
||||
case NON_LETTER_CHARACTER_MATCH:
|
||||
emit_function = &EmitAtomNonLetter;
|
||||
bounds_checked =
|
||||
EmitAtomNonLetter(isolate, compiler, quark, backtrack,
|
||||
cp_offset + j, needs_bounds_check, preloaded);
|
||||
break;
|
||||
case SIMPLE_CHARACTER_MATCH:
|
||||
emit_function = &EmitSimpleCharacter;
|
||||
bounds_checked = EmitSimpleCharacter(isolate, compiler, quark,
|
||||
backtrack, cp_offset + j,
|
||||
needs_bounds_check, preloaded);
|
||||
break;
|
||||
case CASE_CHARACTER_MATCH:
|
||||
emit_function = &EmitAtomLetter;
|
||||
bounds_checked =
|
||||
EmitAtomLetter(isolate, compiler, quark, backtrack,
|
||||
cp_offset + j, needs_bounds_check, preloaded);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (emit_function != nullptr) {
|
||||
bool bounds_check = *checked_up_to < cp_offset + j || read_backward();
|
||||
bool bound_checked =
|
||||
emit_function(isolate, compiler, quark, backtrack, cp_offset + j,
|
||||
bounds_check, preloaded);
|
||||
if (bound_checked) UpdateBoundsCheck(cp_offset + j, checked_up_to);
|
||||
}
|
||||
if (bounds_checked) UpdateBoundsCheck(cp_offset + j, checked_up_to);
|
||||
}
|
||||
} else {
|
||||
DCHECK_EQ(TextElement::CHAR_CLASS, elm.text_type());
|
||||
@@ -3424,8 +3416,8 @@ void BackReferenceNode::Emit(RegExpCompiler* compiler, Trace* trace) {
|
||||
|
||||
DCHECK_EQ(start_reg_ + 1, end_reg_);
|
||||
if (IgnoreCase(flags_)) {
|
||||
assembler->CheckNotBackReferenceIgnoreCase(
|
||||
start_reg_, read_backward(), IsUnicode(flags_), trace->backtrack());
|
||||
assembler->CheckNotBackReferenceIgnoreCase(start_reg_, read_backward(),
|
||||
trace->backtrack());
|
||||
} else {
|
||||
assembler->CheckNotBackReference(start_reg_, read_backward(),
|
||||
trace->backtrack());
|
||||
@@ -3597,12 +3589,17 @@ template <typename... Propagators>
|
||||
class Analysis : public NodeVisitor {
|
||||
public:
|
||||
Analysis(Isolate* isolate, bool is_one_byte)
|
||||
: isolate_(isolate), is_one_byte_(is_one_byte), error_message_(nullptr) {}
|
||||
: isolate_(isolate),
|
||||
is_one_byte_(is_one_byte),
|
||||
error_(RegExpError::kNone) {}
|
||||
|
||||
void EnsureAnalyzed(RegExpNode* that) {
|
||||
StackLimitCheck check(isolate());
|
||||
if (check.HasOverflowed()) {
|
||||
fail("Stack overflow");
|
||||
if (FLAG_correctness_fuzzer_suppressions) {
|
||||
FATAL("Analysis: Aborting on stack overflow");
|
||||
}
|
||||
fail(RegExpError::kAnalysisStackOverflow);
|
||||
return;
|
||||
}
|
||||
if (that->info()->been_analyzed || that->info()->being_analyzed) return;
|
||||
@@ -3612,12 +3609,12 @@ class Analysis : public NodeVisitor {
|
||||
that->info()->been_analyzed = true;
|
||||
}
|
||||
|
||||
bool has_failed() { return error_message_ != nullptr; }
|
||||
const char* error_message() {
|
||||
DCHECK(error_message_ != nullptr);
|
||||
return error_message_;
|
||||
bool has_failed() { return error_ != RegExpError::kNone; }
|
||||
RegExpError error() {
|
||||
DCHECK(error_ != RegExpError::kNone);
|
||||
return error_;
|
||||
}
|
||||
void fail(const char* error_message) { error_message_ = error_message; }
|
||||
void fail(RegExpError error) { error_ = error; }
|
||||
|
||||
Isolate* isolate() const { return isolate_; }
|
||||
|
||||
@@ -3702,19 +3699,19 @@ class Analysis : public NodeVisitor {
|
||||
private:
|
||||
Isolate* isolate_;
|
||||
bool is_one_byte_;
|
||||
const char* error_message_;
|
||||
RegExpError error_;
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(Analysis);
|
||||
};
|
||||
|
||||
const char* AnalyzeRegExp(Isolate* isolate, bool is_one_byte,
|
||||
RegExpError AnalyzeRegExp(Isolate* isolate, bool is_one_byte,
|
||||
RegExpNode* node) {
|
||||
Analysis<AssertionPropagator, EatsAtLeastPropagator> analysis(isolate,
|
||||
is_one_byte);
|
||||
DCHECK_EQ(node->info()->been_analyzed, false);
|
||||
analysis.EnsureAnalyzed(node);
|
||||
DCHECK_IMPLIES(analysis.has_failed(), analysis.error_message() != nullptr);
|
||||
return analysis.has_failed() ? analysis.error_message() : nullptr;
|
||||
DCHECK_IMPLIES(analysis.has_failed(), analysis.error() != RegExpError::kNone);
|
||||
return analysis.has_failed() ? analysis.error() : RegExpError::kNone;
|
||||
}
|
||||
|
||||
void BackReferenceNode::FillInBMInfo(Isolate* isolate, int offset, int budget,
|
||||
|
||||
@@ -422,10 +422,7 @@ struct PreloadState {
|
||||
// Analysis performs assertion propagation and computes eats_at_least_ values.
|
||||
// See the comments on AssertionPropagator and EatsAtLeastPropagator for more
|
||||
// details.
|
||||
//
|
||||
// This method returns nullptr on success or a null-terminated failure message
|
||||
// on failure.
|
||||
const char* AnalyzeRegExp(Isolate* isolate, bool is_one_byte, RegExpNode* node);
|
||||
RegExpError AnalyzeRegExp(Isolate* isolate, bool is_one_byte, RegExpNode* node);
|
||||
|
||||
class FrequencyCollator {
|
||||
public:
|
||||
@@ -502,18 +499,17 @@ class RegExpCompiler {
|
||||
}
|
||||
|
||||
struct CompilationResult final {
|
||||
explicit CompilationResult(const char* error_message)
|
||||
: error_message(error_message) {}
|
||||
explicit CompilationResult(RegExpError err) : error(err) {}
|
||||
CompilationResult(Object code, int registers)
|
||||
: code(code), num_registers(registers) {}
|
||||
|
||||
static CompilationResult RegExpTooBig() {
|
||||
return CompilationResult("RegExp too big");
|
||||
return CompilationResult(RegExpError::kTooLarge);
|
||||
}
|
||||
|
||||
bool Succeeded() const { return error_message == nullptr; }
|
||||
bool Succeeded() const { return error == RegExpError::kNone; }
|
||||
|
||||
const char* const error_message = nullptr;
|
||||
const RegExpError error = RegExpError::kNone;
|
||||
Object code;
|
||||
int num_registers = 0;
|
||||
};
|
||||
@@ -575,7 +571,7 @@ class RegExpCompiler {
|
||||
int next_register_;
|
||||
int unicode_lookaround_stack_register_;
|
||||
int unicode_lookaround_position_register_;
|
||||
std::vector<RegExpNode*>* work_list_;
|
||||
ZoneVector<RegExpNode*>* work_list_;
|
||||
int recursion_depth_;
|
||||
RegExpMacroAssembler* macro_assembler_;
|
||||
bool one_byte_;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#ifndef V8_REGEXP_REGEXP_DOTPRINTER_H_
|
||||
#define V8_REGEXP_REGEXP_DOTPRINTER_H_
|
||||
|
||||
#include "regexp/regexp-shim.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright 2020 the V8 project 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 "regexp/regexp-error.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
const char* kRegExpErrorStrings[] = {
|
||||
#define TEMPLATE(NAME, STRING) STRING,
|
||||
REGEXP_ERROR_MESSAGES(TEMPLATE)
|
||||
#undef TEMPLATE
|
||||
};
|
||||
|
||||
const char* RegExpErrorString(RegExpError error) {
|
||||
DCHECK_LT(error, RegExpError::NumErrors);
|
||||
return kRegExpErrorStrings[static_cast<int>(error)];
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright 2020 the V8 project 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 V8_REGEXP_REGEXP_ERROR_H_
|
||||
#define V8_REGEXP_REGEXP_ERROR_H_
|
||||
|
||||
#include "regexp-shim.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
#define REGEXP_ERROR_MESSAGES(T) \
|
||||
T(None, "") \
|
||||
T(StackOverflow, "Maximum call stack size exceeded") \
|
||||
T(AnalysisStackOverflow, "Stack overflow") \
|
||||
T(TooLarge, "Regular expression too large") \
|
||||
T(UnterminatedGroup, "Unterminated group") \
|
||||
T(UnmatchedParen, "Unmatched ')'") \
|
||||
T(EscapeAtEndOfPattern, "\\ at end of pattern") \
|
||||
T(InvalidPropertyName, "Invalid property name") \
|
||||
T(InvalidEscape, "Invalid escape") \
|
||||
T(InvalidDecimalEscape, "Invalid decimal escape") \
|
||||
T(InvalidUnicodeEscape, "Invalid Unicode escape") \
|
||||
T(NothingToRepeat, "Nothing to repeat") \
|
||||
T(LoneQuantifierBrackets, "Lone quantifier brackets") \
|
||||
T(RangeOutOfOrder, "numbers out of order in {} quantifier") \
|
||||
T(IncompleteQuantifier, "Incomplete quantifier") \
|
||||
T(InvalidQuantifier, "Invalid quantifier") \
|
||||
T(InvalidGroup, "Invalid group") \
|
||||
T(MultipleFlagDashes, "Multiple dashes in flag group") \
|
||||
T(RepeatedFlag, "Repeated flag in flag group") \
|
||||
T(InvalidFlagGroup, "Invalid flag group") \
|
||||
T(TooManyCaptures, "Too many captures") \
|
||||
T(InvalidCaptureGroupName, "Invalid capture group name") \
|
||||
T(DuplicateCaptureGroupName, "Duplicate capture group name") \
|
||||
T(InvalidNamedReference, "Invalid named reference") \
|
||||
T(InvalidNamedCaptureReference, "Invalid named capture referenced") \
|
||||
T(InvalidClassEscape, "Invalid class escape") \
|
||||
T(InvalidClassPropertyName, "Invalid property name in character class") \
|
||||
T(InvalidCharacterClass, "Invalid character class") \
|
||||
T(UnterminatedCharacterClass, "Unterminated character class") \
|
||||
T(OutOfOrderCharacterClass, "Range out of order in character class")
|
||||
|
||||
enum class RegExpError : uint32_t {
|
||||
#define TEMPLATE(NAME, STRING) k##NAME,
|
||||
REGEXP_ERROR_MESSAGES(TEMPLATE)
|
||||
#undef TEMPLATE
|
||||
NumErrors
|
||||
};
|
||||
|
||||
V8_EXPORT_PRIVATE const char* RegExpErrorString(RegExpError error);
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_REGEXP_REGEXP_ERROR_H_
|
||||
@@ -28,18 +28,18 @@ namespace internal {
|
||||
namespace {
|
||||
|
||||
bool BackRefMatchesNoCase(Isolate* isolate, int from, int current, int len,
|
||||
Vector<const uc16> subject, bool unicode) {
|
||||
Vector<const uc16> subject) {
|
||||
Address offset_a =
|
||||
reinterpret_cast<Address>(const_cast<uc16*>(&subject.at(from)));
|
||||
Address offset_b =
|
||||
reinterpret_cast<Address>(const_cast<uc16*>(&subject.at(current)));
|
||||
size_t length = len * kUC16Size;
|
||||
return RegExpMacroAssembler::CaseInsensitiveCompareUC16(
|
||||
offset_a, offset_b, length, unicode ? nullptr : isolate) == 1;
|
||||
return RegExpMacroAssembler::CaseInsensitiveCompareUC16(offset_a, offset_b,
|
||||
length, isolate) == 1;
|
||||
}
|
||||
|
||||
bool BackRefMatchesNoCase(Isolate* isolate, int from, int current, int len,
|
||||
Vector<const uint8_t> subject, bool unicode) {
|
||||
Vector<const uint8_t> subject) {
|
||||
// For Latin1 characters the unicode flag makes no difference.
|
||||
for (int i = 0; i < len; i++) {
|
||||
unsigned int old_char = subject[from++];
|
||||
@@ -82,11 +82,17 @@ int32_t Load32Aligned(const byte* pc) {
|
||||
return *reinterpret_cast<const int32_t*>(pc);
|
||||
}
|
||||
|
||||
int32_t Load16Aligned(const byte* pc) {
|
||||
// TODO(jgruber): Rename to Load16AlignedUnsigned.
|
||||
uint32_t Load16Aligned(const byte* pc) {
|
||||
DCHECK_EQ(0, reinterpret_cast<intptr_t>(pc) & 1);
|
||||
return *reinterpret_cast<const uint16_t*>(pc);
|
||||
}
|
||||
|
||||
int32_t Load16AlignedSigned(const byte* pc) {
|
||||
DCHECK_EQ(0, reinterpret_cast<intptr_t>(pc) & 1);
|
||||
return *reinterpret_cast<const int16_t*>(pc);
|
||||
}
|
||||
|
||||
// A simple abstraction over the backtracking stack used by the interpreter.
|
||||
//
|
||||
// Despite the name 'backtracking' stack, it's actually used as a generic stack
|
||||
@@ -734,26 +740,14 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
|
||||
DISPATCH();
|
||||
}
|
||||
BYTECODE(CHECK_NOT_BACK_REF_NO_CASE_UNICODE) {
|
||||
int from = registers[insn >> BYTECODE_SHIFT];
|
||||
int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
|
||||
if (from >= 0 && len > 0) {
|
||||
if (current + len > subject.length() ||
|
||||
!BackRefMatchesNoCase(isolate, from, current, len, subject, true)) {
|
||||
SET_PC_FROM_OFFSET(Load32Aligned(pc + 4));
|
||||
DISPATCH();
|
||||
}
|
||||
current += len;
|
||||
}
|
||||
ADVANCE(CHECK_NOT_BACK_REF_NO_CASE_UNICODE);
|
||||
DISPATCH();
|
||||
UNREACHABLE(); // TODO(jgruber): Remove this unused bytecode.
|
||||
}
|
||||
BYTECODE(CHECK_NOT_BACK_REF_NO_CASE) {
|
||||
int from = registers[insn >> BYTECODE_SHIFT];
|
||||
int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
|
||||
if (from >= 0 && len > 0) {
|
||||
if (current + len > subject.length() ||
|
||||
!BackRefMatchesNoCase(isolate, from, current, len, subject,
|
||||
false)) {
|
||||
!BackRefMatchesNoCase(isolate, from, current, len, subject)) {
|
||||
SET_PC_FROM_OFFSET(Load32Aligned(pc + 4));
|
||||
DISPATCH();
|
||||
}
|
||||
@@ -763,27 +757,14 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
|
||||
DISPATCH();
|
||||
}
|
||||
BYTECODE(CHECK_NOT_BACK_REF_NO_CASE_UNICODE_BACKWARD) {
|
||||
int from = registers[insn >> BYTECODE_SHIFT];
|
||||
int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
|
||||
if (from >= 0 && len > 0) {
|
||||
if (current - len < 0 ||
|
||||
!BackRefMatchesNoCase(isolate, from, current - len, len, subject,
|
||||
true)) {
|
||||
SET_PC_FROM_OFFSET(Load32Aligned(pc + 4));
|
||||
DISPATCH();
|
||||
}
|
||||
current -= len;
|
||||
}
|
||||
ADVANCE(CHECK_NOT_BACK_REF_NO_CASE_UNICODE_BACKWARD);
|
||||
DISPATCH();
|
||||
UNREACHABLE(); // TODO(jgruber): Remove this unused bytecode.
|
||||
}
|
||||
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) {
|
||||
if (current - len < 0 ||
|
||||
!BackRefMatchesNoCase(isolate, from, current - len, len, subject,
|
||||
false)) {
|
||||
!BackRefMatchesNoCase(isolate, from, current - len, len, subject)) {
|
||||
SET_PC_FROM_OFFSET(Load32Aligned(pc + 4));
|
||||
DISPATCH();
|
||||
}
|
||||
@@ -828,7 +809,7 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
|
||||
}
|
||||
BYTECODE(SKIP_UNTIL_CHAR) {
|
||||
int load_offset = (insn >> BYTECODE_SHIFT);
|
||||
uint32_t advance = Load16Aligned(pc + 4);
|
||||
int32_t advance = Load16AlignedSigned(pc + 4);
|
||||
uint32_t c = Load16Aligned(pc + 6);
|
||||
while (static_cast<uintptr_t>(current + load_offset) <
|
||||
static_cast<uintptr_t>(subject.length())) {
|
||||
@@ -844,7 +825,7 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
|
||||
}
|
||||
BYTECODE(SKIP_UNTIL_CHAR_AND) {
|
||||
int load_offset = (insn >> BYTECODE_SHIFT);
|
||||
uint16_t advance = Load16Aligned(pc + 4);
|
||||
int32_t advance = Load16AlignedSigned(pc + 4);
|
||||
uint16_t c = Load16Aligned(pc + 6);
|
||||
uint32_t mask = Load32Aligned(pc + 8);
|
||||
int32_t maximum_offset = Load32Aligned(pc + 12);
|
||||
@@ -862,7 +843,7 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
|
||||
}
|
||||
BYTECODE(SKIP_UNTIL_CHAR_POS_CHECKED) {
|
||||
int load_offset = (insn >> BYTECODE_SHIFT);
|
||||
uint16_t advance = Load16Aligned(pc + 4);
|
||||
int32_t advance = Load16AlignedSigned(pc + 4);
|
||||
uint16_t c = Load16Aligned(pc + 6);
|
||||
int32_t maximum_offset = Load32Aligned(pc + 8);
|
||||
while (static_cast<uintptr_t>(current + maximum_offset) <=
|
||||
@@ -879,7 +860,7 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
|
||||
}
|
||||
BYTECODE(SKIP_UNTIL_BIT_IN_TABLE) {
|
||||
int load_offset = (insn >> BYTECODE_SHIFT);
|
||||
uint32_t advance = Load16Aligned(pc + 4);
|
||||
int32_t advance = Load16AlignedSigned(pc + 4);
|
||||
const byte* table = pc + 8;
|
||||
while (static_cast<uintptr_t>(current + load_offset) <
|
||||
static_cast<uintptr_t>(subject.length())) {
|
||||
@@ -895,7 +876,7 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
|
||||
}
|
||||
BYTECODE(SKIP_UNTIL_GT_OR_NOT_BIT_IN_TABLE) {
|
||||
int load_offset = (insn >> BYTECODE_SHIFT);
|
||||
uint16_t advance = Load16Aligned(pc + 4);
|
||||
int32_t advance = Load16AlignedSigned(pc + 4);
|
||||
uint16_t limit = Load16Aligned(pc + 6);
|
||||
const byte* table = pc + 8;
|
||||
while (static_cast<uintptr_t>(current + load_offset) <
|
||||
@@ -916,7 +897,7 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
|
||||
}
|
||||
BYTECODE(SKIP_UNTIL_CHAR_OR_CHAR) {
|
||||
int load_offset = (insn >> BYTECODE_SHIFT);
|
||||
uint32_t advance = Load32Aligned(pc + 4);
|
||||
int32_t advance = Load32Aligned(pc + 4);
|
||||
uint16_t c = Load16Aligned(pc + 8);
|
||||
uint16_t c2 = Load16Aligned(pc + 10);
|
||||
while (static_cast<uintptr_t>(current + load_offset) <
|
||||
@@ -1016,6 +997,8 @@ IrregexpInterpreter::Result IrregexpInterpreter::MatchInternal(
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef COMPILING_IRREGEXP_FOR_EXTERNAL_EMBEDDER
|
||||
|
||||
// This method is called through an external reference from RegExpExecInternal
|
||||
// builtin.
|
||||
IrregexpInterpreter::Result IrregexpInterpreter::MatchForCallFromJs(
|
||||
@@ -1042,6 +1025,8 @@ IrregexpInterpreter::Result IrregexpInterpreter::MatchForCallFromJs(
|
||||
start_position, call_origin);
|
||||
}
|
||||
|
||||
#endif // !COMPILING_IRREGEXP_FOR_EXTERNAL_EMBEDDER
|
||||
|
||||
IrregexpInterpreter::Result IrregexpInterpreter::MatchForCallFromRuntime(
|
||||
Isolate* isolate, Handle<JSRegExp> regexp, Handle<String> subject_string,
|
||||
int* registers, int registers_length, int start_position) {
|
||||
|
||||
@@ -0,0 +1,291 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// Copyright 2020 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// This file implements the NativeRegExpMacroAssembler interface for
|
||||
// SpiderMonkey. It provides the same interface as each of V8's
|
||||
// architecture-specific implementations.
|
||||
|
||||
#ifndef RegexpMacroAssemblerArch_h
|
||||
#define RegexpMacroAssemblerArch_h
|
||||
|
||||
#include "jit/MacroAssembler.h"
|
||||
#include "regexp/regexp-macro-assembler.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
struct FrameData {
|
||||
// Character position at the start of the input, stored as a
|
||||
// negative offset from the end of the string (input_end_pointer_).
|
||||
size_t inputStart;
|
||||
|
||||
// The backtrack_stack_pointer_ register points to the top of the stack.
|
||||
// This points to the bottom of the backtrack stack.
|
||||
void* backtrackStackBase;
|
||||
|
||||
// Copy of the input MatchPairs.
|
||||
int32_t* matches; // pointer to capture array
|
||||
int32_t numMatches; // size of capture array
|
||||
};
|
||||
|
||||
class SMRegExpMacroAssembler final : public NativeRegExpMacroAssembler {
|
||||
public:
|
||||
SMRegExpMacroAssembler(JSContext* cx, Isolate* isolate,
|
||||
js::jit::StackMacroAssembler& masm, Zone* zone,
|
||||
Mode mode, uint32_t num_capture_registers);
|
||||
virtual ~SMRegExpMacroAssembler() {} // Nothing to do here
|
||||
|
||||
virtual int stack_limit_slack();
|
||||
virtual IrregexpImplementation Implementation();
|
||||
|
||||
virtual bool Succeed();
|
||||
virtual void Fail();
|
||||
|
||||
virtual void AdvanceCurrentPosition(int by);
|
||||
virtual void PopCurrentPosition();
|
||||
virtual void PushCurrentPosition();
|
||||
virtual void SetCurrentPositionFromEnd(int by);
|
||||
|
||||
virtual void Backtrack();
|
||||
virtual void Bind(Label* label);
|
||||
virtual void GoTo(Label* label);
|
||||
virtual void PushBacktrack(Label* label);
|
||||
|
||||
virtual void CheckCharacter(uint32_t c, Label* on_equal);
|
||||
virtual void CheckNotCharacter(uint32_t c, Label* on_not_equal);
|
||||
virtual void CheckCharacterGT(uc16 limit, Label* on_greater);
|
||||
virtual void CheckCharacterLT(uc16 limit, Label* on_less);
|
||||
virtual void CheckCharacterAfterAnd(uint32_t c, uint32_t mask,
|
||||
Label* on_equal);
|
||||
virtual void CheckNotCharacterAfterAnd(uint32_t c, uint32_t mask,
|
||||
Label* on_not_equal);
|
||||
virtual void CheckNotCharacterAfterMinusAnd(uc16 c, uc16 minus, uc16 mask,
|
||||
Label* on_not_equal);
|
||||
virtual void CheckGreedyLoop(Label* on_tos_equals_current_position);
|
||||
virtual void CheckCharacterInRange(uc16 from, uc16 to, Label* on_in_range);
|
||||
virtual void CheckCharacterNotInRange(uc16 from, uc16 to,
|
||||
Label* on_not_in_range);
|
||||
virtual void CheckAtStart(int cp_offset, Label* on_at_start);
|
||||
virtual void CheckNotAtStart(int cp_offset, Label* on_not_at_start);
|
||||
virtual void CheckPosition(int cp_offset, Label* on_outside_input);
|
||||
virtual void CheckBitInTable(Handle<ByteArray> table, Label* on_bit_set);
|
||||
virtual bool CheckSpecialCharacterClass(uc16 type, Label* on_no_match);
|
||||
virtual void CheckNotBackReference(int start_reg, bool read_backward,
|
||||
Label* on_no_match);
|
||||
virtual void CheckNotBackReferenceIgnoreCase(int start_reg,
|
||||
bool read_backward,
|
||||
Label* on_no_match);
|
||||
|
||||
virtual void LoadCurrentCharacterImpl(int cp_offset, Label* on_end_of_input,
|
||||
bool check_bounds, int characters,
|
||||
int eats_at_least);
|
||||
|
||||
virtual void AdvanceRegister(int reg, int by);
|
||||
virtual void IfRegisterGE(int reg, int comparand, Label* if_ge);
|
||||
virtual void IfRegisterLT(int reg, int comparand, Label* if_lt);
|
||||
virtual void IfRegisterEqPos(int reg, Label* if_eq);
|
||||
virtual void PopRegister(int register_index);
|
||||
virtual void PushRegister(int register_index,
|
||||
StackCheckFlag check_stack_limit);
|
||||
virtual void ReadCurrentPositionFromRegister(int reg);
|
||||
virtual void WriteCurrentPositionToRegister(int reg, int cp_offset);
|
||||
virtual void ReadStackPointerFromRegister(int reg);
|
||||
virtual void WriteStackPointerToRegister(int reg);
|
||||
virtual void SetRegister(int register_index, int to);
|
||||
virtual void ClearRegisters(int reg_from, int reg_to);
|
||||
|
||||
virtual Handle<HeapObject> GetCode(Handle<String> source);
|
||||
|
||||
private:
|
||||
size_t frameSize_ = 0;
|
||||
|
||||
void createStackFrame();
|
||||
void initFrameAndRegs();
|
||||
void successHandler();
|
||||
void exitHandler();
|
||||
void backtrackHandler();
|
||||
void stackOverflowHandler();
|
||||
|
||||
// Push a register on the backtrack stack.
|
||||
void Push(js::jit::Register value);
|
||||
|
||||
// Pop a value from the backtrack stack.
|
||||
void Pop(js::jit::Register target);
|
||||
|
||||
void CheckAtStartImpl(int cp_offset, Label* on_cond,
|
||||
js::jit::Assembler::Condition cond);
|
||||
void CheckCharacterImpl(js::jit::Imm32 c, Label* on_cond,
|
||||
js::jit::Assembler::Condition cond);
|
||||
void CheckCharacterAfterAndImpl(uint32_t c, uint32_t and_with, Label* on_cond,
|
||||
bool negate);
|
||||
void CheckCharacterInRangeImpl(uc16 from, uc16 to, Label* on_cond,
|
||||
js::jit::Assembler::Condition cond);
|
||||
void CheckNotBackReferenceImpl(int start_reg, bool read_backward,
|
||||
Label* on_no_match, bool ignore_case);
|
||||
|
||||
void LoadCurrentCharacterUnchecked(int cp_offset, int characters);
|
||||
|
||||
void JumpOrBacktrack(Label* to);
|
||||
|
||||
// MacroAssembler methods that take a Label can be called with a
|
||||
// null label, which means that we should backtrack if we would jump
|
||||
// to that label. This is a helper to avoid writing out the same
|
||||
// logic a dozen times.
|
||||
inline js::jit::Label* LabelOrBacktrack(Label* to) {
|
||||
return to ? to->inner() : &backtrack_label_;
|
||||
}
|
||||
|
||||
void CheckBacktrackStackLimit();
|
||||
|
||||
static bool GrowBacktrackStack(RegExpStack* regexp_stack);
|
||||
|
||||
static uint32_t CaseInsensitiveCompareStrings(const char16_t* substring1,
|
||||
const char16_t* substring2,
|
||||
size_t byteLength);
|
||||
static uint32_t CaseInsensitiveCompareUCStrings(const char16_t* substring1,
|
||||
const char16_t* substring2,
|
||||
size_t byteLength);
|
||||
|
||||
inline int char_size() { return static_cast<int>(mode_); }
|
||||
inline js::jit::Scale factor() {
|
||||
return mode_ == UC16 ? js::jit::TimesTwo : js::jit::TimesOne;
|
||||
}
|
||||
|
||||
js::jit::Address inputStart() {
|
||||
return js::jit::Address(masm_.getStackPointer(),
|
||||
offsetof(FrameData, inputStart));
|
||||
}
|
||||
js::jit::Address backtrackStackBase() {
|
||||
return js::jit::Address(masm_.getStackPointer(),
|
||||
offsetof(FrameData, backtrackStackBase));
|
||||
}
|
||||
js::jit::Address matches() {
|
||||
return js::jit::Address(masm_.getStackPointer(),
|
||||
offsetof(FrameData, matches));
|
||||
}
|
||||
js::jit::Address numMatches() {
|
||||
return js::jit::Address(masm_.getStackPointer(),
|
||||
offsetof(FrameData, numMatches));
|
||||
}
|
||||
|
||||
// The stack-pointer-relative location of a regexp register.
|
||||
js::jit::Address register_location(int register_index) {
|
||||
return js::jit::Address(masm_.getStackPointer(),
|
||||
register_offset(register_index));
|
||||
}
|
||||
|
||||
int32_t register_offset(int register_index) {
|
||||
MOZ_ASSERT(register_index >= 0 && register_index <= kMaxRegister);
|
||||
if (num_registers_ <= register_index) {
|
||||
num_registers_ = register_index + 1;
|
||||
}
|
||||
static_assert(alignof(uintptr_t) <= alignof(FrameData));
|
||||
return sizeof(FrameData) + register_index * sizeof(uintptr_t*);
|
||||
}
|
||||
|
||||
JSContext* cx_;
|
||||
js::jit::StackMacroAssembler& masm_;
|
||||
|
||||
/*
|
||||
* This assembler uses the following registers:
|
||||
*
|
||||
* - current_character_:
|
||||
* Contains the character (or characters) currently being examined.
|
||||
* Must be loaded using LoadCurrentCharacter before using any of the
|
||||
* dispatch methods. After a matching pass for a global regexp,
|
||||
* temporarily stores the index of capture start.
|
||||
* - current_position_:
|
||||
* Current position in input *as negative byte offset from end of string*.
|
||||
* - input_end_pointer_:
|
||||
* Points to byte after last character in the input. current_position_ is
|
||||
* relative to this.
|
||||
* - backtrack_stack_pointer_:
|
||||
* Points to tip of the (heap-allocated) backtrack stack. The stack grows
|
||||
* downward (like the native stack).
|
||||
* - temp0_, temp1_, temp2_:
|
||||
* Scratch registers.
|
||||
*
|
||||
* The native stack pointer is used to access arguments (InputOutputData),
|
||||
* local variables (FrameData), and irregexp's internal virtual registers
|
||||
* (see register_location).
|
||||
*/
|
||||
|
||||
js::jit::Register current_character_;
|
||||
js::jit::Register current_position_;
|
||||
js::jit::Register input_end_pointer_;
|
||||
js::jit::Register backtrack_stack_pointer_;
|
||||
js::jit::Register temp0_, temp1_, temp2_;
|
||||
|
||||
js::jit::Label entry_label_;
|
||||
js::jit::Label start_label_;
|
||||
js::jit::Label backtrack_label_;
|
||||
js::jit::Label success_label_;
|
||||
js::jit::Label exit_label_;
|
||||
js::jit::Label stack_overflow_label_;
|
||||
js::jit::Label exit_with_exception_label_;
|
||||
|
||||
// When we generate the code to push a backtrack label's address
|
||||
// onto the backtrack stack, we don't know its final address. We
|
||||
// have to patch it after linking. This is slightly delicate, as the
|
||||
// Label itself (which is allocated on the stack) may not exist by
|
||||
// the time we link. The approach is as follows:
|
||||
//
|
||||
// 1. When we push a label on the backtrack stack (PushBacktrack),
|
||||
// we bind the label's patchOffset_ field to the offset within
|
||||
// the code that should be overwritten. This works because each
|
||||
// label is only pushed by a single instruction.
|
||||
//
|
||||
// 2. When we bind a label (Bind), we check to see if it has a
|
||||
// bound patchOffset_. If it does, we create a LabelPatch mapping
|
||||
// its patch offset to the offset of the label itself.
|
||||
//
|
||||
// 3. While linking the code, we walk the list of label patches
|
||||
// and patch the code accordingly.
|
||||
class LabelPatch {
|
||||
public:
|
||||
LabelPatch(js::jit::CodeOffset patchOffset, size_t labelOffset)
|
||||
: patchOffset_(patchOffset), labelOffset_(labelOffset) {}
|
||||
|
||||
js::jit::CodeOffset patchOffset_;
|
||||
size_t labelOffset_ = 0;
|
||||
};
|
||||
|
||||
js::Vector<LabelPatch, 4, js::SystemAllocPolicy> labelPatches_;
|
||||
void AddLabelPatch(js::jit::CodeOffset patchOffset, size_t labelOffset) {
|
||||
js::AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
if (!labelPatches_.emplaceBack(patchOffset, labelOffset)) {
|
||||
oomUnsafe.crash("Irregexp label patch");
|
||||
}
|
||||
}
|
||||
|
||||
Mode mode_;
|
||||
int num_registers_;
|
||||
int num_capture_registers_;
|
||||
js::jit::LiveGeneralRegisterSet savedRegisters_;
|
||||
|
||||
public:
|
||||
using TableVector =
|
||||
js::Vector<PseudoHandle<ByteArrayData>, 4, js::SystemAllocPolicy>;
|
||||
TableVector& tables() { return tables_; }
|
||||
|
||||
private:
|
||||
TableVector tables_;
|
||||
void AddTable(PseudoHandle<ByteArrayData> table) {
|
||||
js::AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
if (!tables_.append(std::move(table))) {
|
||||
oomUnsafe.crash("Irregexp table append");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // RegexpMacroAssemblerArch_h
|
||||
@@ -349,17 +349,15 @@ void RegExpMacroAssemblerTracer::CheckNotBackReference(int start_reg,
|
||||
assembler_->CheckNotBackReference(start_reg, read_backward, on_no_match);
|
||||
}
|
||||
|
||||
|
||||
void RegExpMacroAssemblerTracer::CheckNotBackReferenceIgnoreCase(
|
||||
int start_reg, bool read_backward, bool unicode, Label* on_no_match) {
|
||||
PrintF(" CheckNotBackReferenceIgnoreCase(register=%d, %s %s, label[%08x]);\n",
|
||||
int start_reg, bool read_backward, Label* on_no_match) {
|
||||
PrintF(" CheckNotBackReferenceIgnoreCase(register=%d, %s, label[%08x]);\n",
|
||||
start_reg, read_backward ? "backward" : "forward",
|
||||
unicode ? "unicode" : "non-unicode", LabelToInt(on_no_match));
|
||||
assembler_->CheckNotBackReferenceIgnoreCase(start_reg, read_backward, unicode,
|
||||
LabelToInt(on_no_match));
|
||||
assembler_->CheckNotBackReferenceIgnoreCase(start_reg, read_backward,
|
||||
on_no_match);
|
||||
}
|
||||
|
||||
|
||||
void RegExpMacroAssemblerTracer::CheckPosition(int cp_offset,
|
||||
Label* on_outside_input) {
|
||||
PrintF(" CheckPosition(cp_offset=%d, label[%08x]);\n", cp_offset,
|
||||
|
||||
@@ -33,7 +33,6 @@ class RegExpMacroAssemblerTracer: public RegExpMacroAssembler {
|
||||
void CheckNotBackReference(int start_reg, bool read_backward,
|
||||
Label* on_no_match) override;
|
||||
void CheckNotBackReferenceIgnoreCase(int start_reg, bool read_backward,
|
||||
bool unicode,
|
||||
Label* on_no_match) override;
|
||||
void CheckNotCharacter(unsigned c, Label* on_not_equal) override;
|
||||
void CheckNotCharacterAfterAnd(unsigned c, unsigned and_with,
|
||||
|
||||
@@ -110,34 +110,7 @@ bool NativeRegExpMacroAssembler::CanReadUnaligned() {
|
||||
return FLAG_enable_regexp_unaligned_accesses && !slow_safe();
|
||||
}
|
||||
|
||||
const byte* NativeRegExpMacroAssembler::StringCharacterPosition(
|
||||
String subject, int start_index, const DisallowHeapAllocation& no_gc) {
|
||||
if (subject.IsConsString()) {
|
||||
subject = ConsString::cast(subject).first();
|
||||
} else if (subject.IsSlicedString()) {
|
||||
start_index += SlicedString::cast(subject).offset();
|
||||
subject = SlicedString::cast(subject).parent();
|
||||
}
|
||||
if (subject.IsThinString()) {
|
||||
subject = ThinString::cast(subject).actual();
|
||||
}
|
||||
DCHECK_LE(0, start_index);
|
||||
DCHECK_LE(start_index, subject.length());
|
||||
if (subject.IsSeqOneByteString()) {
|
||||
return reinterpret_cast<const byte*>(
|
||||
SeqOneByteString::cast(subject).GetChars(no_gc) + start_index);
|
||||
} else if (subject.IsSeqTwoByteString()) {
|
||||
return reinterpret_cast<const byte*>(
|
||||
SeqTwoByteString::cast(subject).GetChars(no_gc) + start_index);
|
||||
} else if (subject.IsExternalOneByteString()) {
|
||||
return reinterpret_cast<const byte*>(
|
||||
ExternalOneByteString::cast(subject).GetChars() + start_index);
|
||||
} else {
|
||||
DCHECK(subject.IsExternalTwoByteString());
|
||||
return reinterpret_cast<const byte*>(
|
||||
ExternalTwoByteString::cast(subject).GetChars() + start_index);
|
||||
}
|
||||
}
|
||||
#ifndef COMPILING_IRREGEXP_FOR_EXTERNAL_EMBEDDER
|
||||
|
||||
// This method may only be called after an interrupt.
|
||||
int NativeRegExpMacroAssembler::CheckStackGuardState(
|
||||
@@ -145,9 +118,10 @@ int NativeRegExpMacroAssembler::CheckStackGuardState(
|
||||
Address* return_address, Code re_code, Address* subject,
|
||||
const byte** input_start, const byte** input_end) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
Address old_pc = PointerAuthentication::AuthenticatePC(return_address, 0);
|
||||
DCHECK_LE(re_code.raw_instruction_start(), old_pc);
|
||||
DCHECK_LE(old_pc, re_code.raw_instruction_end());
|
||||
|
||||
DCHECK(re_code.raw_instruction_start() <= *return_address);
|
||||
DCHECK(*return_address <= re_code.raw_instruction_end());
|
||||
StackLimitCheck check(isolate);
|
||||
bool js_has_overflowed = check.JsHasOverflowed();
|
||||
|
||||
@@ -189,9 +163,11 @@ int NativeRegExpMacroAssembler::CheckStackGuardState(
|
||||
}
|
||||
|
||||
if (*code_handle != re_code) { // Return address no longer valid
|
||||
intptr_t delta = code_handle->address() - re_code.address();
|
||||
// Overwrite the return address on the stack.
|
||||
*return_address += delta;
|
||||
intptr_t delta = code_handle->address() - re_code.address();
|
||||
Address new_pc = old_pc + delta;
|
||||
// TODO(v8:10026): avoid replacing a signed pointer.
|
||||
PointerAuthentication::ReplacePC(return_address, new_pc, 0);
|
||||
}
|
||||
|
||||
// If we continue, we need to update the subject string addresses.
|
||||
@@ -206,8 +182,7 @@ int NativeRegExpMacroAssembler::CheckStackGuardState(
|
||||
} else {
|
||||
*subject = subject_handle->ptr();
|
||||
intptr_t byte_length = *input_end - *input_start;
|
||||
*input_start =
|
||||
StringCharacterPosition(*subject_handle, start_index, no_gc);
|
||||
*input_start = subject_handle->AddressOfCharacterAt(start_index, no_gc);
|
||||
*input_end = *input_start + byte_length;
|
||||
}
|
||||
}
|
||||
@@ -255,7 +230,7 @@ int NativeRegExpMacroAssembler::Match(Handle<JSRegExp> regexp,
|
||||
|
||||
DisallowHeapAllocation no_gc;
|
||||
const byte* input_start =
|
||||
StringCharacterPosition(subject_ptr, start_offset + slice_offset, no_gc);
|
||||
subject_ptr.AddressOfCharacterAt(start_offset + slice_offset, no_gc);
|
||||
int byte_length = char_length << char_size_shift;
|
||||
const byte* input_end = input_start + byte_length;
|
||||
return Execute(*subject, start_offset, input_start, input_end, offsets_vector,
|
||||
@@ -301,6 +276,8 @@ int NativeRegExpMacroAssembler::Execute(
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // !COMPILING_IRREGEXP_FOR_EXTERNAL_EMBEDDER
|
||||
|
||||
// clang-format off
|
||||
const byte NativeRegExpMacroAssembler::word_character_map[] = {
|
||||
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#define V8_REGEXP_REGEXP_MACRO_ASSEMBLER_H_
|
||||
|
||||
#include "regexp/regexp-ast.h"
|
||||
#include "regexp/regexp-shim.h"
|
||||
#include "regexp/regexp.h"
|
||||
|
||||
namespace v8 {
|
||||
@@ -86,7 +87,7 @@ class RegExpMacroAssembler {
|
||||
virtual void CheckNotBackReference(int start_reg, bool read_backward,
|
||||
Label* on_no_match) = 0;
|
||||
virtual void CheckNotBackReferenceIgnoreCase(int start_reg,
|
||||
bool read_backward, bool unicode,
|
||||
bool read_backward,
|
||||
Label* on_no_match) = 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
|
||||
@@ -121,6 +122,11 @@ class RegExpMacroAssembler {
|
||||
// not have custom support.
|
||||
// May clobber the current loaded character.
|
||||
virtual bool CheckSpecialCharacterClass(uc16 type, Label* on_no_match);
|
||||
|
||||
// Control-flow integrity:
|
||||
// Define a jump target and bind a label.
|
||||
virtual void BindJumpTarget(Label* label) { Bind(label); }
|
||||
|
||||
virtual void Fail() = 0;
|
||||
virtual Handle<HeapObject> GetCode(Handle<String> source) = 0;
|
||||
virtual void GoTo(Label* label) = 0;
|
||||
@@ -245,9 +251,6 @@ class NativeRegExpMacroAssembler: public RegExpMacroAssembler {
|
||||
static Address GrowStack(Address stack_pointer, Address* stack_top,
|
||||
Isolate* isolate);
|
||||
|
||||
static const byte* StringCharacterPosition(
|
||||
String subject, int start_index, const DisallowHeapAllocation& no_gc);
|
||||
|
||||
static int CheckStackGuardState(Isolate* isolate, int start_index,
|
||||
RegExp::CallOrigin call_origin,
|
||||
Address* return_address, Code re_code,
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -17,11 +17,10 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
RegExpParser::RegExpParser(FlatStringReader* in, Handle<String>* error,
|
||||
JSRegExp::Flags flags, Isolate* isolate, Zone* zone)
|
||||
RegExpParser::RegExpParser(FlatStringReader* in, JSRegExp::Flags flags,
|
||||
Isolate* isolate, Zone* zone)
|
||||
: isolate_(isolate),
|
||||
zone_(zone),
|
||||
error_(error),
|
||||
captures_(nullptr),
|
||||
named_captures_(nullptr),
|
||||
named_back_references_(nullptr),
|
||||
@@ -74,13 +73,12 @@ void RegExpParser::Advance() {
|
||||
if (FLAG_correctness_fuzzer_suppressions) {
|
||||
FATAL("Aborting on stack overflow");
|
||||
}
|
||||
ReportError(CStrVector(
|
||||
MessageFormatter::TemplateString(MessageTemplate::kStackOverflow)));
|
||||
ReportError(RegExpError::kStackOverflow);
|
||||
} else if (zone()->excess_allocation()) {
|
||||
if (FLAG_correctness_fuzzer_suppressions) {
|
||||
FATAL("Aborting on excess zone allocation");
|
||||
}
|
||||
ReportError(CStrVector("Regular expression too large"));
|
||||
ReportError(RegExpError::kTooLarge);
|
||||
} else {
|
||||
current_ = ReadNext<true>();
|
||||
}
|
||||
@@ -132,15 +130,12 @@ bool RegExpParser::IsSyntaxCharacterOrSlash(uc32 c) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
RegExpTree* RegExpParser::ReportError(Vector<const char> message) {
|
||||
RegExpTree* RegExpParser::ReportError(RegExpError error) {
|
||||
if (failed_) return nullptr; // Do not overwrite any existing error.
|
||||
failed_ = true;
|
||||
*error_ = isolate()
|
||||
->factory()
|
||||
->NewStringFromOneByte(Vector<const uint8_t>::cast(message))
|
||||
.ToHandleChecked();
|
||||
// Zip to the end to make sure the no more input is read.
|
||||
error_ = error;
|
||||
error_pos_ = position();
|
||||
// Zip to the end to make sure no more input is read.
|
||||
current_ = kEndMarker;
|
||||
next_pos_ = in()->length();
|
||||
return nullptr;
|
||||
@@ -187,14 +182,14 @@ RegExpTree* RegExpParser::ParseDisjunction() {
|
||||
case kEndMarker:
|
||||
if (state->IsSubexpression()) {
|
||||
// Inside a parenthesized group when hitting end of input.
|
||||
return ReportError(CStrVector("Unterminated group"));
|
||||
return ReportError(RegExpError::kUnterminatedGroup);
|
||||
}
|
||||
DCHECK_EQ(INITIAL, state->group_type());
|
||||
// Parsing completed successfully.
|
||||
return builder->ToRegExp();
|
||||
case ')': {
|
||||
if (!state->IsSubexpression()) {
|
||||
return ReportError(CStrVector("Unmatched ')'"));
|
||||
return ReportError(RegExpError::kUnmatchedParen);
|
||||
}
|
||||
DCHECK_NE(INITIAL, state->group_type());
|
||||
|
||||
@@ -245,7 +240,7 @@ RegExpTree* RegExpParser::ParseDisjunction() {
|
||||
case '*':
|
||||
case '+':
|
||||
case '?':
|
||||
return ReportError(CStrVector("Nothing to repeat"));
|
||||
return ReportError(RegExpError::kNothingToRepeat);
|
||||
case '^': {
|
||||
Advance();
|
||||
if (builder->multiline()) {
|
||||
@@ -300,7 +295,7 @@ RegExpTree* RegExpParser::ParseDisjunction() {
|
||||
case '\\':
|
||||
switch (Next()) {
|
||||
case kEndMarker:
|
||||
return ReportError(CStrVector("\\ at end of pattern"));
|
||||
return ReportError(RegExpError::kEscapeAtEndOfPattern);
|
||||
case 'b':
|
||||
Advance(2);
|
||||
builder->AddAssertion(new (zone()) RegExpAssertion(
|
||||
@@ -340,7 +335,8 @@ RegExpTree* RegExpParser::ParseDisjunction() {
|
||||
if (unicode()) {
|
||||
ZoneList<CharacterRange>* ranges =
|
||||
new (zone()) ZoneList<CharacterRange>(2, zone());
|
||||
std::vector<char> name_1, name_2;
|
||||
ZoneVector<char> name_1(zone());
|
||||
ZoneVector<char> name_2(zone());
|
||||
if (ParsePropertyClassName(&name_1, &name_2)) {
|
||||
if (AddPropertyClassRange(ranges, p == 'P', name_1, name_2)) {
|
||||
RegExpCharacterClass* cc = new (zone())
|
||||
@@ -356,7 +352,7 @@ RegExpTree* RegExpParser::ParseDisjunction() {
|
||||
}
|
||||
}
|
||||
}
|
||||
return ReportError(CStrVector("Invalid property name"));
|
||||
return ReportError(RegExpError::kInvalidPropertyName);
|
||||
} else {
|
||||
builder->AddCharacter(p);
|
||||
}
|
||||
@@ -392,7 +388,7 @@ RegExpTree* RegExpParser::ParseDisjunction() {
|
||||
// With /u, no identity escapes except for syntax characters
|
||||
// are allowed. Otherwise, all identity escapes are allowed.
|
||||
if (unicode()) {
|
||||
return ReportError(CStrVector("Invalid escape"));
|
||||
return ReportError(RegExpError::kInvalidEscape);
|
||||
}
|
||||
uc32 first_digit = Next();
|
||||
if (first_digit == '8' || first_digit == '9') {
|
||||
@@ -406,7 +402,7 @@ RegExpTree* RegExpParser::ParseDisjunction() {
|
||||
Advance();
|
||||
if (unicode() && Next() >= '0' && Next() <= '9') {
|
||||
// With /u, decimal escape with leading 0 are not parsed as octal.
|
||||
return ReportError(CStrVector("Invalid decimal escape"));
|
||||
return ReportError(RegExpError::kInvalidDecimalEscape);
|
||||
}
|
||||
uc32 octal = ParseOctalLiteral();
|
||||
builder->AddCharacter(octal);
|
||||
@@ -447,7 +443,7 @@ RegExpTree* RegExpParser::ParseDisjunction() {
|
||||
// ES#prod-annexB-ExtendedPatternCharacter
|
||||
if (unicode()) {
|
||||
// With /u, invalid escapes are not treated as identity escapes.
|
||||
return ReportError(CStrVector("Invalid unicode escape"));
|
||||
return ReportError(RegExpError::kInvalidUnicodeEscape);
|
||||
}
|
||||
builder->AddCharacter('\\');
|
||||
} else {
|
||||
@@ -465,7 +461,7 @@ RegExpTree* RegExpParser::ParseDisjunction() {
|
||||
builder->AddCharacter('x');
|
||||
} else {
|
||||
// With /u, invalid escapes are not treated as identity escapes.
|
||||
return ReportError(CStrVector("Invalid escape"));
|
||||
return ReportError(RegExpError::kInvalidEscape);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -478,7 +474,7 @@ RegExpTree* RegExpParser::ParseDisjunction() {
|
||||
builder->AddCharacter('u');
|
||||
} else {
|
||||
// With /u, invalid escapes are not treated as identity escapes.
|
||||
return ReportError(CStrVector("Invalid Unicode escape"));
|
||||
return ReportError(RegExpError::kInvalidUnicodeEscape);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -502,7 +498,7 @@ RegExpTree* RegExpParser::ParseDisjunction() {
|
||||
builder->AddCharacter(current());
|
||||
Advance();
|
||||
} else {
|
||||
return ReportError(CStrVector("Invalid escape"));
|
||||
return ReportError(RegExpError::kInvalidEscape);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -510,13 +506,13 @@ RegExpTree* RegExpParser::ParseDisjunction() {
|
||||
case '{': {
|
||||
int dummy;
|
||||
bool parsed = ParseIntervalQuantifier(&dummy, &dummy CHECK_FAILED);
|
||||
if (parsed) return ReportError(CStrVector("Nothing to repeat"));
|
||||
if (parsed) return ReportError(RegExpError::kNothingToRepeat);
|
||||
V8_FALLTHROUGH;
|
||||
}
|
||||
case '}':
|
||||
case ']':
|
||||
if (unicode()) {
|
||||
return ReportError(CStrVector("Lone quantifier brackets"));
|
||||
return ReportError(RegExpError::kLoneQuantifierBrackets);
|
||||
}
|
||||
V8_FALLTHROUGH;
|
||||
default:
|
||||
@@ -551,13 +547,12 @@ RegExpTree* RegExpParser::ParseDisjunction() {
|
||||
case '{':
|
||||
if (ParseIntervalQuantifier(&min, &max)) {
|
||||
if (max < min) {
|
||||
return ReportError(
|
||||
CStrVector("numbers out of order in {} quantifier"));
|
||||
return ReportError(RegExpError::kRangeOutOfOrder);
|
||||
}
|
||||
break;
|
||||
} else if (unicode()) {
|
||||
// With /u, incomplete quantifiers are not allowed.
|
||||
return ReportError(CStrVector("Incomplete quantifier"));
|
||||
return ReportError(RegExpError::kIncompleteQuantifier);
|
||||
}
|
||||
continue;
|
||||
default:
|
||||
@@ -573,7 +568,7 @@ RegExpTree* RegExpParser::ParseDisjunction() {
|
||||
Advance();
|
||||
}
|
||||
if (!builder->AddQuantifierToAtom(min, max, quantifier_type)) {
|
||||
return ReportError(CStrVector("Invalid quantifier"));
|
||||
return ReportError(RegExpError::kInvalidQuantifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -608,7 +603,7 @@ RegExpParser::RegExpParserState* RegExpParser::ParseOpenParenthesis(
|
||||
case 's':
|
||||
case 'm': {
|
||||
if (!FLAG_regexp_mode_modifiers) {
|
||||
ReportError(CStrVector("Invalid group"));
|
||||
ReportError(RegExpError::kInvalidGroup);
|
||||
return nullptr;
|
||||
}
|
||||
Advance();
|
||||
@@ -617,7 +612,7 @@ RegExpParser::RegExpParserState* RegExpParser::ParseOpenParenthesis(
|
||||
switch (current()) {
|
||||
case '-':
|
||||
if (!flags_sense) {
|
||||
ReportError(CStrVector("Multiple dashes in flag group"));
|
||||
ReportError(RegExpError::kMultipleFlagDashes);
|
||||
return nullptr;
|
||||
}
|
||||
flags_sense = false;
|
||||
@@ -631,7 +626,7 @@ RegExpParser::RegExpParserState* RegExpParser::ParseOpenParenthesis(
|
||||
if (current() == 'm') bit = JSRegExp::kMultiline;
|
||||
if (current() == 's') bit = JSRegExp::kDotAll;
|
||||
if (((switch_on | switch_off) & bit) != 0) {
|
||||
ReportError(CStrVector("Repeated flag in flag group"));
|
||||
ReportError(RegExpError::kRepeatedFlag);
|
||||
return nullptr;
|
||||
}
|
||||
if (flags_sense) {
|
||||
@@ -659,7 +654,7 @@ RegExpParser::RegExpParserState* RegExpParser::ParseOpenParenthesis(
|
||||
subexpr_type = GROUPING; // Will break us out of the outer loop.
|
||||
continue;
|
||||
default:
|
||||
ReportError(CStrVector("Invalid flag group"));
|
||||
ReportError(RegExpError::kInvalidFlagGroup);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@@ -683,13 +678,13 @@ RegExpParser::RegExpParserState* RegExpParser::ParseOpenParenthesis(
|
||||
Advance();
|
||||
break;
|
||||
default:
|
||||
ReportError(CStrVector("Invalid group"));
|
||||
ReportError(RegExpError::kInvalidGroup);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
if (subexpr_type == CAPTURE) {
|
||||
if (captures_started_ >= JSRegExp::kMaxCaptures) {
|
||||
ReportError(CStrVector("Too many captures"));
|
||||
ReportError(RegExpError::kTooManyCaptures);
|
||||
return nullptr;
|
||||
}
|
||||
captures_started_++;
|
||||
@@ -838,20 +833,20 @@ const ZoneVector<uc16>* RegExpParser::ParseCaptureGroupName() {
|
||||
if (c == '\\' && current() == 'u') {
|
||||
Advance();
|
||||
if (!ParseUnicodeEscape(&c)) {
|
||||
ReportError(CStrVector("Invalid Unicode escape sequence"));
|
||||
ReportError(RegExpError::kInvalidUnicodeEscape);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// The backslash char is misclassified as both ID_Start and ID_Continue.
|
||||
if (c == '\\') {
|
||||
ReportError(CStrVector("Invalid capture group name"));
|
||||
ReportError(RegExpError::kInvalidCaptureGroupName);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (at_start) {
|
||||
if (!IsIdentifierStart(c)) {
|
||||
ReportError(CStrVector("Invalid capture group name"));
|
||||
ReportError(RegExpError::kInvalidCaptureGroupName);
|
||||
return nullptr;
|
||||
}
|
||||
push_code_unit(name, c);
|
||||
@@ -862,7 +857,7 @@ const ZoneVector<uc16>* RegExpParser::ParseCaptureGroupName() {
|
||||
} else if (IsIdentifierPart(c)) {
|
||||
push_code_unit(name, c);
|
||||
} else {
|
||||
ReportError(CStrVector("Invalid capture group name"));
|
||||
ReportError(RegExpError::kInvalidCaptureGroupName);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@@ -889,7 +884,7 @@ bool RegExpParser::CreateNamedCaptureAtIndex(const ZoneVector<uc16>* name,
|
||||
|
||||
const auto& named_capture_it = named_captures_->find(capture);
|
||||
if (named_capture_it != named_captures_->end()) {
|
||||
ReportError(CStrVector("Duplicate capture group name"));
|
||||
ReportError(RegExpError::kDuplicateCaptureGroupName);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -903,7 +898,7 @@ bool RegExpParser::ParseNamedBackReference(RegExpBuilder* builder,
|
||||
RegExpParserState* state) {
|
||||
// The parser is assumed to be on the '<' in \k<name>.
|
||||
if (current() != '<') {
|
||||
ReportError(CStrVector("Invalid named reference"));
|
||||
ReportError(RegExpError::kInvalidNamedReference);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -936,7 +931,7 @@ void RegExpParser::PatchNamedBackReferences() {
|
||||
if (named_back_references_ == nullptr) return;
|
||||
|
||||
if (named_captures_ == nullptr) {
|
||||
ReportError(CStrVector("Invalid named capture referenced"));
|
||||
ReportError(RegExpError::kInvalidNamedCaptureReference);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -957,7 +952,7 @@ void RegExpParser::PatchNamedBackReferences() {
|
||||
if (capture_it != named_captures_->end()) {
|
||||
index = (*capture_it)->index();
|
||||
} else {
|
||||
ReportError(CStrVector("Invalid named capture referenced"));
|
||||
ReportError(RegExpError::kInvalidNamedCaptureReference);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1378,8 +1373,8 @@ bool IsUnicodePropertyValueCharacter(char c) {
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
bool RegExpParser::ParsePropertyClassName(std::vector<char>* name_1,
|
||||
std::vector<char>* name_2) {
|
||||
bool RegExpParser::ParsePropertyClassName(ZoneVector<char>* name_1,
|
||||
ZoneVector<char>* name_2) {
|
||||
DCHECK(name_1->empty());
|
||||
DCHECK(name_2->empty());
|
||||
// Parse the property class as follows:
|
||||
@@ -1418,8 +1413,8 @@ bool RegExpParser::ParsePropertyClassName(std::vector<char>* name_1,
|
||||
|
||||
bool RegExpParser::AddPropertyClassRange(ZoneList<CharacterRange>* add_to,
|
||||
bool negate,
|
||||
const std::vector<char>& name_1,
|
||||
const std::vector<char>& name_2) {
|
||||
const ZoneVector<char>& name_1,
|
||||
const ZoneVector<char>& name_2) {
|
||||
if (name_2.empty()) {
|
||||
// First attempt to interpret as general category property value name.
|
||||
const char* name = name_1.data();
|
||||
@@ -1456,7 +1451,7 @@ bool RegExpParser::AddPropertyClassRange(ZoneList<CharacterRange>* add_to,
|
||||
}
|
||||
}
|
||||
|
||||
RegExpTree* RegExpParser::GetPropertySequence(const std::vector<char>& name_1) {
|
||||
RegExpTree* RegExpParser::GetPropertySequence(const ZoneVector<char>& name_1) {
|
||||
if (!FLAG_harmony_regexp_sequence) return nullptr;
|
||||
const char* name = name_1.data();
|
||||
const uc32* sequence_list = nullptr;
|
||||
@@ -1522,19 +1517,19 @@ RegExpTree* RegExpParser::GetPropertySequence(const std::vector<char>& name_1) {
|
||||
|
||||
#else // V8_INTL_SUPPORT
|
||||
|
||||
bool RegExpParser::ParsePropertyClassName(std::vector<char>* name_1,
|
||||
std::vector<char>* name_2) {
|
||||
bool RegExpParser::ParsePropertyClassName(ZoneVector<char>* name_1,
|
||||
ZoneVector<char>* name_2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RegExpParser::AddPropertyClassRange(ZoneList<CharacterRange>* add_to,
|
||||
bool negate,
|
||||
const std::vector<char>& name_1,
|
||||
const std::vector<char>& name_2) {
|
||||
const ZoneVector<char>& name_1,
|
||||
const ZoneVector<char>& name_2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RegExpTree* RegExpParser::GetPropertySequence(const std::vector<char>& name) {
|
||||
RegExpTree* RegExpParser::GetPropertySequence(const ZoneVector<char>& name) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -1598,7 +1593,7 @@ uc32 RegExpParser::ParseClassCharacterEscape() {
|
||||
}
|
||||
if (unicode()) {
|
||||
// With /u, invalid escapes are not treated as identity escapes.
|
||||
ReportError(CStrVector("Invalid class escape"));
|
||||
ReportError(RegExpError::kInvalidClassEscape);
|
||||
return 0;
|
||||
}
|
||||
if ((controlLetter >= '0' && controlLetter <= '9') ||
|
||||
@@ -1631,7 +1626,7 @@ uc32 RegExpParser::ParseClassCharacterEscape() {
|
||||
// ES#prod-annexB-LegacyOctalEscapeSequence
|
||||
if (unicode()) {
|
||||
// With /u, decimal escape is not interpreted as octal character code.
|
||||
ReportError(CStrVector("Invalid class escape"));
|
||||
ReportError(RegExpError::kInvalidClassEscape);
|
||||
return 0;
|
||||
}
|
||||
return ParseOctalLiteral();
|
||||
@@ -1641,7 +1636,7 @@ uc32 RegExpParser::ParseClassCharacterEscape() {
|
||||
if (ParseHexEscape(2, &value)) return value;
|
||||
if (unicode()) {
|
||||
// With /u, invalid escapes are not treated as identity escapes.
|
||||
ReportError(CStrVector("Invalid escape"));
|
||||
ReportError(RegExpError::kInvalidEscape);
|
||||
return 0;
|
||||
}
|
||||
// If \x is not followed by a two-digit hexadecimal, treat it
|
||||
@@ -1654,7 +1649,7 @@ uc32 RegExpParser::ParseClassCharacterEscape() {
|
||||
if (ParseUnicodeEscape(&value)) return value;
|
||||
if (unicode()) {
|
||||
// With /u, invalid escapes are not treated as identity escapes.
|
||||
ReportError(CStrVector("Invalid unicode escape"));
|
||||
ReportError(RegExpError::kInvalidUnicodeEscape);
|
||||
return 0;
|
||||
}
|
||||
// If \u is not followed by a two-digit hexadecimal, treat it
|
||||
@@ -1669,11 +1664,11 @@ uc32 RegExpParser::ParseClassCharacterEscape() {
|
||||
Advance();
|
||||
return result;
|
||||
}
|
||||
ReportError(CStrVector("Invalid escape"));
|
||||
ReportError(RegExpError::kInvalidEscape);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void RegExpParser::ParseClassEscape(ZoneList<CharacterRange>* ranges,
|
||||
@@ -1696,17 +1691,18 @@ void RegExpParser::ParseClassEscape(ZoneList<CharacterRange>* ranges,
|
||||
return;
|
||||
}
|
||||
case kEndMarker:
|
||||
ReportError(CStrVector("\\ at end of pattern"));
|
||||
ReportError(RegExpError::kEscapeAtEndOfPattern);
|
||||
return;
|
||||
case 'p':
|
||||
case 'P':
|
||||
if (unicode()) {
|
||||
bool negate = Next() == 'P';
|
||||
Advance(2);
|
||||
std::vector<char> name_1, name_2;
|
||||
ZoneVector<char> name_1(zone);
|
||||
ZoneVector<char> name_2(zone);
|
||||
if (!ParsePropertyClassName(&name_1, &name_2) ||
|
||||
!AddPropertyClassRange(ranges, negate, name_1, name_2)) {
|
||||
ReportError(CStrVector("Invalid property name in character class"));
|
||||
ReportError(RegExpError::kInvalidClassPropertyName);
|
||||
}
|
||||
*is_class_escape = true;
|
||||
return;
|
||||
@@ -1725,10 +1721,6 @@ void RegExpParser::ParseClassEscape(ZoneList<CharacterRange>* ranges,
|
||||
}
|
||||
|
||||
RegExpTree* RegExpParser::ParseCharacterClass(const RegExpBuilder* builder) {
|
||||
static const char* kUnterminated = "Unterminated character class";
|
||||
static const char* kRangeInvalid = "Invalid character class";
|
||||
static const char* kRangeOutOfOrder = "Range out of order in character class";
|
||||
|
||||
DCHECK_EQ(current(), '[');
|
||||
Advance();
|
||||
bool is_negated = false;
|
||||
@@ -1761,7 +1753,7 @@ RegExpTree* RegExpParser::ParseCharacterClass(const RegExpBuilder* builder) {
|
||||
// Either end is an escaped character class. Treat the '-' verbatim.
|
||||
if (unicode()) {
|
||||
// ES2015 21.2.2.15.1 step 1.
|
||||
return ReportError(CStrVector(kRangeInvalid));
|
||||
return ReportError(RegExpError::kInvalidCharacterClass);
|
||||
}
|
||||
if (!is_class_1) ranges->Add(CharacterRange::Singleton(char_1), zone());
|
||||
ranges->Add(CharacterRange::Singleton('-'), zone());
|
||||
@@ -1770,7 +1762,7 @@ RegExpTree* RegExpParser::ParseCharacterClass(const RegExpBuilder* builder) {
|
||||
}
|
||||
// ES2015 21.2.2.15.1 step 6.
|
||||
if (char_1 > char_2) {
|
||||
return ReportError(CStrVector(kRangeOutOfOrder));
|
||||
return ReportError(RegExpError::kOutOfOrderCharacterClass);
|
||||
}
|
||||
ranges->Add(CharacterRange::Range(char_1, char_2), zone());
|
||||
} else {
|
||||
@@ -1778,7 +1770,7 @@ RegExpTree* RegExpParser::ParseCharacterClass(const RegExpBuilder* builder) {
|
||||
}
|
||||
}
|
||||
if (!has_more()) {
|
||||
return ReportError(CStrVector(kUnterminated));
|
||||
return ReportError(RegExpError::kUnterminatedCharacterClass);
|
||||
}
|
||||
Advance();
|
||||
RegExpCharacterClass::CharacterClassFlags character_class_flags;
|
||||
@@ -1795,14 +1787,16 @@ bool RegExpParser::ParseRegExp(Isolate* isolate, Zone* zone,
|
||||
FlatStringReader* input, JSRegExp::Flags flags,
|
||||
RegExpCompileData* result) {
|
||||
DCHECK(result != nullptr);
|
||||
RegExpParser parser(input, &result->error, flags, isolate, zone);
|
||||
RegExpParser parser(input, flags, isolate, zone);
|
||||
RegExpTree* tree = parser.ParsePattern();
|
||||
if (parser.failed()) {
|
||||
DCHECK(tree == nullptr);
|
||||
DCHECK(!result->error.is_null());
|
||||
DCHECK(parser.error_ != RegExpError::kNone);
|
||||
result->error = parser.error_;
|
||||
result->error_pos = parser.error_pos_;
|
||||
} else {
|
||||
DCHECK(tree != nullptr);
|
||||
DCHECK(result->error.is_null());
|
||||
DCHECK(parser.error_ == RegExpError::kNone);
|
||||
if (FLAG_trace_regexp_parser) {
|
||||
StdoutStream os;
|
||||
tree->Print(os, zone);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#define V8_REGEXP_REGEXP_PARSER_H_
|
||||
|
||||
#include "regexp/regexp-ast.h"
|
||||
#include "regexp/regexp-error.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@@ -150,8 +151,8 @@ class RegExpBuilder : public ZoneObject {
|
||||
|
||||
class V8_EXPORT_PRIVATE RegExpParser {
|
||||
public:
|
||||
RegExpParser(FlatStringReader* in, Handle<String>* error,
|
||||
JSRegExp::Flags flags, Isolate* isolate, Zone* zone);
|
||||
RegExpParser(FlatStringReader* in, JSRegExp::Flags flags, Isolate* isolate,
|
||||
Zone* zone);
|
||||
|
||||
static bool ParseRegExp(Isolate* isolate, Zone* zone, FlatStringReader* input,
|
||||
JSRegExp::Flags flags, RegExpCompileData* result);
|
||||
@@ -174,13 +175,13 @@ class V8_EXPORT_PRIVATE RegExpParser {
|
||||
bool ParseUnicodeEscape(uc32* value);
|
||||
bool ParseUnlimitedLengthHexNumber(int max_value, uc32* value);
|
||||
|
||||
bool ParsePropertyClassName(std::vector<char>* name_1,
|
||||
std::vector<char>* name_2);
|
||||
bool ParsePropertyClassName(ZoneVector<char>* name_1,
|
||||
ZoneVector<char>* name_2);
|
||||
bool AddPropertyClassRange(ZoneList<CharacterRange>* add_to, bool negate,
|
||||
const std::vector<char>& name_1,
|
||||
const std::vector<char>& name_2);
|
||||
const ZoneVector<char>& name_1,
|
||||
const ZoneVector<char>& name_2);
|
||||
|
||||
RegExpTree* GetPropertySequence(const std::vector<char>& name_1);
|
||||
RegExpTree* GetPropertySequence(const ZoneVector<char>& name_1);
|
||||
RegExpTree* ParseCharacterClass(const RegExpBuilder* state);
|
||||
|
||||
uc32 ParseOctalLiteral();
|
||||
@@ -199,7 +200,7 @@ class V8_EXPORT_PRIVATE RegExpParser {
|
||||
|
||||
char ParseClassEscape();
|
||||
|
||||
RegExpTree* ReportError(Vector<const char> message);
|
||||
RegExpTree* ReportError(RegExpError error);
|
||||
void Advance();
|
||||
void Advance(int dist);
|
||||
void Reset(int pos);
|
||||
@@ -332,7 +333,8 @@ class V8_EXPORT_PRIVATE RegExpParser {
|
||||
|
||||
Isolate* isolate_;
|
||||
Zone* zone_;
|
||||
Handle<String>* error_;
|
||||
RegExpError error_ = RegExpError::kNone;
|
||||
int error_pos_ = 0;
|
||||
ZoneList<RegExpCapture*>* captures_;
|
||||
ZoneSet<RegExpCapture*, RegExpCaptureNameLess>* named_captures_;
|
||||
ZoneList<RegExpBackReference*>* named_back_references_;
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// Copyright 2019 the V8 project 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 <iostream>
|
||||
|
||||
#include "regexp/regexp-shim.h"
|
||||
#include "regexp/regexp-stack.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
void PrintF(const char* format, ...) {
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
vprintf(format, arguments);
|
||||
va_end(arguments);
|
||||
}
|
||||
|
||||
void PrintF(FILE* out, const char* format, ...) {
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
vfprintf(out, format, arguments);
|
||||
va_end(arguments);
|
||||
}
|
||||
|
||||
StdoutStream::operator std::ostream&() const { return std::cerr; }
|
||||
|
||||
template <typename T>
|
||||
std::ostream& StdoutStream::operator<<(T t) { return std::cerr << t; }
|
||||
|
||||
template std::ostream& StdoutStream::operator<<(char const* c);
|
||||
|
||||
// Origin:
|
||||
// https://github.com/v8/v8/blob/855591a54d160303349a5f0a32fab15825c708d1/src/utils/ostreams.cc#L120-L169
|
||||
// (This is a hand-simplified version.)
|
||||
// Writes the given character to the output escaping everything outside
|
||||
// of printable ASCII range.
|
||||
std::ostream& operator<<(std::ostream& os, const AsUC16& c) {
|
||||
uc16 v = c.value;
|
||||
bool isPrint = 0x20 < v && v <= 0x7e;
|
||||
char buf[10];
|
||||
const char* format = isPrint ? "%c" : (v <= 0xFF) ? "\\x%02x" : "\\u%04x";
|
||||
SprintfLiteral(buf, format, v);
|
||||
return os << buf;
|
||||
}
|
||||
std::ostream& operator<<(std::ostream& os, const AsUC32& c) {
|
||||
int32_t v = c.value;
|
||||
if (v <= String::kMaxUtf16CodeUnit) {
|
||||
return os << AsUC16(v);
|
||||
}
|
||||
char buf[13];
|
||||
SprintfLiteral(buf, "\\u{%06x}", v);
|
||||
return os << buf;
|
||||
}
|
||||
|
||||
HandleScope::HandleScope(Isolate* isolate)
|
||||
: isolate_(isolate) {
|
||||
isolate->openHandleScope(*this);
|
||||
}
|
||||
|
||||
HandleScope::~HandleScope() {
|
||||
isolate_->closeHandleScope(level_, non_gc_level_);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Handle<T>::Handle(T object, Isolate* isolate)
|
||||
: location_(isolate->getHandleLocation(JS::Value(object))) {}
|
||||
|
||||
template Handle<ByteArray>::Handle(ByteArray b, Isolate* isolate);
|
||||
template Handle<HeapObject>::Handle(JS::Value v, Isolate* isolate);
|
||||
template Handle<JSRegExp>::Handle(JSRegExp re, Isolate* isolate);
|
||||
template Handle<String>::Handle(String s, Isolate* isolate);
|
||||
|
||||
template <typename T>
|
||||
Handle<T>::Handle(JS::Value value, Isolate* isolate)
|
||||
: location_(isolate->getHandleLocation(value)) {
|
||||
T::cast(Object(value)); // Assert that value has the correct type.
|
||||
}
|
||||
|
||||
JS::Value* Isolate::getHandleLocation(JS::Value value) {
|
||||
js::AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
if (!handleArena_.Append(value)) {
|
||||
oomUnsafe.crash("Irregexp handle allocation");
|
||||
}
|
||||
return &handleArena_.GetLast();
|
||||
}
|
||||
|
||||
void* Isolate::allocatePseudoHandle(size_t bytes) {
|
||||
PseudoHandle<void> ptr;
|
||||
ptr.reset(js_malloc(bytes));
|
||||
if (!ptr) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!uniquePtrArena_.Append(std::move(ptr))) {
|
||||
return nullptr;
|
||||
}
|
||||
return uniquePtrArena_.GetLast().get();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
PseudoHandle<T> Isolate::takeOwnership(void* ptr) {
|
||||
for (auto iter = uniquePtrArena_.IterFromLast(); !iter.Done(); iter.Prev()) {
|
||||
auto& entry = iter.Get();
|
||||
if (entry.get() == ptr) {
|
||||
PseudoHandle<T> result;
|
||||
result.reset(static_cast<T*>(entry.release()));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
MOZ_CRASH("Tried to take ownership of pseudohandle that is not in the arena");
|
||||
}
|
||||
|
||||
PseudoHandle<ByteArrayData> ByteArray::takeOwnership(Isolate* isolate) {
|
||||
PseudoHandle<ByteArrayData> result =
|
||||
isolate->takeOwnership<ByteArrayData>(value_.toPrivate());
|
||||
value_ = JS::PrivateValue(nullptr);
|
||||
return result;
|
||||
}
|
||||
|
||||
void Isolate::trace(JSTracer* trc) {
|
||||
js::gc::AssertRootMarkingPhase(trc);
|
||||
|
||||
for (auto iter = handleArena_.Iter(); !iter.Done(); iter.Next()) {
|
||||
auto& elem = iter.Get();
|
||||
JS::GCPolicy<JS::Value>::trace(trc, &elem, "Isolate handle arena");
|
||||
}
|
||||
}
|
||||
|
||||
/*static*/ Handle<String> String::Flatten(Isolate* isolate,
|
||||
Handle<String> string) {
|
||||
if (string->IsFlat()) {
|
||||
return string;
|
||||
}
|
||||
js::AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
JSLinearString* linear = string->str()->ensureLinear(isolate->cx());
|
||||
if (!linear) {
|
||||
oomUnsafe.crash("Irregexp String::Flatten");
|
||||
}
|
||||
return Handle<String>(JS::StringValue(linear), isolate);
|
||||
}
|
||||
|
||||
// This is only used for trace messages printing the source of a
|
||||
// regular expression. To keep things simple, we just return an
|
||||
// empty string and don't print anything.
|
||||
std::unique_ptr<char[]> String::ToCString() {
|
||||
return std::unique_ptr<char[]>();
|
||||
}
|
||||
|
||||
byte* Isolate::top_of_regexp_stack() const {
|
||||
return reinterpret_cast<byte*>(regexpStack_->memory_top_address_address());
|
||||
}
|
||||
|
||||
Handle<ByteArray> Isolate::NewByteArray(int length, AllocationType alloc) {
|
||||
MOZ_RELEASE_ASSERT(length >= 0);
|
||||
|
||||
js::AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
|
||||
size_t alloc_size = sizeof(uint32_t) + length;
|
||||
ByteArrayData* data =
|
||||
static_cast<ByteArrayData*>(allocatePseudoHandle(alloc_size));
|
||||
if (!data) {
|
||||
oomUnsafe.crash("Irregexp NewByteArray");
|
||||
}
|
||||
data->length = length;
|
||||
|
||||
return Handle<ByteArray>(JS::PrivateValue(data), this);
|
||||
}
|
||||
|
||||
Handle<FixedArray> Isolate::NewFixedArray(int length) {
|
||||
MOZ_RELEASE_ASSERT(length >= 0);
|
||||
MOZ_CRASH("TODO");
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
Handle<String> Isolate::InternalizeString(const Vector<const CharT>& str) {
|
||||
js::AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
JSAtom* atom = js::AtomizeChars(cx(), str.begin(), str.length());
|
||||
if (!atom) {
|
||||
oomUnsafe.crash("Irregexp InternalizeString");
|
||||
}
|
||||
return Handle<String>(JS::StringValue(atom), this);
|
||||
}
|
||||
|
||||
template Handle<String>
|
||||
Isolate::InternalizeString(const Vector<const uint8_t>& str);
|
||||
template Handle<String>
|
||||
Isolate::InternalizeString(const Vector<const char16_t>& str);
|
||||
|
||||
// TODO: Map flags to jitoptions
|
||||
bool FLAG_correctness_fuzzer_suppressions = false;
|
||||
bool FLAG_enable_regexp_unaligned_accesses = false;
|
||||
bool FLAG_harmony_regexp_sequence = false;
|
||||
bool FLAG_regexp_interpret_all = false;
|
||||
bool FLAG_regexp_mode_modifiers = false;
|
||||
bool FLAG_regexp_optimization = true;
|
||||
bool FLAG_regexp_peephole_optimization = true;
|
||||
bool FLAG_regexp_possessive_quantifier = false;
|
||||
bool FLAG_regexp_tier_up = false;
|
||||
bool FLAG_trace_regexp_assembler = false;
|
||||
bool FLAG_trace_regexp_bytecodes = false;
|
||||
bool FLAG_trace_regexp_parser = false;
|
||||
bool FLAG_trace_regexp_peephole_optimization = false;
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
File diff suppressed because it is too large.
Load diff
@@ -5,6 +5,7 @@
|
||||
#ifndef V8_REGEXP_REGEXP_STACK_H_
|
||||
#define V8_REGEXP_REGEXP_STACK_H_
|
||||
|
||||
#include "regexp/regexp-shim.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@@ -35,6 +36,9 @@ class RegExpStackScope {
|
||||
|
||||
class RegExpStack {
|
||||
public:
|
||||
RegExpStack();
|
||||
~RegExpStack();
|
||||
|
||||
// Number of allocated locations on the stack below the limit.
|
||||
// No sequence of pushes must be longer that this without doing a stack-limit
|
||||
// check.
|
||||
@@ -74,9 +78,6 @@ class RegExpStack {
|
||||
static constexpr size_t kMaximumStackSize = 64 * MB;
|
||||
|
||||
private:
|
||||
RegExpStack();
|
||||
~RegExpStack();
|
||||
|
||||
// Artificial limit used when the thread-local state has been destroyed.
|
||||
static const Address kMemoryTop =
|
||||
static_cast<Address>(static_cast<uintptr_t>(-1));
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#ifndef V8_REGEXP_REGEXP_H_
|
||||
#define V8_REGEXP_REGEXP_H_
|
||||
|
||||
#include "regexp/regexp-error.h"
|
||||
#include "regexp/regexp-shim.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@@ -41,7 +43,11 @@ struct RegExpCompileData {
|
||||
|
||||
// The error message. Only used if an error occurred during parsing or
|
||||
// compilation.
|
||||
Handle<String> error;
|
||||
RegExpError error = RegExpError::kNone;
|
||||
|
||||
// The position at which the error was detected. Only used if an
|
||||
// error occurred.
|
||||
int error_pos = 0;
|
||||
|
||||
// The number of capture groups, without the global capture \0.
|
||||
int capture_count = 0;
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
// Copyright 2020 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
// Automatically generated by regexp/gen-regexp-special-case.cc
|
||||
|
||||
// The following functions are used to build UnicodeSets
|
||||
// for special cases where the case-folding algorithm used by
|
||||
// UnicodeSet::closeOver(USET_CASE_INSENSITIVE) does not match
|
||||
// the algorithm defined in ECMAScript 2020 21.2.2.8.2 (Runtime
|
||||
// Semantics: Canonicalize) step 3.
|
||||
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
#include "regexp/special-case.h"
|
||||
|
||||
#include "unicode/uniset.h"
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
icu::UnicodeSet BuildIgnoreSet() {
|
||||
icu::UnicodeSet set;
|
||||
set.add(0xdf);
|
||||
set.add(0x17f);
|
||||
set.add(0x390);
|
||||
set.add(0x3b0);
|
||||
set.add(0x3f4);
|
||||
set.add(0x1e9e);
|
||||
set.add(0x1f80, 0x1faf);
|
||||
set.add(0x1fb3);
|
||||
set.add(0x1fbc);
|
||||
set.add(0x1fc3);
|
||||
set.add(0x1fcc);
|
||||
set.add(0x1fd3);
|
||||
set.add(0x1fe3);
|
||||
set.add(0x1ff3);
|
||||
set.add(0x1ffc);
|
||||
set.add(0x2126);
|
||||
set.add(0x212a, 0x212b);
|
||||
set.add(0xfb05, 0xfb06);
|
||||
set.freeze();
|
||||
return set;
|
||||
}
|
||||
|
||||
struct IgnoreSetData {
|
||||
IgnoreSetData() : set(BuildIgnoreSet()) {}
|
||||
const icu::UnicodeSet set;
|
||||
};
|
||||
|
||||
//static
|
||||
const icu::UnicodeSet& RegExpCaseFolding::IgnoreSet() {
|
||||
static base::LazyInstance<IgnoreSetData>::type set =
|
||||
LAZY_INSTANCE_INITIALIZER;
|
||||
return set.Pointer()->set;
|
||||
}
|
||||
|
||||
icu::UnicodeSet BuildSpecialAddSet() {
|
||||
icu::UnicodeSet set;
|
||||
set.add(0x4b);
|
||||
set.add(0x53);
|
||||
set.add(0x6b);
|
||||
set.add(0x73);
|
||||
set.add(0xc5);
|
||||
set.add(0xe5);
|
||||
set.add(0x398);
|
||||
set.add(0x3a9);
|
||||
set.add(0x3b8);
|
||||
set.add(0x3c9);
|
||||
set.add(0x3d1);
|
||||
set.freeze();
|
||||
return set;
|
||||
}
|
||||
|
||||
struct SpecialAddSetData {
|
||||
SpecialAddSetData() : set(BuildSpecialAddSet()) {}
|
||||
const icu::UnicodeSet set;
|
||||
};
|
||||
|
||||
//static
|
||||
const icu::UnicodeSet& RegExpCaseFolding::SpecialAddSet() {
|
||||
static base::LazyInstance<SpecialAddSetData>::type set =
|
||||
LAZY_INSTANCE_INITIALIZER;
|
||||
return set.Pointer()->set;
|
||||
}
|
||||
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
#endif // V8_INTL_SUPPORT
|
||||
@@ -6,70 +6,108 @@
|
||||
#define V8_REGEXP_SPECIAL_CASE_H_
|
||||
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
#include "unicode/uversion.h"
|
||||
namespace U_ICU_NAMESPACE {
|
||||
class UnicodeSet;
|
||||
} // namespace U_ICU_NAMESPACE
|
||||
#include "regexp/regexp-shim.h"
|
||||
|
||||
#include "unicode/uchar.h"
|
||||
#include "unicode/uniset.h"
|
||||
#include "unicode/unistr.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
// Functions to build special sets of Unicode characters that need special
|
||||
// handling under "i" mode that cannot use closeOver(USET_CASE_INSENSITIVE).
|
||||
//
|
||||
// For the characters in the "ignore set", the process should not treat other
|
||||
// characters in the result of closeOver(USET_CASE_INSENSITIVE) as case
|
||||
// equivlant under the ECMA262 RegExp "i" mode because these characters are
|
||||
// uppercase themselves that no other characters in the set uppercase to.
|
||||
//
|
||||
// For the characters in the "special add set", the proecess should add only
|
||||
// those characters in the result of closeOver(USET_CASE_INSENSITIVE) which is
|
||||
// not uppercase characters as case equivlant under the ECMA262 RegExp "i" mode
|
||||
// and also that ONE uppercase character that other non uppercase character
|
||||
// uppercase into to the set. Other uppercase characters in the result of
|
||||
// closeOver(USET_CASE_INSENSITIVE) should not be considered because ECMA262
|
||||
// RegExp "i" mode consider two characters as "case equivlant" if both
|
||||
// characters uppercase to the same character.
|
||||
//
|
||||
// For example, consider the following case equivalent set defined by Unicode
|
||||
// standard. Notice there are more than one uppercase characters in this set:
|
||||
// U+212B Å Angstrom Sign - an uppercase character.
|
||||
// U+00C5 Å Latin Capital Letter A with Ring Above - an uppercase character.
|
||||
// U+00E5 å Latin Small Letter A with Ring Above - a lowercase character which
|
||||
// uppercase to U+00C5.
|
||||
// In this case equivlant set is a special set and need special handling while
|
||||
// considering "case equivlant" under the ECMA262 RegExp "i" mode which is
|
||||
// different than Unicode Standard:
|
||||
// * U+212B should be included into the "ignore" set because there are no other
|
||||
// characters, under the ECMA262 "i" mode, are considered as "case equivlant"
|
||||
// to it because U+212B is itself an uppercase but neither U+00C5 nor U+00E5
|
||||
// uppercase to U+212B.
|
||||
// * U+00C5 and U+00E5 will both be included into the "special add" set. While
|
||||
// calculate the "equivlant set" under ECMA262 "i" mode, the process will
|
||||
// add U+00E5, because it is not an uppercase character in the set. The
|
||||
// process will also add U+00C5, because it is the uppercase character which
|
||||
// other non uppercase character, U+00C5, uppercase into.
|
||||
//
|
||||
// For characters not included in "ignore set" and "special add set", the
|
||||
// process will just use closeOver(USET_CASE_INSENSITIVE) to calcualte, which is
|
||||
// much faster.
|
||||
//
|
||||
// Under Unicode 12.0, there are only 7 characters in the "special add set" and
|
||||
// 4 characters in "ignore set" so even the special add process is slower, it is
|
||||
// limited to a small set of cases only.
|
||||
//
|
||||
// The implementation of these two function will be generated by calling ICU
|
||||
// icu::UnicodeSet during the build time into gen/src/regexp/special-case.cc by
|
||||
// the code in src/regexp/gen-regexp-special-case.cc.
|
||||
//
|
||||
// These two function will be used with LazyInstance<> template to generate
|
||||
// global sharable set to reduce memory usage and speed up performance.
|
||||
// Sets of Unicode characters that need special handling under "i" mode
|
||||
|
||||
// Function to build and return the Ignore set.
|
||||
icu::UnicodeSet BuildIgnoreSet();
|
||||
// For non-unicode ignoreCase matches (aka "i", not "iu"), ECMA 262
|
||||
// defines slightly different case-folding rules than Unicode. An
|
||||
// input character should match a pattern character if the result of
|
||||
// the Canonicalize algorithm is the same for both characters.
|
||||
//
|
||||
// Roughly speaking, for "i" regexps, Canonicalize(c) is the same as
|
||||
// c.toUpperCase(), unless a) c.toUpperCase() is a multi-character
|
||||
// string, or b) c is non-ASCII, and c.toUpperCase() is ASCII. See
|
||||
// https://tc39.es/ecma262/#sec-runtime-semantics-canonicalize-ch for
|
||||
// the precise definition.
|
||||
//
|
||||
// While compiling such regular expressions, we need to compute the
|
||||
// set of characters that should match a given input character. (See
|
||||
// GetCaseIndependentLetters and CharacterRange::AddCaseEquivalents.)
|
||||
// For almost all characters, this can be efficiently computed using
|
||||
// UnicodeSet::closeOver(USET_CASE_INSENSITIVE). These sets represent
|
||||
// the remaining special cases.
|
||||
//
|
||||
// For a character c, the rules are as follows:
|
||||
//
|
||||
// 1. If c is in neither IgnoreSet nor SpecialAddSet, then calling
|
||||
// UnicodeSet::closeOver(USET_CASE_INSENSITIVE) on a UnicodeSet
|
||||
// containing c will produce the set of characters that should
|
||||
// match /c/i (or /[c]/i), and only those characters.
|
||||
//
|
||||
// 2. If c is in IgnoreSet, then the only character it should match is
|
||||
// itself. However, closeOver will add additional incorrect
|
||||
// matches. For example, consider SHARP S: 'ß' (U+00DF) and 'ẞ'
|
||||
// (U+1E9E). Although closeOver('ß') = "ßẞ", uppercase('ß') is
|
||||
// "SS". Step 3.e therefore requires that 'ß' canonicalizes to
|
||||
// itself, and should not match 'ẞ'. In these cases, we can skip
|
||||
// the closeOver entirely, because it will never add an equivalent
|
||||
// character.
|
||||
//
|
||||
// 3. If c is in SpecialAddSet, then it should match at least one
|
||||
// character other than itself. However, closeOver will add at
|
||||
// least one additional incorrect match. For example, consider the
|
||||
// letter 'k'. Closing over 'k' gives "kKK" (lowercase k, uppercase
|
||||
// K, U+212A KELVIN SIGN). However, because of step 3.g, KELVIN
|
||||
// SIGN should not match either of the other two characters. As a
|
||||
// result, "k" and "K" are in SpecialAddSet (and KELVIN SIGN is in
|
||||
// IgnoreSet). To find the correct matches for characters in
|
||||
// SpecialAddSet, we closeOver the original character, but filter
|
||||
// out the results that do not have the same canonical value.
|
||||
//
|
||||
// The contents of these sets are calculated at build time by
|
||||
// src/regexp/gen-regexp-special-case.cc, which generates
|
||||
// gen/src/regexp/special-case.cc. This is done by iterating over the
|
||||
// result of closeOver for each BMP character, and finding sets for
|
||||
// which at least one character has a different canonical value than
|
||||
// another character. Characters that match no other characters in
|
||||
// their equivalence class are added to IgnoreSet. Characters that
|
||||
// match at least one other character are added to SpecialAddSet.
|
||||
|
||||
// Function to build and return the Special Add set.
|
||||
icu::UnicodeSet BuildSpecialAddSet();
|
||||
class RegExpCaseFolding final : public AllStatic {
|
||||
public:
|
||||
static const icu::UnicodeSet& IgnoreSet();
|
||||
static const icu::UnicodeSet& SpecialAddSet();
|
||||
|
||||
// This implements ECMAScript 2020 21.2.2.8.2 (Runtime Semantics:
|
||||
// Canonicalize) step 3, which is used to determine whether
|
||||
// characters match when ignoreCase is true and unicode is false.
|
||||
static UChar32 Canonicalize(UChar32 ch) {
|
||||
// a. Assert: ch is a UTF-16 code unit.
|
||||
CHECK_LE(ch, 0xffff);
|
||||
|
||||
// b. Let s be the String value consisting of the single code unit ch.
|
||||
icu::UnicodeString s(ch);
|
||||
|
||||
// c. Let u be the same result produced as if by performing the algorithm
|
||||
// for String.prototype.toUpperCase using s as the this value.
|
||||
// d. Assert: Type(u) is String.
|
||||
icu::UnicodeString& u = s.toUpper();
|
||||
|
||||
// e. If u does not consist of a single code unit, return ch.
|
||||
if (u.length() != 1) {
|
||||
return ch;
|
||||
}
|
||||
|
||||
// f. Let cu be u's single code unit element.
|
||||
UChar32 cu = u.char32At(0);
|
||||
|
||||
// g. If the value of ch >= 128 and the value of cu < 128, return ch.
|
||||
if (ch >= 128 && cu < 128) {
|
||||
return ch;
|
||||
}
|
||||
|
||||
// h. Return cu.
|
||||
return cu;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#
|
||||
# This script modifies V8 regexp source files to make them suitable for
|
||||
# inclusion in SpiderMonkey. Specifically, it:
|
||||
#
|
||||
# 1. Rewrites all #includes of V8 regexp headers to point to their location in
|
||||
# the SM tree: src/regexp/* --> regexp/*
|
||||
# 2. Removes all #includes of other V8 src/* headers. The required definitions
|
||||
# will be provided by regexp-shim.h.
|
||||
#
|
||||
# Usage:
|
||||
# cd js/src/regexp
|
||||
# find . -name "*.h" -o -name "*.cc" | xargs ./update_headers.py
|
||||
#
|
||||
|
||||
import fileinput
|
||||
import re
|
||||
import sys
|
||||
|
||||
# 1. Rewrite includes of V8 regexp headers
|
||||
regexp_include = re.compile('#include "src/regexp')
|
||||
regexp_include_new = '#include "regexp'
|
||||
|
||||
# 2. Remove includes of other V8 headers
|
||||
other_include = re.compile('#include "src/')
|
||||
|
||||
for line in fileinput.input(inplace=1):
|
||||
if regexp_include.search(line):
|
||||
sys.stdout.write(re.sub(regexp_include, regexp_include_new, line))
|
||||
elif other_include.search(line):
|
||||
pass
|
||||
else:
|
||||
sys.stdout.write(line)
|
||||
@@ -0,0 +1,93 @@
|
||||
// Copyright 2014 the V8 project 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 V8_UTIL_FLAGS_H_
|
||||
#define V8_UTIL_FLAGS_H_
|
||||
|
||||
// Origin: https://github.com/v8/v8/blob/1bafcc6b999b23ea1d394f5d267a08183e3c4e19/src/base/flags.h#L15-L90
|
||||
|
||||
namespace v8 {
|
||||
namespace base {
|
||||
|
||||
// The Flags class provides a type-safe way of storing OR-combinations of enum
|
||||
// values. The Flags<T, S> class is a template class, where T is an enum type,
|
||||
// and S is the underlying storage type (usually int).
|
||||
//
|
||||
// The traditional C++ approach for storing OR-combinations of enum values is to
|
||||
// use an int or unsigned int variable. The inconvenience with this approach is
|
||||
// that there's no type checking at all; any enum value can be OR'd with any
|
||||
// other enum value and passed on to a function that takes an int or unsigned
|
||||
// int.
|
||||
template <typename T, typename S = int>
|
||||
class Flags final {
|
||||
public:
|
||||
using flag_type = T;
|
||||
using mask_type = S;
|
||||
|
||||
constexpr Flags() : mask_(0) {}
|
||||
constexpr Flags(flag_type flag)
|
||||
: mask_(static_cast<S>(flag)) {}
|
||||
constexpr explicit Flags(mask_type mask) : mask_(static_cast<S>(mask)) {}
|
||||
|
||||
constexpr bool operator==(flag_type flag) const {
|
||||
return mask_ == static_cast<S>(flag);
|
||||
}
|
||||
constexpr bool operator!=(flag_type flag) const {
|
||||
return mask_ != static_cast<S>(flag);
|
||||
}
|
||||
|
||||
Flags& operator&=(const Flags& flags) {
|
||||
mask_ &= flags.mask_;
|
||||
return *this;
|
||||
}
|
||||
Flags& operator|=(const Flags& flags) {
|
||||
mask_ |= flags.mask_;
|
||||
return *this;
|
||||
}
|
||||
Flags& operator^=(const Flags& flags) {
|
||||
mask_ ^= flags.mask_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr Flags operator&(const Flags& flags) const {
|
||||
return Flags(mask_ & flags.mask_);
|
||||
}
|
||||
constexpr Flags operator|(const Flags& flags) const {
|
||||
return Flags(mask_ | flags.mask_);
|
||||
}
|
||||
constexpr Flags operator^(const Flags& flags) const {
|
||||
return Flags(mask_ ^ flags.mask_);
|
||||
}
|
||||
|
||||
Flags& operator&=(flag_type flag) { return operator&=(Flags(flag)); }
|
||||
Flags& operator|=(flag_type flag) { return operator|=(Flags(flag)); }
|
||||
Flags& operator^=(flag_type flag) { return operator^=(Flags(flag)); }
|
||||
|
||||
constexpr Flags operator&(flag_type flag) const {
|
||||
return operator&(Flags(flag));
|
||||
}
|
||||
constexpr Flags operator|(flag_type flag) const {
|
||||
return operator|(Flags(flag));
|
||||
}
|
||||
constexpr Flags operator^(flag_type flag) const {
|
||||
return operator^(Flags(flag));
|
||||
}
|
||||
|
||||
constexpr Flags operator~() const { return Flags(~mask_); }
|
||||
|
||||
constexpr operator mask_type() const { return mask_; }
|
||||
constexpr bool operator!() const { return !mask_; }
|
||||
|
||||
Flags without(flag_type flag) { return *this & (~Flags(flag)); }
|
||||
|
||||
friend size_t hash_value(const Flags& flags) { return flags.mask_; }
|
||||
|
||||
private:
|
||||
mask_type mask_;
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_UTIL_FLAG_H_
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,204 @@
|
||||
// Copyright 2014 the V8 project 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 V8_UTIL_VECTOR_H_
|
||||
#define V8_UTIL_VECTOR_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
|
||||
#include "js/Utility.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
// Adapted from: https://github.com/v8/v8/blob/5f69bbc233c2d1baf149faf869a7901603929914/src/utils/allocation.h#L36-L58
|
||||
|
||||
template <typename T>
|
||||
T* NewArray(size_t size) {
|
||||
static_assert(std::is_pod<T>::value, "");
|
||||
js::AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
T* result = static_cast<T*>(js_malloc(size * sizeof(T)));
|
||||
if (!result) {
|
||||
oomUnsafe.crash("Irregexp NewArray");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void DeleteArray(T* array) {
|
||||
js_free(array);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
// A non-resizable vector containing a pointer and a length.
|
||||
// The Vector may or may not own the pointer, depending on context.
|
||||
// Origin:
|
||||
// https://github.com/v8/v8/blob/5f69bbc233c2d1baf149faf869a7901603929914/src/utils/vector.h#L20-L134
|
||||
|
||||
template <typename T>
|
||||
class Vector {
|
||||
public:
|
||||
constexpr Vector() : start_(nullptr), length_(0) {}
|
||||
|
||||
constexpr Vector(T* data, size_t length) : start_(data), length_(length) {
|
||||
MOZ_ASSERT_IF(length != 0, data != nullptr);
|
||||
}
|
||||
|
||||
static Vector<T> New(size_t length) {
|
||||
return Vector<T>(NewArray<T>(length), length);
|
||||
}
|
||||
|
||||
// Returns a vector using the same backing storage as this one,
|
||||
// spanning from and including 'from', to but not including 'to'.
|
||||
Vector<T> SubVector(size_t from, size_t to) const {
|
||||
MOZ_ASSERT(from < to);
|
||||
MOZ_ASSERT(to < length_);
|
||||
return Vector<T>(begin() + from, to - from);
|
||||
}
|
||||
|
||||
// Returns the length of the vector. Only use this if you really need an
|
||||
// integer return value. Use {size()} otherwise.
|
||||
int length() const {
|
||||
MOZ_ASSERT(length_ <= std::numeric_limits<int>::max());
|
||||
return static_cast<int>(length_);
|
||||
}
|
||||
|
||||
// Returns the length of the vector as a size_t.
|
||||
constexpr size_t size() const { return length_; }
|
||||
|
||||
// Returns whether or not the vector is empty.
|
||||
constexpr bool empty() const { return length_ == 0; }
|
||||
|
||||
// Access individual vector elements - checks bounds in debug mode.
|
||||
T& operator[](size_t index) const {
|
||||
MOZ_ASSERT(index < length_);
|
||||
return start_[index];
|
||||
}
|
||||
|
||||
const T& at(size_t index) const { return operator[](index); }
|
||||
|
||||
T& first() { return start_[0]; }
|
||||
|
||||
T& last() {
|
||||
MOZ_ASSERT(length_ > 0);
|
||||
return start_[length_ - 1];
|
||||
}
|
||||
|
||||
// Returns a pointer to the start of the data in the vector.
|
||||
constexpr T* begin() const { return start_; }
|
||||
|
||||
// Returns a pointer past the end of the data in the vector.
|
||||
constexpr T* end() const { return start_ + length_; }
|
||||
|
||||
// Returns a clone of this vector with a new backing store.
|
||||
Vector<T> Clone() const {
|
||||
T* result = NewArray<T>(length_);
|
||||
for (size_t i = 0; i < length_; i++) result[i] = start_[i];
|
||||
return Vector<T>(result, length_);
|
||||
}
|
||||
|
||||
void Truncate(size_t length) {
|
||||
MOZ_ASSERT(length <= length_);
|
||||
length_ = length;
|
||||
}
|
||||
|
||||
// Releases the array underlying this vector. Once disposed the
|
||||
// vector is empty.
|
||||
void Dispose() {
|
||||
DeleteArray(start_);
|
||||
start_ = nullptr;
|
||||
length_ = 0;
|
||||
}
|
||||
|
||||
Vector<T> operator+(size_t offset) {
|
||||
MOZ_ASSERT(offset <= length_);
|
||||
return Vector<T>(start_ + offset, length_ - offset);
|
||||
}
|
||||
|
||||
Vector<T> operator+=(size_t offset) {
|
||||
MOZ_ASSERT(offset <= length_);
|
||||
start_ += offset;
|
||||
length_ -= offset;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Implicit conversion from Vector<T> to Vector<const T>.
|
||||
inline operator Vector<const T>() const {
|
||||
return Vector<const T>::cast(*this);
|
||||
}
|
||||
|
||||
template <typename S>
|
||||
static constexpr Vector<T> cast(Vector<S> input) {
|
||||
return Vector<T>(reinterpret_cast<T*>(input.begin()),
|
||||
input.length() * sizeof(S) / sizeof(T));
|
||||
}
|
||||
|
||||
bool operator==(const Vector<const T> other) const {
|
||||
if (length_ != other.length_) return false;
|
||||
if (start_ == other.start_) return true;
|
||||
for (size_t i = 0; i < length_; ++i) {
|
||||
if (start_[i] != other.start_[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
T* start_;
|
||||
size_t length_;
|
||||
};
|
||||
|
||||
// The resulting vector does not contain a null-termination byte. If you want
|
||||
// the null byte, use ArrayVector("foo").
|
||||
inline Vector<const char> CStrVector(const char* data) {
|
||||
return Vector<const char>(data, strlen(data));
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
namespace base {
|
||||
|
||||
// SmallVector uses inline storage first, and reallocates when full.
|
||||
// It is basically equivalent to js::Vector, and is implemented
|
||||
// as a thin wrapper.
|
||||
// V8's implementation: https://github.com/v8/v8/blob/master/src/base/small-vector.h
|
||||
template <typename T, size_t kSize>
|
||||
class SmallVector {
|
||||
public:
|
||||
inline bool empty() const { return inner_.empty(); }
|
||||
inline const T& back() const { return inner_.back(); }
|
||||
inline void pop_back() { inner_.popBack(); };
|
||||
template <typename... Args>
|
||||
inline void emplace_back(Args&&... args) {
|
||||
js::AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
if (!inner_.emplaceBack(args...)) {
|
||||
oomUnsafe.crash("Irregexp SmallVector emplace_back");
|
||||
}
|
||||
};
|
||||
inline size_t size() const { return inner_.length(); }
|
||||
inline const T& at(size_t index) const { return inner_[index]; }
|
||||
|
||||
void resize_no_init(size_t new_size) {
|
||||
js::AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
if (!inner_.resizeUninitialized(new_size)) {
|
||||
oomUnsafe.crash("Irregexp SmallVector resize");
|
||||
}
|
||||
}
|
||||
private:
|
||||
js::Vector<T, kSize, js::SystemAllocPolicy> inner_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace base
|
||||
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_UTIL_VECTOR_H_
|
||||
@@ -0,0 +1,375 @@
|
||||
// Copyright 2019 the V8 project 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 V8_UTIL_ZONE_H_
|
||||
#define V8_UTIL_ZONE_H_
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "ds/LifoAlloc.h"
|
||||
#include "ds/Sort.h"
|
||||
#include "regexp/util/vector.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
// V8::Zone ~= LifoAlloc
|
||||
class Zone {
|
||||
public:
|
||||
Zone(size_t defaultChunkSize) : lifoAlloc_(defaultChunkSize) {
|
||||
lifoAlloc_.setAsInfallibleByDefault();
|
||||
}
|
||||
|
||||
void* New(size_t size) {
|
||||
js::LifoAlloc::AutoFallibleScope fallible(&lifoAlloc_);
|
||||
js::AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
void* result = lifoAlloc_.alloc(size);
|
||||
if (!result) {
|
||||
oomUnsafe.crash("Irregexp Zone::new");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void DeleteAll() { lifoAlloc_.freeAll(); }
|
||||
|
||||
// Returns true if the total memory allocated exceeds a threshold.
|
||||
static const size_t kExcessLimit = 256 * 1024 * 1024;
|
||||
bool excess_allocation() const {
|
||||
return lifoAlloc_.computedSizeOfExcludingThis() > kExcessLimit;
|
||||
}
|
||||
private:
|
||||
js::LifoAlloc lifoAlloc_;
|
||||
};
|
||||
|
||||
// Superclass for classes allocated in a Zone.
|
||||
// Origin: https://github.com/v8/v8/blob/7b3332844212d78ee87a9426f3a6f7f781a8fbfa/src/zone/zone.h#L138-L155
|
||||
class ZoneObject {
|
||||
public:
|
||||
// Allocate a new ZoneObject of 'size' bytes in the Zone.
|
||||
void* operator new(size_t size, Zone* zone) { return zone->New(size); }
|
||||
|
||||
// Ideally, the delete operator should be private instead of
|
||||
// public, but unfortunately the compiler sometimes synthesizes
|
||||
// (unused) destructors for classes derived from ZoneObject, which
|
||||
// require the operator to be visible. MSVC requires the delete
|
||||
// operator to be public.
|
||||
|
||||
// ZoneObjects should never be deleted individually; use
|
||||
// Zone::DeleteAll() to delete all zone objects in one go.
|
||||
void operator delete(void*, size_t) { MOZ_CRASH("unreachable"); }
|
||||
void operator delete(void* pointer, Zone* zone) { MOZ_CRASH("unreachable"); }
|
||||
};
|
||||
|
||||
// ZoneLists are growable lists with constant-time access to the
|
||||
// elements. The list itself and all its elements are allocated in the
|
||||
// Zone. ZoneLists cannot be deleted individually; you can delete all
|
||||
// objects in the Zone by calling Zone::DeleteAll().
|
||||
// Used throughout irregexp.
|
||||
// Origin: https://github.com/v8/v8/blob/5e514a969376dc63517d575b062758efd36cd757/src/zone/zone.h#L173-L318
|
||||
// Inlines: https://github.com/v8/v8/blob/5e514a969376dc63517d575b062758efd36cd757/src/zone/zone-list-inl.h#L17-L155
|
||||
template <typename T>
|
||||
class ZoneList final {
|
||||
public:
|
||||
// Construct a new ZoneList with the given capacity; the length is
|
||||
// always zero. The capacity must be non-negative.
|
||||
ZoneList(int capacity, Zone* zone) { Initialize(capacity, zone); }
|
||||
// Construct a new ZoneList from a std::initializer_list
|
||||
ZoneList(std::initializer_list<T> list, Zone* zone) {
|
||||
Initialize(static_cast<int>(list.size()), zone);
|
||||
for (auto& i : list) Add(i, zone);
|
||||
}
|
||||
// Construct a new ZoneList by copying the elements of the given ZoneList.
|
||||
ZoneList(const ZoneList<T>& other, Zone* zone) {
|
||||
Initialize(other.length(), zone);
|
||||
AddAll(other, zone);
|
||||
}
|
||||
|
||||
void* operator new(size_t size, Zone* zone) { return zone->New(size); }
|
||||
|
||||
// Returns a reference to the element at index i. This reference is not safe
|
||||
// to use after operations that can change the list's backing store
|
||||
// (e.g. Add).
|
||||
inline T& operator[](int i) const {
|
||||
MOZ_ASSERT(0 < i);
|
||||
MOZ_ASSERT(static_cast<unsigned>(i) < static_cast<unsigned>(length_));
|
||||
return data_[i];
|
||||
}
|
||||
inline T& at(int i) const { return operator[](i); }
|
||||
inline T& last() const { return at(length_ - 1); }
|
||||
inline T& first() const { return at(0); }
|
||||
|
||||
using iterator = T*;
|
||||
inline iterator begin() const { return &data_[0]; }
|
||||
inline iterator end() const { return &data_[length_]; }
|
||||
|
||||
inline bool is_empty() const { return length_ == 0; }
|
||||
inline int length() const { return length_; }
|
||||
inline int capacity() const { return capacity_; }
|
||||
|
||||
Vector<T> ToVector() const { return Vector<T>(data_, length_); }
|
||||
Vector<T> ToVector(int start, int length) const {
|
||||
return Vector<T>(data_ + start, std::min(length_ - start, length));
|
||||
}
|
||||
|
||||
Vector<const T> ToConstVector() const {
|
||||
return Vector<const T>(data_, length_);
|
||||
}
|
||||
|
||||
inline void Initialize(int capacity, Zone* zone) {
|
||||
MOZ_ASSERT(capacity >= 0);
|
||||
data_ = (capacity > 0) ? NewData(capacity, zone) : nullptr;
|
||||
capacity_ = capacity;
|
||||
length_ = 0;
|
||||
}
|
||||
|
||||
// Adds a copy of the given 'element' to the end of the list,
|
||||
// expanding the list if necessary.
|
||||
void Add(const T& element, Zone* zone) {
|
||||
if (length_ < capacity_) {
|
||||
data_[length_++] = element;
|
||||
} else {
|
||||
ZoneList<T>::ResizeAdd(element, zone);
|
||||
}
|
||||
}
|
||||
// Add all the elements from the argument list to this list.
|
||||
void AddAll(const ZoneList<T>& other, Zone* zone) {
|
||||
AddAll(other.ToVector(), zone);
|
||||
}
|
||||
// Add all the elements from the vector to this list.
|
||||
void AddAll(const Vector<T>& other, Zone* zone) {
|
||||
int result_length = length_ + other.length();
|
||||
if (capacity_ < result_length) {
|
||||
Resize(result_length, zone);
|
||||
}
|
||||
if (std::is_fundamental<T>()) {
|
||||
memcpy(data_ + length_, other.begin(), sizeof(*data_) * other.length());
|
||||
} else {
|
||||
for (int i = 0; i < other.length(); i++) {
|
||||
data_[length_ + i] = other.at(i);
|
||||
}
|
||||
}
|
||||
length_ = result_length;
|
||||
}
|
||||
|
||||
// Overwrites the element at the specific index.
|
||||
void Set(int index, const T& element) {
|
||||
MOZ_ASSERT(index >= 0 && index <= length_);
|
||||
data_[index] = element;
|
||||
}
|
||||
|
||||
// Removes the i'th element without deleting it even if T is a
|
||||
// pointer type; moves all elements above i "down". Returns the
|
||||
// removed element. This function's complexity is linear in the
|
||||
// size of the list.
|
||||
T Remove(int i) {
|
||||
T element = at(i);
|
||||
length_--;
|
||||
while (i < length_) {
|
||||
data_[i] = data_[i + 1];
|
||||
i++;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
// Removes the last element without deleting it even if T is a
|
||||
// pointer type. Returns the removed element.
|
||||
inline T RemoveLast() { return Remove(length_ - 1); }
|
||||
|
||||
// Clears the list by freeing the storage memory. If you want to keep the
|
||||
// memory, use Rewind(0) instead. Be aware, that even if T is a
|
||||
// pointer type, clearing the list doesn't delete the entries.
|
||||
inline void Clear() {
|
||||
data_ = nullptr;
|
||||
capacity_ = 0;
|
||||
length_ = 0;
|
||||
}
|
||||
|
||||
// Drops all but the first 'pos' elements from the list.
|
||||
inline void Rewind(int pos) {
|
||||
MOZ_ASSERT(0 <= pos && pos <= length_);
|
||||
length_ = pos;
|
||||
}
|
||||
|
||||
inline bool Contains(const T& elm) const {
|
||||
for (int i = 0; i < length_; i++) {
|
||||
if (data_[i] == elm) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename CompareFunction>
|
||||
void StableSort(CompareFunction cmp, size_t start, size_t length) {
|
||||
js::AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
T* scratch = static_cast<T*>(js_malloc(length * sizeof(T)));
|
||||
if (!scratch) {
|
||||
oomUnsafe.crash("Irregexp stable sort scratch space");
|
||||
}
|
||||
auto comparator = [cmp](const T& a, const T& b, bool* lessOrEqual) {
|
||||
*lessOrEqual = cmp(&a, &b) <= 0;
|
||||
return true;
|
||||
};
|
||||
MOZ_ALWAYS_TRUE(js::MergeSort(begin() + start, length, scratch,
|
||||
comparator));
|
||||
js_free(scratch);
|
||||
}
|
||||
|
||||
void operator delete(void* pointer) { MOZ_CRASH("unreachable"); }
|
||||
void operator delete(void* pointer, Zone* zone) { MOZ_CRASH("unreachable"); }
|
||||
|
||||
private:
|
||||
T* data_;
|
||||
int capacity_;
|
||||
int length_;
|
||||
|
||||
inline T* NewData(int n, Zone* zone) {
|
||||
return static_cast<T*>(zone->New(n * sizeof(T)));
|
||||
}
|
||||
|
||||
// Increase the capacity of a full list, and add an element.
|
||||
// List must be full already.
|
||||
void ResizeAdd(const T& element, Zone* zone) {
|
||||
MOZ_ASSERT(length_ >= capacity_);
|
||||
// Grow the list capacity by 100%, but make sure to let it grow
|
||||
// even when the capacity is zero (possible initial case).
|
||||
int new_capacity = 1 + 2 * capacity_;
|
||||
// Since the element reference could be an element of the list, copy
|
||||
// it out of the old backing storage before resizing.
|
||||
T temp = element;
|
||||
Resize(new_capacity, zone);
|
||||
data_[length_++] = temp;
|
||||
}
|
||||
|
||||
// Resize the list.
|
||||
void Resize(int new_capacity, Zone* zone) {
|
||||
MOZ_ASSERT(length_ <= new_capacity);
|
||||
T* new_data = NewData(new_capacity, zone);
|
||||
if (length_ > 0) {
|
||||
memcpy(new_data, data_, length_ * sizeof(T));
|
||||
}
|
||||
data_ = new_data;
|
||||
capacity_ = new_capacity;
|
||||
}
|
||||
|
||||
ZoneList& operator=(const ZoneList&) = delete;
|
||||
ZoneList() = delete;
|
||||
ZoneList(const ZoneList&) = delete;
|
||||
};
|
||||
|
||||
// Origin: https://github.com/v8/v8/blob/5e514a969376dc63517d575b062758efd36cd757/src/zone/zone-allocator.h#L14-L77
|
||||
template <typename T>
|
||||
class ZoneAllocator {
|
||||
public:
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
using value_type = T;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
template <class O>
|
||||
struct rebind {
|
||||
using other = ZoneAllocator<O>;
|
||||
};
|
||||
|
||||
explicit ZoneAllocator(Zone* zone) : zone_(zone) {}
|
||||
template <typename U>
|
||||
ZoneAllocator(const ZoneAllocator<U>& other)
|
||||
: ZoneAllocator<T>(other.zone_) {}
|
||||
template <typename U>
|
||||
friend class ZoneAllocator;
|
||||
|
||||
T* allocate(size_t n) { return static_cast<T*>(zone_->New(n * sizeof(T))); }
|
||||
void deallocate(T* p, size_t) {} // noop for zones
|
||||
|
||||
bool operator==(ZoneAllocator const& other) const {
|
||||
return zone_ == other.zone_;
|
||||
}
|
||||
bool operator!=(ZoneAllocator const& other) const {
|
||||
return zone_ != other.zone_;
|
||||
}
|
||||
|
||||
private:
|
||||
Zone* zone_;
|
||||
};
|
||||
|
||||
// Zone wrappers for std containers:
|
||||
// Origin: https://github.com/v8/v8/blob/5e514a969376dc63517d575b062758efd36cd757/src/zone/zone-containers.h#L25-L169
|
||||
|
||||
// A wrapper subclass for std::vector to make it easy to construct one
|
||||
// that uses a zone allocator.
|
||||
// Used throughout irregexp
|
||||
template <typename T>
|
||||
class ZoneVector : public std::vector<T, ZoneAllocator<T>> {
|
||||
public:
|
||||
ZoneVector(Zone* zone)
|
||||
: std::vector<T, ZoneAllocator<T>>(ZoneAllocator<T>(zone)) {}
|
||||
|
||||
// Constructs a new vector and fills it with the contents of the range
|
||||
// [first, last).
|
||||
template <class Iter>
|
||||
ZoneVector(Iter first, Iter last, Zone* zone)
|
||||
: std::vector<T, ZoneAllocator<T>>(first, last, ZoneAllocator<T>(zone)) {}
|
||||
};
|
||||
|
||||
// A wrapper subclass for std::list to make it easy to construct one
|
||||
// that uses a zone allocator.
|
||||
// Used in regexp-bytecode-peephole.cc
|
||||
template <typename T>
|
||||
class ZoneLinkedList : public std::list<T, ZoneAllocator<T>> {
|
||||
public:
|
||||
// Constructs an empty list.
|
||||
explicit ZoneLinkedList(Zone* zone)
|
||||
: std::list<T, ZoneAllocator<T>>(ZoneAllocator<T>(zone)) {}
|
||||
};
|
||||
|
||||
// A wrapper subclass for std::set to make it easy to construct one that uses
|
||||
// a zone allocator.
|
||||
// Used in regexp-parser.cc
|
||||
template <typename K, typename Compare = std::less<K>>
|
||||
class ZoneSet : public std::set<K, Compare, ZoneAllocator<K>> {
|
||||
public:
|
||||
// Constructs an empty set.
|
||||
explicit ZoneSet(Zone* zone)
|
||||
: std::set<K, Compare, ZoneAllocator<K>>(Compare(),
|
||||
ZoneAllocator<K>(zone)) {}
|
||||
};
|
||||
|
||||
// A wrapper subclass for std::map to make it easy to construct one that uses
|
||||
// a zone allocator.
|
||||
// Used in regexp-bytecode-peephole.cc
|
||||
template <typename K, typename V, typename Compare = std::less<K>>
|
||||
class ZoneMap
|
||||
: public std::map<K, V, Compare, ZoneAllocator<std::pair<const K, V>>> {
|
||||
public:
|
||||
// Constructs an empty map.
|
||||
explicit ZoneMap(Zone* zone)
|
||||
: std::map<K, V, Compare, ZoneAllocator<std::pair<const K, V>>>(
|
||||
Compare(), ZoneAllocator<std::pair<const K, V>>(zone)) {}
|
||||
};
|
||||
|
||||
// A wrapper subclass for std::unordered_map to make it easy to construct one
|
||||
// that uses a zone allocator.
|
||||
// Used in regexp-bytecode-peephole.cc
|
||||
template <typename K, typename V, typename Hash = std::hash<K>,
|
||||
typename KeyEqual = std::equal_to<K>>
|
||||
class ZoneUnorderedMap
|
||||
: public std::unordered_map<K, V, Hash, KeyEqual,
|
||||
ZoneAllocator<std::pair<const K, V>>> {
|
||||
public:
|
||||
// Constructs an empty map.
|
||||
explicit ZoneUnorderedMap(Zone* zone, size_t bucket_count = 100)
|
||||
: std::unordered_map<K, V, Hash, KeyEqual,
|
||||
ZoneAllocator<std::pair<const K, V>>>(
|
||||
bucket_count, Hash(), KeyEqual(),
|
||||
ZoneAllocator<std::pair<const K, V>>(zone)) {}
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_UTIL_FLAG_H_
|
||||
@@ -112,6 +112,7 @@ class GlobalObject : public NativeObject
|
||||
MODULE_PROTO,
|
||||
IMPORT_ENTRY_PROTO,
|
||||
EXPORT_ENTRY_PROTO,
|
||||
REQUESTED_MODULE_PROTO,
|
||||
REGEXP_STATICS,
|
||||
WARNED_ONCE_FLAGS,
|
||||
RUNTIME_CODEGEN_ENABLED,
|
||||
@@ -524,6 +525,11 @@ class GlobalObject : public NativeObject
|
||||
return value.isUndefined() ? nullptr : &value.toObject();
|
||||
}
|
||||
|
||||
JSObject* maybeGetRequestedModulePrototype() {
|
||||
Value value = getSlot(REQUESTED_MODULE_PROTO);
|
||||
return value.isUndefined() ? nullptr : &value.toObject();
|
||||
}
|
||||
|
||||
JSObject* getModulePrototype() {
|
||||
JSObject* proto = maybeGetModulePrototype();
|
||||
MOZ_ASSERT(proto);
|
||||
@@ -542,6 +548,12 @@ class GlobalObject : public NativeObject
|
||||
return proto;
|
||||
}
|
||||
|
||||
JSObject* getRequestedModulePrototype() {
|
||||
JSObject* proto = maybeGetRequestedModulePrototype();
|
||||
MOZ_ASSERT(proto);
|
||||
return proto;
|
||||
}
|
||||
|
||||
static JSFunction*
|
||||
getOrCreateTypedArrayConstructor(JSContext* cx, Handle<GlobalObject*> global) {
|
||||
if (!ensureConstructor(cx, global, JSProto_TypedArray))
|
||||
@@ -831,6 +843,7 @@ class GlobalObject : public NativeObject
|
||||
static bool initModuleProto(JSContext* cx, Handle<GlobalObject*> global);
|
||||
static bool initImportEntryProto(JSContext* cx, Handle<GlobalObject*> global);
|
||||
static bool initExportEntryProto(JSContext* cx, Handle<GlobalObject*> global);
|
||||
static bool initRequestedModuleProto(JSContext* cx, Handle<GlobalObject*> global);
|
||||
|
||||
// Implemented in builtin/TypedObject.cpp
|
||||
static bool initTypedObjectModule(JSContext* cx, Handle<GlobalObject*> global);
|
||||
|
||||
@@ -11,7 +11,7 @@ FINAL_LIBRARY = "js"
|
||||
# Includes should be relative to parent path
|
||||
LOCAL_INCLUDES += ["!..", ".."]
|
||||
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'AsmJS.cpp',
|
||||
'WasmBaselineCompile.cpp',
|
||||
'WasmBinaryFormat.cpp',
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <stddef.h> // size_t
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "webrtc/common_audio/ring_buffer.h"
|
||||
#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h"
|
||||
@@ -1573,33 +1574,36 @@ AecCore* WebRtcAec_CreateAec() {
|
||||
#endif
|
||||
aec->extended_filter_enabled = 0;
|
||||
|
||||
// Assembly optimization
|
||||
WebRtcAec_FilterFar = FilterFar;
|
||||
WebRtcAec_ScaleErrorSignal = ScaleErrorSignal;
|
||||
WebRtcAec_FilterAdaptation = FilterAdaptation;
|
||||
WebRtcAec_OverdriveAndSuppress = OverdriveAndSuppress;
|
||||
WebRtcAec_ComfortNoise = ComfortNoise;
|
||||
WebRtcAec_SubbandCoherence = SubbandCoherence;
|
||||
static bool initted = false;
|
||||
if (!initted) {
|
||||
initted = true;
|
||||
// Assembly optimization
|
||||
WebRtcAec_FilterFar = FilterFar;
|
||||
WebRtcAec_ScaleErrorSignal = ScaleErrorSignal;
|
||||
WebRtcAec_FilterAdaptation = FilterAdaptation;
|
||||
WebRtcAec_OverdriveAndSuppress = OverdriveAndSuppress;
|
||||
WebRtcAec_ComfortNoise = ComfortNoise;
|
||||
WebRtcAec_SubbandCoherence = SubbandCoherence;
|
||||
|
||||
#if defined(WEBRTC_ARCH_X86_FAMILY)
|
||||
if (WebRtc_GetCPUInfo(kSSE2)) {
|
||||
WebRtcAec_InitAec_SSE2();
|
||||
}
|
||||
if (WebRtc_GetCPUInfo(kSSE2)) {
|
||||
WebRtcAec_InitAec_SSE2();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(MIPS_FPU_LE)
|
||||
WebRtcAec_InitAec_mips();
|
||||
WebRtcAec_InitAec_mips();
|
||||
#endif
|
||||
|
||||
#if defined(WEBRTC_ARCH_ARM_NEON)
|
||||
WebRtcAec_InitAec_neon();
|
||||
#elif defined(WEBRTC_DETECT_ARM_NEON)
|
||||
if ((WebRtc_GetCPUFeaturesARM() & kCPUFeatureNEON) != 0) {
|
||||
WebRtcAec_InitAec_neon();
|
||||
}
|
||||
#elif defined(WEBRTC_DETECT_ARM_NEON)
|
||||
if ((WebRtc_GetCPUFeaturesARM() & kCPUFeatureNEON) != 0) {
|
||||
WebRtcAec_InitAec_neon();
|
||||
}
|
||||
#endif
|
||||
|
||||
aec_rdft_init();
|
||||
aec_rdft_init();
|
||||
}
|
||||
|
||||
return aec;
|
||||
}
|
||||
|
||||
@@ -302,6 +302,9 @@ typedef long ssize_t;
|
||||
#include <mach/mach_init.h>
|
||||
#include <mach/vm_map.h>
|
||||
#include <malloc/malloc.h>
|
||||
#ifndef _pthread_self
|
||||
#define _pthread_self() pthread_self()
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
+1
-1
@@ -313,7 +313,7 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
T& operator*() const { return *get(); }
|
||||
typename AddLvalueReference<T>::Type operator*() const { return *get(); }
|
||||
Pointer operator->() const
|
||||
{
|
||||
MOZ_ASSERT(get(), "dereferencing a UniquePtr containing nullptr");
|
||||
|
||||
@@ -11,4 +11,4 @@ The in-tree copy is updated by running
|
||||
sh update.sh
|
||||
from within the modules/brotli directory.
|
||||
|
||||
Current version: [commit d6d98957ca8ccb1ef45922e978bb10efca0ea541].
|
||||
Current version: 1.0.9 Re-release [commit e61745a6b7add50d380cfd7d3883dd6c62fc2c71].
|
||||
@@ -0,0 +1,15 @@
|
||||
/* 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 "./constants.h"
|
||||
|
||||
const BrotliPrefixCodeRange
|
||||
_kBrotliPrefixCodeRanges[BROTLI_NUM_BLOCK_LEN_SYMBOLS] = {
|
||||
{1, 2}, {5, 2}, {9, 2}, {13, 2}, {17, 3}, {25, 3},
|
||||
{33, 3}, {41, 3}, {49, 4}, {65, 4}, {81, 4}, {97, 4},
|
||||
{113, 5}, {145, 5}, {177, 5}, {209, 5}, {241, 6}, {305, 6},
|
||||
{369, 7}, {497, 8}, {753, 9}, {1265, 10}, {2289, 11}, {4337, 12},
|
||||
{8433, 13}, {16625, 24}};
|
||||
@@ -4,9 +4,18 @@
|
||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Common constants used in decoder and encoder API.
|
||||
*/
|
||||
|
||||
#ifndef BROTLI_COMMON_CONSTANTS_H_
|
||||
#define BROTLI_COMMON_CONSTANTS_H_
|
||||
|
||||
#include "./platform.h"
|
||||
#include <brotli/port.h>
|
||||
#include <brotli/types.h>
|
||||
|
||||
/* Specification: 7.3. Encoding of the context map */
|
||||
#define BROTLI_CONTEXT_MAP_MAX_RLE 16
|
||||
|
||||
@@ -29,12 +38,31 @@
|
||||
#define BROTLI_INITIAL_REPEATED_CODE_LENGTH 8
|
||||
|
||||
/* "Large Window Brotli" */
|
||||
|
||||
/**
|
||||
* The theoretical maximum number of distance bits specified for large window
|
||||
* brotli, for 64-bit encoders and decoders. Even when in practice 32-bit
|
||||
* encoders and decoders only support up to 30 max distance bits, the value is
|
||||
* set to 62 because it affects the large window brotli file format.
|
||||
* Specifically, it affects the encoding of simple huffman tree for distances,
|
||||
* see Specification RFC 7932 chapter 3.4.
|
||||
*/
|
||||
#define BROTLI_LARGE_MAX_DISTANCE_BITS 62U
|
||||
#define BROTLI_LARGE_MIN_WBITS 10
|
||||
/**
|
||||
* The maximum supported large brotli window bits by the encoder and decoder.
|
||||
* Large window brotli allows up to 62 bits, however the current encoder and
|
||||
* decoder, designed for 32-bit integers, only support up to 30 bits maximum.
|
||||
*/
|
||||
#define BROTLI_LARGE_MAX_WBITS 30
|
||||
|
||||
/* Specification: 4. Encoding of distances */
|
||||
#define BROTLI_NUM_DISTANCE_SHORT_CODES 16
|
||||
/**
|
||||
* Maximal number of "postfix" bits.
|
||||
*
|
||||
* Number of "postfix" bits is stored as 2 bits in meta-block header.
|
||||
*/
|
||||
#define BROTLI_MAX_NPOSTFIX 3
|
||||
#define BROTLI_MAX_NDIRECT 120
|
||||
#define BROTLI_MAX_DISTANCE_BITS 24U
|
||||
@@ -45,9 +73,22 @@
|
||||
#define BROTLI_NUM_DISTANCE_SYMBOLS \
|
||||
BROTLI_DISTANCE_ALPHABET_SIZE( \
|
||||
BROTLI_MAX_NDIRECT, BROTLI_MAX_NPOSTFIX, BROTLI_LARGE_MAX_DISTANCE_BITS)
|
||||
|
||||
/* ((1 << 26) - 4) is the maximal distance that can be expressed in RFC 7932
|
||||
brotli stream using NPOSTFIX = 0 and NDIRECT = 0. With other NPOSTFIX and
|
||||
NDIRECT values distances up to ((1 << 29) + 88) could be expressed. */
|
||||
#define BROTLI_MAX_DISTANCE 0x3FFFFFC
|
||||
|
||||
/* ((1 << 31) - 4) is the safe distance limit. Using this number as a limit
|
||||
allows safe distance calculation without overflows, given the distance
|
||||
alphabet size is limited to corresponding size
|
||||
(see kLargeWindowDistanceCodeLimits). */
|
||||
#define BROTLI_MAX_ALLOWED_DISTANCE 0x7FFFFFFC
|
||||
|
||||
|
||||
/* Specification: 4. Encoding of Literal Insertion Lengths and Copy Lengths */
|
||||
#define BROTLI_NUM_INS_COPY_CODES 24
|
||||
|
||||
/* 7.1. Context modes and context ID lookup for literals */
|
||||
/* "context IDs for literals are in the range of 0..63" */
|
||||
#define BROTLI_LITERAL_CONTEXT_BITS 6
|
||||
@@ -61,4 +102,99 @@
|
||||
#define BROTLI_WINDOW_GAP 16
|
||||
#define BROTLI_MAX_BACKWARD_LIMIT(W) (((size_t)1 << (W)) - BROTLI_WINDOW_GAP)
|
||||
|
||||
typedef struct BrotliDistanceCodeLimit {
|
||||
uint32_t max_alphabet_size;
|
||||
uint32_t max_distance;
|
||||
} BrotliDistanceCodeLimit;
|
||||
|
||||
/* This function calculates maximal size of distance alphabet, such that the
|
||||
distances greater than the given values can not be represented.
|
||||
|
||||
This limits are designed to support fast and safe 32-bit decoders.
|
||||
"32-bit" means that signed integer values up to ((1 << 31) - 1) could be
|
||||
safely expressed.
|
||||
|
||||
Brotli distance alphabet symbols do not represent consecutive distance
|
||||
ranges. Each distance alphabet symbol (excluding direct distances and short
|
||||
codes), represent interleaved (for NPOSTFIX > 0) range of distances.
|
||||
A "group" of consecutive (1 << NPOSTFIX) symbols represent non-interleaved
|
||||
range. Two consecutive groups require the same amount of "extra bits".
|
||||
|
||||
It is important that distance alphabet represents complete "groups".
|
||||
To avoid complex logic on encoder side about interleaved ranges
|
||||
it was decided to restrict both sides to complete distance code "groups".
|
||||
*/
|
||||
BROTLI_UNUSED_FUNCTION BrotliDistanceCodeLimit BrotliCalculateDistanceCodeLimit(
|
||||
uint32_t max_distance, uint32_t npostfix, uint32_t ndirect) {
|
||||
BrotliDistanceCodeLimit result;
|
||||
/* Marking this function as unused, because not all files
|
||||
including "constants.h" use it -> compiler warns about that. */
|
||||
BROTLI_UNUSED(&BrotliCalculateDistanceCodeLimit);
|
||||
if (max_distance <= ndirect) {
|
||||
/* This case never happens / exists only for the sake of completeness. */
|
||||
result.max_alphabet_size = max_distance + BROTLI_NUM_DISTANCE_SHORT_CODES;
|
||||
result.max_distance = max_distance;
|
||||
return result;
|
||||
} else {
|
||||
/* The first prohibited value. */
|
||||
uint32_t forbidden_distance = max_distance + 1;
|
||||
/* Subtract "directly" encoded region. */
|
||||
uint32_t offset = forbidden_distance - ndirect - 1;
|
||||
uint32_t ndistbits = 0;
|
||||
uint32_t tmp;
|
||||
uint32_t half;
|
||||
uint32_t group;
|
||||
/* Postfix for the last dcode in the group. */
|
||||
uint32_t postfix = (1u << npostfix) - 1;
|
||||
uint32_t extra;
|
||||
uint32_t start;
|
||||
/* Remove postfix and "head-start". */
|
||||
offset = (offset >> npostfix) + 4;
|
||||
/* Calculate the number of distance bits. */
|
||||
tmp = offset / 2;
|
||||
/* Poor-man's log2floor, to avoid extra dependencies. */
|
||||
while (tmp != 0) {ndistbits++; tmp = tmp >> 1;}
|
||||
/* One bit is covered with subrange addressing ("half"). */
|
||||
ndistbits--;
|
||||
/* Find subrange. */
|
||||
half = (offset >> ndistbits) & 1;
|
||||
/* Calculate the "group" part of dcode. */
|
||||
group = ((ndistbits - 1) << 1) | half;
|
||||
/* Calculated "group" covers the prohibited distance value. */
|
||||
if (group == 0) {
|
||||
/* This case is added for correctness; does not occur for limit > 128. */
|
||||
result.max_alphabet_size = ndirect + BROTLI_NUM_DISTANCE_SHORT_CODES;
|
||||
result.max_distance = ndirect;
|
||||
return result;
|
||||
}
|
||||
/* Decrement "group", so it is the last permitted "group". */
|
||||
group--;
|
||||
/* After group was decremented, ndistbits and half must be recalculated. */
|
||||
ndistbits = (group >> 1) + 1;
|
||||
/* The last available distance in the subrange has all extra bits set. */
|
||||
extra = (1u << ndistbits) - 1;
|
||||
/* Calculate region start. NB: ndistbits >= 1. */
|
||||
start = (1u << (ndistbits + 1)) - 4;
|
||||
/* Move to subregion. */
|
||||
start += (group & 1) << ndistbits;
|
||||
/* Calculate the alphabet size. */
|
||||
result.max_alphabet_size = ((group << npostfix) | postfix) + ndirect +
|
||||
BROTLI_NUM_DISTANCE_SHORT_CODES + 1;
|
||||
/* Calculate the maximal distance representable by alphabet. */
|
||||
result.max_distance = ((start + extra) << npostfix) + postfix + ndirect + 1;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/* Represents the range of values belonging to a prefix code:
|
||||
[offset, offset + 2^nbits) */
|
||||
typedef struct {
|
||||
uint16_t offset;
|
||||
uint8_t nbits;
|
||||
} BrotliPrefixCodeRange;
|
||||
|
||||
/* "Soft-private", it is exported, but not "advertised" as API. */
|
||||
BROTLI_COMMON_API extern const BrotliPrefixCodeRange
|
||||
_kBrotliPrefixCodeRanges[BROTLI_NUM_BLOCK_LEN_SYMBOLS];
|
||||
|
||||
#endif /* BROTLI_COMMON_CONSTANTS_H_ */
|
||||
@@ -0,0 +1,156 @@
|
||||
#include "./context.h"
|
||||
|
||||
#include <brotli/types.h>
|
||||
|
||||
/* Common context lookup table for all context modes. */
|
||||
const uint8_t _kBrotliContextLookupTable[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,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
8, 12, 16, 12, 12, 20, 12, 16, 24, 28, 12, 12, 32, 12, 36, 12,
|
||||
44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 32, 32, 24, 40, 28, 12,
|
||||
12, 48, 52, 52, 52, 48, 52, 52, 52, 48, 52, 52, 52, 52, 52, 48,
|
||||
52, 52, 52, 52, 52, 48, 52, 52, 52, 52, 52, 24, 12, 28, 12, 12,
|
||||
12, 56, 60, 60, 60, 56, 60, 60, 60, 56, 60, 60, 60, 60, 60, 56,
|
||||
60, 60, 60, 60, 60, 56, 60, 60, 60, 60, 60, 24, 12, 28, 12, 0,
|
||||
/* UTF8 continuation byte range. */
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
/* UTF8 lead byte range. */
|
||||
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,
|
||||
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,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
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, 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, 1, 1, 1, 1, 1,
|
||||
1, 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, 1, 1, 1, 1, 0,
|
||||
/* UTF8 continuation byte range. */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* UTF8 lead byte range. */
|
||||
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, 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,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
|
||||
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
|
||||
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
|
||||
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
|
||||
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,
|
||||
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_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,
|
||||
};
|
||||
@@ -88,6 +88,7 @@
|
||||
#ifndef BROTLI_COMMON_CONTEXT_H_
|
||||
#define BROTLI_COMMON_CONTEXT_H_
|
||||
|
||||
#include <brotli/port.h>
|
||||
#include <brotli/types.h>
|
||||
|
||||
typedef enum ContextType {
|
||||
@@ -97,163 +98,14 @@ typedef enum ContextType {
|
||||
CONTEXT_SIGNED = 3
|
||||
} ContextType;
|
||||
|
||||
/* "Soft-private", it is exported, but not "advertised" as API. */
|
||||
/* Common context lookup table for all context modes. */
|
||||
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,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
8, 12, 16, 12, 12, 20, 12, 16, 24, 28, 12, 12, 32, 12, 36, 12,
|
||||
44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 32, 32, 24, 40, 28, 12,
|
||||
12, 48, 52, 52, 52, 48, 52, 52, 52, 48, 52, 52, 52, 52, 52, 48,
|
||||
52, 52, 52, 52, 52, 48, 52, 52, 52, 52, 52, 24, 12, 28, 12, 12,
|
||||
12, 56, 60, 60, 60, 56, 60, 60, 60, 56, 60, 60, 60, 60, 60, 56,
|
||||
60, 60, 60, 60, 60, 56, 60, 60, 60, 60, 60, 24, 12, 28, 12, 0,
|
||||
/* UTF8 continuation byte range. */
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
/* UTF8 lead byte range. */
|
||||
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,
|
||||
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,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
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, 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, 1, 1, 1, 1, 1,
|
||||
1, 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, 1, 1, 1, 1, 0,
|
||||
/* UTF8 continuation byte range. */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* UTF8 lead byte range. */
|
||||
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, 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,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
|
||||
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
|
||||
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
|
||||
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
|
||||
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,
|
||||
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_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,
|
||||
};
|
||||
BROTLI_COMMON_API extern const uint8_t _kBrotliContextLookupTable[2048];
|
||||
|
||||
typedef const uint8_t* ContextLut;
|
||||
|
||||
/* typeof(MODE) == ContextType; returns ContextLut */
|
||||
#define BROTLI_CONTEXT_LUT(MODE) (&kContextLookup[(MODE) << 9])
|
||||
#define BROTLI_CONTEXT_LUT(MODE) (&_kBrotliContextLookupTable[(MODE) << 9])
|
||||
|
||||
/* typeof(LUT) == ContextLut */
|
||||
#define BROTLI_CONTEXT(P1, P2, LUT) ((LUT)[P1] | ((LUT) + 256)[P2])
|
||||
|
||||
@@ -5,12 +5,13 @@
|
||||
*/
|
||||
|
||||
#include "./dictionary.h"
|
||||
#include "./platform.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef BROTLI_EXTERNAL_DICTIONARY_DATA
|
||||
#if !defined(BROTLI_EXTERNAL_DICTIONARY_DATA)
|
||||
static const uint8_t kBrotliDictionaryData[] =
|
||||
{
|
||||
116,105,109,101,100,111,119,110,108,105,102,101,108,101,102,116,98,97,99,107,99,
|
||||
@@ -5862,7 +5863,11 @@ static const uint8_t kBrotliDictionaryData[] =
|
||||
;
|
||||
#endif /* !BROTLI_EXTERNAL_DICTIONARY_DATA */
|
||||
|
||||
#if !defined(BROTLI_EXTERNAL_DICTIONARY_DATA)
|
||||
static const BrotliDictionary kBrotliDictionary = {
|
||||
#else
|
||||
static BrotliDictionary kBrotliDictionary = {
|
||||
#endif
|
||||
/* size_bits_by_length */
|
||||
{
|
||||
0, 0, 0, 0, 10, 10, 11, 11,
|
||||
@@ -5895,9 +5900,13 @@ const BrotliDictionary* BrotliGetDictionary() {
|
||||
}
|
||||
|
||||
void BrotliSetDictionaryData(const uint8_t* data) {
|
||||
#if defined(BROTLI_EXTERNAL_DICTIONARY_DATA)
|
||||
if (!!data && !kBrotliDictionary.data) {
|
||||
kBrotliDictionary.data = data;
|
||||
}
|
||||
#else
|
||||
BROTLI_UNUSED(data); // Appease -Werror=unused-parameter
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/* Copyright 2016 Google Inc. All Rights Reserved.
|
||||
|
||||
Distributed under MIT license.
|
||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "./platform.h"
|
||||
#include <brotli/types.h>
|
||||
|
||||
/* Default brotli_alloc_func */
|
||||
void* BrotliDefaultAllocFunc(void* opaque, size_t size) {
|
||||
BROTLI_UNUSED(opaque);
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
/* Default brotli_free_func */
|
||||
void BrotliDefaultFreeFunc(void* opaque, void* address) {
|
||||
BROTLI_UNUSED(opaque);
|
||||
free(address);
|
||||
}
|
||||
@@ -24,12 +24,11 @@
|
||||
#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)
|
||||
#if defined(OS_LINUX) || defined(OS_CYGWIN) || defined(__EMSCRIPTEN__)
|
||||
#include <endian.h>
|
||||
#elif defined(OS_FREEBSD)
|
||||
#include <machine/endian.h>
|
||||
@@ -41,6 +40,10 @@
|
||||
#define BROTLI_X_BIG_ENDIAN BIG_ENDIAN
|
||||
#endif
|
||||
|
||||
#if BROTLI_MSVC_VERSION_CHECK(12, 0, 0)
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
#if defined(BROTLI_ENABLE_LOG) || defined(BROTLI_DEBUG)
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
@@ -308,8 +311,7 @@ static BROTLI_INLINE void BrotliUnalignedWrite64(void* p, uint64_t 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)
|
||||
#if BROTLI_SANITIZED
|
||||
/* 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.
|
||||
@@ -334,7 +336,7 @@ extern "C" {
|
||||
#define BrotliUnalignedRead32 __sanitizer_unaligned_load32
|
||||
#define BrotliUnalignedRead64 __sanitizer_unaligned_load64
|
||||
#define BrotliUnalignedWrite64 __sanitizer_unaligned_store64
|
||||
#else
|
||||
#else /* BROTLI_SANITIZED */
|
||||
static BROTLI_INLINE uint16_t BrotliUnalignedRead16(const void* p) {
|
||||
return *(const uint16_t*)p;
|
||||
}
|
||||
@@ -356,7 +358,7 @@ static BROTLI_INLINE void BrotliUnalignedWrite64(void* p, uint64_t v) {
|
||||
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];
|
||||
return (uint64_t) ((const 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;
|
||||
@@ -374,7 +376,7 @@ static BROTLI_INLINE void BrotliUnalignedWrite64(void* p, uint64_t v) {
|
||||
}
|
||||
#endif /* BROTLI_GNUC_HAS_ATTRIBUTE(aligned, 2, 7, 0) */
|
||||
#endif /* BROTLI_64_BITS */
|
||||
#endif /* ASAN / TSAN / MSAN */
|
||||
#endif /* BROTLI_SANITIZED */
|
||||
#endif /* BROTLI_ALIGNED_READ */
|
||||
|
||||
#if BROTLI_LITTLE_ENDIAN
|
||||
@@ -466,20 +468,20 @@ static BROTLI_INLINE void BROTLI_UNALIGNED_STORE64LE(void* p, uint64_t v) {
|
||||
#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)
|
||||
#define BROTLI_DCHECK(x) assert(x)
|
||||
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_DCHECK(x)
|
||||
#define BROTLI_DUMP() (void)(0)
|
||||
#endif
|
||||
|
||||
@@ -523,17 +525,41 @@ BROTLI_MIN_MAX(size_t) BROTLI_MIN_MAX(uint32_t) BROTLI_MIN_MAX(uint8_t)
|
||||
(A)[(J)] = __brotli_swap_tmp; \
|
||||
}
|
||||
|
||||
/* Default brotli_alloc_func */
|
||||
static void* BrotliDefaultAllocFunc(void* opaque, size_t size) {
|
||||
BROTLI_UNUSED(opaque);
|
||||
return malloc(size);
|
||||
#if BROTLI_64_BITS
|
||||
#if BROTLI_GNUC_HAS_BUILTIN(__builtin_ctzll, 3, 4, 0) || \
|
||||
BROTLI_INTEL_VERSION_CHECK(16, 0, 0)
|
||||
#define BROTLI_TZCNT64 __builtin_ctzll
|
||||
#elif BROTLI_MSVC_VERSION_CHECK(12, 0, 0)
|
||||
#if defined(BROTLI_TARGET_X64)
|
||||
#define BROTLI_TZCNT64 _tzcnt_u64
|
||||
#else /* BROTLI_TARGET_X64 */
|
||||
static BROTLI_INLINE uint32_t BrotliBsf64Msvc(uint64_t x) {
|
||||
uint32_t lsb;
|
||||
_BitScanForward64(&lsb, x);
|
||||
return lsb;
|
||||
}
|
||||
#define BROTLI_TZCNT64 BrotliBsf64Msvc
|
||||
#endif /* BROTLI_TARGET_X64 */
|
||||
#endif /* __builtin_ctzll */
|
||||
#endif /* BROTLI_64_BITS */
|
||||
|
||||
#if BROTLI_GNUC_HAS_BUILTIN(__builtin_clz, 3, 4, 0) || \
|
||||
BROTLI_INTEL_VERSION_CHECK(16, 0, 0)
|
||||
#define BROTLI_BSR32(x) (31u ^ (uint32_t)__builtin_clz(x))
|
||||
#elif BROTLI_MSVC_VERSION_CHECK(12, 0, 0)
|
||||
static BROTLI_INLINE uint32_t BrotliBsr32Msvc(uint32_t x) {
|
||||
unsigned long msb;
|
||||
_BitScanReverse(&msb, x);
|
||||
return (uint32_t)msb;
|
||||
}
|
||||
#define BROTLI_BSR32 BrotliBsr32Msvc
|
||||
#endif /* __builtin_clz */
|
||||
|
||||
/* Default brotli_alloc_func */
|
||||
BROTLI_COMMON_API void* BrotliDefaultAllocFunc(void* opaque, size_t size);
|
||||
|
||||
/* Default brotli_free_func */
|
||||
static void BrotliDefaultFreeFunc(void* opaque, void* address) {
|
||||
BROTLI_UNUSED(opaque);
|
||||
free(address);
|
||||
}
|
||||
BROTLI_COMMON_API void BrotliDefaultFreeFunc(void* opaque, void* address);
|
||||
|
||||
BROTLI_UNUSED_FUNCTION void BrotliSuppressUnusedFunctions(void) {
|
||||
BROTLI_UNUSED(&BrotliSuppressUnusedFunctions);
|
||||
|
||||
@@ -24,8 +24,8 @@ static const char kPrefixSuffix[217] =
|
||||
/* 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 */
|
||||
"t \4ize \2\xc2\xa0\4ous \5 the \2e "; /* \0 - implicit trailing zero. */
|
||||
/* Cx _2 _7___ ___ _A _F _5 _8 */
|
||||
|
||||
static const uint16_t kPrefixSuffixMap[50] = {
|
||||
0x00, 0x02, 0x05, 0x0E, 0x13, 0x16, 0x18, 0x1E, 0x23, 0x25,
|
||||
@@ -160,12 +160,13 @@ static const uint8_t kTransformsData[] = {
|
||||
0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 34,
|
||||
};
|
||||
|
||||
static BrotliTransforms kBrotliTransforms = {
|
||||
static const BrotliTransforms kBrotliTransforms = {
|
||||
sizeof(kPrefixSuffix),
|
||||
(const uint8_t*)kPrefixSuffix,
|
||||
kPrefixSuffixMap,
|
||||
sizeof(kTransformsData) / (3 * sizeof(kTransformsData[0])),
|
||||
kTransformsData,
|
||||
NULL, /* no extra parameters */
|
||||
{0, 12, 27, 23, 42, 63, 56, 48, 59, 64}
|
||||
};
|
||||
|
||||
@@ -190,6 +191,48 @@ static int ToUpperCase(uint8_t* p) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
static int Shift(uint8_t* word, int word_len, uint16_t parameter) {
|
||||
/* Limited sign extension: scalar < (1 << 24). */
|
||||
uint32_t scalar =
|
||||
(parameter & 0x7FFFu) + (0x1000000u - (parameter & 0x8000u));
|
||||
if (word[0] < 0x80) {
|
||||
/* 1-byte rune / 0sssssss / 7 bit scalar (ASCII). */
|
||||
scalar += (uint32_t)word[0];
|
||||
word[0] = (uint8_t)(scalar & 0x7Fu);
|
||||
return 1;
|
||||
} else if (word[0] < 0xC0) {
|
||||
/* Continuation / 10AAAAAA. */
|
||||
return 1;
|
||||
} else if (word[0] < 0xE0) {
|
||||
/* 2-byte rune / 110sssss AAssssss / 11 bit scalar. */
|
||||
if (word_len < 2) return 1;
|
||||
scalar += (uint32_t)((word[1] & 0x3Fu) | ((word[0] & 0x1Fu) << 6u));
|
||||
word[0] = (uint8_t)(0xC0 | ((scalar >> 6u) & 0x1F));
|
||||
word[1] = (uint8_t)((word[1] & 0xC0) | (scalar & 0x3F));
|
||||
return 2;
|
||||
} else if (word[0] < 0xF0) {
|
||||
/* 3-byte rune / 1110ssss AAssssss BBssssss / 16 bit scalar. */
|
||||
if (word_len < 3) return word_len;
|
||||
scalar += (uint32_t)((word[2] & 0x3Fu) | ((word[1] & 0x3Fu) << 6u) |
|
||||
((word[0] & 0x0Fu) << 12u));
|
||||
word[0] = (uint8_t)(0xE0 | ((scalar >> 12u) & 0x0F));
|
||||
word[1] = (uint8_t)((word[1] & 0xC0) | ((scalar >> 6u) & 0x3F));
|
||||
word[2] = (uint8_t)((word[2] & 0xC0) | (scalar & 0x3F));
|
||||
return 3;
|
||||
} else if (word[0] < 0xF8) {
|
||||
/* 4-byte rune / 11110sss AAssssss BBssssss CCssssss / 21 bit scalar. */
|
||||
if (word_len < 4) return word_len;
|
||||
scalar += (uint32_t)((word[3] & 0x3Fu) | ((word[2] & 0x3Fu) << 6u) |
|
||||
((word[1] & 0x3Fu) << 12u) | ((word[0] & 0x07u) << 18u));
|
||||
word[0] = (uint8_t)(0xF0 | ((scalar >> 18u) & 0x07));
|
||||
word[1] = (uint8_t)((word[1] & 0xC0) | ((scalar >> 12u) & 0x3F));
|
||||
word[2] = (uint8_t)((word[2] & 0xC0) | ((scalar >> 6u) & 0x3F));
|
||||
word[3] = (uint8_t)((word[3] & 0xC0) | (scalar & 0x3F));
|
||||
return 4;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int BrotliTransformDictionaryWord(uint8_t* dst, const uint8_t* word, int len,
|
||||
const BrotliTransforms* transforms, int transform_idx) {
|
||||
int idx = 0;
|
||||
@@ -221,6 +264,19 @@ int BrotliTransformDictionaryWord(uint8_t* dst, const uint8_t* word, int len,
|
||||
uppercase += step;
|
||||
len -= step;
|
||||
}
|
||||
} else if (t == BROTLI_TRANSFORM_SHIFT_FIRST) {
|
||||
uint16_t param = (uint16_t)(transforms->params[transform_idx * 2]
|
||||
+ (transforms->params[transform_idx * 2 + 1] << 8u));
|
||||
Shift(&dst[idx - len], len, param);
|
||||
} else if (t == BROTLI_TRANSFORM_SHIFT_ALL) {
|
||||
uint16_t param = (uint16_t)(transforms->params[transform_idx * 2]
|
||||
+ (transforms->params[transform_idx * 2 + 1] << 8u));
|
||||
uint8_t* shift = &dst[idx - len];
|
||||
while (len > 0) {
|
||||
int step = Shift(shift, len, param);
|
||||
shift += step;
|
||||
len -= step;
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
|
||||
@@ -37,6 +37,8 @@ enum BrotliWordTransformType {
|
||||
BROTLI_TRANSFORM_OMIT_FIRST_7 = 18,
|
||||
BROTLI_TRANSFORM_OMIT_FIRST_8 = 19,
|
||||
BROTLI_TRANSFORM_OMIT_FIRST_9 = 20,
|
||||
BROTLI_TRANSFORM_SHIFT_FIRST = 21,
|
||||
BROTLI_TRANSFORM_SHIFT_ALL = 22,
|
||||
BROTLI_NUM_TRANSFORM_TYPES /* Counts transforms, not a transform itself. */
|
||||
};
|
||||
|
||||
@@ -50,6 +52,9 @@ typedef struct BrotliTransforms {
|
||||
uint32_t num_transforms;
|
||||
/* Each entry is a [prefix_id, transform, suffix_id] triplet. */
|
||||
const uint8_t* transforms;
|
||||
/* Shift for BROTLI_TRANSFORM_SHIFT_FIRST and BROTLI_TRANSFORM_SHIFT_ALL,
|
||||
must be NULL if and only if no such transforms are present. */
|
||||
const uint8_t* params;
|
||||
/* Indices of transforms like ["", BROTLI_TRANSFORM_OMIT_LAST_#, ""].
|
||||
0-th element corresponds to ["", BROTLI_TRANSFORM_IDENTITY, ""].
|
||||
-1, if cut-off transform does not exist. */
|
||||
|
||||
@@ -14,13 +14,13 @@
|
||||
BrotliEncoderVersion methods. */
|
||||
|
||||
/* Semantic version, calculated as (MAJOR << 24) | (MINOR << 12) | PATCH */
|
||||
#define BROTLI_VERSION 0x1000007
|
||||
#define BROTLI_VERSION 0x1000009
|
||||
|
||||
/* 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
|
||||
#define BROTLI_ABI_VERSION 0x1009000
|
||||
|
||||
#endif /* BROTLI_COMMON_VERSION_H_ */
|
||||
@@ -15,6 +15,17 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
const uint32_t kBrotliBitMask[33] = { 0x00000000,
|
||||
0x00000001, 0x00000003, 0x00000007, 0x0000000F,
|
||||
0x0000001F, 0x0000003F, 0x0000007F, 0x000000FF,
|
||||
0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF,
|
||||
0x00001FFF, 0x00003FFF, 0x00007FFF, 0x0000FFFF,
|
||||
0x0001FFFF, 0x0003FFFF, 0x0007FFFF, 0x000FFFFF,
|
||||
0x001FFFFF, 0x003FFFFF, 0x007FFFFF, 0x00FFFFFF,
|
||||
0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF, 0x0FFFFFFF,
|
||||
0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF
|
||||
};
|
||||
|
||||
void BrotliInitBitReader(BrotliBitReader* const br) {
|
||||
br->val_ = 0;
|
||||
br->bit_pos_ = sizeof(br->val_) << 3;
|
||||
@@ -43,6 +54,23 @@ BROTLI_BOOL BrotliWarmupBitReader(BrotliBitReader* const br) {
|
||||
return BROTLI_TRUE;
|
||||
}
|
||||
|
||||
BROTLI_BOOL BrotliSafeReadBits32Slow(BrotliBitReader* const br,
|
||||
uint32_t n_bits, uint32_t* val) {
|
||||
uint32_t low_val;
|
||||
uint32_t high_val;
|
||||
BrotliBitReaderState memento;
|
||||
BROTLI_DCHECK(n_bits <= 32);
|
||||
BROTLI_DCHECK(n_bits > 24);
|
||||
BrotliBitReaderSaveState(br, &memento);
|
||||
if (!BrotliSafeReadBits(br, 16, &low_val) ||
|
||||
!BrotliSafeReadBits(br, n_bits - 16, &high_val)) {
|
||||
BrotliBitReaderRestoreState(br, &memento);
|
||||
return BROTLI_FALSE;
|
||||
}
|
||||
*val = low_val | (high_val << 16);
|
||||
return BROTLI_TRUE;
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <string.h> /* memcpy */
|
||||
|
||||
#include "../common/constants.h"
|
||||
#include "../common/platform.h"
|
||||
#include <brotli/types.h>
|
||||
|
||||
@@ -20,16 +21,7 @@ extern "C" {
|
||||
|
||||
#define BROTLI_SHORT_FILL_BIT_WINDOW_READ (sizeof(brotli_reg_t) >> 1)
|
||||
|
||||
static const uint32_t kBitMask[33] = { 0x00000000,
|
||||
0x00000001, 0x00000003, 0x00000007, 0x0000000F,
|
||||
0x0000001F, 0x0000003F, 0x0000007F, 0x000000FF,
|
||||
0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF,
|
||||
0x00001FFF, 0x00003FFF, 0x00007FFF, 0x0000FFFF,
|
||||
0x0001FFFF, 0x0003FFFF, 0x0007FFFF, 0x000FFFFF,
|
||||
0x001FFFFF, 0x003FFFFF, 0x007FFFFF, 0x00FFFFFF,
|
||||
0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF, 0x0FFFFFFF,
|
||||
0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF
|
||||
};
|
||||
BROTLI_INTERNAL extern const uint32_t kBrotliBitMask[33];
|
||||
|
||||
static BROTLI_INLINE uint32_t BitMask(uint32_t n) {
|
||||
if (BROTLI_IS_CONSTANT(n) || BROTLI_HAS_UBFX) {
|
||||
@@ -37,7 +29,7 @@ static BROTLI_INLINE uint32_t BitMask(uint32_t n) {
|
||||
"Unsigned Bit Field Extract" UBFX instruction on ARM. */
|
||||
return ~((0xFFFFFFFFu) << n);
|
||||
} else {
|
||||
return kBitMask[n];
|
||||
return kBrotliBitMask[n];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +57,12 @@ BROTLI_INTERNAL void BrotliInitBitReader(BrotliBitReader* const br);
|
||||
reading. */
|
||||
BROTLI_INTERNAL BROTLI_BOOL BrotliWarmupBitReader(BrotliBitReader* const br);
|
||||
|
||||
/* Fallback for BrotliSafeReadBits32. Extracted as noninlined method to unburden
|
||||
the main code-path. Never called for RFC brotli streams, required only for
|
||||
"large-window" mode and other extensions. */
|
||||
BROTLI_INTERNAL BROTLI_NOINLINE BROTLI_BOOL BrotliSafeReadBits32Slow(
|
||||
BrotliBitReader* const br, uint32_t n_bits, uint32_t* val);
|
||||
|
||||
static BROTLI_INLINE void BrotliBitReaderSaveState(
|
||||
BrotliBitReader* const from, BrotliBitReaderState* to) {
|
||||
to->val_ = from->val_;
|
||||
@@ -87,8 +85,11 @@ static BROTLI_INLINE uint32_t BrotliGetAvailableBits(
|
||||
}
|
||||
|
||||
/* Returns amount of unread bytes the bit reader still has buffered from the
|
||||
BrotliInput, including whole bytes in br->val_. */
|
||||
BrotliInput, including whole bytes in br->val_. Result is capped with
|
||||
maximal ring-buffer size (larger number won't be utilized anyway). */
|
||||
static BROTLI_INLINE size_t BrotliGetRemainingBytes(BrotliBitReader* br) {
|
||||
static const size_t kCap = (size_t)1 << BROTLI_LARGE_MAX_WBITS;
|
||||
if (br->avail_in > kCap) return kCap;
|
||||
return br->avail_in + (BrotliGetAvailableBits(br) >> 3);
|
||||
}
|
||||
|
||||
@@ -237,15 +238,17 @@ static BROTLI_INLINE void BrotliBitReaderUnload(BrotliBitReader* br) {
|
||||
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",
|
||||
BROTLI_LOG(("[BrotliTakeBits] %d %d %d val: %6x\n",
|
||||
(int)br->avail_in, (int)br->bit_pos_, (int)n_bits, (int)*val));
|
||||
BrotliDropBits(br, n_bits);
|
||||
}
|
||||
|
||||
/* Reads the specified number of bits from |br| and advances the bit pos.
|
||||
Assumes that there is enough input to perform BrotliFillBitWindow. */
|
||||
static BROTLI_INLINE uint32_t BrotliReadBits(
|
||||
Assumes that there is enough input to perform BrotliFillBitWindow.
|
||||
Up to 24 bits are allowed to be requested from this method. */
|
||||
static BROTLI_INLINE uint32_t BrotliReadBits24(
|
||||
BrotliBitReader* const br, uint32_t n_bits) {
|
||||
BROTLI_DCHECK(n_bits <= 24);
|
||||
if (BROTLI_64_BITS || (n_bits <= 16)) {
|
||||
uint32_t val;
|
||||
BrotliFillBitWindow(br, n_bits);
|
||||
@@ -262,10 +265,32 @@ static BROTLI_INLINE uint32_t BrotliReadBits(
|
||||
}
|
||||
}
|
||||
|
||||
/* Same as BrotliReadBits24, but allows reading up to 32 bits. */
|
||||
static BROTLI_INLINE uint32_t BrotliReadBits32(
|
||||
BrotliBitReader* const br, uint32_t n_bits) {
|
||||
BROTLI_DCHECK(n_bits <= 32);
|
||||
if (BROTLI_64_BITS || (n_bits <= 16)) {
|
||||
uint32_t val;
|
||||
BrotliFillBitWindow(br, n_bits);
|
||||
BrotliTakeBits(br, n_bits, &val);
|
||||
return val;
|
||||
} else {
|
||||
uint32_t low_val;
|
||||
uint32_t high_val;
|
||||
BrotliFillBitWindow(br, 16);
|
||||
BrotliTakeBits(br, 16, &low_val);
|
||||
BrotliFillBitWindow(br, 16);
|
||||
BrotliTakeBits(br, n_bits - 16, &high_val);
|
||||
return low_val | (high_val << 16);
|
||||
}
|
||||
}
|
||||
|
||||
/* Tries to read the specified amount of bits. Returns BROTLI_FALSE, if there
|
||||
is not enough input. |n_bits| MUST be positive. */
|
||||
is not enough input. |n_bits| MUST be positive.
|
||||
Up to 24 bits are allowed to be requested from this method. */
|
||||
static BROTLI_INLINE BROTLI_BOOL BrotliSafeReadBits(
|
||||
BrotliBitReader* const br, uint32_t n_bits, uint32_t* val) {
|
||||
BROTLI_DCHECK(n_bits <= 24);
|
||||
while (BrotliGetAvailableBits(br) < n_bits) {
|
||||
if (!BrotliPullByte(br)) {
|
||||
return BROTLI_FALSE;
|
||||
@@ -275,6 +300,23 @@ static BROTLI_INLINE BROTLI_BOOL BrotliSafeReadBits(
|
||||
return BROTLI_TRUE;
|
||||
}
|
||||
|
||||
/* Same as BrotliSafeReadBits, but allows reading up to 32 bits. */
|
||||
static BROTLI_INLINE BROTLI_BOOL BrotliSafeReadBits32(
|
||||
BrotliBitReader* const br, uint32_t n_bits, uint32_t* val) {
|
||||
BROTLI_DCHECK(n_bits <= 32);
|
||||
if (BROTLI_64_BITS || (n_bits <= 24)) {
|
||||
while (BrotliGetAvailableBits(br) < n_bits) {
|
||||
if (!BrotliPullByte(br)) {
|
||||
return BROTLI_FALSE;
|
||||
}
|
||||
}
|
||||
BrotliTakeBits(br, n_bits, val);
|
||||
return BROTLI_TRUE;
|
||||
} else {
|
||||
return BrotliSafeReadBits32Slow(br, n_bits, val);
|
||||
}
|
||||
}
|
||||
|
||||
/* Advances the bit reader position to the next byte boundary and verifies
|
||||
that any skipped bits are set to zero. */
|
||||
static BROTLI_INLINE BROTLI_BOOL BrotliJumpToByteBoundary(BrotliBitReader* br) {
|
||||
|
||||
+353
-251
@@ -41,7 +41,8 @@ extern "C" {
|
||||
|
||||
/* We need the slack region for the following reasons:
|
||||
- doing up to two 16-byte copies for fast backward copying
|
||||
- inserting transformed dictionary word (5 prefix + 24 base + 8 suffix) */
|
||||
- inserting transformed dictionary word:
|
||||
5 prefix + 24 base + 8 suffix */
|
||||
static const uint32_t kRingBufferWriteAheadSlack = 42;
|
||||
|
||||
static const uint8_t kCodeLengthCodeOrder[BROTLI_CODE_LENGTH_CODES] = {
|
||||
@@ -274,7 +275,8 @@ static BrotliDecoderErrorCode BROTLI_NOINLINE DecodeMetaBlockLength(
|
||||
s->loop_counter = i;
|
||||
return BROTLI_DECODER_NEEDS_MORE_INPUT;
|
||||
}
|
||||
if (i + 1 == s->size_nibbles && s->size_nibbles > 4 && bits == 0) {
|
||||
if (i + 1 == (int)s->size_nibbles && s->size_nibbles > 4 &&
|
||||
bits == 0) {
|
||||
return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_NIBBLE);
|
||||
}
|
||||
s->meta_block_remaining_len |= (int)(bits << (i * 4));
|
||||
@@ -323,7 +325,8 @@ static BrotliDecoderErrorCode BROTLI_NOINLINE DecodeMetaBlockLength(
|
||||
s->loop_counter = i;
|
||||
return BROTLI_DECODER_NEEDS_MORE_INPUT;
|
||||
}
|
||||
if (i + 1 == s->size_nibbles && s->size_nibbles > 1 && bits == 0) {
|
||||
if (i + 1 == (int)s->size_nibbles && s->size_nibbles > 1 &&
|
||||
bits == 0) {
|
||||
return BROTLI_FAILURE(
|
||||
BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_META_NIBBLE);
|
||||
}
|
||||
@@ -470,32 +473,34 @@ static BROTLI_INLINE uint32_t Log2Floor(uint32_t x) {
|
||||
Totally 1..4 symbols are read, 1..11 bits each.
|
||||
The list of symbols MUST NOT contain duplicates. */
|
||||
static BrotliDecoderErrorCode ReadSimpleHuffmanSymbols(
|
||||
uint32_t alphabet_size, uint32_t max_symbol, BrotliDecoderState* s) {
|
||||
uint32_t alphabet_size_max, uint32_t alphabet_size_limit,
|
||||
BrotliDecoderState* s) {
|
||||
/* max_bits == 1..11; symbol == 0..3; 1..44 bits will be read. */
|
||||
BrotliBitReader* br = &s->br;
|
||||
uint32_t max_bits = Log2Floor(alphabet_size - 1);
|
||||
uint32_t i = s->sub_loop_counter;
|
||||
uint32_t num_symbols = s->symbol;
|
||||
BrotliMetablockHeaderArena* h = &s->arena.header;
|
||||
uint32_t max_bits = Log2Floor(alphabet_size_max - 1);
|
||||
uint32_t i = h->sub_loop_counter;
|
||||
uint32_t num_symbols = h->symbol;
|
||||
while (i <= num_symbols) {
|
||||
uint32_t v;
|
||||
if (BROTLI_PREDICT_FALSE(!BrotliSafeReadBits(br, max_bits, &v))) {
|
||||
s->sub_loop_counter = i;
|
||||
s->substate_huffman = BROTLI_STATE_HUFFMAN_SIMPLE_READ;
|
||||
h->sub_loop_counter = i;
|
||||
h->substate_huffman = BROTLI_STATE_HUFFMAN_SIMPLE_READ;
|
||||
return BROTLI_DECODER_NEEDS_MORE_INPUT;
|
||||
}
|
||||
if (v >= max_symbol) {
|
||||
if (v >= alphabet_size_limit) {
|
||||
return
|
||||
BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_ALPHABET);
|
||||
}
|
||||
s->symbols_lists_array[i] = (uint16_t)v;
|
||||
BROTLI_LOG_UINT(s->symbols_lists_array[i]);
|
||||
h->symbols_lists_array[i] = (uint16_t)v;
|
||||
BROTLI_LOG_UINT(h->symbols_lists_array[i]);
|
||||
++i;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_symbols; ++i) {
|
||||
uint32_t k = i + 1;
|
||||
for (; k <= num_symbols; ++k) {
|
||||
if (s->symbols_lists_array[i] == s->symbols_lists_array[k]) {
|
||||
if (h->symbols_lists_array[i] == h->symbols_lists_array[k]) {
|
||||
return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_SAME);
|
||||
}
|
||||
}
|
||||
@@ -588,27 +593,28 @@ static BROTLI_INLINE void ProcessRepeatedCodeLength(uint32_t code_len,
|
||||
static BrotliDecoderErrorCode ReadSymbolCodeLengths(
|
||||
uint32_t alphabet_size, BrotliDecoderState* s) {
|
||||
BrotliBitReader* br = &s->br;
|
||||
uint32_t symbol = s->symbol;
|
||||
uint32_t repeat = s->repeat;
|
||||
uint32_t space = s->space;
|
||||
uint32_t prev_code_len = s->prev_code_len;
|
||||
uint32_t repeat_code_len = s->repeat_code_len;
|
||||
uint16_t* symbol_lists = s->symbol_lists;
|
||||
uint16_t* code_length_histo = s->code_length_histo;
|
||||
int* next_symbol = s->next_symbol;
|
||||
BrotliMetablockHeaderArena* h = &s->arena.header;
|
||||
uint32_t symbol = h->symbol;
|
||||
uint32_t repeat = h->repeat;
|
||||
uint32_t space = h->space;
|
||||
uint32_t prev_code_len = h->prev_code_len;
|
||||
uint32_t repeat_code_len = h->repeat_code_len;
|
||||
uint16_t* symbol_lists = h->symbol_lists;
|
||||
uint16_t* code_length_histo = h->code_length_histo;
|
||||
int* next_symbol = h->next_symbol;
|
||||
if (!BrotliWarmupBitReader(br)) {
|
||||
return BROTLI_DECODER_NEEDS_MORE_INPUT;
|
||||
}
|
||||
while (symbol < alphabet_size && space > 0) {
|
||||
const HuffmanCode* p = s->table;
|
||||
const HuffmanCode* p = h->table;
|
||||
uint32_t code_len;
|
||||
BROTLI_HC_MARK_TABLE_FOR_FAST_LOAD(p);
|
||||
if (!BrotliCheckInputAmount(br, BROTLI_SHORT_FILL_BIT_WINDOW_READ)) {
|
||||
s->symbol = symbol;
|
||||
s->repeat = repeat;
|
||||
s->prev_code_len = prev_code_len;
|
||||
s->repeat_code_len = repeat_code_len;
|
||||
s->space = space;
|
||||
h->symbol = symbol;
|
||||
h->repeat = repeat;
|
||||
h->prev_code_len = prev_code_len;
|
||||
h->repeat_code_len = repeat_code_len;
|
||||
h->space = space;
|
||||
return BROTLI_DECODER_NEEDS_MORE_INPUT;
|
||||
}
|
||||
BrotliFillBitWindow16(br);
|
||||
@@ -630,16 +636,17 @@ static BrotliDecoderErrorCode ReadSymbolCodeLengths(
|
||||
symbol_lists, code_length_histo, next_symbol);
|
||||
}
|
||||
}
|
||||
s->space = space;
|
||||
h->space = space;
|
||||
return BROTLI_DECODER_SUCCESS;
|
||||
}
|
||||
|
||||
static BrotliDecoderErrorCode SafeReadSymbolCodeLengths(
|
||||
uint32_t alphabet_size, BrotliDecoderState* s) {
|
||||
BrotliBitReader* br = &s->br;
|
||||
BrotliMetablockHeaderArena* h = &s->arena.header;
|
||||
BROTLI_BOOL get_byte = BROTLI_FALSE;
|
||||
while (s->symbol < alphabet_size && s->space > 0) {
|
||||
const HuffmanCode* p = s->table;
|
||||
while (h->symbol < alphabet_size && h->space > 0) {
|
||||
const HuffmanCode* p = h->table;
|
||||
uint32_t code_len;
|
||||
uint32_t available_bits;
|
||||
uint32_t bits = 0;
|
||||
@@ -659,9 +666,9 @@ static BrotliDecoderErrorCode SafeReadSymbolCodeLengths(
|
||||
code_len = BROTLI_HC_FAST_LOAD_VALUE(p); /* code_len == 0..17 */
|
||||
if (code_len < BROTLI_REPEAT_PREVIOUS_CODE_LENGTH) {
|
||||
BrotliDropBits(br, BROTLI_HC_FAST_LOAD_BITS(p));
|
||||
ProcessSingleCodeLength(code_len, &s->symbol, &s->repeat, &s->space,
|
||||
&s->prev_code_len, s->symbol_lists, s->code_length_histo,
|
||||
s->next_symbol);
|
||||
ProcessSingleCodeLength(code_len, &h->symbol, &h->repeat, &h->space,
|
||||
&h->prev_code_len, h->symbol_lists, h->code_length_histo,
|
||||
h->next_symbol);
|
||||
} else { /* code_len == 16..17, extra_bits == 2..3 */
|
||||
uint32_t extra_bits = code_len - 14U;
|
||||
uint32_t repeat_delta = (bits >> BROTLI_HC_FAST_LOAD_BITS(p)) &
|
||||
@@ -672,9 +679,9 @@ static BrotliDecoderErrorCode SafeReadSymbolCodeLengths(
|
||||
}
|
||||
BrotliDropBits(br, BROTLI_HC_FAST_LOAD_BITS(p) + extra_bits);
|
||||
ProcessRepeatedCodeLength(code_len, repeat_delta, alphabet_size,
|
||||
&s->symbol, &s->repeat, &s->space, &s->prev_code_len,
|
||||
&s->repeat_code_len, s->symbol_lists, s->code_length_histo,
|
||||
s->next_symbol);
|
||||
&h->symbol, &h->repeat, &h->space, &h->prev_code_len,
|
||||
&h->repeat_code_len, h->symbol_lists, h->code_length_histo,
|
||||
h->next_symbol);
|
||||
}
|
||||
}
|
||||
return BROTLI_DECODER_SUCCESS;
|
||||
@@ -684,9 +691,10 @@ static BrotliDecoderErrorCode SafeReadSymbolCodeLengths(
|
||||
Each code is 2..4 bits long. In total 30..72 bits are used. */
|
||||
static BrotliDecoderErrorCode ReadCodeLengthCodeLengths(BrotliDecoderState* s) {
|
||||
BrotliBitReader* br = &s->br;
|
||||
uint32_t num_codes = s->repeat;
|
||||
unsigned space = s->space;
|
||||
uint32_t i = s->sub_loop_counter;
|
||||
BrotliMetablockHeaderArena* h = &s->arena.header;
|
||||
uint32_t num_codes = h->repeat;
|
||||
unsigned space = h->space;
|
||||
uint32_t i = h->sub_loop_counter;
|
||||
for (; i < BROTLI_CODE_LENGTH_CODES; ++i) {
|
||||
const uint8_t code_len_idx = kCodeLengthCodeOrder[i];
|
||||
uint32_t ix;
|
||||
@@ -699,21 +707,21 @@ static BrotliDecoderErrorCode ReadCodeLengthCodeLengths(BrotliDecoderState* s) {
|
||||
ix = 0;
|
||||
}
|
||||
if (kCodeLengthPrefixLength[ix] > available_bits) {
|
||||
s->sub_loop_counter = i;
|
||||
s->repeat = num_codes;
|
||||
s->space = space;
|
||||
s->substate_huffman = BROTLI_STATE_HUFFMAN_COMPLEX;
|
||||
h->sub_loop_counter = i;
|
||||
h->repeat = num_codes;
|
||||
h->space = space;
|
||||
h->substate_huffman = BROTLI_STATE_HUFFMAN_COMPLEX;
|
||||
return BROTLI_DECODER_NEEDS_MORE_INPUT;
|
||||
}
|
||||
}
|
||||
v = kCodeLengthPrefixValue[ix];
|
||||
BrotliDropBits(br, kCodeLengthPrefixLength[ix]);
|
||||
s->code_length_code_lengths[code_len_idx] = (uint8_t)v;
|
||||
BROTLI_LOG_ARRAY_INDEX(s->code_length_code_lengths, code_len_idx);
|
||||
h->code_length_code_lengths[code_len_idx] = (uint8_t)v;
|
||||
BROTLI_LOG_ARRAY_INDEX(h->code_length_code_lengths, code_len_idx);
|
||||
if (v != 0) {
|
||||
space = space - (32U >> v);
|
||||
++num_codes;
|
||||
++s->code_length_histo[v];
|
||||
++h->code_length_histo[v];
|
||||
if (space - 1U >= 32U) {
|
||||
/* space is 0 or wrapped around. */
|
||||
break;
|
||||
@@ -737,49 +745,48 @@ static BrotliDecoderErrorCode ReadCodeLengthCodeLengths(BrotliDecoderState* s) {
|
||||
encoded with predefined entropy code. 32 - 74 bits are used.
|
||||
B.2) Decoded table is used to decode code lengths of symbols in resulting
|
||||
Huffman table. In worst case 3520 bits are read. */
|
||||
static BrotliDecoderErrorCode ReadHuffmanCode(uint32_t alphabet_size,
|
||||
uint32_t max_symbol,
|
||||
static BrotliDecoderErrorCode ReadHuffmanCode(uint32_t alphabet_size_max,
|
||||
uint32_t alphabet_size_limit,
|
||||
HuffmanCode* table,
|
||||
uint32_t* opt_table_size,
|
||||
BrotliDecoderState* s) {
|
||||
BrotliBitReader* br = &s->br;
|
||||
/* Unnecessary masking, but might be good for safety. */
|
||||
alphabet_size &= 0x7FF;
|
||||
BrotliMetablockHeaderArena* h = &s->arena.header;
|
||||
/* State machine. */
|
||||
for (;;) {
|
||||
switch (s->substate_huffman) {
|
||||
switch (h->substate_huffman) {
|
||||
case BROTLI_STATE_HUFFMAN_NONE:
|
||||
if (!BrotliSafeReadBits(br, 2, &s->sub_loop_counter)) {
|
||||
if (!BrotliSafeReadBits(br, 2, &h->sub_loop_counter)) {
|
||||
return BROTLI_DECODER_NEEDS_MORE_INPUT;
|
||||
}
|
||||
BROTLI_LOG_UINT(s->sub_loop_counter);
|
||||
BROTLI_LOG_UINT(h->sub_loop_counter);
|
||||
/* The value is used as follows:
|
||||
1 for simple code;
|
||||
0 for no skipping, 2 skips 2 code lengths, 3 skips 3 code lengths */
|
||||
if (s->sub_loop_counter != 1) {
|
||||
s->space = 32;
|
||||
s->repeat = 0; /* num_codes */
|
||||
memset(&s->code_length_histo[0], 0, sizeof(s->code_length_histo[0]) *
|
||||
if (h->sub_loop_counter != 1) {
|
||||
h->space = 32;
|
||||
h->repeat = 0; /* num_codes */
|
||||
memset(&h->code_length_histo[0], 0, sizeof(h->code_length_histo[0]) *
|
||||
(BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH + 1));
|
||||
memset(&s->code_length_code_lengths[0], 0,
|
||||
sizeof(s->code_length_code_lengths));
|
||||
s->substate_huffman = BROTLI_STATE_HUFFMAN_COMPLEX;
|
||||
memset(&h->code_length_code_lengths[0], 0,
|
||||
sizeof(h->code_length_code_lengths));
|
||||
h->substate_huffman = BROTLI_STATE_HUFFMAN_COMPLEX;
|
||||
continue;
|
||||
}
|
||||
/* Fall through. */
|
||||
|
||||
case BROTLI_STATE_HUFFMAN_SIMPLE_SIZE:
|
||||
/* Read symbols, codes & code lengths directly. */
|
||||
if (!BrotliSafeReadBits(br, 2, &s->symbol)) { /* num_symbols */
|
||||
s->substate_huffman = BROTLI_STATE_HUFFMAN_SIMPLE_SIZE;
|
||||
if (!BrotliSafeReadBits(br, 2, &h->symbol)) { /* num_symbols */
|
||||
h->substate_huffman = BROTLI_STATE_HUFFMAN_SIMPLE_SIZE;
|
||||
return BROTLI_DECODER_NEEDS_MORE_INPUT;
|
||||
}
|
||||
s->sub_loop_counter = 0;
|
||||
h->sub_loop_counter = 0;
|
||||
/* Fall through. */
|
||||
|
||||
case BROTLI_STATE_HUFFMAN_SIMPLE_READ: {
|
||||
BrotliDecoderErrorCode result =
|
||||
ReadSimpleHuffmanSymbols(alphabet_size, max_symbol, s);
|
||||
ReadSimpleHuffmanSymbols(alphabet_size_max, alphabet_size_limit, s);
|
||||
if (result != BROTLI_DECODER_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
@@ -788,21 +795,21 @@ static BrotliDecoderErrorCode ReadHuffmanCode(uint32_t alphabet_size,
|
||||
|
||||
case BROTLI_STATE_HUFFMAN_SIMPLE_BUILD: {
|
||||
uint32_t table_size;
|
||||
if (s->symbol == 3) {
|
||||
if (h->symbol == 3) {
|
||||
uint32_t bits;
|
||||
if (!BrotliSafeReadBits(br, 1, &bits)) {
|
||||
s->substate_huffman = BROTLI_STATE_HUFFMAN_SIMPLE_BUILD;
|
||||
h->substate_huffman = BROTLI_STATE_HUFFMAN_SIMPLE_BUILD;
|
||||
return BROTLI_DECODER_NEEDS_MORE_INPUT;
|
||||
}
|
||||
s->symbol += bits;
|
||||
h->symbol += bits;
|
||||
}
|
||||
BROTLI_LOG_UINT(s->symbol);
|
||||
BROTLI_LOG_UINT(h->symbol);
|
||||
table_size = BrotliBuildSimpleHuffmanTable(
|
||||
table, HUFFMAN_TABLE_BITS, s->symbols_lists_array, s->symbol);
|
||||
table, HUFFMAN_TABLE_BITS, h->symbols_lists_array, h->symbol);
|
||||
if (opt_table_size) {
|
||||
*opt_table_size = table_size;
|
||||
}
|
||||
s->substate_huffman = BROTLI_STATE_HUFFMAN_NONE;
|
||||
h->substate_huffman = BROTLI_STATE_HUFFMAN_NONE;
|
||||
return BROTLI_DECODER_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -813,44 +820,45 @@ static BrotliDecoderErrorCode ReadHuffmanCode(uint32_t alphabet_size,
|
||||
if (result != BROTLI_DECODER_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
BrotliBuildCodeLengthsHuffmanTable(s->table,
|
||||
s->code_length_code_lengths,
|
||||
s->code_length_histo);
|
||||
memset(&s->code_length_histo[0], 0, sizeof(s->code_length_histo));
|
||||
BrotliBuildCodeLengthsHuffmanTable(h->table,
|
||||
h->code_length_code_lengths,
|
||||
h->code_length_histo);
|
||||
memset(&h->code_length_histo[0], 0, sizeof(h->code_length_histo));
|
||||
for (i = 0; i <= BROTLI_HUFFMAN_MAX_CODE_LENGTH; ++i) {
|
||||
s->next_symbol[i] = (int)i - (BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1);
|
||||
s->symbol_lists[s->next_symbol[i]] = 0xFFFF;
|
||||
h->next_symbol[i] = (int)i - (BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1);
|
||||
h->symbol_lists[h->next_symbol[i]] = 0xFFFF;
|
||||
}
|
||||
|
||||
s->symbol = 0;
|
||||
s->prev_code_len = BROTLI_INITIAL_REPEATED_CODE_LENGTH;
|
||||
s->repeat = 0;
|
||||
s->repeat_code_len = 0;
|
||||
s->space = 32768;
|
||||
s->substate_huffman = BROTLI_STATE_HUFFMAN_LENGTH_SYMBOLS;
|
||||
h->symbol = 0;
|
||||
h->prev_code_len = BROTLI_INITIAL_REPEATED_CODE_LENGTH;
|
||||
h->repeat = 0;
|
||||
h->repeat_code_len = 0;
|
||||
h->space = 32768;
|
||||
h->substate_huffman = BROTLI_STATE_HUFFMAN_LENGTH_SYMBOLS;
|
||||
}
|
||||
/* Fall through. */
|
||||
|
||||
case BROTLI_STATE_HUFFMAN_LENGTH_SYMBOLS: {
|
||||
uint32_t table_size;
|
||||
BrotliDecoderErrorCode result = ReadSymbolCodeLengths(max_symbol, s);
|
||||
BrotliDecoderErrorCode result = ReadSymbolCodeLengths(
|
||||
alphabet_size_limit, s);
|
||||
if (result == BROTLI_DECODER_NEEDS_MORE_INPUT) {
|
||||
result = SafeReadSymbolCodeLengths(max_symbol, s);
|
||||
result = SafeReadSymbolCodeLengths(alphabet_size_limit, s);
|
||||
}
|
||||
if (result != BROTLI_DECODER_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (s->space != 0) {
|
||||
BROTLI_LOG(("[ReadHuffmanCode] space = %d\n", (int)s->space));
|
||||
if (h->space != 0) {
|
||||
BROTLI_LOG(("[ReadHuffmanCode] space = %d\n", (int)h->space));
|
||||
return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_HUFFMAN_SPACE);
|
||||
}
|
||||
table_size = BrotliBuildHuffmanTable(
|
||||
table, HUFFMAN_TABLE_BITS, s->symbol_lists, s->code_length_histo);
|
||||
table, HUFFMAN_TABLE_BITS, h->symbol_lists, h->code_length_histo);
|
||||
if (opt_table_size) {
|
||||
*opt_table_size = table_size;
|
||||
}
|
||||
s->substate_huffman = BROTLI_STATE_HUFFMAN_NONE;
|
||||
h->substate_huffman = BROTLI_STATE_HUFFMAN_NONE;
|
||||
return BROTLI_DECODER_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -867,8 +875,8 @@ static BROTLI_INLINE uint32_t ReadBlockLength(const HuffmanCode* table,
|
||||
uint32_t code;
|
||||
uint32_t nbits;
|
||||
code = ReadSymbol(table, br);
|
||||
nbits = kBlockLengthPrefixCode[code].nbits; /* nbits == 2..24 */
|
||||
return kBlockLengthPrefixCode[code].offset + BrotliReadBits(br, nbits);
|
||||
nbits = _kBrotliPrefixCodeRanges[code].nbits; /* nbits == 2..24 */
|
||||
return _kBrotliPrefixCodeRanges[code].offset + BrotliReadBits24(br, nbits);
|
||||
}
|
||||
|
||||
/* WARNING: if state is not BROTLI_STATE_READ_BLOCK_LENGTH_NONE, then
|
||||
@@ -886,13 +894,14 @@ static BROTLI_INLINE BROTLI_BOOL SafeReadBlockLength(
|
||||
}
|
||||
{
|
||||
uint32_t bits;
|
||||
uint32_t nbits = kBlockLengthPrefixCode[index].nbits; /* nbits == 2..24 */
|
||||
uint32_t nbits = _kBrotliPrefixCodeRanges[index].nbits;
|
||||
uint32_t offset = _kBrotliPrefixCodeRanges[index].offset;
|
||||
if (!BrotliSafeReadBits(br, nbits, &bits)) {
|
||||
s->block_length_index = index;
|
||||
s->substate_read_block_length = BROTLI_STATE_READ_BLOCK_LENGTH_SUFFIX;
|
||||
return BROTLI_FALSE;
|
||||
}
|
||||
*result = kBlockLengthPrefixCode[index].offset + bits;
|
||||
*result = offset + bits;
|
||||
s->substate_read_block_length = BROTLI_STATE_READ_BLOCK_LENGTH_NONE;
|
||||
return BROTLI_TRUE;
|
||||
}
|
||||
@@ -952,22 +961,22 @@ static BROTLI_NOINLINE void InverseMoveToFrontTransform(
|
||||
/* Decodes a series of Huffman table using ReadHuffmanCode function. */
|
||||
static BrotliDecoderErrorCode HuffmanTreeGroupDecode(
|
||||
HuffmanTreeGroup* group, BrotliDecoderState* s) {
|
||||
if (s->substate_tree_group != BROTLI_STATE_TREE_GROUP_LOOP) {
|
||||
s->next = group->codes;
|
||||
s->htree_index = 0;
|
||||
s->substate_tree_group = BROTLI_STATE_TREE_GROUP_LOOP;
|
||||
BrotliMetablockHeaderArena* h = &s->arena.header;
|
||||
if (h->substate_tree_group != BROTLI_STATE_TREE_GROUP_LOOP) {
|
||||
h->next = group->codes;
|
||||
h->htree_index = 0;
|
||||
h->substate_tree_group = BROTLI_STATE_TREE_GROUP_LOOP;
|
||||
}
|
||||
while (s->htree_index < group->num_htrees) {
|
||||
while (h->htree_index < group->num_htrees) {
|
||||
uint32_t table_size;
|
||||
BrotliDecoderErrorCode result =
|
||||
ReadHuffmanCode(group->alphabet_size, group->max_symbol,
|
||||
s->next, &table_size, s);
|
||||
BrotliDecoderErrorCode result = ReadHuffmanCode(group->alphabet_size_max,
|
||||
group->alphabet_size_limit, h->next, &table_size, s);
|
||||
if (result != BROTLI_DECODER_SUCCESS) return result;
|
||||
group->htrees[s->htree_index] = s->next;
|
||||
s->next += table_size;
|
||||
++s->htree_index;
|
||||
group->htrees[h->htree_index] = h->next;
|
||||
h->next += table_size;
|
||||
++h->htree_index;
|
||||
}
|
||||
s->substate_tree_group = BROTLI_STATE_TREE_GROUP_NONE;
|
||||
h->substate_tree_group = BROTLI_STATE_TREE_GROUP_NONE;
|
||||
return BROTLI_DECODER_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -985,15 +994,16 @@ static BrotliDecoderErrorCode DecodeContextMap(uint32_t context_map_size,
|
||||
BrotliDecoderState* s) {
|
||||
BrotliBitReader* br = &s->br;
|
||||
BrotliDecoderErrorCode result = BROTLI_DECODER_SUCCESS;
|
||||
BrotliMetablockHeaderArena* h = &s->arena.header;
|
||||
|
||||
switch ((int)s->substate_context_map) {
|
||||
switch ((int)h->substate_context_map) {
|
||||
case BROTLI_STATE_CONTEXT_MAP_NONE:
|
||||
result = DecodeVarLenUint8(s, br, num_htrees);
|
||||
if (result != BROTLI_DECODER_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
(*num_htrees)++;
|
||||
s->context_index = 0;
|
||||
h->context_index = 0;
|
||||
BROTLI_LOG_UINT(context_map_size);
|
||||
BROTLI_LOG_UINT(*num_htrees);
|
||||
*context_map_arg =
|
||||
@@ -1005,7 +1015,7 @@ static BrotliDecoderErrorCode DecodeContextMap(uint32_t context_map_size,
|
||||
memset(*context_map_arg, 0, (size_t)context_map_size);
|
||||
return BROTLI_DECODER_SUCCESS;
|
||||
}
|
||||
s->substate_context_map = BROTLI_STATE_CONTEXT_MAP_READ_PREFIX;
|
||||
h->substate_context_map = BROTLI_STATE_CONTEXT_MAP_READ_PREFIX;
|
||||
/* Fall through. */
|
||||
|
||||
case BROTLI_STATE_CONTEXT_MAP_READ_PREFIX: {
|
||||
@@ -1016,38 +1026,38 @@ static BrotliDecoderErrorCode DecodeContextMap(uint32_t context_map_size,
|
||||
return BROTLI_DECODER_NEEDS_MORE_INPUT;
|
||||
}
|
||||
if ((bits & 1) != 0) { /* Use RLE for zeros. */
|
||||
s->max_run_length_prefix = (bits >> 1) + 1;
|
||||
h->max_run_length_prefix = (bits >> 1) + 1;
|
||||
BrotliDropBits(br, 5);
|
||||
} else {
|
||||
s->max_run_length_prefix = 0;
|
||||
h->max_run_length_prefix = 0;
|
||||
BrotliDropBits(br, 1);
|
||||
}
|
||||
BROTLI_LOG_UINT(s->max_run_length_prefix);
|
||||
s->substate_context_map = BROTLI_STATE_CONTEXT_MAP_HUFFMAN;
|
||||
BROTLI_LOG_UINT(h->max_run_length_prefix);
|
||||
h->substate_context_map = BROTLI_STATE_CONTEXT_MAP_HUFFMAN;
|
||||
}
|
||||
/* Fall through. */
|
||||
|
||||
case BROTLI_STATE_CONTEXT_MAP_HUFFMAN: {
|
||||
uint32_t alphabet_size = *num_htrees + s->max_run_length_prefix;
|
||||
uint32_t alphabet_size = *num_htrees + h->max_run_length_prefix;
|
||||
result = ReadHuffmanCode(alphabet_size, alphabet_size,
|
||||
s->context_map_table, NULL, s);
|
||||
h->context_map_table, NULL, s);
|
||||
if (result != BROTLI_DECODER_SUCCESS) return result;
|
||||
s->code = 0xFFFF;
|
||||
s->substate_context_map = BROTLI_STATE_CONTEXT_MAP_DECODE;
|
||||
h->code = 0xFFFF;
|
||||
h->substate_context_map = BROTLI_STATE_CONTEXT_MAP_DECODE;
|
||||
}
|
||||
/* Fall through. */
|
||||
|
||||
case BROTLI_STATE_CONTEXT_MAP_DECODE: {
|
||||
uint32_t context_index = s->context_index;
|
||||
uint32_t max_run_length_prefix = s->max_run_length_prefix;
|
||||
uint32_t context_index = h->context_index;
|
||||
uint32_t max_run_length_prefix = h->max_run_length_prefix;
|
||||
uint8_t* context_map = *context_map_arg;
|
||||
uint32_t code = s->code;
|
||||
uint32_t code = h->code;
|
||||
BROTLI_BOOL skip_preamble = (code != 0xFFFF);
|
||||
while (context_index < context_map_size || skip_preamble) {
|
||||
if (!skip_preamble) {
|
||||
if (!SafeReadSymbol(s->context_map_table, br, &code)) {
|
||||
s->code = 0xFFFF;
|
||||
s->context_index = context_index;
|
||||
if (!SafeReadSymbol(h->context_map_table, br, &code)) {
|
||||
h->code = 0xFFFF;
|
||||
h->context_index = context_index;
|
||||
return BROTLI_DECODER_NEEDS_MORE_INPUT;
|
||||
}
|
||||
BROTLI_LOG_UINT(code);
|
||||
@@ -1068,8 +1078,8 @@ static BrotliDecoderErrorCode DecodeContextMap(uint32_t context_map_size,
|
||||
{
|
||||
uint32_t reps;
|
||||
if (!BrotliSafeReadBits(br, code, &reps)) {
|
||||
s->code = code;
|
||||
s->context_index = context_index;
|
||||
h->code = code;
|
||||
h->context_index = context_index;
|
||||
return BROTLI_DECODER_NEEDS_MORE_INPUT;
|
||||
}
|
||||
reps += 1U << code;
|
||||
@@ -1089,13 +1099,13 @@ static BrotliDecoderErrorCode DecodeContextMap(uint32_t context_map_size,
|
||||
case BROTLI_STATE_CONTEXT_MAP_TRANSFORM: {
|
||||
uint32_t bits;
|
||||
if (!BrotliSafeReadBits(br, 1, &bits)) {
|
||||
s->substate_context_map = BROTLI_STATE_CONTEXT_MAP_TRANSFORM;
|
||||
h->substate_context_map = BROTLI_STATE_CONTEXT_MAP_TRANSFORM;
|
||||
return BROTLI_DECODER_NEEDS_MORE_INPUT;
|
||||
}
|
||||
if (bits != 0) {
|
||||
InverseMoveToFrontTransform(*context_map_arg, context_map_size, s);
|
||||
}
|
||||
s->substate_context_map = BROTLI_STATE_CONTEXT_MAP_NONE;
|
||||
h->substate_context_map = BROTLI_STATE_CONTEXT_MAP_NONE;
|
||||
return BROTLI_DECODER_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1457,32 +1467,28 @@ static BrotliDecoderErrorCode ReadContextModes(BrotliDecoderState* s) {
|
||||
}
|
||||
|
||||
static BROTLI_INLINE void TakeDistanceFromRingBuffer(BrotliDecoderState* s) {
|
||||
if (s->distance_code == 0) {
|
||||
--s->dist_rb_idx;
|
||||
s->distance_code = s->dist_rb[s->dist_rb_idx & 3];
|
||||
int offset = s->distance_code - 3;
|
||||
if (s->distance_code <= 3) {
|
||||
/* Compensate double distance-ring-buffer roll for dictionary items. */
|
||||
s->distance_context = 1;
|
||||
s->distance_context = 1 >> s->distance_code;
|
||||
s->distance_code = s->dist_rb[(s->dist_rb_idx - offset) & 3];
|
||||
s->dist_rb_idx -= s->distance_context;
|
||||
} else {
|
||||
int distance_code = s->distance_code << 1;
|
||||
/* kDistanceShortCodeIndexOffset has 2-bit values from LSB:
|
||||
3, 2, 1, 0, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2 */
|
||||
const uint32_t kDistanceShortCodeIndexOffset = 0xAAAFFF1B;
|
||||
/* kDistanceShortCodeValueOffset has 2-bit values from LSB:
|
||||
-0, 0,-0, 0,-1, 1,-2, 2,-3, 3,-1, 1,-2, 2,-3, 3 */
|
||||
const uint32_t kDistanceShortCodeValueOffset = 0xFA5FA500;
|
||||
int v = (s->dist_rb_idx +
|
||||
(int)(kDistanceShortCodeIndexOffset >> distance_code)) & 0x3;
|
||||
s->distance_code = s->dist_rb[v];
|
||||
v = (int)(kDistanceShortCodeValueOffset >> distance_code) & 0x3;
|
||||
if ((distance_code & 0x3) != 0) {
|
||||
s->distance_code += v;
|
||||
int index_delta = 3;
|
||||
int delta;
|
||||
int base = s->distance_code - 10;
|
||||
if (s->distance_code < 10) {
|
||||
base = s->distance_code - 4;
|
||||
} else {
|
||||
s->distance_code -= v;
|
||||
if (s->distance_code <= 0) {
|
||||
/* A huge distance will cause a BROTLI_FAILURE() soon.
|
||||
This is a little faster than failing here. */
|
||||
s->distance_code = 0x7FFFFFFF;
|
||||
}
|
||||
index_delta = 2;
|
||||
}
|
||||
/* Unpack one of six 4-bit values. */
|
||||
delta = ((0x605142 >> (4 * base)) & 0xF) - 3;
|
||||
s->distance_code = s->dist_rb[(s->dist_rb_idx + index_delta) & 0x3] + delta;
|
||||
if (s->distance_code <= 0) {
|
||||
/* A huge distance will cause a BROTLI_FAILURE() soon.
|
||||
This is a little faster than failing here. */
|
||||
s->distance_code = 0x7FFFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1497,62 +1503,153 @@ static BROTLI_INLINE BROTLI_BOOL SafeReadBits(
|
||||
}
|
||||
}
|
||||
|
||||
static BROTLI_INLINE BROTLI_BOOL SafeReadBits32(
|
||||
BrotliBitReader* const br, uint32_t n_bits, uint32_t* val) {
|
||||
if (n_bits != 0) {
|
||||
return BrotliSafeReadBits32(br, n_bits, val);
|
||||
} else {
|
||||
*val = 0;
|
||||
return BROTLI_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
RFC 7932 Section 4 with "..." shortenings and "[]" emendations.
|
||||
|
||||
Each distance ... is represented with a pair <distance code, extra bits>...
|
||||
The distance code is encoded using a prefix code... The number of extra bits
|
||||
can be 0..24... Two additional parameters: NPOSTFIX (0..3), and ...
|
||||
NDIRECT (0..120) ... are encoded in the meta-block header...
|
||||
|
||||
The first 16 distance symbols ... reference past distances... ring buffer ...
|
||||
Next NDIRECT distance symbols ... represent distances from 1 to NDIRECT...
|
||||
[For] distance symbols 16 + NDIRECT and greater ... the number of extra bits
|
||||
... is given by the following formula:
|
||||
|
||||
[ xcode = dcode - NDIRECT - 16 ]
|
||||
ndistbits = 1 + [ xcode ] >> (NPOSTFIX + 1)
|
||||
|
||||
...
|
||||
*/
|
||||
|
||||
/*
|
||||
RFC 7932 Section 9.2 with "..." shortenings and "[]" emendations.
|
||||
|
||||
... to get the actual value of the parameter NDIRECT, left-shift this
|
||||
four-bit number by NPOSTFIX bits ...
|
||||
*/
|
||||
|
||||
/* Remaining formulas from RFC 7932 Section 4 could be rewritten as following:
|
||||
|
||||
alphabet_size = 16 + NDIRECT + (max_distbits << (NPOSTFIX + 1))
|
||||
|
||||
half = ((xcode >> NPOSTFIX) & 1) << ndistbits
|
||||
postfix = xcode & ((1 << NPOSTFIX) - 1)
|
||||
range_start = 2 * (1 << ndistbits - 1 - 1)
|
||||
|
||||
distance = (range_start + half + extra) << NPOSTFIX + postfix + NDIRECT + 1
|
||||
|
||||
NB: ndistbits >= 1 -> range_start >= 0
|
||||
NB: range_start has factor 2, as the range is covered by 2 "halves"
|
||||
NB: extra -1 offset in range_start formula covers the absence of
|
||||
ndistbits = 0 case
|
||||
NB: when NPOSTFIX = 0, NDIRECT is not greater than 15
|
||||
|
||||
In other words, xcode has the following binary structure - XXXHPPP:
|
||||
- XXX represent the number of extra distance bits
|
||||
- H selects upper / lower range of distances
|
||||
- PPP represent "postfix"
|
||||
|
||||
"Regular" distance encoding has NPOSTFIX = 0; omitting the postfix part
|
||||
simplifies distance calculation.
|
||||
|
||||
Using NPOSTFIX > 0 allows cheaper encoding of regular structures, e.g. where
|
||||
most of distances have the same reminder of division by 2/4/8. For example,
|
||||
the table of int32_t values that come from different sources; if it is likely
|
||||
that 3 highest bytes of values from the same source are the same, then
|
||||
copy distance often looks like 4x + y.
|
||||
|
||||
Distance calculation could be rewritten to:
|
||||
|
||||
ndistbits = NDISTBITS(NDIRECT, NPOSTFIX)[dcode]
|
||||
distance = OFFSET(NDIRECT, NPOSTFIX)[dcode] + extra << NPOSTFIX
|
||||
|
||||
NDISTBITS and OFFSET could be pre-calculated, as NDIRECT and NPOSTFIX could
|
||||
change only once per meta-block.
|
||||
*/
|
||||
|
||||
/* Calculates distance lookup table.
|
||||
NB: it is possible to have all 64 tables precalculated. */
|
||||
static void CalculateDistanceLut(BrotliDecoderState* s) {
|
||||
BrotliMetablockBodyArena* b = &s->arena.body;
|
||||
uint32_t npostfix = s->distance_postfix_bits;
|
||||
uint32_t ndirect = s->num_direct_distance_codes;
|
||||
uint32_t alphabet_size_limit = s->distance_hgroup.alphabet_size_limit;
|
||||
uint32_t postfix = 1u << npostfix;
|
||||
uint32_t j;
|
||||
uint32_t bits = 1;
|
||||
uint32_t half = 0;
|
||||
|
||||
/* Skip short codes. */
|
||||
uint32_t i = BROTLI_NUM_DISTANCE_SHORT_CODES;
|
||||
|
||||
/* Fill direct codes. */
|
||||
for (j = 0; j < ndirect; ++j) {
|
||||
b->dist_extra_bits[i] = 0;
|
||||
b->dist_offset[i] = j + 1;
|
||||
++i;
|
||||
}
|
||||
|
||||
/* Fill regular distance codes. */
|
||||
while (i < alphabet_size_limit) {
|
||||
uint32_t base = ndirect + ((((2 + half) << bits) - 4) << npostfix) + 1;
|
||||
/* Always fill the complete group. */
|
||||
for (j = 0; j < postfix; ++j) {
|
||||
b->dist_extra_bits[i] = (uint8_t)bits;
|
||||
b->dist_offset[i] = base + j;
|
||||
++i;
|
||||
}
|
||||
bits = bits + half;
|
||||
half = half ^ 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Precondition: s->distance_code < 0. */
|
||||
static BROTLI_INLINE BROTLI_BOOL ReadDistanceInternal(
|
||||
int safe, BrotliDecoderState* s, BrotliBitReader* br) {
|
||||
int distval;
|
||||
BrotliMetablockBodyArena* b = &s->arena.body;
|
||||
uint32_t code;
|
||||
uint32_t bits;
|
||||
BrotliBitReaderState memento;
|
||||
HuffmanCode* distance_tree = s->distance_hgroup.htrees[s->dist_htree_index];
|
||||
if (!safe) {
|
||||
s->distance_code = (int)ReadSymbol(distance_tree, br);
|
||||
code = ReadSymbol(distance_tree, br);
|
||||
} else {
|
||||
uint32_t code;
|
||||
BrotliBitReaderSaveState(br, &memento);
|
||||
if (!SafeReadSymbol(distance_tree, br, &code)) {
|
||||
return BROTLI_FALSE;
|
||||
}
|
||||
s->distance_code = (int)code;
|
||||
}
|
||||
--s->block_length[2];
|
||||
/* Convert the distance code to the actual distance by possibly
|
||||
looking up past distances from the s->ringbuffer. */
|
||||
looking up past distances from the s->dist_rb. */
|
||||
s->distance_context = 0;
|
||||
if ((s->distance_code & ~0xF) == 0) {
|
||||
if ((code & ~0xFu) == 0) {
|
||||
s->distance_code = (int)code;
|
||||
TakeDistanceFromRingBuffer(s);
|
||||
--s->block_length[2];
|
||||
return BROTLI_TRUE;
|
||||
}
|
||||
distval = s->distance_code - (int)s->num_direct_distance_codes;
|
||||
if (distval >= 0) {
|
||||
uint32_t nbits;
|
||||
int postfix;
|
||||
int offset;
|
||||
if (!safe && (s->distance_postfix_bits == 0)) {
|
||||
nbits = ((uint32_t)distval >> 1) + 1;
|
||||
offset = ((2 + (distval & 1)) << nbits) - 4;
|
||||
s->distance_code = (int)s->num_direct_distance_codes + offset +
|
||||
(int)BrotliReadBits(br, nbits);
|
||||
} else {
|
||||
/* This branch also works well when s->distance_postfix_bits == 0. */
|
||||
uint32_t bits;
|
||||
postfix = distval & s->distance_postfix_mask;
|
||||
distval >>= s->distance_postfix_bits;
|
||||
nbits = ((uint32_t)distval >> 1) + 1;
|
||||
if (safe) {
|
||||
if (!SafeReadBits(br, nbits, &bits)) {
|
||||
s->distance_code = -1; /* Restore precondition. */
|
||||
BrotliBitReaderRestoreState(br, &memento);
|
||||
return BROTLI_FALSE;
|
||||
}
|
||||
} else {
|
||||
bits = BrotliReadBits(br, nbits);
|
||||
}
|
||||
offset = ((2 + (distval & 1)) << nbits) - 4;
|
||||
s->distance_code = (int)s->num_direct_distance_codes +
|
||||
((offset + (int)bits) << s->distance_postfix_bits) + postfix;
|
||||
if (!safe) {
|
||||
bits = BrotliReadBits32(br, b->dist_extra_bits[code]);
|
||||
} else {
|
||||
if (!SafeReadBits32(br, b->dist_extra_bits[code], &bits)) {
|
||||
++s->block_length[2];
|
||||
BrotliBitReaderRestoreState(br, &memento);
|
||||
return BROTLI_FALSE;
|
||||
}
|
||||
}
|
||||
s->distance_code = s->distance_code - BROTLI_NUM_DISTANCE_SHORT_CODES + 1;
|
||||
--s->block_length[2];
|
||||
s->distance_code =
|
||||
(int)(b->dist_offset[code] + (bits << s->distance_postfix_bits));
|
||||
return BROTLI_TRUE;
|
||||
}
|
||||
|
||||
@@ -1588,9 +1685,9 @@ static BROTLI_INLINE BROTLI_BOOL ReadCommandInternal(
|
||||
*insert_length = v.insert_len_offset;
|
||||
if (!safe) {
|
||||
if (BROTLI_PREDICT_FALSE(v.insert_len_extra_bits != 0)) {
|
||||
insert_len_extra = BrotliReadBits(br, v.insert_len_extra_bits);
|
||||
insert_len_extra = BrotliReadBits24(br, v.insert_len_extra_bits);
|
||||
}
|
||||
copy_length = BrotliReadBits(br, v.copy_len_extra_bits);
|
||||
copy_length = BrotliReadBits24(br, v.copy_len_extra_bits);
|
||||
} else {
|
||||
if (!SafeReadBits(br, v.insert_len_extra_bits, &insert_len_extra) ||
|
||||
!SafeReadBits(br, v.copy_len_extra_bits, ©_length)) {
|
||||
@@ -1935,21 +2032,6 @@ static BROTLI_NOINLINE BrotliDecoderErrorCode SafeProcessCommands(
|
||||
return ProcessCommandsInternal(1, s);
|
||||
}
|
||||
|
||||
/* Returns the maximum number of distance symbols which can only represent
|
||||
distances not exceeding BROTLI_MAX_ALLOWED_DISTANCE. */
|
||||
static uint32_t BrotliMaxDistanceSymbol(uint32_t ndirect, uint32_t npostfix) {
|
||||
static const uint32_t bound[BROTLI_MAX_NPOSTFIX + 1] = {0, 4, 12, 28};
|
||||
static const uint32_t diff[BROTLI_MAX_NPOSTFIX + 1] = {73, 126, 228, 424};
|
||||
uint32_t postfix = 1U << npostfix;
|
||||
if (ndirect < bound[npostfix]) {
|
||||
return ndirect + diff[npostfix] + postfix;
|
||||
} else if (ndirect > bound[npostfix] + postfix) {
|
||||
return ndirect + diff[npostfix];
|
||||
} else {
|
||||
return bound[npostfix] + diff[npostfix] + postfix;
|
||||
}
|
||||
}
|
||||
|
||||
BrotliDecoderResult BrotliDecoderDecompress(
|
||||
size_t encoded_size, const uint8_t* encoded_buffer, size_t* decoded_size,
|
||||
uint8_t* decoded_buffer) {
|
||||
@@ -2167,33 +2249,23 @@ BrotliDecoderResult BrotliDecoderDecompressStream(
|
||||
s->state = BROTLI_STATE_UNCOMPRESSED;
|
||||
break;
|
||||
}
|
||||
s->state = BROTLI_STATE_BEFORE_COMPRESSED_METABLOCK_HEADER;
|
||||
/* Fall through. */
|
||||
|
||||
case BROTLI_STATE_BEFORE_COMPRESSED_METABLOCK_HEADER: {
|
||||
BrotliMetablockHeaderArena* h = &s->arena.header;
|
||||
s->loop_counter = 0;
|
||||
/* Initialize compressed metablock header arena. */
|
||||
h->sub_loop_counter = 0;
|
||||
/* Make small negative indexes addressable. */
|
||||
h->symbol_lists =
|
||||
&h->symbols_lists_array[BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1];
|
||||
h->substate_huffman = BROTLI_STATE_HUFFMAN_NONE;
|
||||
h->substate_tree_group = BROTLI_STATE_TREE_GROUP_NONE;
|
||||
h->substate_context_map = BROTLI_STATE_CONTEXT_MAP_NONE;
|
||||
s->state = BROTLI_STATE_HUFFMAN_CODE_0;
|
||||
break;
|
||||
|
||||
case BROTLI_STATE_UNCOMPRESSED: {
|
||||
result = CopyUncompressedBlockToOutput(
|
||||
available_out, next_out, total_out, s);
|
||||
if (result != BROTLI_DECODER_SUCCESS) {
|
||||
break;
|
||||
}
|
||||
s->state = BROTLI_STATE_METABLOCK_DONE;
|
||||
break;
|
||||
}
|
||||
|
||||
case BROTLI_STATE_METADATA:
|
||||
for (; s->meta_block_remaining_len > 0; --s->meta_block_remaining_len) {
|
||||
uint32_t bits;
|
||||
/* Read one byte and ignore it. */
|
||||
if (!BrotliSafeReadBits(br, 8, &bits)) {
|
||||
result = BROTLI_DECODER_NEEDS_MORE_INPUT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result == BROTLI_DECODER_SUCCESS) {
|
||||
s->state = BROTLI_STATE_METABLOCK_DONE;
|
||||
}
|
||||
break;
|
||||
/* Fall through. */
|
||||
|
||||
case BROTLI_STATE_HUFFMAN_CODE_0:
|
||||
if (s->loop_counter >= 3) {
|
||||
@@ -2247,6 +2319,30 @@ BrotliDecoderResult BrotliDecoderDecompressStream(
|
||||
break;
|
||||
}
|
||||
|
||||
case BROTLI_STATE_UNCOMPRESSED: {
|
||||
result = CopyUncompressedBlockToOutput(
|
||||
available_out, next_out, total_out, s);
|
||||
if (result != BROTLI_DECODER_SUCCESS) {
|
||||
break;
|
||||
}
|
||||
s->state = BROTLI_STATE_METABLOCK_DONE;
|
||||
break;
|
||||
}
|
||||
|
||||
case BROTLI_STATE_METADATA:
|
||||
for (; s->meta_block_remaining_len > 0; --s->meta_block_remaining_len) {
|
||||
uint32_t bits;
|
||||
/* Read one byte and ignore it. */
|
||||
if (!BrotliSafeReadBits(br, 8, &bits)) {
|
||||
result = BROTLI_DECODER_NEEDS_MORE_INPUT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result == BROTLI_DECODER_SUCCESS) {
|
||||
s->state = BROTLI_STATE_METABLOCK_DONE;
|
||||
}
|
||||
break;
|
||||
|
||||
case BROTLI_STATE_METABLOCK_HEADER_2: {
|
||||
uint32_t bits;
|
||||
if (!BrotliSafeReadBits(br, 6, &bits)) {
|
||||
@@ -2255,11 +2351,9 @@ BrotliDecoderResult BrotliDecoderDecompressStream(
|
||||
}
|
||||
s->distance_postfix_bits = bits & BitMask(2);
|
||||
bits >>= 2;
|
||||
s->num_direct_distance_codes = BROTLI_NUM_DISTANCE_SHORT_CODES +
|
||||
(bits << s->distance_postfix_bits);
|
||||
s->num_direct_distance_codes = bits << s->distance_postfix_bits;
|
||||
BROTLI_LOG_UINT(s->num_direct_distance_codes);
|
||||
BROTLI_LOG_UINT(s->distance_postfix_bits);
|
||||
s->distance_postfix_mask = (int)BitMask(s->distance_postfix_bits);
|
||||
s->context_modes =
|
||||
(uint8_t*)BROTLI_DECODER_ALLOC(s, (size_t)s->num_block_types[0]);
|
||||
if (s->context_modes == 0) {
|
||||
@@ -2291,17 +2385,19 @@ BrotliDecoderResult BrotliDecoderDecompressStream(
|
||||
/* Fall through. */
|
||||
|
||||
case BROTLI_STATE_CONTEXT_MAP_2: {
|
||||
uint32_t num_direct_codes =
|
||||
s->num_direct_distance_codes - BROTLI_NUM_DISTANCE_SHORT_CODES;
|
||||
uint32_t num_distance_codes = BROTLI_DISTANCE_ALPHABET_SIZE(
|
||||
s->distance_postfix_bits, num_direct_codes,
|
||||
(s->large_window ? BROTLI_LARGE_MAX_DISTANCE_BITS :
|
||||
BROTLI_MAX_DISTANCE_BITS));
|
||||
uint32_t max_distance_symbol = (s->large_window ?
|
||||
BrotliMaxDistanceSymbol(
|
||||
num_direct_codes, s->distance_postfix_bits) :
|
||||
num_distance_codes);
|
||||
uint32_t npostfix = s->distance_postfix_bits;
|
||||
uint32_t ndirect = s->num_direct_distance_codes;
|
||||
uint32_t distance_alphabet_size_max = BROTLI_DISTANCE_ALPHABET_SIZE(
|
||||
npostfix, ndirect, BROTLI_MAX_DISTANCE_BITS);
|
||||
uint32_t distance_alphabet_size_limit = distance_alphabet_size_max;
|
||||
BROTLI_BOOL allocation_success = BROTLI_TRUE;
|
||||
if (s->large_window) {
|
||||
BrotliDistanceCodeLimit limit = BrotliCalculateDistanceCodeLimit(
|
||||
BROTLI_MAX_ALLOWED_DISTANCE, npostfix, ndirect);
|
||||
distance_alphabet_size_max = BROTLI_DISTANCE_ALPHABET_SIZE(
|
||||
npostfix, ndirect, BROTLI_LARGE_MAX_DISTANCE_BITS);
|
||||
distance_alphabet_size_limit = limit.max_alphabet_size;
|
||||
}
|
||||
result = DecodeContextMap(
|
||||
s->num_block_types[2] << BROTLI_DISTANCE_CONTEXT_BITS,
|
||||
&s->num_dist_htrees, &s->dist_context_map, s);
|
||||
@@ -2315,8 +2411,8 @@ BrotliDecoderResult BrotliDecoderDecompressStream(
|
||||
s, &s->insert_copy_hgroup, BROTLI_NUM_COMMAND_SYMBOLS,
|
||||
BROTLI_NUM_COMMAND_SYMBOLS, s->num_block_types[1]);
|
||||
allocation_success &= BrotliDecoderHuffmanTreeGroupInit(
|
||||
s, &s->distance_hgroup, num_distance_codes,
|
||||
max_distance_symbol, s->num_dist_htrees);
|
||||
s, &s->distance_hgroup, distance_alphabet_size_max,
|
||||
distance_alphabet_size_limit, s->num_dist_htrees);
|
||||
if (!allocation_success) {
|
||||
return SaveErrorCode(s,
|
||||
BROTLI_FAILURE(BROTLI_DECODER_ERROR_ALLOC_TREE_GROUPS));
|
||||
@@ -2338,18 +2434,24 @@ BrotliDecoderResult BrotliDecoderDecompressStream(
|
||||
result = HuffmanTreeGroupDecode(hgroup, s);
|
||||
if (result != BROTLI_DECODER_SUCCESS) break;
|
||||
s->loop_counter++;
|
||||
if (s->loop_counter >= 3) {
|
||||
PrepareLiteralDecoding(s);
|
||||
s->dist_context_map_slice = s->dist_context_map;
|
||||
s->htree_command = s->insert_copy_hgroup.htrees[0];
|
||||
if (!BrotliEnsureRingBuffer(s)) {
|
||||
result = BROTLI_FAILURE(BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_2);
|
||||
break;
|
||||
}
|
||||
s->state = BROTLI_STATE_COMMAND_BEGIN;
|
||||
if (s->loop_counter < 3) {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
s->state = BROTLI_STATE_BEFORE_COMPRESSED_METABLOCK_BODY;
|
||||
}
|
||||
/* Fall through. */
|
||||
|
||||
case BROTLI_STATE_BEFORE_COMPRESSED_METABLOCK_BODY:
|
||||
PrepareLiteralDecoding(s);
|
||||
s->dist_context_map_slice = s->dist_context_map;
|
||||
s->htree_command = s->insert_copy_hgroup.htrees[0];
|
||||
if (!BrotliEnsureRingBuffer(s)) {
|
||||
result = BROTLI_FAILURE(BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_2);
|
||||
break;
|
||||
}
|
||||
CalculateDistanceLut(s);
|
||||
s->state = BROTLI_STATE_COMMAND_BEGIN;
|
||||
/* Fall through. */
|
||||
|
||||
case BROTLI_STATE_COMMAND_BEGIN:
|
||||
/* Fall through. */
|
||||
|
||||
@@ -18,12 +18,6 @@ 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. */
|
||||
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, 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 */
|
||||
@@ -82,7 +76,7 @@ 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);
|
||||
return (HuffmanCode) ((value & 0xFFFF) << 16) | (bits & 0xFF);
|
||||
}
|
||||
|
||||
#define BROTLI_HC_MARK_TABLE_FOR_FAST_LOAD(H) uint32_t __fastload_##H = (*H)
|
||||
@@ -100,7 +94,7 @@ BROTLI_INTERNAL void BrotliBuildCodeLengthsHuffmanTable(HuffmanCode* root_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);
|
||||
int root_bits, const uint16_t* const symbol_lists, uint16_t* count);
|
||||
|
||||
/* Builds a simple Huffman table. The |num_symbols| parameter is to be
|
||||
interpreted as follows: 0 means 1 symbol, 1 means 2 symbols,
|
||||
@@ -110,13 +104,13 @@ 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). */
|
||||
/* alphabet_size_limit is needed due to simple codes, since
|
||||
log2(alphabet_size_max) could be greater than log2(alphabet_size_limit). */
|
||||
typedef struct {
|
||||
HuffmanCode** htrees;
|
||||
HuffmanCode* codes;
|
||||
uint16_t alphabet_size;
|
||||
uint16_t max_symbol;
|
||||
uint16_t alphabet_size_max;
|
||||
uint16_t alphabet_size_limit;
|
||||
uint16_t num_htrees;
|
||||
} HuffmanTreeGroup;
|
||||
|
||||
|
||||
@@ -13,24 +13,6 @@
|
||||
#include "../common/constants.h"
|
||||
#include <brotli/types.h>
|
||||
|
||||
/* Represents the range of values belonging to a prefix code:
|
||||
[offset, offset + 2^nbits) */
|
||||
struct PrefixCodeRange {
|
||||
uint16_t offset;
|
||||
uint8_t nbits;
|
||||
};
|
||||
|
||||
static const struct PrefixCodeRange
|
||||
kBlockLengthPrefixCode[BROTLI_NUM_BLOCK_LEN_SYMBOLS] = {
|
||||
{ 1, 2}, { 5, 2}, { 9, 2}, { 13, 2},
|
||||
{ 17, 3}, { 25, 3}, { 33, 3}, { 41, 3},
|
||||
{ 49, 4}, { 65, 4}, { 81, 4}, { 97, 4},
|
||||
{ 113, 5}, { 145, 5}, { 177, 5}, { 209, 5},
|
||||
{ 241, 6}, { 305, 6}, { 369, 7}, { 497, 8},
|
||||
{ 753, 9}, { 1265, 10}, {2289, 11}, {4337, 12},
|
||||
{8433, 13}, {16625, 24}
|
||||
};
|
||||
|
||||
typedef struct CmdLutElement {
|
||||
uint8_t insert_len_extra_bits;
|
||||
uint8_t copy_len_extra_bits;
|
||||
|
||||
@@ -33,10 +33,7 @@ BROTLI_BOOL BrotliDecoderStateInit(BrotliDecoderState* s,
|
||||
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;
|
||||
s->substate_uncompressed = BROTLI_STATE_UNCOMPRESSED_NONE;
|
||||
s->substate_huffman = BROTLI_STATE_HUFFMAN_NONE;
|
||||
s->substate_decode_uint8 = BROTLI_STATE_DECODE_UINT8_NONE;
|
||||
s->substate_read_block_length = BROTLI_STATE_READ_BLOCK_LENGTH_NONE;
|
||||
|
||||
@@ -59,8 +56,6 @@ BROTLI_BOOL BrotliDecoderStateInit(BrotliDecoderState* s,
|
||||
s->context_map_slice = NULL;
|
||||
s->dist_context_map_slice = NULL;
|
||||
|
||||
s->sub_loop_counter = 0;
|
||||
|
||||
s->literal_hgroup.codes = NULL;
|
||||
s->literal_hgroup.htrees = NULL;
|
||||
s->insert_copy_hgroup.codes = NULL;
|
||||
@@ -84,9 +79,6 @@ BROTLI_BOOL BrotliDecoderStateInit(BrotliDecoderState* s,
|
||||
s->block_type_trees = NULL;
|
||||
s->block_len_trees = NULL;
|
||||
|
||||
/* Make small negative indexes addressable. */
|
||||
s->symbol_lists = &s->symbols_lists_array[BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1];
|
||||
|
||||
s->mtf_upper_bound = 63;
|
||||
|
||||
s->dictionary = BrotliGetDictionary();
|
||||
@@ -142,17 +134,20 @@ void BrotliDecoderStateCleanup(BrotliDecoderState* s) {
|
||||
}
|
||||
|
||||
BROTLI_BOOL BrotliDecoderHuffmanTreeGroupInit(BrotliDecoderState* s,
|
||||
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];
|
||||
HuffmanTreeGroup* group, uint32_t alphabet_size_max,
|
||||
uint32_t alphabet_size_limit, uint32_t ntrees) {
|
||||
/* 376 = 256 (1-st level table) + 4 + 7 + 15 + 31 + 63 (2-nd level mix-tables)
|
||||
This number is discovered "unlimited" "enough" calculator; it is actually
|
||||
a wee bigger than required in several cases (especially for alphabets with
|
||||
less than 16 symbols). */
|
||||
const size_t max_table_size = alphabet_size_limit + 376;
|
||||
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_DECODER_ALLOC(s,
|
||||
code_size + htree_size);
|
||||
group->alphabet_size = (uint16_t)alphabet_size;
|
||||
group->max_symbol = (uint16_t)max_symbol;
|
||||
group->alphabet_size_max = (uint16_t)alphabet_size_max;
|
||||
group->alphabet_size_limit = (uint16_t)alphabet_size_limit;
|
||||
group->num_htrees = (uint16_t)ntrees;
|
||||
group->htrees = p;
|
||||
group->codes = (HuffmanCode*)(&p[ntrees]);
|
||||
|
||||
+144
-37
@@ -21,6 +21,95 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Graphviz diagram that describes state transitions:
|
||||
|
||||
digraph States {
|
||||
graph [compound=true]
|
||||
concentrate=true
|
||||
node [shape="box"]
|
||||
|
||||
UNINITED -> {LARGE_WINDOW_BITS -> INITIALIZE}
|
||||
subgraph cluster_metablock_workflow {
|
||||
style="rounded"
|
||||
label=< <B>METABLOCK CYCLE</B> >
|
||||
METABLOCK_BEGIN -> METABLOCK_HEADER
|
||||
METABLOCK_HEADER:sw -> METADATA
|
||||
METABLOCK_HEADER:s -> UNCOMPRESSED
|
||||
METABLOCK_HEADER:se -> METABLOCK_DONE:ne
|
||||
METADATA:s -> METABLOCK_DONE:w
|
||||
UNCOMPRESSED:s -> METABLOCK_DONE:n
|
||||
METABLOCK_DONE:e -> METABLOCK_BEGIN:e [constraint="false"]
|
||||
}
|
||||
INITIALIZE -> METABLOCK_BEGIN
|
||||
METABLOCK_DONE -> DONE
|
||||
|
||||
subgraph cluster_compressed_metablock {
|
||||
style="rounded"
|
||||
label=< <B>COMPRESSED METABLOCK</B> >
|
||||
|
||||
subgraph cluster_command {
|
||||
style="rounded"
|
||||
label=< <B>HOT LOOP</B> >
|
||||
|
||||
_METABLOCK_DONE_PORT_ [shape=point style=invis]
|
||||
|
||||
{
|
||||
// Set different shape for nodes returning from "compressed metablock".
|
||||
node [shape=invhouse]; CMD_INNER CMD_POST_DECODE_LITERALS;
|
||||
CMD_POST_WRAP_COPY; CMD_INNER_WRITE; CMD_POST_WRITE_1;
|
||||
}
|
||||
|
||||
CMD_BEGIN -> CMD_INNER -> CMD_POST_DECODE_LITERALS -> CMD_POST_WRAP_COPY
|
||||
|
||||
// IO ("write") nodes are not in the hot loop!
|
||||
CMD_INNER_WRITE [style=dashed]
|
||||
CMD_INNER -> CMD_INNER_WRITE
|
||||
CMD_POST_WRITE_1 [style=dashed]
|
||||
CMD_POST_DECODE_LITERALS -> CMD_POST_WRITE_1
|
||||
CMD_POST_WRITE_2 [style=dashed]
|
||||
CMD_POST_WRAP_COPY -> CMD_POST_WRITE_2
|
||||
|
||||
CMD_POST_WRITE_1 -> CMD_BEGIN:s [constraint="false"]
|
||||
CMD_INNER_WRITE -> {CMD_INNER CMD_POST_DECODE_LITERALS}
|
||||
[constraint="false"]
|
||||
CMD_BEGIN:ne -> CMD_POST_DECODE_LITERALS [constraint="false"]
|
||||
CMD_POST_WRAP_COPY -> CMD_BEGIN [constraint="false"]
|
||||
CMD_POST_DECODE_LITERALS -> CMD_BEGIN:ne [constraint="false"]
|
||||
CMD_POST_WRITE_2 -> CMD_POST_WRAP_COPY [constraint="false"]
|
||||
{rank=same; CMD_BEGIN; CMD_INNER; CMD_POST_DECODE_LITERALS;
|
||||
CMD_POST_WRAP_COPY}
|
||||
{rank=same; CMD_INNER_WRITE; CMD_POST_WRITE_1; CMD_POST_WRITE_2}
|
||||
|
||||
{CMD_INNER CMD_POST_DECODE_LITERALS CMD_POST_WRAP_COPY} ->
|
||||
_METABLOCK_DONE_PORT_ [style=invis]
|
||||
{CMD_INNER_WRITE CMD_POST_WRITE_1} -> _METABLOCK_DONE_PORT_
|
||||
[constraint="false" style=invis]
|
||||
}
|
||||
|
||||
BEFORE_COMPRESSED_METABLOCK_HEADER:s -> HUFFMAN_CODE_0:n
|
||||
HUFFMAN_CODE_0 -> HUFFMAN_CODE_1 -> HUFFMAN_CODE_2 -> HUFFMAN_CODE_3
|
||||
HUFFMAN_CODE_0 -> METABLOCK_HEADER_2 -> CONTEXT_MODES -> CONTEXT_MAP_1
|
||||
CONTEXT_MAP_1 -> CONTEXT_MAP_2 -> TREE_GROUP
|
||||
TREE_GROUP -> BEFORE_COMPRESSED_METABLOCK_BODY:e
|
||||
BEFORE_COMPRESSED_METABLOCK_BODY:s -> CMD_BEGIN:n
|
||||
|
||||
HUFFMAN_CODE_3:e -> HUFFMAN_CODE_0:ne [constraint="false"]
|
||||
{rank=same; HUFFMAN_CODE_0; HUFFMAN_CODE_1; HUFFMAN_CODE_2; HUFFMAN_CODE_3}
|
||||
{rank=same; METABLOCK_HEADER_2; CONTEXT_MODES; CONTEXT_MAP_1; CONTEXT_MAP_2;
|
||||
TREE_GROUP}
|
||||
}
|
||||
METABLOCK_HEADER:e -> BEFORE_COMPRESSED_METABLOCK_HEADER:n
|
||||
|
||||
_METABLOCK_DONE_PORT_ -> METABLOCK_DONE:se
|
||||
[constraint="false" ltail=cluster_command]
|
||||
|
||||
UNINITED [shape=Mdiamond];
|
||||
DONE [shape=Msquare];
|
||||
}
|
||||
|
||||
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
BROTLI_STATE_UNINITED,
|
||||
BROTLI_STATE_LARGE_WINDOW_BITS,
|
||||
@@ -39,6 +128,7 @@ typedef enum {
|
||||
BROTLI_STATE_METABLOCK_DONE,
|
||||
BROTLI_STATE_COMMAND_POST_WRITE_1,
|
||||
BROTLI_STATE_COMMAND_POST_WRITE_2,
|
||||
BROTLI_STATE_BEFORE_COMPRESSED_METABLOCK_HEADER,
|
||||
BROTLI_STATE_HUFFMAN_CODE_0,
|
||||
BROTLI_STATE_HUFFMAN_CODE_1,
|
||||
BROTLI_STATE_HUFFMAN_CODE_2,
|
||||
@@ -46,6 +136,7 @@ typedef enum {
|
||||
BROTLI_STATE_CONTEXT_MAP_1,
|
||||
BROTLI_STATE_CONTEXT_MAP_2,
|
||||
BROTLI_STATE_TREE_GROUP,
|
||||
BROTLI_STATE_BEFORE_COMPRESSED_METABLOCK_BODY,
|
||||
BROTLI_STATE_DONE
|
||||
} BrotliRunningState;
|
||||
|
||||
@@ -98,6 +189,50 @@ typedef enum {
|
||||
BROTLI_STATE_READ_BLOCK_LENGTH_SUFFIX
|
||||
} BrotliRunningReadBlockLengthState;
|
||||
|
||||
typedef struct BrotliMetablockHeaderArena {
|
||||
BrotliRunningTreeGroupState substate_tree_group;
|
||||
BrotliRunningContextMapState substate_context_map;
|
||||
BrotliRunningHuffmanState substate_huffman;
|
||||
|
||||
uint32_t sub_loop_counter;
|
||||
|
||||
uint32_t repeat_code_len;
|
||||
uint32_t prev_code_len;
|
||||
|
||||
/* For ReadHuffmanCode. */
|
||||
uint32_t symbol;
|
||||
uint32_t repeat;
|
||||
uint32_t space;
|
||||
|
||||
/* Huffman table for "histograms". */
|
||||
HuffmanCode table[32];
|
||||
/* List of heads of symbol chains. */
|
||||
uint16_t* symbol_lists;
|
||||
/* Storage from symbol_lists. */
|
||||
uint16_t symbols_lists_array[BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1 +
|
||||
BROTLI_NUM_COMMAND_SYMBOLS];
|
||||
/* Tails of symbol chains. */
|
||||
int next_symbol[32];
|
||||
uint8_t code_length_code_lengths[BROTLI_CODE_LENGTH_CODES];
|
||||
/* Population counts for the code lengths. */
|
||||
uint16_t code_length_histo[16];
|
||||
|
||||
/* For HuffmanTreeGroupDecode. */
|
||||
int htree_index;
|
||||
HuffmanCode* next;
|
||||
|
||||
/* For DecodeContextMap. */
|
||||
uint32_t context_index;
|
||||
uint32_t max_run_length_prefix;
|
||||
uint32_t code;
|
||||
HuffmanCode context_map_table[BROTLI_HUFFMAN_MAX_SIZE_272];
|
||||
} BrotliMetablockHeaderArena;
|
||||
|
||||
typedef struct BrotliMetablockBodyArena {
|
||||
uint8_t dist_extra_bits[544];
|
||||
uint32_t dist_offset[544];
|
||||
} BrotliMetablockBodyArena;
|
||||
|
||||
struct BrotliDecoderStateStruct {
|
||||
BrotliRunningState state;
|
||||
|
||||
@@ -110,7 +245,8 @@ struct BrotliDecoderStateStruct {
|
||||
brotli_free_func free_func;
|
||||
void* memory_manager_opaque;
|
||||
|
||||
/* Temporary storage for remaining input. */
|
||||
/* Temporary storage for remaining input. Brotli stream format is designed in
|
||||
a way, that 64 bits are enough to make progress in decoding. */
|
||||
union {
|
||||
uint64_t u64;
|
||||
uint8_t u8[8];
|
||||
@@ -125,7 +261,6 @@ struct BrotliDecoderStateStruct {
|
||||
int dist_rb_idx;
|
||||
int dist_rb[4];
|
||||
int error_code;
|
||||
uint32_t sub_loop_counter;
|
||||
uint8_t* ringbuffer;
|
||||
uint8_t* ringbuffer_end;
|
||||
HuffmanCode* htree_command;
|
||||
@@ -153,13 +288,10 @@ struct BrotliDecoderStateStruct {
|
||||
uint32_t block_type_rb[6];
|
||||
uint32_t distance_postfix_bits;
|
||||
uint32_t num_direct_distance_codes;
|
||||
int distance_postfix_mask;
|
||||
uint32_t num_dist_htrees;
|
||||
uint8_t* dist_context_map;
|
||||
HuffmanCode* literal_htree;
|
||||
uint8_t dist_htree_index;
|
||||
uint32_t repeat_code_len;
|
||||
uint32_t prev_code_len;
|
||||
|
||||
int copy_length;
|
||||
int distance_code;
|
||||
@@ -168,33 +300,6 @@ struct BrotliDecoderStateStruct {
|
||||
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. */
|
||||
uint32_t symbol;
|
||||
uint32_t repeat;
|
||||
uint32_t space;
|
||||
|
||||
HuffmanCode table[32];
|
||||
/* List of heads of symbol chains. */
|
||||
uint16_t* symbol_lists;
|
||||
/* Storage from symbol_lists. */
|
||||
uint16_t symbols_lists_array[BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1 +
|
||||
BROTLI_NUM_COMMAND_SYMBOLS];
|
||||
/* Tails of symbol chains. */
|
||||
int next_symbol[32];
|
||||
uint8_t code_length_code_lengths[BROTLI_CODE_LENGTH_CODES];
|
||||
/* Population counts for the code lengths. */
|
||||
uint16_t code_length_histo[16];
|
||||
|
||||
/* For HuffmanTreeGroupDecode. */
|
||||
int htree_index;
|
||||
HuffmanCode* next;
|
||||
|
||||
/* 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. */
|
||||
uint32_t mtf_upper_bound;
|
||||
uint32_t mtf[64 + 1];
|
||||
@@ -203,10 +308,7 @@ struct BrotliDecoderStateStruct {
|
||||
|
||||
/* States inside function calls. */
|
||||
BrotliRunningMetablockHeaderState substate_metablock_header;
|
||||
BrotliRunningTreeGroupState substate_tree_group;
|
||||
BrotliRunningContextMapState substate_context_map;
|
||||
BrotliRunningUncompressedState substate_uncompressed;
|
||||
BrotliRunningHuffmanState substate_huffman;
|
||||
BrotliRunningDecodeUint8State substate_decode_uint8;
|
||||
BrotliRunningReadBlockLengthState substate_read_block_length;
|
||||
|
||||
@@ -229,6 +331,11 @@ struct BrotliDecoderStateStruct {
|
||||
const BrotliTransforms* transforms;
|
||||
|
||||
uint32_t trivial_literal_contexts[8]; /* 256 bits */
|
||||
|
||||
union {
|
||||
BrotliMetablockHeaderArena header;
|
||||
BrotliMetablockBodyArena body;
|
||||
} arena;
|
||||
};
|
||||
|
||||
typedef struct BrotliDecoderStateStruct BrotliDecoderStateInternal;
|
||||
@@ -241,8 +348,8 @@ 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 max_symbol, uint32_t ntrees);
|
||||
BrotliDecoderState* s, HuffmanTreeGroup* group, uint32_t alphabet_size_max,
|
||||
uint32_t alphabet_size_limit, uint32_t ntrees);
|
||||
|
||||
#define BROTLI_DECODER_ALLOC(S, L) S->alloc_func(S->memory_manager_opaque, L)
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "./backward_references.h"
|
||||
|
||||
#include "../common/constants.h"
|
||||
#include "../common/context.h"
|
||||
#include "../common/dictionary.h"
|
||||
#include "../common/platform.h"
|
||||
#include <brotli/types.h>
|
||||
@@ -119,17 +120,17 @@ static BROTLI_INLINE size_t ComputeDistanceCode(size_t distance,
|
||||
#undef CAT
|
||||
#undef EXPAND_CAT
|
||||
|
||||
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,
|
||||
void BrotliCreateBackwardReferences(size_t num_bytes,
|
||||
size_t position, const uint8_t* ringbuffer, size_t ringbuffer_mask,
|
||||
ContextLut literal_context_lut, const BrotliEncoderParams* params,
|
||||
Hasher* 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( \
|
||||
num_bytes, position, ringbuffer, \
|
||||
ringbuffer_mask, params, hasher, dist_cache, \
|
||||
CreateBackwardReferencesNH ## N(num_bytes, \
|
||||
position, ringbuffer, ringbuffer_mask, \
|
||||
literal_context_lut, params, hasher, dist_cache, \
|
||||
last_insert_len, commands, num_commands, num_literals); \
|
||||
return;
|
||||
FOR_GENERIC_HASHERS(CASE_)
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#define BROTLI_ENC_BACKWARD_REFERENCES_H_
|
||||
|
||||
#include "../common/constants.h"
|
||||
#include "../common/context.h"
|
||||
#include "../common/dictionary.h"
|
||||
#include "../common/platform.h"
|
||||
#include <brotli/types.h>
|
||||
@@ -25,10 +26,10 @@ extern "C" {
|
||||
initially the total amount of commands output by previous
|
||||
CreateBackwardReferences calls, and must be incremented by the amount written
|
||||
by this call. */
|
||||
BROTLI_INTERNAL 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,
|
||||
BROTLI_INTERNAL void BrotliCreateBackwardReferences(size_t num_bytes,
|
||||
size_t position, const uint8_t* ringbuffer, size_t ringbuffer_mask,
|
||||
ContextLut literal_context_lut, const BrotliEncoderParams* params,
|
||||
Hasher* 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)
|
||||
|
||||
Loaded 100 of 196 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user