diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js
index f8a2fd303..4bce03c10 100644
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -77,7 +77,8 @@ pref("extensions.systemAddon.update.url", "https://aus5.mozilla.org/update/3/Sys
pref("extensions.autoDisableScopes", 15);
// Require signed add-ons by default
-pref("xpinstall.signatures.required", true);
+// ... except on TenFourFox
+pref("xpinstall.signatures.required", false);
pref("xpinstall.signatures.devInfoURL", "https://wiki.mozilla.org/Addons/Extension_Signing");
// Dictionary download preference
@@ -129,7 +130,7 @@ pref("app.update.enabled", true);
pref("app.update.auto", true);
// See chart in nsUpdateService.js source for more details
-pref("app.update.mode", 1);
+pref("app.update.mode", 2);
// If set to true, the Update Service will present no UI for any event.
pref("app.update.silent", false);
@@ -144,7 +145,7 @@ pref("app.update.badge", false);
// If set to true, the Update Service will apply updates in the background
// when it finishes downloading them.
-pref("app.update.staging.enabled", true);
+pref("app.update.staging.enabled", false);
// Update service URL:
pref("app.update.url", "https://aus5.mozilla.org/update/6/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%SYSTEM_CAPABILITIES%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml");
@@ -222,7 +223,8 @@ pref("browser.fixup.domainwhitelist.localhost", true);
pref("general.useragent.locale", "@AB_CD@");
pref("general.skins.selectedSkin", "classic/1.0");
-pref("general.smoothScroll", true);
+// too slow on TenFourFox
+pref("general.smoothScroll", false);
#ifdef UNIX_BUT_NOT_MAC
pref("general.autoScroll", false);
#else
@@ -241,7 +243,7 @@ pref("browser.shell.defaultBrowserCheckCount", 0);
pref("browser.startup.page", 1);
pref("browser.startup.homepage", "chrome://branding/locale/browserconfig.properties");
-pref("browser.slowStartup.notificationDisabled", false);
+pref("browser.slowStartup.notificationDisabled", true); // STFU!
pref("browser.slowStartup.timeThreshold", 40000);
pref("browser.slowStartup.maxSamples", 5);
@@ -1011,15 +1013,15 @@ pref("browser.sessionstore.resume_from_crash", true);
pref("browser.sessionstore.resume_session_once", false);
// minimal interval between two save operations in milliseconds
-pref("browser.sessionstore.interval", 15000);
+pref("browser.sessionstore.interval", 25000);
// on which sites to save text data, POSTDATA and cookies
// 0 = everywhere, 1 = unencrypted sites, 2 = nowhere
pref("browser.sessionstore.privacy_level", 0);
// how many tabs can be reopened (per window)
-pref("browser.sessionstore.max_tabs_undo", 10);
+pref("browser.sessionstore.max_tabs_undo", 4);
// how many windows can be reopened (per session) - on non-OS X platforms this
// pref may be ignored when dealing with pop-up windows to ensure proper startup
-pref("browser.sessionstore.max_windows_undo", 3);
+pref("browser.sessionstore.max_windows_undo", 2);
// number of crashes that can occur before the about:sessionrestore page is displayed
// (this pref has no effect if more than 6 hours have passed since the last crash)
pref("browser.sessionstore.max_resumed_crashes", 1);
@@ -1149,7 +1151,7 @@ pref("dom.ipc.shims.enabledWarnings", false);
// Start the browser in e10s mode
pref("browser.tabs.remote.autostart", false);
-pref("browser.tabs.remote.desktopbehavior", true);
+pref("browser.tabs.remote.desktopbehavior", false);
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
// When this pref is true the Windows process sandbox will set up dummy
@@ -1427,11 +1429,7 @@ pref("plain_text.wrap_long_lines", true);
pref("dom.debug.propagate_gesture_events_through_content", false);
// The request URL of the GeoLocation backend.
-#ifdef RELEASE_BUILD
-pref("geo.wifi.uri", "https://www.googleapis.com/geolocation/v1/geolocate?key=%GOOGLE_API_KEY%");
-#else
pref("geo.wifi.uri", "https://location.services.mozilla.com/v1/geolocate?key=%MOZILLA_API_KEY%");
-#endif
#ifdef XP_MACOSX
#ifdef RELEASE_BUILD
@@ -1617,7 +1615,7 @@ pref("reader.parse-node-limit", 0);
// and because (normally) these errors are not persisted anywhere.
pref("reader.errors.includeURLs", true);
-pref("browser.pocket.enabled", true);
+pref("browser.pocket.enabled", false);
pref("browser.pocket.api", "api.getpocket.com");
pref("browser.pocket.site", "getpocket.com");
pref("browser.pocket.oAuthConsumerKey", "40249-e88c401e1b1f2242d9e441c4");
diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
index 4f2642a0e..179a70fac 100644
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1222,7 +1222,7 @@ var gBrowserInit = {
PanelUI.init();
LightweightThemeListener.init();
- Services.telemetry.getHistogramById("E10S_WINDOW").add(gMultiProcessBrowser);
+ //Services.telemetry.getHistogramById("E10S_WINDOW").add(gMultiProcessBrowser);
SidebarUI.startDelayedLoad();
@@ -1354,6 +1354,7 @@ var gBrowserInit = {
Cu.reportError("Could not end startup crash tracking: " + ex);
}
+/*
// Delay this a minute because there's no rush
setTimeout(() => {
this.gmpInstallManager = new GMPInstallManager();
@@ -1375,6 +1376,7 @@ var gBrowserInit = {
let h264Works = v.canPlayType("video/mp4") != "";
Services.telemetry.getHistogramById("VIDEO_CAN_CREATE_H264_DECODER").add(h264Works);
}, 90 * 1000);
+*/
SessionStore.promiseInitialized.then(() => {
// Bail out if the window has been closed in the meantime.
@@ -1389,6 +1391,7 @@ var gBrowserInit = {
// Telemetry for master-password - we do this after 5 seconds as it
// can cause IO if NSS/PSM has not already initialized.
+/*
setTimeout(() => {
if (window.closed) {
return;
@@ -1403,13 +1406,14 @@ var gBrowserInit = {
Services.telemetry.getHistogramById("MASTER_PASSWORD_ENABLED").add(mpEnabled);
}
}, 5000);
+*/
PanicButtonNotifier.init();
});
this.delayedStartupFinished = true;
Services.obs.notifyObservers(window, "browser-delayed-startup-finished", "");
- TelemetryTimestamps.add("delayedStartupFinished");
+ //TelemetryTimestamps.add("delayedStartupFinished");
},
// Returns the URI(s) to load at startup.
@@ -2784,16 +2788,20 @@ var BrowserOnClick = {
break;
case "Browser:SetSSLErrorReportAuto":
Services.prefs.setBoolPref("security.ssl.errorReporting.automatic", msg.json.automatic);
+/*
let bin = TLS_ERROR_REPORT_TELEMETRY_AUTO_UNCHECKED;
if (msg.json.automatic) {
bin = TLS_ERROR_REPORT_TELEMETRY_AUTO_CHECKED;
}
Services.telemetry.getHistogramById("TLS_ERROR_REPORT_UI").add(bin);
+*/
break;
case "Browser:SSLErrorReportTelemetry":
+/*
let reportStatus = msg.data.reportStatus;
Services.telemetry.getHistogramById("TLS_ERROR_REPORT_UI")
.add(reportStatus);
+*/
break;
case "Browser:OverrideWeakCrypto":
let weakCryptoOverride = Cc["@mozilla.org/security/weakcryptooverride;1"]
@@ -2825,11 +2833,13 @@ var BrowserOnClick = {
return;
}
+/*
let bin = TLS_ERROR_REPORT_TELEMETRY_MANUAL_SEND;
if (Services.prefs.getBoolPref("security.ssl.errorReporting.automatic")) {
bin = TLS_ERROR_REPORT_TELEMETRY_AUTO_SEND;
}
Services.telemetry.getHistogramById("TLS_ERROR_REPORT_UI").add(bin);
+*/
let serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
.getService(Ci.nsISerializationHelper);
@@ -2906,13 +2916,15 @@ var BrowserOnClick = {
},
onAboutCertError: function (browser, elementId, isTopFrame, location, securityInfoAsString) {
- let secHistogram = Services.telemetry.getHistogramById("SECURITY_UI");
+ //let secHistogram = Services.telemetry.getHistogramById("SECURITY_UI");
switch (elementId) {
case "exceptionDialogButton":
+/*
if (isTopFrame) {
secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_TOP_CLICK_ADD_EXCEPTION);
}
+*/
let serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
.getService(Ci.nsISerializationHelper);
@@ -2943,16 +2955,20 @@ var BrowserOnClick = {
break;
case "returnButton":
+/*
if (isTopFrame) {
secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_TOP_GET_ME_OUT_OF_HERE);
}
+*/
goBackFromErrorPage();
break;
case "advancedButton":
+/*
if (isTopFrame) {
secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_TOP_UNDERSTAND_RISKS);
}
+*/
let errorInfo = getDetailedCertErrorInfo(location,
securityInfoAsString);
@@ -2974,6 +2990,7 @@ var BrowserOnClick = {
onAboutBlocked: function (elementId, reason, isTopFrame, location) {
// Depending on what page we are displaying here (malware/phishing/unwanted)
// use the right strings and links for each.
+/*
let bucketName = "";
let sendTelemetry = false;
if (reason === 'malware') {
@@ -2989,11 +3006,14 @@ var BrowserOnClick = {
let secHistogram = Services.telemetry.getHistogramById("SECURITY_UI");
let nsISecTel = Ci.nsISecurityUITelemetry;
bucketName += isTopFrame ? "TOP_" : "FRAME_";
+*/
switch (elementId) {
case "getMeOutButton":
+/*
if (sendTelemetry) {
secHistogram.add(nsISecTel[bucketName + "GET_ME_OUT_OF_HERE"]);
}
+*/
getMeOutOfHere();
break;
@@ -3003,17 +3023,21 @@ var BrowserOnClick = {
// We log even if malware/phishing/unwanted info URL couldn't be found:
// the measurement is for how many users clicked the WHY BLOCKED button
+/*
if (sendTelemetry) {
secHistogram.add(nsISecTel[bucketName + "WHY_BLOCKED"]);
}
+*/
openHelpLink("phishing-malware", false, "current");
break;
case "ignoreWarningButton":
if (gPrefService.getBoolPref("browser.safebrowsing.allowOverride")) {
+/*
if (sendTelemetry) {
secHistogram.add(nsISecTel[bucketName + "IGNORE_WARNING"]);
}
+*/
this.ignoreWarningButton(reason);
}
break;
@@ -3808,6 +3832,7 @@ const BrowserSearch = {
},
recordSearchInTelemetry: function (engine, source) {
+/*
const SOURCES = [
"abouthome",
"contextmenu",
@@ -3825,11 +3850,14 @@ const BrowserSearch = {
let count = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS");
count.add(countId);
+*/
},
recordOneoffSearchInTelemetry: function (engine, source, type, where) {
+/*
let id = this._getSearchEngineId(engine) + "." + source;
BrowserUITelemetry.countOneoffSearchEvent(id, type, where);
+*/
}
};
@@ -3992,6 +4020,7 @@ function toOpenWindowByType(inType, uri, features)
function OpenBrowserWindow(options)
{
+/*
var telemetryObj = {};
TelemetryStopwatch.start("FX_NEW_WINDOW_MS", telemetryObj);
@@ -4016,6 +4045,7 @@ function OpenBrowserWindow(options)
// is being closed right after it was opened to avoid leaking.
Services.obs.addObserver(newDocumentShown, "document-shown", false);
Services.obs.addObserver(windowClosed, "domwindowclosed", false);
+*/
var charsetArg = new String();
var handler = Components.classes["@mozilla.org/browser/clh;1"]
@@ -4813,6 +4843,7 @@ var CombinedStopReload = {
var TabsProgressListener = {
onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+/*
// Collect telemetry data about tab load times.
if (aWebProgress.isTopLevel) {
if (aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) {
@@ -4827,6 +4858,7 @@ var TabsProgressListener = {
TelemetryStopwatch.cancel("FX_PAGE_LOAD_MS", aBrowser);
}
}
+*/
// Attach a listener to watch for "click" events bubbling up from error
// pages and other similar pages (like about:newtab). This lets us fix bugs
@@ -5278,11 +5310,13 @@ var gTabletModePageCounter = {
},
finish() {
+/*
if (this.enabled) {
let histogram = Services.telemetry.getKeyedHistogramById("FX_TABLETMODE_PAGE_LOAD");
histogram.add("tablet", this._tabletCount);
histogram.add("desktop", this._desktopCount);
}
+*/
},
};
@@ -6851,10 +6885,12 @@ var gIdentityHandler = {
disableMixedContentProtection() {
// Use telemetry to measure how often unblocking happens
const kMIXED_CONTENT_UNBLOCK_EVENT = 2;
+/*
let histogram =
Services.telemetry.getHistogramById(
"MIXED_CONTENT_UNBLOCK_COUNTER");
histogram.add(kMIXED_CONTENT_UNBLOCK_EVENT);
+*/
// Reload the page with the content unblocked
BrowserReloadWithFlags(
Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_MIXED_CONTENT);
diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml
index 667ae6a5c..d82a3a987 100644
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1947,7 +1947,7 @@
if (animate) {
requestAnimationFrame(function () {
- this.tabContainer._handleTabTelemetryStart(t, aURI);
+ //this.tabContainer._handleTabTelemetryStart(t, aURI);
// kick the animation off
t.setAttribute("fadein", "true");
@@ -2132,7 +2132,7 @@
return;
}
- this.tabContainer._handleTabTelemetryStart(aTab);
+ //this.tabContainer._handleTabTelemetryStart(aTab);
this._blurTab(aTab);
aTab.style.maxWidth = ""; // ensure that fade-out transition happens
@@ -4608,7 +4608,7 @@
} catch (ex) {
this._tabAnimationLoggingEnabled = false;
}
- this._browserNewtabpageEnabled = Services.prefs.getBoolPref("browser.newtabpage.enabled");
+ this._browserNewtabpageEnabled = false; //Services.prefs.getBoolPref("browser.newtabpage.enabled");
]]>
@@ -5263,6 +5263,7 @@
@@ -5285,6 +5287,7 @@
@@ -5351,7 +5355,7 @@
var tab = event.target;
- this._handleTabTelemetryEnd(tab);
+ //this._handleTabTelemetryEnd(tab);
if (tab.getAttribute("fadein") == "true") {
if (tab._fullyOpen)
diff --git a/browser/components/about/AboutRedirector.cpp b/browser/components/about/AboutRedirector.cpp
index 78f55c9b6..ad7cee3c0 100644
--- a/browser/components/about/AboutRedirector.cpp
+++ b/browser/components/about/AboutRedirector.cpp
@@ -86,7 +86,7 @@ static RedirEntry kRedirMap[] = {
nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
nsIAboutModule::ALLOW_SCRIPT |
nsIAboutModule::ENABLE_INDEXED_DB },
- { "newtab", "chrome://browser/content/newtab/newTab.xhtml",
+ { "newtab", "about:blank",
nsIAboutModule::ALLOW_SCRIPT },
#ifndef RELEASE_BUILD
{ "remote-newtab", "chrome://browser/content/remote-newtab/newTab.xhtml",
diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js
index 8f32378b0..6d39b4280 100644
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -509,6 +509,7 @@ BrowserGlue.prototype = {
},
_handleURLBarTelemetry(input) {
+/*
if (!input ||
input.id != "urlbar" ||
input.inPrivateContext ||
@@ -563,6 +564,7 @@ BrowserGlue.prototype = {
Cu.reportError("Unknown FX_URLBAR_SELECTED_RESULT_TYPE type: " +
actionType);
}
+*/
},
_syncSearchEngines: function () {
@@ -706,7 +708,7 @@ BrowserGlue.prototype = {
const STATE_USER_CLOSED_NOTIFICATION = 4;
let update = function(response) {
- Services.telemetry.getHistogramById("SLOW_ADDON_WARNING_STATES").add(response);
+ //Services.telemetry.getHistogramById("SLOW_ADDON_WARNING_STATES").add(response);
}
let complete = false;
@@ -718,7 +720,7 @@ BrowserGlue.prototype = {
}
complete = true;
update(response);
- Services.telemetry.getHistogramById("SLOW_ADDON_WARNING_RESPONSE_TIME").add(Date.now() - start);
+ //Services.telemetry.getHistogramById("SLOW_ADDON_WARNING_RESPONSE_TIME").add(Date.now() - start);
};
update(STATE_WARNING_DISPLAYED);
@@ -1049,6 +1051,7 @@ BrowserGlue.prototype = {
},
_firstWindowTelemetry: function(aWindow) {
+/*
#ifdef XP_WIN
let SCALING_PROBE_NAME = "DISPLAY_SCALING_MSWIN";
#elifdef XP_MACOSX
@@ -1062,6 +1065,7 @@ BrowserGlue.prototype = {
let scaling = aWindow.devicePixelRatio * 100;
Services.telemetry.getHistogramById(SCALING_PROBE_NAME).add(scaling);
}
+*/
},
// the first browser window has finished initializing
@@ -1324,6 +1328,7 @@ BrowserGlue.prototype = {
}
#endif
+#ifdef I_LOVE_TELE_AND_I_CANNOT_LIE
try {
// Report default browser status on startup to telemetry
// so we can track whether we are the default.
@@ -1337,6 +1342,7 @@ BrowserGlue.prototype = {
.add(promptCount);
}
catch (ex) { /* Don't break the default prompt if telemetry is broken. */ }
+#endif
if (willPrompt) {
Services.tm.mainThread.dispatch(function() {
@@ -1815,6 +1821,7 @@ BrowserGlue.prototype = {
// available backup compared to that session.
if (profileLastUse > lastBackupTime) {
let backupAge = Math.round((profileLastUse - lastBackupTime) / 86400000);
+/*
// Report the age of the last available backup.
try {
Services.telemetry
@@ -1823,6 +1830,7 @@ BrowserGlue.prototype = {
} catch (ex) {
Cu.reportError("Unable to report telemetry.");
}
+*/
if (backupAge > BOOKMARKS_BACKUP_MAX_INTERVAL_DAYS)
this._bookmarksBackupIdleTime /= 2;
@@ -2687,7 +2695,7 @@ ContentPermissionPrompt.prototype = {
},
_promptGeo : function(aRequest) {
- var secHistogram = Services.telemetry.getHistogramById("SECURITY_UI");
+ //var secHistogram = Services.telemetry.getHistogramById("SECURITY_UI");
var message;
@@ -2697,7 +2705,7 @@ ContentPermissionPrompt.prototype = {
action: null,
expireType: null,
callback: function() {
- secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST_SHARE_LOCATION);
+ //secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST_SHARE_LOCATION);
},
}];
@@ -2715,7 +2723,7 @@ ContentPermissionPrompt.prototype = {
action: Ci.nsIPermissionManager.ALLOW_ACTION,
expireType: null,
callback: function() {
- secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST_ALWAYS_SHARE);
+ //secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST_ALWAYS_SHARE);
},
});
@@ -2725,12 +2733,12 @@ ContentPermissionPrompt.prototype = {
action: Ci.nsIPermissionManager.DENY_ACTION,
expireType: null,
callback: function() {
- secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST_NEVER_SHARE);
+ //secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST_NEVER_SHARE);
},
});
}
- secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST);
+ //secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST);
this._showPrompt(aRequest, message, "geo", actions, "geolocation",
"geo-notification-icon", options);
@@ -2950,11 +2958,15 @@ var DefaultBrowserCheck = {
if (isDefault || runTime > 600) {
this._setAsDefaultTimer.cancel();
this._setAsDefaultTimer = null;
+/*
Services.telemetry.getHistogramById("BROWSER_SET_DEFAULT_TIME_TO_COMPLETION_SECONDS")
.add(runTime);
+*/
}
+/*
Services.telemetry.getHistogramById("BROWSER_IS_USER_DEFAULT_ERROR")
.add(isDefaultError);
+*/
}, 1000, Ci.nsITimer.TYPE_REPEATING_SLACK);
} catch (ex) {
setAsDefaultError = true;
@@ -2964,10 +2976,12 @@ var DefaultBrowserCheck = {
// to be inverse of each other, but that is only because this function is
// called when the browser is set as the default. During startup we record
// the BROWSER_IS_USER_DEFAULT value without recording BROWSER_SET_USER_DEFAULT_ERROR.
+/*
Services.telemetry.getHistogramById("BROWSER_IS_USER_DEFAULT")
.add(!setAsDefaultError);
Services.telemetry.getHistogramById("BROWSER_SET_DEFAULT_ERROR")
.add(setAsDefaultError);
+*/
},
_createPopup: function(win, notNowStrings, neverStrings) {
@@ -3080,11 +3094,13 @@ var DefaultBrowserCheck = {
ShellService.shouldCheckDefaultBrowser = false;
}
+#ifdef I_LOVE_TELE_AND_I_CANNOT_LIE
try {
let resultEnum = rv * 2 + shouldAsk.value;
Services.telemetry.getHistogramById("BROWSER_SET_DEFAULT_RESULT")
.add(resultEnum);
} catch (ex) { /* Don't break if Telemetry is acting up. */ }
+#endif
}
},
diff --git a/config/expandlibs_exec.py b/config/expandlibs_exec.py
index c05343022..d605ed7c3 100644
--- a/config/expandlibs_exec.py
+++ b/config/expandlibs_exec.py
@@ -305,6 +305,8 @@ class SectionFinder(object):
def print_command(out, args):
print >>out, "Executing: " + " ".join(args)
+ if not hasattr(args, 'tmp'):
+ return
for tmp in [f for f in args.tmp if os.path.isfile(f)]:
print >>out, tmp + ":"
with open(tmp) as file:
diff --git a/dom/base/nsContentIterator.cpp b/dom/base/nsContentIterator.cpp
index bf3ba0c62..e777780a9 100644
--- a/dom/base/nsContentIterator.cpp
+++ b/dom/base/nsContentIterator.cpp
@@ -377,8 +377,8 @@ nsContentIterator::Init(nsIDOMRange* aDOMRange)
// Does mFirst node really intersect the range? The range could be
// 'degenerate', i.e., not collapsed but still contain no content.
if (mFirst &&
- NS_WARN_IF(!NodeIsInTraversalRange(mFirst, mPre, startNode,
- startIndx, endNode, endIndx))) {
+ !NodeIsInTraversalRange(mFirst, mPre, startNode, startIndx,
+ endNode, endIndx)) {
mFirst = nullptr;
}
} else {
@@ -405,8 +405,8 @@ nsContentIterator::Init(nsIDOMRange* aDOMRange)
// 'degenerate', i.e., not collapsed but still contain no content.
if (mFirst &&
- NS_WARN_IF(!NodeIsInTraversalRange(mFirst, mPre, startNode, startIndx,
- endNode, endIndx))) {
+ !NodeIsInTraversalRange(mFirst, mPre, startNode, startIndx,
+ endNode, endIndx)) {
mFirst = nullptr;
}
}
@@ -448,9 +448,9 @@ nsContentIterator::Init(nsIDOMRange* aDOMRange)
mLast = GetPrevSibling(endNode);
NS_WARN_IF(!mLast);
- if (NS_WARN_IF(!NodeIsInTraversalRange(mLast, mPre,
- startNode, startIndx,
- endNode, endIndx))) {
+ if (!NodeIsInTraversalRange(mLast, mPre,
+ startNode, startIndx,
+ endNode, endIndx)) {
mLast = nullptr;
}
} else {
@@ -485,7 +485,7 @@ nsContentIterator::Init(nsIDOMRange* aDOMRange)
// If either first or last is null, they both have to be null!
- if (NS_WARN_IF(!mFirst) || NS_WARN_IF(!mLast)) {
+ if (!mFirst || !mLast) {
mFirst = nullptr;
mLast = nullptr;
}
diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp
index 3cda5200b..fb899e43b 100644
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -178,6 +178,7 @@ static uint32_t sForgetSkippableBeforeCC = 0;
static uint32_t sPreviousSuspectedCount = 0;
static uint32_t sCleanupsSinceLastGC = UINT32_MAX;
static bool sNeedsFullCC = false;
+static bool sNeedsFullGC = false;
static bool sNeedsGCAfterCC = false;
static bool sIncrementalCC = false;
static bool sDidPaintAfterPreviousICCSlice = false;
@@ -1312,7 +1313,14 @@ nsJSContext::GarbageCollectNow(JS::gcreason::Reason aReason,
}
JSGCInvocationKind gckind = aShrinking == ShrinkingGC ? GC_SHRINK : GC_NORMAL;
- JS::PrepareForFullGC(sRuntime);
+
+ if (sNeedsFullGC || aReason != JS::gcreason::CC_WAITING) {
+ sNeedsFullGC = false;
+ JS::PrepareForFullGC(sRuntime);
+ } else {
+ CycleCollectedJSRuntime::Get()->PrepareWaitingZonesForGC();
+ }
+
if (aIncremental == IncrementalGC) {
JS::StartIncrementalGC(sRuntime, gckind, aReason, aSliceMillis);
} else {
@@ -2009,6 +2017,8 @@ nsJSContext::RunNextCollectorTimer()
void
nsJSContext::PokeGC(JS::gcreason::Reason aReason, int aDelay)
{
+ sNeedsFullGC = sNeedsFullGC || aReason != JS::gcreason::CC_WAITING;
+
if (sGCTimer || sInterSliceGCTimer || sShuttingDown) {
// There's already a timer for GC'ing, just return
return;
@@ -2279,13 +2289,20 @@ DOMGCSliceCallback(JSRuntime *aRt, JS::GCProgress aProgress, const JS::GCDescrip
} else {
nsJSContext::KillFullGCTimer();
+// bug 1052793
+#if(1)
+ }
+#else
// Avoid shrinking during heavy activity, which is suggested by
// compartment GC. We don't need to shrink after a shrinking GC as this
// happens automatically in this case.
+#endif
if (aDesc.invocationKind_ == GC_NORMAL) {
nsJSContext::PokeShrinkGCBuffers();
}
+#if(0)
}
+#endif
if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
nsCycleCollector_dispatchDeferredDeletion();
@@ -2380,6 +2397,7 @@ mozilla::dom::StartupJSEnvironment()
sLikelyShortLivingObjectsNeedingGC = 0;
sPostGCEventsToConsole = false;
sNeedsFullCC = false;
+ sNeedsFullGC = false;
sNeedsGCAfterCC = false;
gNameSpaceManager = nullptr;
sRuntime = nullptr;
diff --git a/dom/base/nsJSUtils.h b/dom/base/nsJSUtils.h
index c0fc22d61..b662aef29 100644
--- a/dom/base/nsJSUtils.h
+++ b/dom/base/nsJSUtils.h
@@ -205,10 +205,10 @@ public:
return str && init(aContext, str);
}
- bool init(JSContext* aContext, jsid id)
+ bool init(JSContext* aContext, jsid jid)
{
JS::Rooted v(aContext);
- return JS_IdToValue(aContext, id, &v) && init(aContext, v);
+ return JS_IdToValue(aContext, jid, &v) && init(aContext, v);
}
bool init(const JS::Value &v);
diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h
index 7df97fd8e..138b6a26a 100644
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -1783,11 +1783,12 @@ GetCallbackFromCallbackObject(T& aObj)
return GetCallbackFromCallbackObjectHelper::Get(aObj);
}
+// id is a reserved word to our gcc.
static inline bool
-AtomizeAndPinJSString(JSContext* cx, jsid& id, const char* chars)
+AtomizeAndPinJSString(JSContext* cx, jsid& jid, const char* chars)
{
if (JSString *str = ::JS_AtomizeAndPinString(cx, chars)) {
- id = INTERNED_STRING_TO_JSID(cx, str);
+ jid = INTERNED_STRING_TO_JSID(cx, str);
return true;
}
return false;
@@ -3106,26 +3107,26 @@ CreateGlobal(JSContext* aCx, T* aNative, nsWrapperCache* aCache,
*/
class PinnedStringId
{
- jsid id;
+ jsid jid;
public:
- PinnedStringId() : id(JSID_VOID) {}
+ PinnedStringId() : jid(JSID_VOID) {}
bool init(JSContext *cx, const char *string) {
JSString* str = JS_AtomizeAndPinString(cx, string);
if (!str)
return false;
- id = INTERNED_STRING_TO_JSID(cx, str);
+ jid = INTERNED_STRING_TO_JSID(cx, str);
return true;
}
operator const jsid& () {
- return id;
+ return jid;
}
operator JS::Handle () {
/* This is safe because we have pinned the string. */
- return JS::Handle::fromMarkedLocation(&id);
+ return JS::Handle::fromMarkedLocation(&jid);
}
};
diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp
index b97b89e95..5c4632c8a 100644
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -3893,9 +3893,25 @@ void HTMLMediaElement::ChangeReadyState(nsMediaReadyState aState)
UpdateAudioChannelPlayingState();
// Handle raising of "waiting" event during seek (see 4.8.10.9)
+ // or
+ // 4.8.12.7 Ready states:
+ // "If the previous ready state was HAVE_FUTURE_DATA or more, and the new
+ // ready state is HAVE_CURRENT_DATA or less
+ // If the media element was potentially playing before its readyState
+ // attribute changed to a value lower than HAVE_FUTURE_DATA, and the element
+ // has not ended playback, and playback has not stopped due to errors,
+ // paused for user interaction, or paused for in-band content, the user agent
+ // must queue a task to fire a simple event named timeupdate at the element,
+ // and queue a task to fire a simple event named waiting at the element."
+ // (bug 1298594)
if (mPlayingBeforeSeek &&
mReadyState < nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA) {
DispatchAsyncEvent(NS_LITERAL_STRING("waiting"));
+ } else if (oldState >= nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA &&
+ mReadyState < nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA &&
+ !Paused() && !Ended() && !mError) {
+ FireTimeUpdate(false);
+ DispatchAsyncEvent(NS_LITERAL_STRING("waiting"));
}
if (oldState < nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA &&
@@ -3905,9 +3921,12 @@ void HTMLMediaElement::ChangeReadyState(nsMediaReadyState aState)
mLoadedDataFired = true;
}
+// bug 1298594
+#if(0)
if (mReadyState == nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA) {
mWaitingFired = false;
}
+#endif
if (oldState < nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA &&
mReadyState >= nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA) {
diff --git a/dom/html/nsGenericHTMLElement.h b/dom/html/nsGenericHTMLElement.h
index 40b9a4a2b..657588dbc 100644
--- a/dom/html/nsGenericHTMLElement.h
+++ b/dom/html/nsGenericHTMLElement.h
@@ -410,9 +410,9 @@ public:
return SetTokenList(nsGkAtoms::itemtype, aType);
}
NS_IMETHOD GetItemId(nsAString& aId) final override {
- nsString id;
- GetItemId(id);
- aId.Assign(id);
+ nsString hid;
+ GetItemId(hid);
+ aId.Assign(hid);
return NS_OK;
}
NS_IMETHOD SetItemId(const nsAString& aId) final override {
diff --git a/dom/media/MediaData.h b/dom/media/MediaData.h
index 50f674184..ce1040cf2 100644
--- a/dom/media/MediaData.h
+++ b/dom/media/MediaData.h
@@ -303,6 +303,10 @@ public:
bool mSentToCompositor;
+ // True if this video frame is reported as dropped
+ // for missing the compositor deadline. (bug 1299018)
+ bool mIsDropped = false;
+
VideoData(int64_t aOffset,
int64_t aTime,
int64_t aDuration,
diff --git a/dom/media/MediaDecoderStateMachine.cpp b/dom/media/MediaDecoderStateMachine.cpp
index 74b6d57c3..b1d29a521 100644
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -85,13 +85,13 @@ namespace detail {
// trying to decode the video, we'll skip decoding video up to the next
// keyframe. We may increase this value for an individual decoder if we
// encounter video frames which take a long time to decode.
-static const uint32_t LOW_AUDIO_USECS = 300000;
+static const uint32_t LOW_AUDIO_USECS = 1000000;
// If more than this many usecs of decoded audio is queued, we'll hold off
// decoding more audio. If we increase the low audio threshold (see
// LOW_AUDIO_USECS above) we'll also increase this value to ensure it's not
// less than the low audio threshold.
-const int64_t AMPLE_AUDIO_USECS = 1000000;
+const int64_t AMPLE_AUDIO_USECS = 2000000;
} // namespace detail
@@ -105,13 +105,13 @@ const int64_t NO_VIDEO_AMPLE_AUDIO_DIVISOR = 8;
// If we have fewer than LOW_VIDEO_FRAMES decoded frames, and
// we're not "prerolling video", we'll skip the video up to the next keyframe
// which is at or after the current playback position.
-static const uint32_t LOW_VIDEO_FRAMES = 2;
+static const uint32_t LOW_VIDEO_FRAMES = 8;
// Threshold in usecs that used to check if we are low on decoded video.
// If the last video frame's end time |mDecodedVideoEndTime| is more than
// |LOW_VIDEO_THRESHOLD_USECS*mPlaybackRate| after the current clock in
// Advanceframe(), the video decode is lagging, and we skip to next keyframe.
-static const int32_t LOW_VIDEO_THRESHOLD_USECS = 60000;
+static const int32_t LOW_VIDEO_THRESHOLD_USECS = 16000;
// Arbitrary "frame duration" when playing only audio.
static const int AUDIO_DURATION_USECS = 40000;
@@ -128,7 +128,7 @@ namespace detail {
// ourselves to be running low on undecoded data. We determine how much
// undecoded data we have remaining using the reader's GetBuffered()
// implementation.
-static const int64_t LOW_DATA_THRESHOLD_USECS = 5000000;
+static const int64_t LOW_DATA_THRESHOLD_USECS = 20000000;
// LOW_DATA_THRESHOLD_USECS needs to be greater than AMPLE_AUDIO_USECS, otherwise
// the skip-to-keyframe logic can activate when we're running low on data.
@@ -174,8 +174,8 @@ static int64_t DurationToUsecs(TimeDuration aDuration) {
return static_cast(aDuration.ToSeconds() * USECS_PER_S);
}
-static const uint32_t MIN_VIDEO_QUEUE_SIZE = 3;
-static const uint32_t MAX_VIDEO_QUEUE_SIZE = 10;
+static const uint32_t MIN_VIDEO_QUEUE_SIZE = 30;
+static const uint32_t MAX_VIDEO_QUEUE_SIZE = 30;
static const uint32_t VIDEO_QUEUE_SEND_TO_COMPOSITOR_SIZE = 9999;
static uint32_t sVideoQueueDefaultSize = MAX_VIDEO_QUEUE_SIZE;
@@ -456,6 +456,19 @@ void MediaDecoderStateMachine::DiscardStreamData()
}
break;
}
+
+ // TenFourFox. Don't try to push video frames that are already past.
+ // This just wastes time in the compositor.
+ while(true) {
+ const MediaData* v = VideoQueue().PeekFront();
+ FrameStatistics& frameStats = *mFrameStats;
+ if (v && v->mTime < clockTime) {
+ RefPtr releaseMe = VideoQueue().PopFront();
+ frameStats.NotifyDecodedFrames(0, 0, 1); // Frame dropped
+ continue;
+ }
+ break;
+ }
}
bool MediaDecoderStateMachine::HaveEnoughDecodedAudio(int64_t aAmpleAudioUSecs)
@@ -532,6 +545,30 @@ MediaDecoderStateMachine::NeedToSkipToNextKeyframe()
return false;
}
+// TenFourFox. On our slower systems the audio decode thread can run
+// wildly ahead of the video decode thread, which can cause a situation
+// where we get more than one keyframe behind and never catch up. In
+// that situation we should just dump the queued frames if we separate by
+// more than a certain interval because there's no point in displaying
+// them; they'll just hog the compositor which obviously can't keep up.
+// (This situation is mitigated by our extra code in DiscardStreamData(),
+// so this code here mostly runs as an emergency backup.)
+const MediaData* a = AudioQueue().PeekFront();
+const MediaData* v = VideoQueue().PeekFront();
+int64_t videoTime = 0;
+// Use the original AMPLE_AUDIO_USECS, since this is the minimum needed
+// to keep something approaching synchronized and we may well have the
+// audio that far ahead even if we're actually keeping up.
+if (a && v && (a->mTime > ((videoTime = v->mTime) + mozilla::detail::AMPLE_AUDIO_USECS))) {
+ //if (mMediaSink && ((videoTime >> 20) & 1)) mMediaSink->Redraw();
+ //fprintf(stderr, "TenFourFox detected loss of video sync @a=%lld v=%lld; resetting.\n", a->mTime, videoTime);
+ FrameStatistics& frameStats = *mFrameStats;
+ frameStats.NotifyDecodedFrames(0, 0, VideoQueue().GetSize());
+ VideoQueue().Reset();
+ StartBuffering(); // resync, see if we can get more data
+ return true;
+}
+
// We'll skip the video decode to the next keyframe if we're low on
// audio, or if we're low on video, provided we're not running low on
// data to decode. If we're running low on downloaded data to decode,
@@ -873,7 +910,7 @@ MediaDecoderStateMachine::MaybeFinishDecodeFirstFrame()
}
void
-MediaDecoderStateMachine::OnVideoDecoded(MediaData* aVideoSample)
+MediaDecoderStateMachine::OnVideoDecoded(MediaData* aVideoSample, TimeStamp aDecodeStartTime)
{
MOZ_ASSERT(OnTaskQueue());
RefPtr video(aVideoSample);
@@ -917,7 +954,7 @@ MediaDecoderStateMachine::OnVideoDecoded(MediaData* aVideoSample)
if (mReader->IsAsync()) {
return;
}
- TimeDuration decodeTime = TimeStamp::Now() - mVideoDecodeStartTime;
+ TimeDuration decodeTime = TimeStamp::Now() - aDecodeStartTime;
if (!IsDecodingFirstFrame() &&
THRESHOLD_FACTOR * DurationToUsecs(decodeTime) > mLowAudioThresholdUsecs &&
!HasLowUndecodedData())
@@ -1757,24 +1794,31 @@ MediaDecoderStateMachine::RequestVideoData()
// Time the video decode, so that if it's slow, we can increase our low
// audio threshold to reduce the chance of an audio underrun while we're
// waiting for a video decode to complete.
- mVideoDecodeStartTime = TimeStamp::Now();
+ TimeStamp videoDecodeStartTime = TimeStamp::Now();
bool skipToNextKeyFrame = mSentFirstFrameLoadedEvent &&
NeedToSkipToNextKeyframe();
- int64_t currentTime = mState == DECODER_STATE_SEEKING ? 0 : GetMediaTime();
+ int64_t currentTime =
+ mState == DECODER_STATE_SEEKING || !mSentFirstFrameLoadedEvent
+ ? 0 : GetMediaTime() + StartTime();
SAMPLE_LOG("Queueing video task - queued=%i, decoder-queued=%o, skip=%i, time=%lld",
VideoQueue().GetSize(), mReader->SizeOfVideoQueueInFrames(), skipToNextKeyFrame,
currentTime);
+ RefPtr self = this;
if (mSentFirstFrameLoadedEvent) {
mVideoDataRequest.Begin(
InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__,
&MediaDecoderReader::RequestVideoData,
skipToNextKeyFrame, currentTime)
- ->Then(OwnerThread(), __func__, this,
- &MediaDecoderStateMachine::OnVideoDecoded,
- &MediaDecoderStateMachine::OnVideoNotDecoded));
+ ->Then(OwnerThread(), __func__,
+ [self, videoDecodeStartTime] (MediaData* aVideoSample) {
+ self->OnVideoDecoded(aVideoSample, videoDecodeStartTime);
+ },
+ [self] (MediaDecoderReader::NotDecodedReason aReason) {
+ self->OnVideoNotDecoded(aReason);
+ }));
} else {
mVideoDataRequest.Begin(
InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__,
@@ -1784,9 +1828,13 @@ MediaDecoderStateMachine::RequestVideoData()
&StartTimeRendezvous::ProcessFirstSample,
&StartTimeRendezvous::FirstSampleRejected)
->CompletionPromise()
- ->Then(OwnerThread(), __func__, this,
- &MediaDecoderStateMachine::OnVideoDecoded,
- &MediaDecoderStateMachine::OnVideoNotDecoded));
+ ->Then(OwnerThread(), __func__,
+ [self, videoDecodeStartTime] (MediaData* aVideoSample) {
+ self->OnVideoDecoded(aVideoSample, videoDecodeStartTime);
+ },
+ [self] (MediaDecoderReader::NotDecodedReason aReason) {
+ self->OnVideoNotDecoded(aReason);
+ }));
}
}
@@ -2661,6 +2709,13 @@ void MediaDecoderStateMachine::UpdateNextFrameStatus()
if (status != mNextFrameStatus) {
DECODER_LOG("Changed mNextFrameStatus to %s", statusString);
+ // bug 1298594
+ if(status == MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING ||
+ status == MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE) {
+ // Ensure currentTime is up to date prior updating mNextFrameStatus so that
+ // the MediaDecoderOwner fire events at correct currentTime.
+ UpdatePlaybackPositionPeriodically();
+ }
}
mNextFrameStatus = status;
diff --git a/dom/media/MediaDecoderStateMachine.h b/dom/media/MediaDecoderStateMachine.h
index e677c73a4..c671a1c27 100644
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -378,7 +378,7 @@ private:
// TODO: Those callback function may receive demuxed-only data.
// Need to figure out a suitable API name for this case.
void OnAudioDecoded(MediaData* aAudioSample);
- void OnVideoDecoded(MediaData* aVideoSample);
+ void OnVideoDecoded(MediaData* aVideoSample, TimeStamp aDecodeStartTime);
void OnNotDecoded(MediaData::Type aType, MediaDecoderReader::NotDecodedReason aReason);
void OnAudioNotDecoded(MediaDecoderReader::NotDecodedReason aReason)
{
@@ -813,11 +813,6 @@ private:
bool HaveStartTime() { return mStartTimeRendezvous && mStartTimeRendezvous->HaveStartTime(); }
int64_t StartTime() { return mStartTimeRendezvous->StartTime(); }
- // Time at which the last video sample was requested. If it takes too long
- // before the sample arrives, we will increase the amount of audio we buffer.
- // This is necessary for legacy synchronous decoders to prevent underruns.
- TimeStamp mVideoDecodeStartTime;
-
// Queue of audio frames. This queue is threadsafe, and is accessed from
// the audio, decoder, state machine, and main threads.
MediaQueue mAudioQueue;
diff --git a/dom/media/mediasink/VideoSink.cpp b/dom/media/mediasink/VideoSink.cpp
index 5f39decb8..c26ff7ea2 100644
--- a/dom/media/mediasink/VideoSink.cpp
+++ b/dom/media/mediasink/VideoSink.cpp
@@ -290,6 +290,11 @@ VideoSink::RenderVideoFrames(int32_t aMaxFrames,
VideoData* frame = frames[i]->As();
frame->mSentToCompositor = true;
+ // This frame is behind the current time. Let's report it as dropped.
+ // bug 1299018
+ if (aClockTime >= frame->GetEndTime()) {
+ frame->mIsDropped = true;
+ }
if (!frame->mImage || !frame->mImage->IsValid()) {
continue;
@@ -342,6 +347,7 @@ VideoSink::UpdateRenderedVideoFrames()
// the current frame.
NS_ASSERTION(clockTime >= 0, "Should have positive clock time.");
+#if(0)
int64_t remainingTime = -1;
if (VideoQueue().GetSize() > 0) {
RefPtr currentFrame = VideoQueue().PopFront();
@@ -353,7 +359,8 @@ VideoSink::UpdateRenderedVideoFrames()
break;
}
++framesRemoved;
- if (!currentFrame->As()->mSentToCompositor) {
+ if (!currentFrame->As()->mSentToCompositor ||
+ currentFrame->As()->mIsDropped /* bug 1299018 */) {
mFrameStats.NotifyDecodedFrames(0, 0, 1);
VSINK_LOG_V("discarding video frame mTime=%lld clock_time=%lld",
currentFrame->mTime, clockTime);
@@ -378,6 +385,45 @@ VideoSink::UpdateRenderedVideoFrames()
TimeStamp target = nowTime + TimeDuration::FromMicroseconds(
remainingTime / mAudioSink->GetPlaybackParams().mPlaybackRate);
+#else
+ // bug 1258870
+ // Skip frames up to the playback position.
+ int64_t lastDisplayedFrameEndTime = 0;
+ while (VideoQueue().GetSize() > 0 &&
+ clockTime >= VideoQueue().PeekFront()->GetEndTime()) { // bug 1298594
+ RefPtr frame = VideoQueue().PopFront();
+ if ( frame->As()->mSentToCompositor &&
+ !frame->As()->mIsDropped /* bug 1299018 */) {
+ lastDisplayedFrameEndTime = frame->GetEndTime();
+ mFrameStats.NotifyPresentedFrame();
+ } else {
+ mFrameStats.NotifyDecodedFrames(0, 0, 1);
+ VSINK_LOG_V("discarding video frame mTime=%lld clock_time=%lld",
+ frame->mTime, clockTime);
+ }
+ }
+
+ // The presentation end time of the last video frame displayed is either
+ // the end time of the current frame, or if we dropped all frames in the
+ // queue, the end time of the last frame we removed from the queue.
+ RefPtr currentFrame = VideoQueue().PeekFront();
+ mVideoFrameEndTime = currentFrame ? currentFrame->GetEndTime() : lastDisplayedFrameEndTime;
+
+ RenderVideoFrames(mVideoQueueSendToCompositorSize, clockTime, nowTime);
+
+ // Get the timestamp of the next frame. Schedule the next update at
+ // the start time of the next frame. If we don't have a next frame,
+ // we will run render loops again upon incoming frames.
+ nsTArray> frames;
+ VideoQueue().GetFirstElements(2, &frames);
+ if (frames.Length() < 2) {
+ return;
+ }
+
+ int64_t nextFrameTime = frames[1]->mTime;
+ TimeStamp target = nowTime + TimeDuration::FromMicroseconds((nextFrameTime - clockTime) / mAudioSink->GetPlaybackParams().mPlaybackRate);
+#endif
+
RefPtr self = this;
mUpdateScheduler.Ensure(target, [self] () {
self->UpdateRenderedVideoFramesByTimer();
diff --git a/dom/media/mediasource/TrackBuffersManager.cpp b/dom/media/mediasource/TrackBuffersManager.cpp
index 40f788d31..2f36772d7 100644
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -1232,6 +1232,9 @@ TrackBuffersManager::CompleteCodedFrameProcessing()
if (HasVideo()) {
const auto& track = mVideoTracks.mBuffers.LastElement();
MOZ_ASSERT(track.IsEmpty() || track[0]->mKeyframe);
+fprintf(stderr, "track %i timecode %lld /// track %i timecode %lld\n",
+ track.Length()-2, track[(track.Length()-2)]->mTimecode,
+ track.Length()-1, track[track.Length()-1]->mTimecode);
for (uint32_t i = 1; i < track.Length(); i++) {
MOZ_ASSERT((track[i-1]->mTrackInfo->GetID() == track[i]->mTrackInfo->GetID() && track[i-1]->mTimecode <= track[i]->mTimecode) ||
track[i]->mKeyframe);
@@ -1610,7 +1613,17 @@ TrackBuffersManager::InsertFrames(TrackBuffer& aSamples,
intersection.Intersection(aIntervals);
if (intersection.Length()) {
- RemoveFrames(aIntervals, trackBuffer, trackBuffer.mNextInsertionIndex.refOr(0));
+ if (aSamples[0]->mKeyframe) {
+ // We are starting a new GOP, we do not have to worry about breaking an
+ // existing current coded frame group. Reset the next insertion index
+ // so the search for when to start our frames removal can be exhaustive.
+ trackBuffer.mNextInsertionIndex.reset();
+ }
+ size_t index =
+ RemoveFrames(aIntervals, trackBuffer, trackBuffer.mNextInsertionIndex.refOr(0));
+ if (index) {
+ trackBuffer.mNextInsertionIndex = Some(index);
+ }
}
// 16. Add the coded frame with the presentation timestamp, decode timestamp, and frame duration to the track buffer.
@@ -1645,7 +1658,7 @@ TrackBuffersManager::InsertFrames(TrackBuffer& aSamples,
trackBuffer.mSanitizedBufferedRanges += range;
}
-void
+size_t
TrackBuffersManager::RemoveFrames(const TimeIntervals& aIntervals,
TrackData& aTrackData,
uint32_t aStartIndex)
@@ -1664,7 +1677,7 @@ TrackBuffersManager::RemoveFrames(const TimeIntervals& aIntervals,
// If highest end timestamp for track buffer is set and less than or equal to presentation timestamp:
// Remove all coded frames from track buffer that have a presentation timestamp greater than or equal to highest end timestamp and less than frame end timestamp"
for (uint32_t i = aStartIndex; i < data.Length(); i++) {
- MediaRawData* sample = data[i].get();
+ const RefPtr sample = data[i];
TimeInterval sampleInterval =
TimeInterval(TimeUnit::FromMicroseconds(sample->mTime),
TimeUnit::FromMicroseconds(sample->GetEndTime()));
@@ -1677,13 +1690,13 @@ TrackBuffersManager::RemoveFrames(const TimeIntervals& aIntervals,
}
if (firstRemovedIndex.isNothing()) {
- return;
+ return 0;
}
// Remove decoding dependencies of the coded frames removed in the previous step:
// Remove all coded frames between the coded frames removed in the previous step and the next random access point after those removed frames.
for (uint32_t i = lastRemovedIndex + 1; i < data.Length(); i++) {
- MediaRawData* sample = data[i].get();
+ const RefPtr& sample = data[i];
if (sample->mKeyframe) {
break;
}
@@ -1693,7 +1706,7 @@ TrackBuffersManager::RemoveFrames(const TimeIntervals& aIntervals,
int64_t maxSampleDuration = 0;
TimeIntervals removedIntervals;
for (uint32_t i = firstRemovedIndex.ref(); i <= lastRemovedIndex; i++) {
- MediaRawData* sample = data[i].get();
+ const RefPtr sample = data[i];
TimeInterval sampleInterval =
TimeInterval(TimeUnit::FromMicroseconds(sample->mTime),
TimeUnit::FromMicroseconds(sample->GetEndTime()));
@@ -1741,6 +1754,8 @@ TrackBuffersManager::RemoveFrames(const TimeIntervals& aIntervals,
data.RemoveElementsAt(firstRemovedIndex.ref(),
lastRemovedIndex - firstRemovedIndex.ref() + 1);
+
+ return firstRemovedIndex.ref();
}
void
diff --git a/dom/media/mediasource/TrackBuffersManager.h b/dom/media/mediasource/TrackBuffersManager.h
index 977e90c91..406e1697c 100644
--- a/dom/media/mediasource/TrackBuffersManager.h
+++ b/dom/media/mediasource/TrackBuffersManager.h
@@ -291,9 +291,12 @@ private:
void InsertFrames(TrackBuffer& aSamples,
const media::TimeIntervals& aIntervals,
TrackData& aTrackData);
- void RemoveFrames(const media::TimeIntervals& aIntervals,
- TrackData& aTrackData,
- uint32_t aStartIndex);
+ // Remove all frames and their dependencies contained in aIntervals.
+ // Return the index at which frames were first removed or 0 if no frames
+ // removed.
+ size_t RemoveFrames(const media::TimeIntervals& aIntervals,
+ TrackData& aTrackData,
+ uint32_t aStartIndex);
// Find index of sample. Return a negative value if not found.
uint32_t FindSampleIndex(const TrackBuffer& aTrackBuffer,
const media::TimeInterval& aInterval);
diff --git a/dom/smil/nsSMILAnimationFunction.cpp b/dom/smil/nsSMILAnimationFunction.cpp
index 276a85f48..9c378e1dd 100644
--- a/dom/smil/nsSMILAnimationFunction.cpp
+++ b/dom/smil/nsSMILAnimationFunction.cpp
@@ -424,8 +424,13 @@ nsSMILAnimationFunction::InterpolateResult(const nsSMILValueArray& aValues,
if (NS_SUCCEEDED(rv)) {
MOZ_ASSERT(from, "NULL from-value during interpolation");
MOZ_ASSERT(to, "NULL to-value during interpolation");
+#if(0)
+// Bogus assertion that keeps firing. intervalProgress is always within
+// this range even when it does fire, usually some critically small value
+// like -6.1679056923618572e-18.
MOZ_ASSERT(0.0f <= intervalProgress && intervalProgress < 1.0f,
"Interval progress should be in the range [0, 1)");
+#endif
rv = from->Interpolate(*to, intervalProgress, aResult);
}
}
diff --git a/dom/smil/nsSMILTimeContainer.cpp b/dom/smil/nsSMILTimeContainer.cpp
index 96c772ab9..2ea6d4304 100644
--- a/dom/smil/nsSMILTimeContainer.cpp
+++ b/dom/smil/nsSMILTimeContainer.cpp
@@ -317,6 +317,8 @@ nsSMILTimeContainer::NotifyTimeChange()
// milestone elements. This is because any timed element with dependents and
// with significant transitions yet to fire should have their next milestone
// registered. Other timed elements don't matter.
+// bug 1321357
+#if(0)
AutoRestore saveHolding(mHoldingEntries);
mHoldingEntries = true;
const MilestoneEntry* p = mMilestoneEntries.Elements();
@@ -331,4 +333,24 @@ nsSMILTimeContainer::NotifyTimeChange()
"queue of milestones");
++p;
}
+#else
+ // Copy the timed elements to a separate array before calling
+ // HandleContainerTimeChange on each of them in case doing so mutates
+ // mMilestoneEntries.
+ nsTArray> elems;
+
+ {
+ AutoRestore saveHolding(mHoldingEntries);
+ mHoldingEntries = true;
+ for (const MilestoneEntry* p = mMilestoneEntries.Elements();
+ p < mMilestoneEntries.Elements() + mMilestoneEntries.Length();
+ ++p) {
+ elems.AppendElement(p->mTimebase.get());
+ }
+ }
+
+ for (auto& elem : elems) {
+ elem->TimedElement().HandleContainerTimeChange();
+ }
+#endif
}
diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp
index dd81ad415..85201cd5c 100644
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -94,7 +94,8 @@ using mozilla::dom::indexedDB::IndexedDatabaseManager;
#define WORKER_DEFAULT_ALLOCATION_THRESHOLD 30
// Half the size of the actual C stack, to be safe.
-#define WORKER_CONTEXT_NATIVE_STACK_LIMIT 128 * sizeof(size_t) * 1024
+// We need a little larger than regular Firefox.
+#define WORKER_CONTEXT_NATIVE_STACK_LIMIT 512 * sizeof(size_t) * 1024
// The maximum number of threads to use for workers, overridable via pref.
#define MAX_WORKERS_PER_DOMAIN 10
diff --git a/dom/xul/nsXULPrototypeCache.cpp b/dom/xul/nsXULPrototypeCache.cpp
index b4867d51b..7e03e3c9f 100644
--- a/dom/xul/nsXULPrototypeCache.cpp
+++ b/dom/xul/nsXULPrototypeCache.cpp
@@ -391,7 +391,7 @@ nsXULPrototypeCache::GetOutputStream(nsIURI* uri, nsIObjectOutputStream** stream
nsCOMPtr storageStream;
bool found = mOutputStreamTable.Get(uri, getter_AddRefs(storageStream));
if (found) {
- objectOutput = do_CreateInstance("mozilla.org/binaryoutputstream;1");
+ objectOutput = do_CreateInstance("@mozilla.org/binaryoutputstream;1");
if (!objectOutput) return NS_ERROR_OUT_OF_MEMORY;
nsCOMPtr outputStream
= do_QueryInterface(storageStream);
diff --git a/gfx/2d/FilterProcessing.h b/gfx/2d/FilterProcessing.h
index 802d791a0..347375420 100644
--- a/gfx/2d/FilterProcessing.h
+++ b/gfx/2d/FilterProcessing.h
@@ -12,10 +12,17 @@
namespace mozilla {
namespace gfx {
+#if defined WORDS_BIGENDIAN || defined IS_BIG_ENDIAN || defined __BIG_ENDIAN__
+const ptrdiff_t B8G8R8A8_COMPONENT_BYTEOFFSET_B = 3;
+const ptrdiff_t B8G8R8A8_COMPONENT_BYTEOFFSET_G = 2;
+const ptrdiff_t B8G8R8A8_COMPONENT_BYTEOFFSET_R = 1;
+const ptrdiff_t B8G8R8A8_COMPONENT_BYTEOFFSET_A = 0;
+#else
const ptrdiff_t B8G8R8A8_COMPONENT_BYTEOFFSET_B = 0;
const ptrdiff_t B8G8R8A8_COMPONENT_BYTEOFFSET_G = 1;
const ptrdiff_t B8G8R8A8_COMPONENT_BYTEOFFSET_R = 2;
const ptrdiff_t B8G8R8A8_COMPONENT_BYTEOFFSET_A = 3;
+#endif
class FilterProcessing
{
diff --git a/gfx/harfbuzz/src/hb-blob.cc b/gfx/harfbuzz/src/hb-blob.cc
index a6870dc06..b96b3158c 100644
--- a/gfx/harfbuzz/src/hb-blob.cc
+++ b/gfx/harfbuzz/src/hb-blob.cc
@@ -328,6 +328,7 @@ hb_blob_is_immutable (hb_blob_t *blob)
unsigned int
hb_blob_get_length (hb_blob_t *blob)
{
+if (!blob) return 0; // wallpaper TenFourFox issue 309
return blob->length;
}
diff --git a/gfx/src/nsRegion.cpp b/gfx/src/nsRegion.cpp
index 8f1834409..e42193c78 100644
--- a/gfx/src/nsRegion.cpp
+++ b/gfx/src/nsRegion.cpp
@@ -743,6 +743,8 @@ nsIntRegion nsRegion::ScaleToNearestPixels (float aScaleX, float aScaleY,
nsIntRegion nsRegion::ScaleToOutsidePixels (float aScaleX, float aScaleY,
nscoord aAppUnitsPerPixel) const
{
+// bug 1247979
+#if(0)
nsIntRegion result;
nsRegionRectIterator rgnIter(*this);
const nsRect* currentRect;
@@ -752,6 +754,27 @@ nsIntRegion nsRegion::ScaleToOutsidePixels (float aScaleX, float aScaleY,
result.Or(result, deviceRect);
}
return result;
+#else
+ // make a copy of the region so that we can mutate it inplace
+ nsRegion region = *this;
+ int n;
+ pixman_box32_t *boxes = pixman_region32_rectangles(®ion.mImpl, &n);
+ boxes = pixman_region32_rectangles(®ion.mImpl, &n);
+ for (int i=0; i(output.forget());
- } else {
+ if (!otsContext.Process(&output, aData, aLength)) {
+ // Failed to decode/sanitize the font, so discard it.
aSaneLength = 0;
return nullptr;
}
+
+ aSaneLength = output.Tell();
+ return static_cast(output.forget());
}
void
@@ -376,6 +379,47 @@ CopyWOFFMetadata(const uint8_t* aFontData,
*aMetaOrigLen = woff->metaOrigLen;
}
+// TenFourFox issue 311: check for bad data: fonts.
+static nsresult
+HashDataFontURL(nsIURI *aFontURI)
+{
+ nsCString spec;
+ nsresult rv = aFontURI->GetAsciiSpec(spec);
+ if (NS_FAILED(rv) || spec.Length() < 8)
+ return NS_ERROR_FAILURE; // can't be valid
+
+ // Hash the data URL.
+ nsAutoCString fullHash;
+ nsCOMPtr crypto = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = crypto->Init(nsICryptoHash::SHA1);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = crypto->Update(reinterpret_cast(spec.BeginReading()),
+ spec.Length());
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = crypto->Finish(true, fullHash);
+ if (NS_SUCCEEDED(rv)) {
+#if(0)
+ fprintf(stderr, "URL: %s -> hash: %s\n", spec.get(), fullHash.get());
+#endif
+ if (0 ||
+ fullHash.Equals("7bEdowSouswJQEDqXZ1HyJ/nAVU=") ||
+ fullHash.Equals("5L3jz1VLjOoygPOTw9UX/2M+Wh4=") ||
+ fullHash.Equals("ViO/mt90XnXJm7kgYJNIa4w2u7M=") ||
+ fullHash.Equals("s3Hh0b18aP3UvzzEIuczok5AKOQ=") ||
+ fullHash.Equals("bw95WsPe1C3i6ywQkwLd7tZUVT4=") ||
+ fullHash.Equals("YFKGFitMqlw1z0z+kITI2oiGjUM=") ||
+ fullHash.Equals("d0fwa3E00upl5Vj+C8K+NFhfiNQ=") ||
+ 0) {
+ fprintf(stderr, "Warning: TenFourFox blocking ATSUI-incompatible data: font (with SHA-1 URL hash of %s).\n", fullHash.get());
+ return NS_ERROR_FAILURE; // bad font
+ }
+ return NS_OK;
+ }
+ fprintf(stderr, "hash failed: %08x\n", (uint32_t)rv);
+ return rv;
+}
+
void
gfxUserFontEntry::LoadNextSrc()
{
@@ -494,6 +538,8 @@ gfxUserFontEntry::LoadNextSrc()
uint32_t bufferLength = 0;
// sync load font immediately
+ rv = HashDataFontURL(currSrc.mURI);
+ if (NS_SUCCEEDED(rv))
rv = mFontSet->SyncLoadFontData(this, &currSrc, buffer,
bufferLength);
@@ -682,6 +728,7 @@ gfxUserFontEntry::LoadPlatformFont(const uint8_t* aFontData, uint32_t& aLength)
NS_ConvertUTF16toUTF8(mFamilyName).get(),
this, uint32_t(mFontSet->mGeneration), fontCompressionRatio));
}
+ fe->AddRef(); // BADFIX???
mPlatformFontEntry = fe;
SetLoadState(STATUS_LOADED);
gfxUserFontSet::UserFontCache::CacheFont(fe);
@@ -775,6 +822,7 @@ gfxUserFontEntry::GetUserFontSets(nsTArray& aResult)
gfxUserFontSet::gfxUserFontSet()
: mFontFamilies(4),
mLocalRulesUsed(false),
+ mRebuildLocalRules(false),
mDownloadCount(0),
mDownloadSize(0)
{
@@ -944,6 +992,7 @@ void
gfxUserFontSet::RebuildLocalRules()
{
if (mLocalRulesUsed) {
+ mRebuildLocalRules = true;
DoRebuildUserFontSet();
}
}
diff --git a/gfx/thebes/gfxUserFontSet.h b/gfx/thebes/gfxUserFontSet.h
index 8f6a5fcb9..081df4f2c 100644
--- a/gfx/thebes/gfxUserFontSet.h
+++ b/gfx/thebes/gfxUserFontSet.h
@@ -523,6 +523,9 @@ protected:
// true when local names have been looked up, false otherwise
bool mLocalRulesUsed;
+ // true when rules using local names need to be redone
+ bool mRebuildLocalRules;
+
// performance stats
uint32_t mDownloadCount;
uint64_t mDownloadSize;
diff --git a/intl/uconv/util/nsUnicodeDecodeHelper.cpp b/intl/uconv/util/nsUnicodeDecodeHelper.cpp
index 9d3491d86..57bd2952e 100644
--- a/intl/uconv/util/nsUnicodeDecodeHelper.cpp
+++ b/intl/uconv/util/nsUnicodeDecodeHelper.cpp
@@ -31,7 +31,7 @@ nsresult nsUnicodeDecodeHelper::ConvertByTable(
while ((srcLen > 0) && (dest < destEnd)) {
bool charFound;
if (aScanClass == uMultibytesCharset) {
- NS_ASSERTION(aShiftInTable, "shift table missing");
+ //NS_ASSERTION(aShiftInTable, "shift table missing");
charFound = uScanShift(aShiftInTable, nullptr, (uint8_t *)src,
reinterpret_cast(&med), srcLen,
(uint32_t *)&bcr);
diff --git a/ipc/chromium/src/base/id_map.h b/ipc/chromium/src/base/id_map.h
index 3b7430766..884925b10 100644
--- a/ipc/chromium/src/base/id_map.h
+++ b/ipc/chromium/src/base/id_map.h
@@ -54,13 +54,13 @@ class IDMap {
// the list. The caller either must generate all unique IDs itself and use
// this function, or allow this object to generate IDs and call Add. These
// two methods may not be mixed, or duplicate IDs may be generated
- void AddWithID(T* data, int32_t id) {
- DCHECK(data_.find(id) == data_.end()) << "Inserting duplicate item";
- data_[id] = data;
+ void AddWithID(T* data, int32_t cid) {
+ DCHECK(data_.find(cid) == data_.end()) << "Inserting duplicate item";
+ data_[cid] = data;
}
- void Remove(int32_t id) {
- iterator i = data_.find(id);
+ void Remove(int32_t cid) {
+ iterator i = data_.find(cid);
if (i == data_.end()) {
NOTREACHED() << "Attempting to remove an item not in the list";
return;
@@ -84,8 +84,8 @@ class IDMap {
return false;
}
- T* Lookup(int32_t id) const {
- const_iterator i = data_.find(id);
+ T* Lookup(int32_t cid) const {
+ const_iterator i = data_.find(cid);
if (i == data_.end())
return NULL;
return i->second;
diff --git a/ipc/chromium/src/third_party/libevent/event.c b/ipc/chromium/src/third_party/libevent/event.c
index a979f1f26..8352a8b74 100644
--- a/ipc/chromium/src/third_party/libevent/event.c
+++ b/ipc/chromium/src/third_party/libevent/event.c
@@ -2551,7 +2551,14 @@ insert_common_timeout_inorder(struct common_timeout_list *ctl,
* there's some wacky threading issue going on, we do a search from
* the end of 'ev' to find the right insertion point.
*/
- TAILQ_FOREACH_REVERSE(e, &ctl->events,
+
+// 10.4's queue.h is wrong here.
+#define SM_TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for((var) = TAILQ_LAST(head, headname); \
+ (var) != NULL ; \
+ (var) = TAILQ_PREV(var, headname, field))
+
+ SM_TAILQ_FOREACH_REVERSE(e, &ctl->events,
event_list, ev_timeout_pos.ev_next_with_common_timeout) {
/* This timercmp is a little sneaky, since both ev and e have
* magic values in tv_usec. Fortunately, they ought to have
diff --git a/ipc/chromium/src/third_party/libevent/kqueue.c b/ipc/chromium/src/third_party/libevent/kqueue.c
index e5a530f80..8b8547ea9 100644
--- a/ipc/chromium/src/third_party/libevent/kqueue.c
+++ b/ipc/chromium/src/third_party/libevent/kqueue.c
@@ -137,6 +137,25 @@ kq_init(struct event_base *base)
goto err;
kqueueop->events_size = kqueueop->changes_size = NEVENT;
+ /* Check for Mac OS X kqueue bug. */
+ /* This reverts M1304266 and Chromium bug 626534. */
+ memset(&kqueueop->changes[0], 0, sizeof kqueueop->changes[0]);
+ kqueueop->changes[0].ident = -1;
+ kqueueop->changes[0].filter = EVFILT_READ;
+ kqueueop->changes[0].flags = EV_ADD;
+ /*
+ * If kqueue works, then kevent will succeed, and it will
+ * stick an error in events[0]. If kqueue is broken, then
+ * kevent will fail.
+ */
+ if (kevent(kq,
+ kqueueop->changes, 1, kqueueop->events, NEVENT, NULL) != 1 ||
+ (int)kqueueop->events[0].ident != -1 ||
+ kqueueop->events[0].flags != EV_ERROR) {
+ event_warn("%s: detected broken kqueue; not using.", __func__);
+ goto err;
+ }
+
base->evsigsel = &kqsigops;
return (kqueueop);
diff --git a/ipc/testshell/XPCShellEnvironment.cpp b/ipc/testshell/XPCShellEnvironment.cpp
index 221bb3fb0..f8e9af724 100644
--- a/ipc/testshell/XPCShellEnvironment.cpp
+++ b/ipc/testshell/XPCShellEnvironment.cpp
@@ -71,7 +71,7 @@ private:
};
inline XPCShellEnvironment*
-Environment(Handle global)
+Environment(JS::Handle global)
{
AutoSafeJSContext cx;
JSAutoCompartment ac(cx, global);
diff --git a/js/public/TrackedOptimizationInfo.h b/js/public/TrackedOptimizationInfo.h
index 642f4d773..ee9931278 100644
--- a/js/public/TrackedOptimizationInfo.h
+++ b/js/public/TrackedOptimizationInfo.h
@@ -144,6 +144,7 @@ namespace JS {
\
_(ICNameStub_ReadSlot) \
_(ICNameStub_CallGetter) \
+ _(ICNameStub_TypeOfNoProperty) \
\
_(CantInlineGeneric) \
_(CantInlineNoTarget) \
diff --git a/js/src/doc/JITOptimizations/Outcomes.md b/js/src/doc/JITOptimizations/Outcomes.md
index c0e1b2e25..e83227ae4 100644
--- a/js/src/doc/JITOptimizations/Outcomes.md
+++ b/js/src/doc/JITOptimizations/Outcomes.md
@@ -412,6 +412,11 @@ a scope chain object.
An inline cache element which loads a bare variable name by calling a
getter function on the global object.
+### ICNameStub_TypeOfNoProperty
+
+An inline cache element which loads undefined for the type
+of a missing property.
+
## Call Inlining Outcomes
Optimization outcomes of attempts to inline function calls.
diff --git a/js/src/gc/Nursery.cpp b/js/src/gc/Nursery.cpp
index 4d7ba1217..7e002f916 100644
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -102,6 +102,7 @@ js::Nursery::init(uint32_t maxNurseryBytes)
setCurrentChunk(0);
updateDecommittedRegion();
+#if DEBUG
char* env = getenv("JS_GC_PROFILE_NURSERY");
if (env) {
if (0 == strcmp(env, "help")) {
@@ -112,6 +113,7 @@ js::Nursery::init(uint32_t maxNurseryBytes)
enableProfiling_ = true;
profileThreshold_ = atoi(env);
}
+#endif
MOZ_ASSERT(isEnabled());
return true;
@@ -407,9 +409,15 @@ js::TenuringTracer::TenuringTracer(JSRuntime* rt, Nursery* nursery)
{
}
+#if DEBUG
#define TIME_START(name) int64_t timestampStart_##name = enableProfiling_ ? PRMJ_Now() : 0
#define TIME_END(name) int64_t timestampEnd_##name = enableProfiling_ ? PRMJ_Now() : 0
#define TIME_TOTAL(name) (timestampEnd_##name - timestampStart_##name)
+#else
+#define TIME_START(name)
+#define TIME_END(name)
+#define TIME_TOTAL(name)
+#endif
void
js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason, ObjectGroupList* pretenureGroups)
@@ -437,7 +445,9 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason, ObjectGroupList
TraceMinorGCStart();
+#if DEBUG
int64_t timestampStart_total = PRMJ_Now();
+#endif
AutoTraceSession session(rt, JS::HeapState::MinorCollecting);
AutoStopVerifyingBarriers av(rt, false);
@@ -568,6 +578,9 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason, ObjectGroupList
if (rt->gc.usage.gcBytes() >= rt->gc.tunables.gcMaxBytes())
disable();
+#ifndef DEBUG
+ TraceMinorGCEnd();
+#else
int64_t totalTime = PRMJ_Now() - timestampStart_total;
rt->addTelemetry(JS_TELEMETRY_GC_MINOR_US, totalTime);
rt->addTelemetry(JS_TELEMETRY_GC_MINOR_REASON, reason);
@@ -618,6 +631,7 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason, ObjectGroupList
fprintf(stderr, "\n");
#undef FMT
}
+#endif
}
#undef TIME_START
diff --git a/js/src/gc/Statistics.cpp b/js/src/gc/Statistics.cpp
index 0b8cd6af7..1126a5761 100644
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -911,6 +911,7 @@ Statistics::endGC()
for (int i = 0; i < PHASE_LIMIT; i++)
phaseTotals[j][i] += phaseTimes[j][i];
+#if DEBUG
int64_t total, longest;
gcDuration(&total, &longest);
@@ -938,6 +939,7 @@ Statistics::endGC()
if (fp)
printStats();
+#endif
// Clear the timers at the end of a GC because we accumulate time in
// between GCs for some (which come before PHASE_GC_BEGIN in the list.)
@@ -968,7 +970,7 @@ Statistics::beginSlice(const ZoneGCStats& zoneStats, JSGCInvocationKind gckind,
return;
}
- runtime->addTelemetry(JS_TELEMETRY_GC_REASON, reason);
+ //runtime->addTelemetry(JS_TELEMETRY_GC_REASON, reason);
// Slice callbacks should only fire for the outermost level.
if (gcDepth == 1) {
@@ -987,6 +989,7 @@ Statistics::endSlice()
slices.back().endTimestamp = JS_GetCurrentEmbedderTime();
slices.back().endFaults = GetPageFaultCount();
+#if DEBUG
int64_t sliceTime = slices.back().end - slices.back().start;
runtime->addTelemetry(JS_TELEMETRY_GC_SLICE_MS, t(sliceTime));
runtime->addTelemetry(JS_TELEMETRY_GC_RESET, !!slices.back().resetReason);
@@ -1003,6 +1006,7 @@ Statistics::endSlice()
runtime->addTelemetry(JS_TELEMETRY_GC_SLOW_PHASE, phases[longest].telemetryBucket);
}
}
+#endif
}
bool last = !runtime->gc.isIncrementalGCInProgress();
diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp
index 8a033347b..79482681a 100644
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -91,8 +91,19 @@ BaselineCompiler::compile()
AutoTraceLog logScript(logger, scriptEvent);
AutoTraceLog logCompile(logger, TraceLogger_BaselineCompilation);
+#if(0)
if (!script->ensureHasTypes(cx) || !script->ensureHasAnalyzedArgsUsage(cx))
return Method_Error;
+#else
+ // This makes a non-trivial difference in Baseline-heavy code.
+ if (!script->ensureHasTypes(cx))
+ return Method_Error;
+
+ if (script->argumentsHasVarBinding()) {
+ if (!script->ensureHasAnalyzedArgsUsage(cx))
+ return Method_Error;
+ }
+#endif
// When a Debugger set the collectCoverageInfo flag, we recompile baseline
// scripts without entering the interpreter again. We have to create the
diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp
index 9e70e2448..5157da785 100644
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -8703,6 +8703,7 @@ IonBuilder::pushScalarLoadFromTypedObject(MDefinition* obj,
elemType,
DoesNotRequireMemoryBarrier,
adjustment);
+ load->setTarget(MLoadUnboxedScalar::TypedArrayTarget);
current->add(load);
current->push(load);
@@ -9549,6 +9550,7 @@ IonBuilder::jsop_getelem_typed(MDefinition* obj, MDefinition* index,
// Load the element.
MLoadUnboxedScalar* load = MLoadUnboxedScalar::New(alloc(), elements, index, arrayType);
+ load->setTarget(MLoadUnboxedScalar::TypedArrayTarget);
current->add(load);
current->push(load);
@@ -10094,6 +10096,7 @@ IonBuilder::jsop_setelem_typed(Scalar::Type arrayType,
MStoreUnboxedScalar* store =
MStoreUnboxedScalar::New(alloc(), elements, id, toWrite, arrayType,
MStoreUnboxedScalar::TruncateInput);
+ store->setTarget(MStoreUnboxedScalar::TypedArrayTarget);
ins = store;
}
diff --git a/js/src/jit/IonCaches.cpp b/js/src/jit/IonCaches.cpp
index 56437abf6..3db31dc49 100644
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -4103,7 +4103,7 @@ GenerateGetTypedOrUnboxedArrayElement(JSContext* cx, MacroAssembler& masm,
if (IsAnyTypedArray(array)) {
// Guard on the initialized length.
- Address length(object, TypedArrayObject::lengthOffset());
+ Address length(object, TypedArrayObject::lengthOffset() + NUNBOX32_PAYLOAD_OFFSET);
masm.branch32(Assembler::BelowOrEqual, length, indexReg, &failures);
// Save the object register on the stack in case of failure.
@@ -4927,6 +4927,62 @@ IsCacheableNameCallGetter(HandleObject scopeChain, HandleObject obj, HandleObjec
IsCacheableGetPropCallScripted(obj, holder, shape);
}
+bool
+NameIC::attachTypeOfNoProperty(JSContext* cx, HandleScript outerScript, IonScript* ion,
+ HandleObject scopeChain)
+{
+ MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
+ Label failures;
+ StubAttacher attacher(*this);
+
+ Register scratchReg = outputReg().valueReg().scratchReg();
+
+ masm.movePtr(scopeChainReg(), scratchReg);
+
+ // Generate scope chain guards.
+ // Since the property was not defined on any object, iterate until reaching the global.
+ JSObject* tobj = scopeChain;
+ while (true) {
+ GenerateScopeChainGuard(masm, tobj, scratchReg, nullptr, &failures);
+
+ if (tobj->is())
+ break;
+
+ // Load the next link.
+ tobj = &tobj->as().enclosingScope();
+ masm.extractObject(Address(scratchReg, ScopeObject::offsetOfEnclosingScope()), scratchReg);
+ }
+
+ masm.moveValue(UndefinedValue(), outputReg().valueReg());
+ attacher.jumpRejoin(masm);
+
+ masm.bind(&failures);
+ attacher.jumpNextStub(masm);
+
+ return linkAndAttachStub(cx, masm, attacher, ion, "generic",
+ JS::TrackedOutcome::ICNameStub_TypeOfNoProperty);
+}
+
+static bool
+IsCacheableNameNoProperty(HandleObject scopeChain, HandleObject obj,
+ HandleObject holder, HandleShape shape, jsbytecode* pc,
+ NameIC& cache)
+{
+ if (cache.isTypeOf() && !shape) {
+ MOZ_ASSERT(!obj);
+ MOZ_ASSERT(!holder);
+ MOZ_ASSERT(scopeChain);
+
+ // Assert those extra things checked by IsCacheableNoProperty().
+ MOZ_ASSERT(cache.outputReg().hasValue());
+ MOZ_ASSERT(pc != nullptr);
+
+ return true;
+ }
+
+ return false;
+}
+
bool
NameIC::update(JSContext* cx, HandleScript outerScript, size_t cacheIndex, HandleObject scopeChain,
MutableHandleValue vp)
@@ -4969,6 +5025,9 @@ NameIC::update(JSContext* cx, HandleScript outerScript, size_t cacheIndex, Handl
{
return false;
}
+ } else if (IsCacheableNameNoProperty(scopeChain, obj, holder, shape, pc, cache)) {
+ if (!cache.attachTypeOfNoProperty(cx, outerScript, ion, scopeChain))
+ return false;
}
}
diff --git a/js/src/jit/IonCaches.h b/js/src/jit/IonCaches.h
index f99dd2b57..2600c271f 100644
--- a/js/src/jit/IonCaches.h
+++ b/js/src/jit/IonCaches.h
@@ -791,6 +791,9 @@ class NameIC : public IonCache
HandleObject scopeChain, HandleObject obj, HandleObject holder,
HandleShape shape, void* returnAddr);
+ bool attachTypeOfNoProperty(JSContext* cx, HandleScript outerScript, IonScript* ion,
+ HandleObject scopeChain);
+
static bool
update(JSContext* cx, HandleScript outerScript, size_t cacheIndex, HandleObject scopeChain,
MutableHandleValue vp);
diff --git a/js/src/jit/IonOptimizationLevels.cpp b/js/src/jit/IonOptimizationLevels.cpp
index 5143c1411..b660143f3 100644
--- a/js/src/jit/IonOptimizationLevels.cpp
+++ b/js/src/jit/IonOptimizationLevels.cpp
@@ -46,7 +46,7 @@ OptimizationInfo::initNormalOptimizationInfo()
inliningMaxCallerBytecodeLength_ = 1500;
maxInlineDepth_ = 3;
scalarReplacement_ = true;
- smallFunctionMaxInlineDepth_ = 10;
+ smallFunctionMaxInlineDepth_ = 13;
compilerWarmUpThreshold_ = CompilerWarmupThreshold;
inliningWarmUpThresholdFactor_ = 0.125;
inliningRecompileThresholdFactor_ = 4;
diff --git a/js/src/jit/JitSpewer.cpp b/js/src/jit/JitSpewer.cpp
index d7d7c4821..ce30027f6 100644
--- a/js/src/jit/JitSpewer.cpp
+++ b/js/src/jit/JitSpewer.cpp
@@ -490,6 +490,8 @@ jit::CheckLogging()
EnableIonDebugSyncLogging();
if (ContainsFlag(env, "profiling"))
EnableChannel(JitSpew_Profiling);
+ if (ContainsFlag(env, "irregexp"))
+ EnableChannel(JitSpew_Irregexp);
if (ContainsFlag(env, "trackopts"))
EnableChannel(JitSpew_OptimizationTracking);
if (ContainsFlag(env, "all"))
diff --git a/js/src/jit/JitSpewer.h b/js/src/jit/JitSpewer.h
index 82b76b4b1..2f443651e 100644
--- a/js/src/jit/JitSpewer.h
+++ b/js/src/jit/JitSpewer.h
@@ -92,7 +92,8 @@ namespace jit {
/* Debug info about snapshots */ \
_(IonSnapshots) \
/* Generated inline cache stubs */ \
- _(IonIC)
+ _(IonIC) \
+ _(Irregexp)
enum JitSpewChannel {
#define JITSPEW_CHANNEL(name) JitSpew_##name,
diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp
index 419330a78..b5a53fbef 100644
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -4823,6 +4823,20 @@ MTableSwitch::foldsTo(TempAllocator& alloc)
if (numSuccessors() == 1 || (op->type() != MIRType_Value && !IsNumberType(op->type())))
return MGoto::New(alloc, getDefault());
+ if (op->isConstantValue()) {
+ Value v = op->constantValue();
+ if (v.isInt32()) {
+ int32_t i = v.toInt32() - low_;
+ MBasicBlock* target;
+ if (size_t(i) < numCases())
+ target = getCase(size_t(i));
+ else
+ target = getDefault();
+ MOZ_ASSERT(target);
+ return MGoto::New(alloc, target);
+ }
+ }
+
return this;
}
diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h
index 490ef04d9..6068edfc8 100644
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -9545,6 +9545,13 @@ class MLoadUnboxedScalar
bool requiresBarrier_;
int32_t offsetAdjustment_;
bool canonicalizeDoubles_;
+ public:
+ enum TargetKind {
+ ScalarTarget = 0,
+ TypedArrayTarget
+ };
+ private:
+ TargetKind target_;
MLoadUnboxedScalar(MDefinition* elements, MDefinition* index,
Scalar::Type storageType, MemoryBarrierRequirement requiresBarrier,
@@ -9562,6 +9569,7 @@ class MLoadUnboxedScalar
setGuard(); // Not removable or movable
else
setMovable();
+ target_ = ScalarTarget;
MOZ_ASSERT(IsValidElementsType(elements, offsetAdjustment));
MOZ_ASSERT(index->type() == MIRType_Int32);
MOZ_ASSERT(storageType >= 0 && storageType < Scalar::MaxTypedArrayViewType);
@@ -9582,6 +9590,12 @@ class MLoadUnboxedScalar
canonicalizeDoubles);
}
+ TargetKind target() {
+ return target_;
+ }
+ void setTarget(TargetKind t) {
+ target_ = t;
+ }
void setSimdRead(Scalar::Type type, unsigned numElems) {
readType_ = type;
numElems_ = numElems;
@@ -9832,6 +9846,10 @@ class MStoreUnboxedScalar
DontTruncateInput,
TruncateInput
};
+ enum TargetKind {
+ ScalarTarget = 0,
+ TypedArrayTarget
+ };
private:
Scalar::Type storageType_;
@@ -9842,6 +9860,8 @@ class MStoreUnboxedScalar
bool requiresBarrier_;
int32_t offsetAdjustment_;
unsigned numElems_; // used only for SIMD
+
+ TargetKind target_;
MStoreUnboxedScalar(MDefinition* elements, MDefinition* index, MDefinition* value,
Scalar::Type storageType, TruncateInputKind truncateInput,
@@ -9858,6 +9878,7 @@ class MStoreUnboxedScalar
setGuard(); // Not removable or movable
else
setMovable();
+ target_ = ScalarTarget;
MOZ_ASSERT(IsValidElementsType(elements, offsetAdjustment));
MOZ_ASSERT(index->type() == MIRType_Int32);
MOZ_ASSERT(storageType >= 0 && storageType < Scalar::MaxTypedArrayViewType);
@@ -9910,6 +9931,12 @@ class MStoreUnboxedScalar
int32_t offsetAdjustment() const {
return offsetAdjustment_;
}
+ TargetKind target() {
+ return target_;
+ }
+ void setTarget(TargetKind t) {
+ target_ = t;
+ }
TruncateKind operandTruncateKind(size_t index) const override;
bool canConsumeFloat32(MUse* use) const override {
diff --git a/js/src/jit/SharedIC.cpp b/js/src/jit/SharedIC.cpp
index 3d3a30f48..a64df068b 100644
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -2564,7 +2564,8 @@ TryAttachNativeGetAccessorPropStub(JSContext* cx, HandleScript script, jsbytecod
bool isScripted = false;
bool cacheableCall = IsCacheableGetPropCall(cx, obj, holder, shape, &isScripted,
- isTemporarilyUnoptimizable);
+ isTemporarilyUnoptimizable,
+ isDOMProxy);
// Try handling scripted getters.
if (cacheableCall && isScripted && !isDOMProxy && engine == ICStubCompiler::Engine::Baseline) {
diff --git a/js/src/jsapi.h b/js/src/jsapi.h
index 358d09b1e..77aa95251 100644
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -4341,10 +4341,10 @@ extern JS_PUBLIC_API(const char16_t*)
JS_GetTwoByteFlatStringChars(const JS::AutoCheckCannotGC& nogc, JSFlatString* str);
static MOZ_ALWAYS_INLINE JSFlatString*
-JSID_TO_FLAT_STRING(jsid id)
+JSID_TO_FLAT_STRING(jsid jid)
{
- MOZ_ASSERT(JSID_IS_STRING(id));
- return (JSFlatString*)(JSID_BITS(id));
+ MOZ_ASSERT(JSID_IS_STRING(jid));
+ return (JSFlatString*)(JSID_BITS(jid));
}
static MOZ_ALWAYS_INLINE JSFlatString*
diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h
index 53518d371..667e82b02 100644
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -219,7 +219,7 @@ extern JS_FRIEND_API(void)
DumpValue(const JS::Value& val);
extern JS_FRIEND_API(void)
-DumpId(jsid id);
+DumpId(jsid jid);
extern JS_FRIEND_API(void)
DumpInterpreterFrame(JSContext* cx, InterpreterFrame* start = nullptr);
@@ -715,7 +715,7 @@ NewFunctionWithReserved(JSContext* cx, JSNative call, unsigned nargs, unsigned f
JS_FRIEND_API(JSFunction*)
NewFunctionByIdWithReserved(JSContext* cx, JSNative native, unsigned nargs, unsigned flags,
- jsid id);
+ jsid jid);
JS_FRIEND_API(const JS::Value&)
GetFunctionNativeReserved(JSObject* fun, size_t which);
@@ -2495,14 +2495,14 @@ SET_JITINFO(JSFunction * func, const JSJitInfo* info)
static MOZ_ALWAYS_INLINE jsid
JSID_FROM_BITS(size_t bits)
{
- jsid id;
- JSID_BITS(id) = bits;
- return id;
+ jsid jid;
+ JSID_BITS(jid) = bits;
+ return jid;
}
namespace js {
namespace detail {
-bool IdMatchesAtom(jsid id, JSAtom* atom);
+bool IdMatchesAtom(jsid jid, JSAtom* atom);
} // namespace detail
} // namespace js
@@ -2531,28 +2531,28 @@ static MOZ_ALWAYS_INLINE jsid
NON_INTEGER_ATOM_TO_JSID(JSAtom* atom)
{
MOZ_ASSERT(((size_t)atom & 0x7) == 0);
- jsid id = JSID_FROM_BITS((size_t)atom);
- MOZ_ASSERT(js::detail::IdMatchesAtom(id, atom));
- return id;
+ jsid jid = JSID_FROM_BITS((size_t)atom);
+ MOZ_ASSERT(js::detail::IdMatchesAtom(jid, atom));
+ return jid;
}
/* All strings stored in jsids are atomized, but are not necessarily property names. */
static MOZ_ALWAYS_INLINE bool
-JSID_IS_ATOM(jsid id)
+JSID_IS_ATOM(jsid jid)
{
- return JSID_IS_STRING(id);
+ return JSID_IS_STRING(jid);
}
static MOZ_ALWAYS_INLINE bool
-JSID_IS_ATOM(jsid id, JSAtom* atom)
+JSID_IS_ATOM(jsid jid, JSAtom* atom)
{
- return id == JSID_FROM_BITS((size_t)atom);
+ return jid == JSID_FROM_BITS((size_t)atom);
}
static MOZ_ALWAYS_INLINE JSAtom*
-JSID_TO_ATOM(jsid id)
+JSID_TO_ATOM(jsid jid)
{
- return (JSAtom*)JSID_TO_STRING(id);
+ return (JSAtom*)JSID_TO_STRING(jid);
}
JS_STATIC_ASSERT(sizeof(jsid) == sizeof(void*));
@@ -2560,15 +2560,15 @@ JS_STATIC_ASSERT(sizeof(jsid) == sizeof(void*));
namespace js {
static MOZ_ALWAYS_INLINE JS::Value
-IdToValue(jsid id)
+IdToValue(jsid jid)
{
- if (JSID_IS_STRING(id))
- return JS::StringValue(JSID_TO_STRING(id));
- if (JSID_IS_INT(id))
- return JS::Int32Value(JSID_TO_INT(id));
- if (JSID_IS_SYMBOL(id))
- return JS::SymbolValue(JSID_TO_SYMBOL(id));
- MOZ_ASSERT(JSID_IS_VOID(id));
+ if (JSID_IS_STRING(jid))
+ return JS::StringValue(JSID_TO_STRING(jid));
+ if (JSID_IS_INT(jid))
+ return JS::Int32Value(JSID_TO_INT(jid));
+ if (JSID_IS_SYMBOL(jid))
+ return JS::SymbolValue(JSID_TO_SYMBOL(jid));
+ MOZ_ASSERT(JSID_IS_VOID(jid));
return JS::UndefinedValue();
}
diff --git a/js/src/json.cpp b/js/src/json.cpp
index 34579d925..79f10b993 100644
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -375,6 +375,10 @@ JO(JSContext* cx, HandleObject obj, StringifyContext* scx)
bool wroteMember = false;
RootedId id(cx);
for (size_t i = 0, len = propertyList.length(); i < len; i++) {
+// bug 1257164
+ if (!CheckForInterrupt(cx))
+ return false;
+
/*
* Steps 8a-8b. Note that the call to Str is broken up into 1) getting
* the property; 2) processing for toJSON, calling the replacer, and
@@ -454,6 +458,10 @@ JA(JSContext* cx, HandleObject obj, StringifyContext* scx)
/* Steps 7-10. */
RootedValue outputValue(cx);
for (uint32_t i = 0; i < length; i++) {
+// bug 1257164
+ if (!CheckForInterrupt(cx))
+ return false;
+
/*
* Steps 8a-8c. Again note how the call to the spec's Str method
* is broken up into getting the property, running it past toJSON
@@ -731,6 +739,10 @@ Walk(JSContext* cx, HandleObject holder, HandleId name, HandleValue reviver, Mut
RootedId id(cx);
RootedValue newElement(cx);
for (uint32_t i = 0; i < length; i++) {
+// bug 1257164
+ if (!CheckForInterrupt(cx))
+ return false;
+
if (!IndexToId(cx, i, &id))
return false;
@@ -761,6 +773,10 @@ Walk(JSContext* cx, HandleObject holder, HandleId name, HandleValue reviver, Mut
RootedId id(cx);
RootedValue newElement(cx);
for (size_t i = 0, len = keys.length(); i < len; i++) {
+// bug 1257164
+ if (!CheckForInterrupt(cx))
+ return false;
+
/* Step 2b(ii)(1). */
id = keys[i];
if (!Walk(cx, obj, id, reviver, &newElement))
diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp
index 788e38bf1..778117a4c 100644
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -3522,7 +3522,7 @@ js::detail::CopyScript(JSContext* cx, HandleObject scriptStaticScope, HandleScri
/* RegExps */
AutoObjectVector regexps(cx);
- for (unsigned i = 0; i < nregexps; i++) {
+ if (nregexps != 0) {
HeapPtrObject* vector = src->regexps()->vector;
for (unsigned i = 0; i < nregexps; i++) {
JSObject* clone = CloneScriptRegExpObject(cx, vector[i]->as());
diff --git a/js/src/vm/ObjectGroup.cpp b/js/src/vm/ObjectGroup.cpp
index ad002b4a0..35bbd3a50 100644
--- a/js/src/vm/ObjectGroup.cpp
+++ b/js/src/vm/ObjectGroup.cpp
@@ -539,7 +539,7 @@ ObjectGroup::defaultNewGroup(ExclusiveContext* cx, const Class* clasp,
}
ObjectGroupFlags initialFlags = 0;
- if (!proto.isObject() || proto.toObject()->isNewGroupUnknown())
+ if (proto.isLazy() || (proto.isObject() && proto.toObject()->isNewGroupUnknown()))
initialFlags = OBJECT_FLAG_DYNAMIC_MASK;
Rooted protoRoot(cx, proto);
diff --git a/js/src/vm/UnboxedObject.cpp b/js/src/vm/UnboxedObject.cpp
index f1b377d57..2c689e682 100644
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -344,6 +344,11 @@ UnboxedPlainObject::ensureExpando(JSContext* cx, Handle obj
if (!expando)
return nullptr;
+ // Don't track property types for expando objects. This allows Baseline
+ // and Ion AddSlot ICs to guard on the unboxed group without guarding on
+ // the expando group.
+ MarkObjectGroupUnknownProperties(cx, expando->group());
+
// If the expando is tenured then the original object must also be tenured.
// Otherwise barriers triggered on the original object for writes to the
// expando (as can happen in the JIT) won't see the tenured->nursery edge.
diff --git a/js/xpconnect/wrappers/XrayWrapper.cpp b/js/xpconnect/wrappers/XrayWrapper.cpp
index a5a5d0900..6e5922cbe 100644
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -636,8 +636,8 @@ JSXrayTraits::delete_(JSContext* cx, HandleObject wrapper, HandleId id, ObjectOp
bool
JSXrayTraits::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
- Handle desc,
- Handle existingDesc,
+ JS::Handle desc,
+ JS::Handle existingDesc,
ObjectOpResult& result,
bool* defined)
{
@@ -1473,8 +1473,8 @@ XPCWrappedNativeXrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsW
bool
XPCWrappedNativeXrayTraits::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
- Handle desc,
- Handle existingDesc,
+ JS::Handle desc,
+ JS::Handle existingDesc,
JS::ObjectOpResult& result, bool* defined)
{
*defined = false;
@@ -1633,8 +1633,8 @@ DOMXrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper, Handl
bool
DOMXrayTraits::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
- Handle desc,
- Handle existingDesc,
+ JS::Handle desc,
+ JS::Handle existingDesc,
JS::ObjectOpResult& result, bool* defined)
{
// Check for an indexed property on a Window. If that's happening, do
@@ -2032,7 +2032,7 @@ RecreateLostWaivers(JSContext* cx, const JSPropertyDescriptor* orig,
template
bool
XrayWrapper::defineProperty(JSContext* cx, HandleObject wrapper,
- HandleId id, Handle desc,
+ HandleId id, JS::Handle desc,
ObjectOpResult& result) const
{
assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::SET);
diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp
index 1ba70cd30..2e66c4e18 100644
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -460,7 +460,8 @@ public:
* Add the given hit regions to the hit regions to the hit retions for this
* PaintedLayer.
*/
- void AccumulateEventRegions(ContainerState* aState, nsDisplayLayerEventRegions* aEventRegions);
+// bug 1247979
+ void AccumulateEventRegions(ContainerState* aState, nsDisplayLayerEventRegions* aEventRegions, nsRegion &tmp);
/**
* If this represents only a nsDisplayImage, and the image type supports being
@@ -3319,6 +3320,8 @@ void ContainerState::FinishPaintedLayerData(PaintedLayerData& aData, FindOpaqueB
containingPaintedLayerData->mReferenceFrame);
containingPaintedLayerData->mMaybeHitRegion.Or(
containingPaintedLayerData->mMaybeHitRegion, rect);
+ // bug 1256373
+ containingPaintedLayerData->mMaybeHitRegion.SimplifyOutward(8);
}
nsLayoutUtils::TransformToAncestorAndCombineRegions(
data->mHitRegion.GetBounds(),
@@ -3540,16 +3543,28 @@ PaintedLayerData::Accumulate(ContainerState* aState,
}
void
-PaintedLayerData::AccumulateEventRegions(ContainerState* aState, nsDisplayLayerEventRegions* aEventRegions)
+PaintedLayerData::AccumulateEventRegions(ContainerState* aState, nsDisplayLayerEventRegions* aEventRegions, nsRegion &tmp)
{
FLB_LOG_PAINTED_LAYER_DECISION(this, "Accumulating event regions %p against pld=%p\n", aEventRegions, this);
+/* tmp from bug 1247979 */
+#if(0)
mHitRegion.Or(mHitRegion, aEventRegions->HitRegion());
mMaybeHitRegion.Or(mMaybeHitRegion, aEventRegions->MaybeHitRegion());
mDispatchToContentHitRegion.Or(mDispatchToContentHitRegion, aEventRegions->DispatchToContentHitRegion());
mNoActionRegion.Or(mNoActionRegion, aEventRegions->NoActionRegion());
mHorizontalPanRegion.Or(mHorizontalPanRegion, aEventRegions->HorizontalPanRegion());
mVerticalPanRegion.Or(mVerticalPanRegion, aEventRegions->VerticalPanRegion());
+#else
+ // we use a tmp region to avoid modifying the regions in place which is gauranteed to do a new allocation.
+ //
+ tmp.Or(mHitRegion, aEventRegions->HitRegion()); mHitRegion = tmp;
+ tmp.Or(mMaybeHitRegion, aEventRegions->MaybeHitRegion()); mMaybeHitRegion = tmp;
+ tmp.Or(mDispatchToContentHitRegion, aEventRegions->DispatchToContentHitRegion()); mDispatchToContentHitRegion = tmp;
+ tmp.Or(mNoActionRegion, aEventRegions->NoActionRegion()); mNoActionRegion = tmp;
+ tmp.Or(mHorizontalPanRegion, aEventRegions->HorizontalPanRegion()); mHorizontalPanRegion = tmp;
+ tmp.Or(mVerticalPanRegion, aEventRegions->VerticalPanRegion()); mVerticalPanRegion = tmp;
+#endif
// Calculate scaled versions of the bounds of mHitRegion and mMaybeHitRegion
// for quick access in FindPaintedLayerFor().
@@ -3699,8 +3714,12 @@ ContainerState::ComputeOpaqueRect(nsDisplayItem* aItem,
{
bool snapOpaque;
nsRegion opaque = aItem->GetOpaqueRegion(mBuilder, &snapOpaque);
+ if (opaque.IsEmpty()) {
+ return nsIntRegion();
+ } // bug 1220466
+
nsIntRegion opaquePixels;
- if (!opaque.IsEmpty()) {
+// if (!opaque.IsEmpty()) {
nsRegion opaqueClipped;
nsRegionRectIterator iter(opaque);
for (const nsRect* r = iter.Next(); r; r = iter.Next()) {
@@ -3737,7 +3756,7 @@ ContainerState::ComputeOpaqueRect(nsDisplayItem* aItem,
*aOpaqueForAnimatedGeometryRootParent = true;
}
}
- }
+// }
return opaquePixels;
}
@@ -3815,6 +3834,7 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList)
nsDisplayList savedItems;
nsDisplayItem* item;
+ nsRegion tmpRegion;
while ((item = aList->RemoveBottom()) != nullptr) {
// Peek ahead to the next item and try merging with it or swapping with it
// if necessary.
@@ -4001,7 +4021,10 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList)
// So we'll do a little hand holding and pass the clip instead of the
// visible rect for the two important cases.
nscolor uniformColor = NS_RGBA(0,0,0,0);
- nscolor* uniformColorPtr = !mayDrawOutOfOrder ? &uniformColor : nullptr;
+// bug 1220466 and comments
+ //nscolor* uniformColorPtr = !mayDrawOutOfOrder ? &uniformColor : nullptr;
+ nscolor* uniformColorPtr = (!mayDrawOutOfOrder && !IsInInactiveLayer())
+ ? &uniformColor : nullptr;
nsIntRect clipRectUntyped;
const DisplayItemClip& layerClip = shouldFixToViewport ? fixedToViewportClip : itemClip;
ParentLayerIntRect layerClipRect;
@@ -4162,7 +4185,7 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList)
if (itemType == nsDisplayItem::TYPE_LAYER_EVENT_REGIONS) {
nsDisplayLayerEventRegions* eventRegions =
static_cast(item);
- paintedLayerData->AccumulateEventRegions(this, eventRegions);
+ paintedLayerData->AccumulateEventRegions(this, eventRegions, tmpRegion);
} else {
// check to see if the new item has rounded rect clips in common with
// other items in the layer
diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp
index 9c4d5e552..b4673a099 100644
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -748,6 +748,27 @@ nsLayoutUtils::FindContentFor(ViewID aId)
}
}
+nsIFrame*
+GetScrollFrameFromContent(nsIContent* aContent)
+{
+ nsIFrame* frame = aContent->GetPrimaryFrame();
+ if (aContent->OwnerDoc()->GetRootElement() == aContent) {
+ nsIPresShell* presShell = frame ? frame->PresContext()->PresShell() :
+nullptr;
+ if (!presShell) {
+ presShell = aContent->OwnerDoc()->GetShell();
+ }
+ // We want the scroll frame, the root scroll frame differs from all
+ // others in that the primary frame is not the scroll frame.
+ nsIFrame* rootScrollFrame = presShell ? presShell->GetRootScrollFrame() :
+nullptr;
+ if (rootScrollFrame) {
+ frame = rootScrollFrame;
+ }
+ }
+ return frame;
+}
+
nsIScrollableFrame*
nsLayoutUtils::FindScrollableFrameFor(ViewID aId)
{
@@ -756,6 +777,7 @@ nsLayoutUtils::FindScrollableFrameFor(ViewID aId)
return nullptr;
}
+#if(0) // backbugs from whatever the hell it was
nsIFrame* scrolledFrame = content->GetPrimaryFrame();
if (scrolledFrame && content->OwnerDoc()->GetRootElement() == content) {
// The content is the root element of a subdocument, so return the root scrollable
@@ -763,6 +785,10 @@ nsLayoutUtils::FindScrollableFrameFor(ViewID aId)
scrolledFrame = scrolledFrame->PresContext()->PresShell()->GetRootScrollFrame();
}
return scrolledFrame ? scrolledFrame->GetScrollTargetFrame() : nullptr;
+#else
+ nsIFrame* scrollFrame = GetScrollFrameFromContent(content);
+ return scrollFrame ? scrollFrame->GetScrollTargetFrame() : nullptr;
+#endif
}
static nsRect
@@ -1140,8 +1166,51 @@ nsLayoutUtils::SetDisplayPortMargins(nsIContent* aContent,
}
// Display port margins changing means that the set of visible images may
- // have drastically changed. Schedule an update.
- aPresShell->ScheduleImageVisibilityUpdate();
+ // have drastically changed. Check if we should schedule an update.
+ nsIFrame* frame = GetScrollFrameFromContent(aContent);
+ nsIScrollableFrame* scrollableFrame = frame ? frame->GetScrollTargetFrame() : nullptr;
+ if (!scrollableFrame) {
+ return true;
+ }
+
+ nsRect oldDisplayPort;
+ bool hadDisplayPort =
+ scrollableFrame->GetDisplayPortAtLastImageVisibilityUpdate(&oldDisplayPort);
+
+ nsRect newDisplayPort;
+ Unused << GetDisplayPort(aContent, &newDisplayPort);
+
+ bool needImageVisibilityUpdate = !hadDisplayPort;
+ // Check if the width or height was or is going to be 0, mostly
+ // just so we don't divide by zero in the next check.
+ if (newDisplayPort.width == 0 || oldDisplayPort.width == 0 ||
+ newDisplayPort.height == 0 || oldDisplayPort.height == 0) {
+ needImageVisibilityUpdate = true;
+ }
+ // Check if the total size has changed by a large factor.
+ if (!needImageVisibilityUpdate) {
+ if ((newDisplayPort.width/((float)oldDisplayPort.width) > 2.f) ||
+ (oldDisplayPort.width/((float)newDisplayPort.width) > 2.f) ||
+ (newDisplayPort.height/((float)oldDisplayPort.height) > 2.f) ||
+ (oldDisplayPort.height/((float)newDisplayPort.height) > 2.f)) {
+ needImageVisibilityUpdate = true;
+ }
+ }
+ // Check if it's moved by a significant amount.
+ if (!needImageVisibilityUpdate) {
+ if (nsRect* baseData = static_cast(aContent->GetProperty(nsGkAtoms::DisplayPortBase))) {
+ nsRect base = *baseData;
+ if ((std::abs(newDisplayPort.X() - oldDisplayPort.X()) > base.width) ||
+ (std::abs(newDisplayPort.XMost() - oldDisplayPort.XMost()) > base.width) ||
+ (std::abs(newDisplayPort.Y() - oldDisplayPort.Y()) > base.height) ||
+ (std::abs(newDisplayPort.YMost() - oldDisplayPort.YMost()) > base.height)) {
+ needImageVisibilityUpdate = true;
+ }
+ }
+ }
+ if (needImageVisibilityUpdate) {
+ aPresShell->ScheduleImageVisibilityUpdate();
+ }
return true;
}
diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp
index 206f51b77..98cece187 100644
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -5710,6 +5710,7 @@ PresShell::MarkImagesInSubtreeVisible(nsIFrame* aFrame, const nsRect& aRect)
nsIScrollableFrame* scrollFrame = do_QueryFrame(aFrame);
if (scrollFrame) {
+ scrollFrame->NotifyImageVisibilityUpdate();
nsRect displayPort;
bool usingDisplayport =
nsLayoutUtils::GetDisplayPortForVisibilityTesting(aFrame->GetContent(),
diff --git a/layout/base/nsPresShell.h b/layout/base/nsPresShell.h
index d25109287..e0ccdc500 100644
--- a/layout/base/nsPresShell.h
+++ b/layout/base/nsPresShell.h
@@ -55,9 +55,9 @@ class CSSStyleSheet;
class EventDispatchingCallback;
} // namespace mozilla
-// 250ms. This is actually pref-controlled, but we use this value if we fail
+// This is actually pref-controlled, but we use this value if we fail
// to get the pref for any reason.
-#define PAINTLOCK_EVENT_DELAY 250
+#define PAINTLOCK_EVENT_DELAY 100
class PresShell final : public nsIPresShell,
public nsStubDocumentObserver,
diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp
index dc2a3e1ac..15ff42276 100644
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -2542,11 +2542,31 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
child->MarkAbsoluteFramesForDisplayList(aBuilder, dirty);
+ // moved here from below
+ if (aBuilder->IsBuildingLayerEventRegions()) {
+ // If this frame has a different animated geometry root than its parent,
+ // make sure we accumulate event regions for its layer.
+ if (buildingForChild.IsAnimatedGeometryRoot()) {
+ nsDisplayLayerEventRegions* eventRegions =
+ new (aBuilder) nsDisplayLayerEventRegions(aBuilder, child);
+ eventRegions->AddFrame(aBuilder, child);
+ aBuilder->SetLayerEventRegions(eventRegions);
+ aLists.BorderBackground()->AppendNewToTop(eventRegions);
+ }
+ }
+
+ nsDisplayLayerEventRegions* eventRegions = aBuilder->GetLayerEventRegions();
+ if (eventRegions) {
+ eventRegions->AddFrame(aBuilder, child);
+ }
+
if (!pseudoStackingContext) {
// THIS IS THE COMMON CASE.
// Not a pseudo or real stacking context. Do the simple thing and
// return early.
+// bug 1220466 moves this above.
+#if(0)
if (aBuilder->IsBuildingLayerEventRegions()) {
// If this frame has a different animated geometry root than its parent,
// make sure we accumulate event regions for its layer.
@@ -2563,6 +2583,7 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
if (eventRegions) {
eventRegions->AddFrame(aBuilder, child);
}
+#endif
aBuilder->AdjustWindowDraggingRegion(child);
child->BuildDisplayList(aBuilder, dirty, aLists);
aBuilder->DisplayCaret(child, dirty, aLists.Content());
@@ -2577,6 +2598,8 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
// stacking context's positioned descendant list, because they might be
// z-index:non-auto
nsDisplayListCollection pseudoStack;
+// bug 1220466
+#if(0)
if (aBuilder->IsBuildingLayerEventRegions()) {
nsDisplayLayerEventRegions* eventRegions =
new (aBuilder) nsDisplayLayerEventRegions(aBuilder, child);
@@ -2584,6 +2607,7 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
aBuilder->SetLayerEventRegions(eventRegions);
pseudoStack.BorderBackground()->AppendNewToTop(eventRegions);
}
+#endif
aBuilder->AdjustWindowDraggingRegion(child);
child->BuildDisplayList(aBuilder, dirty, pseudoStack);
aBuilder->DisplayCaret(child, dirty, pseudoStack.Content());
diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp
index 4592d4fb2..03cc4c947 100644
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -1837,6 +1837,8 @@ ScrollFrameHelper::ScrollFrameHelper(nsContainerFrame* aOuter,
, mLastPos(-1, -1)
, mScrollPosForLayerPixelAlignment(-1, -1)
, mLastUpdateImagesPos(-1, -1)
+ , mHadDisplayPortAtLastImageUpdate(false)
+ , mDisplayPortAtLastImageUpdate()
, mNeverHasVerticalScrollbar(false)
, mNeverHasHorizontalScrollbar(false)
, mHasVerticalScrollbar(false)
@@ -2476,6 +2478,23 @@ ScrollFrameHelper::ScheduleSyntheticMouseMove()
ScrollActivityCallback, this, 100, nsITimer::TYPE_ONE_SHOT);
}
+void
+ScrollFrameHelper::NotifyImageVisibilityUpdate()
+{
+ mLastUpdateImagesPos = GetScrollPosition();
+ mHadDisplayPortAtLastImageUpdate =
+ nsLayoutUtils::GetDisplayPort(mOuter->GetContent(), &mDisplayPortAtLastImageUpdate);
+}
+
+bool
+ScrollFrameHelper::GetDisplayPortAtLastImageVisibilityUpdate(nsRect* aDisplayPort)
+{
+ if (mHadDisplayPortAtLastImageUpdate) {
+ *aDisplayPort = mDisplayPortAtLastImageUpdate;
+ }
+ return mHadDisplayPortAtLastImageUpdate;
+}
+
void
ScrollFrameHelper::ScrollToImpl(nsPoint aPt, const nsRect& aRange, nsIAtom* aOrigin)
{
@@ -2528,10 +2547,6 @@ ScrollFrameHelper::ScrollToImpl(nsPoint aPt, const nsRect& aRange, nsIAtom* aOri
needImageVisibilityUpdate = true;
}
- if (needImageVisibilityUpdate) {
- presContext->PresShell()->ScheduleImageVisibilityUpdate();
- }
-
// notify the listeners.
for (uint32_t i = 0; i < mListeners.Length(); i++) {
mListeners[i]->ScrollPositionWillChange(pt.x, pt.y);
@@ -2549,6 +2564,7 @@ ScrollFrameHelper::ScrollToImpl(nsPoint aPt, const nsRect& aRange, nsIAtom* aOri
ScrollVisual();
+#if(0) // this won't ever happen
if (LastScrollOrigin() == nsGkAtoms::apz) {
// If this was an apz scroll and the displayport (relative to the
// scrolled frame) hasn't changed, then this won't trigger
@@ -2562,10 +2578,19 @@ ScrollFrameHelper::ScrollToImpl(nsPoint aPt, const nsRect& aRange, nsIAtom* aOri
if (!displayPort.IsEqualEdges(oldDisplayPort)) {
mOuter->SchedulePaint();
+
+ if (needImageVisibilityUpdate) {
+ presContext->PresShell()->ScheduleImageVisibilityUpdate();
+ }
}
} else {
+#endif
mOuter->SchedulePaint();
- }
+
+ if (needImageVisibilityUpdate) {
+ presContext->PresShell()->ScheduleImageVisibilityUpdate();
+ }
+// }
if (mOuter->ChildrenHavePerspective()) {
// The overflow areas of descendants may depend on the scroll position,
@@ -2884,7 +2909,7 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsDisplayListSet& aLists)
{
if (aBuilder->IsForImageVisibility()) {
- mLastUpdateImagesPos = GetScrollPosition();
+ NotifyImageVisibilityUpdate();
}
mOuter->DisplayBorderBackgroundOutline(aBuilder, aLists);
diff --git a/layout/generic/nsGfxScrollFrame.h b/layout/generic/nsGfxScrollFrame.h
index f8c554e60..7f6301559 100644
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -366,6 +366,8 @@ public:
bool DecideScrollableLayer(nsDisplayListBuilder* aBuilder,
nsRect* aDirtyRect,
bool aAllowCreateDisplayPort);
+ void NotifyImageVisibilityUpdate();
+ bool GetDisplayPortAtLastImageVisibilityUpdate(nsRect* aDisplayPort);
void ScheduleSyntheticMouseMove();
static void ScrollActivityCallback(nsITimer *aTimer, void* anInstance);
@@ -459,6 +461,8 @@ public:
// The scroll position where we last updated image visibility.
nsPoint mLastUpdateImagesPos;
+ bool mHadDisplayPortAtLastImageUpdate;
+ nsRect mDisplayPortAtLastImageUpdate;
nsRect mPrevScrolledRect;
@@ -857,6 +861,12 @@ public:
bool aAllowCreateDisplayPort) override {
return mHelper.DecideScrollableLayer(aBuilder, aDirtyRect, aAllowCreateDisplayPort);
}
+ virtual void NotifyImageVisibilityUpdate() override {
+ mHelper.NotifyImageVisibilityUpdate();
+ }
+ virtual bool GetDisplayPortAtLastImageVisibilityUpdate(nsRect* aDisplayPort) override {
+ return mHelper.GetDisplayPortAtLastImageVisibilityUpdate(aDisplayPort);
+ }
// nsIStatefulFrame
NS_IMETHOD SaveState(nsPresState** aState) override {
@@ -1328,7 +1338,12 @@ public:
bool aAllowCreateDisplayPort) override {
return mHelper.DecideScrollableLayer(aBuilder, aDirtyRect, aAllowCreateDisplayPort);
}
-
+ virtual void NotifyImageVisibilityUpdate() override {
+ mHelper.NotifyImageVisibilityUpdate();
+ }
+ virtual bool GetDisplayPortAtLastImageVisibilityUpdate(nsRect* aDisplayPort) override {
+ return mHelper.GetDisplayPortAtLastImageVisibilityUpdate(aDisplayPort);
+ }
#ifdef DEBUG_FRAME_DUMP
virtual nsresult GetFrameName(nsAString& aResult) const override;
diff --git a/layout/generic/nsIScrollableFrame.h b/layout/generic/nsIScrollableFrame.h
index bb7050077..8c9ee9211 100644
--- a/layout/generic/nsIScrollableFrame.h
+++ b/layout/generic/nsIScrollableFrame.h
@@ -450,6 +450,18 @@ public:
virtual bool DecideScrollableLayer(nsDisplayListBuilder* aBuilder,
nsRect* aDirtyRect,
bool aAllowCreateDisplayPort) = 0;
+
+ /**
+ * Notification that this scroll frame is getting its image visibility updated.
+ */
+ virtual void NotifyImageVisibilityUpdate() = 0;
+
+ /**
+ * Returns true if this scroll frame had a display port at the last image
+ * visibility update and fills in aDisplayPort with that displayport. Returns
+ * false otherwise, and doesn't touch aDisplayPort.
+ */
+ virtual bool GetDisplayPortAtLastImageVisibilityUpdate(nsRect* aDisplayPort) = 0;
};
#endif
diff --git a/layout/style/FontFaceSet.cpp b/layout/style/FontFaceSet.cpp
index ab4dddd2c..b302c27c3 100644
--- a/layout/style/FontFaceSet.cpp
+++ b/layout/style/FontFaceSet.cpp
@@ -788,8 +788,11 @@ FontFaceSet::UpdateRules(const nsTArray& aRules)
CheckLoadingFinished();
}
- // local rules have been rebuilt, so clear the flag
- mUserFontSet->mLocalRulesUsed = false;
+ // if local rules needed to be rebuilt, they have been rebuilt at this point
+ if (mUserFontSet->mRebuildLocalRules) {
+ mUserFontSet->mLocalRulesUsed = false;
+ mUserFontSet->mRebuildLocalRules = false;
+ }
if (LOG_ENABLED() && !mRuleFaces.IsEmpty()) {
LOG(("userfonts (%p) userfont rules update (%s) rule count: %d",
@@ -874,7 +877,8 @@ FontFaceSet::InsertRuleFontFace(FontFace* aFontFace, SheetType aSheetType,
// if local rules were used, don't use the old font entry
// for rules containing src local usage
- if (mUserFontSet->mLocalRulesUsed) {
+ if (mUserFontSet->mLocalRulesUsed &&
+ mUserFontSet->mRebuildLocalRules) {
nsCSSValue val;
aFontFace->GetDesc(eCSSFontDesc_Src, val);
nsCSSUnit unit = val.GetUnit();
diff --git a/mfbt/IntegerPrintfMacros.h b/mfbt/IntegerPrintfMacros.h
index c534a0ba2..172602281 100644
--- a/mfbt/IntegerPrintfMacros.h
+++ b/mfbt/IntegerPrintfMacros.h
@@ -49,4 +49,22 @@
# define PRIXPTR "X" /* uintptr_t */
#endif
+/* Fix issues with the 10.4 SDK using weird printf macros but not working
+ properly with gcc-4.8. This causes failures in tests/coverage/simple.js
+ and other less visible places (i.e., using %qu instead of %llu). */
+
+#define __104PRI_64_LENGTH_MODIFIER__ "ll"
+#undef PRId64
+# define PRId64 __104PRI_64_LENGTH_MODIFIER__ "d"
+#undef PRIi64
+# define PRIi64 __104PRI_64_LENGTH_MODIFIER__ "i"
+#undef PRIo64
+# define PRIo64 __104PRI_64_LENGTH_MODIFIER__ "o"
+#undef PRIu64
+# define PRIu64 __104PRI_64_LENGTH_MODIFIER__ "u"
+#undef PRIx64
+# define PRIx64 __104PRI_64_LENGTH_MODIFIER__ "x"
+#undef PRIX64
+# define PRIX64 __104PRI_64_LENGTH_MODIFIER__ "X"
+
#endif /* mozilla_IntegerPrintfMacros_h_ */
diff --git a/mfbt/Scoped.h b/mfbt/Scoped.h
index de8a166f0..681983aab 100644
--- a/mfbt/Scoped.h
+++ b/mfbt/Scoped.h
@@ -198,7 +198,7 @@ struct MOZ_NON_TEMPORARY_CLASS name : public mozilla::Scoped > \
} \
name& operator=(name&& aRhs) \
{ \
- Super::operator=(Move(aRhs)); \
+ Super::operator=(mozilla::Move(aRhs)); \
return *this; \
} \
explicit name(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) \
@@ -211,7 +211,7 @@ struct MOZ_NON_TEMPORARY_CLASS name : public mozilla::Scoped > \
{} \
name(name&& aRhs \
MOZ_GUARD_OBJECT_NOTIFIER_PARAM) \
- : Super(Move(aRhs) \
+ : Super(mozilla::Move(aRhs) \
MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT) \
{} \
private: \
diff --git a/mfbt/XorShift128PlusRNG.h b/mfbt/XorShift128PlusRNG.h
index d38fe117d..d1a825b65 100644
--- a/mfbt/XorShift128PlusRNG.h
+++ b/mfbt/XorShift128PlusRNG.h
@@ -13,7 +13,8 @@
#include "mozilla/Attributes.h"
#include "mozilla/FloatingPoint.h"
-#include
+// Incorporate our local patches for inttypes.h.
+#include "mozilla/IntegerPrintfMacros.h"
namespace mozilla {
namespace non_crypto {
diff --git a/modules/brotli/dec/Makefile b/modules/brotli/dec/Makefile
index fa2fc3d49..4d11ed195 100644
--- a/modules/brotli/dec/Makefile
+++ b/modules/brotli/dec/Makefile
@@ -4,7 +4,7 @@ include ../shared.mk
CFLAGS += -Wall
-OBJS = bit_reader.o decode.o dictionary.o huffman.o state.o streams.o
+OBJS = bit_reader.o decode.o dictionary.o huffman.o state.o
all : $(OBJS)
diff --git a/modules/brotli/dec/bit_reader.c b/modules/brotli/dec/bit_reader.c
index 351cbc2a1..cde90af56 100644
--- a/modules/brotli/dec/bit_reader.c
+++ b/modules/brotli/dec/bit_reader.c
@@ -1,38 +1,23 @@
/* Copyright 2013 Google Inc. All Rights Reserved.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+ Distributed under MIT license.
+ See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
*/
/* Bit reading helpers */
-#include
-
#include "./bit_reader.h"
+
#include "./port.h"
+#include "./types.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
-void BrotliInitBitReader(BrotliBitReader* const br, BrotliInput input) {
- BROTLI_DCHECK(br != NULL);
-
- br->input_ = input;
+void BrotliInitBitReader(BrotliBitReader* const br) {
br->val_ = 0;
br->bit_pos_ = sizeof(br->val_) << 3;
- br->avail_in = 0;
- br->eos_ = 0;
- br->next_in = br->buf_;
}
int BrotliWarmupBitReader(BrotliBitReader* const br) {
@@ -43,16 +28,21 @@ int BrotliWarmupBitReader(BrotliBitReader* const br) {
if (!BROTLI_ALIGNED_READ) {
aligned_read_mask = 0;
}
- while (br->bit_pos_ == (sizeof(br->val_) << 3) ||
- (((size_t)br->next_in) & aligned_read_mask) != 0) {
- if (!br->avail_in) {
+ if (BrotliGetAvailableBits(br) == 0) {
+ if (!BrotliPullByte(br)) {
return 0;
}
- BrotliPullByte(br);
+ }
+
+ while ((((size_t)br->next_in) & aligned_read_mask) != 0) {
+ if (!BrotliPullByte(br)) {
+ /* If we consumed all the input, we don't care about the alignment. */
+ return 1;
+ }
}
return 1;
}
#if defined(__cplusplus) || defined(c_plusplus)
-} /* extern "C" */
+} /* extern "C" */
#endif
diff --git a/modules/brotli/dec/bit_reader.h b/modules/brotli/dec/bit_reader.h
index 1fe393f3f..7096afaa3 100644
--- a/modules/brotli/dec/bit_reader.h
+++ b/modules/brotli/dec/bit_reader.h
@@ -1,16 +1,7 @@
/* Copyright 2013 Google Inc. All Rights Reserved.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+ Distributed under MIT license.
+ See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
*/
/* Bit reading helpers */
@@ -18,129 +9,169 @@
#ifndef BROTLI_DEC_BIT_READER_H_
#define BROTLI_DEC_BIT_READER_H_
-#include
+#include /* memcpy */
+
#include "./port.h"
-#include "./streams.h"
#include "./types.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
-#define BROTLI_READ_SIZE 1024
-/* 128 bytes, plus 8 bytes slack for valid 128-byte BrotliCheckInputAmount with
- some bytes read in val_ of bit reader. */
-#define BROTLI_IMPLICIT_ZEROES 136
-#define BROTLI_IBUF_SIZE (BROTLI_READ_SIZE + BROTLI_IMPLICIT_ZEROES)
-#define BROTLI_IBUF_MASK (BROTLI_READ_SIZE - 1)
+#if (BROTLI_64_BITS)
+#define BROTLI_SHORT_FILL_BIT_WINDOW_READ 4
+typedef uint64_t reg_t;
+#else
+#define BROTLI_SHORT_FILL_BIT_WINDOW_READ 2
+typedef uint32_t reg_t;
+#endif
-/* Masking with this expression turns to a single "Unsigned Bit Field Extract"
- UBFX instruction on ARM. */
-static BROTLI_INLINE uint32_t BitMask(int n) { return ~((0xffffffff) << n); }
+static const uint32_t kBitMask[33] = { 0x0000,
+ 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
+};
+
+static BROTLI_INLINE uint32_t BitMask(uint32_t n) {
+ if (IS_CONSTANT(n) || BROTLI_HAS_UBFX) {
+ /* Masking with this expression turns to a single
+ "Unsigned Bit Field Extract" UBFX instruction on ARM. */
+ return ~((0xffffffffU) << n);
+ } else {
+ return kBitMask[n];
+ }
+}
typedef struct {
-#if (BROTLI_64_BITS_LITTLE_ENDIAN)
- uint64_t val_; /* pre-fetched bits */
-#else
- uint32_t val_; /* pre-fetched bits */
-#endif
- uint32_t bit_pos_; /* current bit-reading position in val_ */
- uint8_t* next_in; /* the byte we're reading from */
- uint32_t avail_in;
- int eos_; /* input stream is finished */
- BrotliInput input_; /* input callback */
-
- /* Input byte buffer, consist of a ringbuffer and a "slack" region where */
- /* bytes from the start of the ringbuffer are copied. */
- uint8_t buf_[BROTLI_IBUF_SIZE];
+ reg_t val_; /* pre-fetched bits */
+ uint32_t bit_pos_; /* current bit-reading position in val_ */
+ const uint8_t* next_in; /* the byte we're reading from */
+ size_t avail_in;
} BrotliBitReader;
+typedef struct {
+ reg_t val_;
+ uint32_t bit_pos_;
+ const uint8_t* next_in;
+ size_t avail_in;
+} BrotliBitReaderState;
+
/* Initializes the bitreader fields. */
-void BrotliInitBitReader(BrotliBitReader* const br, BrotliInput input);
+BROTLI_INTERNAL void BrotliInitBitReader(BrotliBitReader* const br);
/* Ensures that accumulator is not empty. May consume one byte of input.
Returns 0 if data is required but there is no input available.
- For BROTLI_BUILD_PORTABLE this function also prepares bit reader for aligned
+ For BROTLI_ALIGNED_READ this function also prepares bit reader for aligned
reading. */
-int BrotliWarmupBitReader(BrotliBitReader* const br);
-static BROTLI_INLINE void BrotliPullByte(BrotliBitReader* const br);
-/* Pulls data from the input to the the read buffer.
+BROTLI_INTERNAL int BrotliWarmupBitReader(BrotliBitReader* const br);
- Returns 0 if one of:
- - the input callback returned an error, or
- - there is no more input and the position is past the end of the stream.
- - finish is false and less than BROTLI_READ_SIZE are available - a next call
- when more data is available makes it continue including the partially read
- data
+static BROTLI_INLINE void BrotliBitReaderSaveState(
+ BrotliBitReader* const from, BrotliBitReaderState* to) {
+ to->val_ = from->val_;
+ to->bit_pos_ = from->bit_pos_;
+ to->next_in = from->next_in;
+ to->avail_in = from->avail_in;
+}
- If finish is true and the end of the stream is reached,
- BROTLI_IMPLICIT_ZEROES additional zero bytes are copied to the ringbuffer.
-*/
-static BROTLI_INLINE int BrotliReadInput(
- BrotliBitReader* const br, int finish) {
- if (PREDICT_FALSE(br->eos_)) {
- return 0;
- } else {
- size_t i;
- int bytes_read;
- if (br->next_in != br->buf_) {
- for (i = 0; i < br->avail_in; i++) {
- br->buf_[i] = br->next_in[i];
- }
- br->next_in = br->buf_;
- }
- bytes_read = BrotliRead(br->input_, br->next_in + br->avail_in,
- (size_t)(BROTLI_READ_SIZE - br->avail_in));
- if (bytes_read < 0) {
- return 0;
- }
- br->avail_in += (uint32_t)bytes_read;
- if (br->avail_in < BROTLI_READ_SIZE) {
- if (!finish) {
- return 0;
- }
- br->eos_ = 1;
- /* Store BROTLI_IMPLICIT_ZEROES bytes of zero after the stream end. */
- memset(br->next_in + br->avail_in, 0, BROTLI_IMPLICIT_ZEROES);
- br->avail_in += BROTLI_IMPLICIT_ZEROES;
- }
- return 1;
- }
+static BROTLI_INLINE void BrotliBitReaderRestoreState(
+ BrotliBitReader* const to, BrotliBitReaderState* from) {
+ to->val_ = from->val_;
+ to->bit_pos_ = from->bit_pos_;
+ to->next_in = from->next_in;
+ to->avail_in = from->avail_in;
+}
+
+static BROTLI_INLINE uint32_t BrotliGetAvailableBits(
+ const BrotliBitReader* br) {
+ return (BROTLI_64_BITS ? 64 : 32) - br->bit_pos_;
}
/* Returns amount of unread bytes the bit reader still has buffered from the
BrotliInput, including whole bytes in br->val_. */
static BROTLI_INLINE size_t BrotliGetRemainingBytes(BrotliBitReader* br) {
- size_t result = br->avail_in + sizeof(br->val_) - (br->bit_pos_ >> 3);
- if (!br->eos_) {
- return result;
- }
- if (result <= BROTLI_IMPLICIT_ZEROES) {
- return 0;
- }
- return result - BROTLI_IMPLICIT_ZEROES;
+ return br->avail_in + (BrotliGetAvailableBits(br) >> 3);
}
/* Checks if there is at least num bytes left in the input ringbuffer (excluding
- the bits remaining in br->val_). The maximum value for num is
- BROTLI_IMPLICIT_ZEROES bytes. */
+ the bits remaining in br->val_). */
static BROTLI_INLINE int BrotliCheckInputAmount(
BrotliBitReader* const br, size_t num) {
return br->avail_in >= num;
}
+static BROTLI_INLINE uint16_t BrotliLoad16LE(const uint8_t* in) {
+ if (BROTLI_LITTLE_ENDIAN) {
+ return *((const uint16_t*)in);
+ } else if (BROTLI_BIG_ENDIAN) {
+ uint16_t value = *((const uint16_t*)in);
+ return (uint16_t)(((value & 0xFFU) << 8) | ((value & 0xFF00U) >> 8));
+ } else {
+ return (uint16_t)(in[0] | (in[1] << 8));
+ }
+}
+
+static BROTLI_INLINE uint32_t BrotliLoad32LE(const uint8_t* in) {
+ if (BROTLI_LITTLE_ENDIAN) {
+ return *((const uint32_t*)in);
+ } else if (BROTLI_BIG_ENDIAN) {
+ uint32_t value = *((const uint32_t*)in);
+ return ((value & 0xFFU) << 24) | ((value & 0xFF00U) << 8) |
+ ((value & 0xFF0000U) >> 8) | ((value & 0xFF000000U) >> 24);
+ } else {
+ uint32_t value = (uint32_t)(*(in++));
+ value |= (uint32_t)(*(in++)) << 8;
+ value |= (uint32_t)(*(in++)) << 16;
+ value |= (uint32_t)(*(in++)) << 24;
+ return value;
+ }
+}
+
+#if (BROTLI_64_BITS)
+static BROTLI_INLINE uint64_t BrotliLoad64LE(const uint8_t* in) {
+ if (BROTLI_LITTLE_ENDIAN) {
+ return *((const uint64_t*)in);
+ } else if (BROTLI_BIG_ENDIAN) {
+ uint64_t value = *((const uint64_t*)in);
+ return
+ ((value & 0xFFU) << 56) |
+ ((value & 0xFF00U) << 40) |
+ ((value & 0xFF0000U) << 24) |
+ ((value & 0xFF000000U) << 8) |
+ ((value & 0xFF00000000U) >> 8) |
+ ((value & 0xFF0000000000U) >> 24) |
+ ((value & 0xFF000000000000U) >> 40) |
+ ((value & 0xFF00000000000000U) >> 56);
+ } else {
+ uint64_t value = (uint64_t)(*(in++));
+ value |= (uint64_t)(*(in++)) << 8;
+ value |= (uint64_t)(*(in++)) << 16;
+ value |= (uint64_t)(*(in++)) << 24;
+ value |= (uint64_t)(*(in++)) << 32;
+ value |= (uint64_t)(*(in++)) << 40;
+ value |= (uint64_t)(*(in++)) << 48;
+ value |= (uint64_t)(*(in++)) << 56;
+ return value;
+ }
+}
+#endif
+
/* Guarantees that there are at least n_bits + 1 bits in accumulator.
Precondition: accumulator contains at least 1 bit.
n_bits should be in the range [1..24] for regular build. For portable
non-64-bit little endian build only 16 bits are safe to request. */
static BROTLI_INLINE void BrotliFillBitWindow(
- BrotliBitReader* const br, int n_bits) {
-#if (BROTLI_64_BITS_LITTLE_ENDIAN)
+ BrotliBitReader* const br, uint32_t n_bits) {
+#if (BROTLI_64_BITS)
if (!BROTLI_ALIGNED_READ && IS_CONSTANT(n_bits) && (n_bits <= 8)) {
if (br->bit_pos_ >= 56) {
br->val_ >>= 56;
br->bit_pos_ ^= 56; /* here same as -= 56 because of the if condition */
- br->val_ |= (*(const uint64_t*)(br->next_in)) << 8;
+ br->val_ |= BrotliLoad64LE(br->next_in) << 8;
br->avail_in -= 7;
br->next_in += 7;
}
@@ -148,7 +179,7 @@ static BROTLI_INLINE void BrotliFillBitWindow(
if (br->bit_pos_ >= 48) {
br->val_ >>= 48;
br->bit_pos_ ^= 48; /* here same as -= 48 because of the if condition */
- br->val_ |= (*(const uint64_t*)(br->next_in)) << 16;
+ br->val_ |= BrotliLoad64LE(br->next_in) << 16;
br->avail_in -= 6;
br->next_in += 6;
}
@@ -156,17 +187,17 @@ static BROTLI_INLINE void BrotliFillBitWindow(
if (br->bit_pos_ >= 32) {
br->val_ >>= 32;
br->bit_pos_ ^= 32; /* here same as -= 32 because of the if condition */
- br->val_ |= ((uint64_t)(*(const uint32_t*)(br->next_in))) << 32;
- br->avail_in -= 4;
- br->next_in += 4;
+ br->val_ |= ((uint64_t)BrotliLoad32LE(br->next_in)) << 32;
+ br->avail_in -= BROTLI_SHORT_FILL_BIT_WINDOW_READ;
+ br->next_in += BROTLI_SHORT_FILL_BIT_WINDOW_READ;
}
}
-#elif (BROTLI_LITTLE_ENDIAN)
+#else
if (!BROTLI_ALIGNED_READ && IS_CONSTANT(n_bits) && (n_bits <= 8)) {
if (br->bit_pos_ >= 24) {
br->val_ >>= 24;
br->bit_pos_ ^= 24; /* here same as -= 24 because of the if condition */
- br->val_ |= (*(const uint32_t*)(br->next_in)) << 8;
+ br->val_ |= BrotliLoad32LE(br->next_in) << 8;
br->avail_in -= 3;
br->next_in += 3;
}
@@ -174,69 +205,105 @@ static BROTLI_INLINE void BrotliFillBitWindow(
if (br->bit_pos_ >= 16) {
br->val_ >>= 16;
br->bit_pos_ ^= 16; /* here same as -= 16 because of the if condition */
- br->val_ |= ((uint32_t)(*(const uint16_t*)(br->next_in))) << 16;
- br->avail_in -= 2;
- br->next_in += 2;
+ br->val_ |= ((uint32_t)BrotliLoad16LE(br->next_in)) << 16;
+ br->avail_in -= BROTLI_SHORT_FILL_BIT_WINDOW_READ;
+ br->next_in += BROTLI_SHORT_FILL_BIT_WINDOW_READ;
}
}
-#else
- while (br->bit_pos_ >= 16) {
- BrotliPullByte(br);
- }
#endif
}
+/* Mosltly like BrotliFillBitWindow, but guarantees only 16 bits and reads no
+ more than BROTLI_SHORT_FILL_BIT_WINDOW_READ bytes of input. */
+static BROTLI_INLINE void BrotliFillBitWindow16(BrotliBitReader* const br) {
+ BrotliFillBitWindow(br, 17);
+}
+
/* Pulls one byte of input to accumulator. */
-static BROTLI_INLINE void BrotliPullByte(BrotliBitReader* const br) {
+static BROTLI_INLINE int BrotliPullByte(BrotliBitReader* const br) {
+ if (br->avail_in == 0) {
+ return 0;
+ }
br->val_ >>= 8;
-#if (BROTLI_64_BITS_LITTLE_ENDIAN)
- br->val_ |= ((uint64_t)*br->next_in) << 56;
+#if (BROTLI_64_BITS)
+ br->val_ |= ((uint64_t)*br->next_in) << 56;
#else
- br->val_ |= ((uint32_t)*br->next_in) << 24;
+ br->val_ |= ((uint32_t)*br->next_in) << 24;
#endif
br->bit_pos_ -= 8;
--br->avail_in;
++br->next_in;
+ return 1;
}
-/* Like BrotliGetBits, but does not mask the result, it is only guaranteed
-that it has minimum n_bits. */
-static BROTLI_INLINE uint32_t BrotliGetBitsUnmasked(
- BrotliBitReader* const br, int n_bits) {
- BrotliFillBitWindow(br, n_bits);
- return (uint32_t)(br->val_ >> br->bit_pos_);
+/* Returns currently available bits.
+ The number of valid bits could be calclulated by BrotliGetAvailableBits. */
+static BROTLI_INLINE reg_t BrotliGetBitsUnmasked(BrotliBitReader* const br) {
+ return br->val_ >> br->bit_pos_;
+}
+
+/* Like BrotliGetBits, but does not mask the result.
+ The result contains at least 16 valid bits. */
+static BROTLI_INLINE uint32_t BrotliGet16BitsUnmasked(
+ BrotliBitReader* const br) {
+ BrotliFillBitWindow(br, 16);
+ return (uint32_t)BrotliGetBitsUnmasked(br);
}
/* Returns the specified number of bits from br without advancing bit pos. */
static BROTLI_INLINE uint32_t BrotliGetBits(
- BrotliBitReader* const br, int n_bits) {
+ BrotliBitReader* const br, uint32_t n_bits) {
BrotliFillBitWindow(br, n_bits);
- return (uint32_t)(br->val_ >> br->bit_pos_) & BitMask(n_bits);
+ return (uint32_t)BrotliGetBitsUnmasked(br) & BitMask(n_bits);
+}
+
+/* Tries to peek the specified amount of bits. Returns 0, if there is not
+ enough input. */
+static BROTLI_INLINE int BrotliSafeGetBits(
+ BrotliBitReader* const br, uint32_t n_bits, uint32_t* val) {
+ while (BrotliGetAvailableBits(br) < n_bits) {
+ if (!BrotliPullByte(br)) {
+ return 0;
+ }
+ }
+ *val = (uint32_t)BrotliGetBitsUnmasked(br) & BitMask(n_bits);
+ return 1;
}
/* Advances the bit pos by n_bits. */
static BROTLI_INLINE void BrotliDropBits(
- BrotliBitReader* const br, int n_bits) {
- br->bit_pos_ += (uint32_t)n_bits;
+ BrotliBitReader* const br, uint32_t n_bits) {
+ br->bit_pos_ += n_bits;
+}
+
+static BROTLI_INLINE void BrotliBitReaderUnload(BrotliBitReader* br) {
+ uint32_t unused_bytes = BrotliGetAvailableBits(br) >> 3;
+ uint32_t unused_bits = unused_bytes << 3;
+ br->avail_in += unused_bytes;
+ br->next_in -= unused_bytes;
+ if (unused_bits == sizeof(br->val_) << 3) {
+ br->val_ = 0;
+ } else {
+ br->val_ <<= unused_bits;
+ }
+ br->bit_pos_ += unused_bits;
}
/* Reads the specified number of bits from br and advances the bit pos.
Precondition: accumulator MUST contain at least n_bits. */
static BROTLI_INLINE void BrotliTakeBits(
- BrotliBitReader* const br, int n_bits, uint32_t* val) {
- *val = (uint32_t)(br->val_ >> br->bit_pos_) & BitMask(n_bits);
-#ifdef BROTLI_DECODE_DEBUG
- printf("[BrotliReadBits] %d %d %d val: %6x\n",
- (int)br->avail_in, (int)br->bit_pos_, n_bits, (int)*val);
-#endif
- br->bit_pos_ += (uint32_t)n_bits;
+ BrotliBitReader* const br, uint32_t n_bits, uint32_t* val) {
+ *val = (uint32_t)BrotliGetBitsUnmasked(br) & BitMask(n_bits);
+ BROTLI_LOG(("[BrotliReadBits] %d %d %d val: %6x\n",
+ (int)br->avail_in, (int)br->bit_pos_, n_bits, (int)*val));
+ 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(
- BrotliBitReader* const br, int n_bits) {
- if (BROTLI_64_BITS_LITTLE_ENDIAN || (n_bits <= 16)) {
+ BrotliBitReader* const br, uint32_t n_bits) {
+ if (BROTLI_64_BITS || (n_bits <= 16)) {
uint32_t val;
BrotliFillBitWindow(br, n_bits);
BrotliTakeBits(br, n_bits, &val);
@@ -253,14 +320,13 @@ static BROTLI_INLINE uint32_t BrotliReadBits(
}
/* Tries to read the specified amount of bits. Returns 0, if there is not
- enough input. */
+ enough input. n_bits MUST be positive. */
static BROTLI_INLINE int BrotliSafeReadBits(
- BrotliBitReader* const br, int n_bits, uint32_t* val) {
- while (br->bit_pos_ + (uint32_t)n_bits > (sizeof(br->val_) << 3)) {
- if (br->avail_in == 0) {
+ BrotliBitReader* const br, uint32_t n_bits, uint32_t* val) {
+ while (BrotliGetAvailableBits(br) < n_bits) {
+ if (!BrotliPullByte(br)) {
return 0;
}
- BrotliPullByte(br);
}
BrotliTakeBits(br, n_bits, val);
return 1;
@@ -269,7 +335,7 @@ static BROTLI_INLINE int BrotliSafeReadBits(
/* Advances the bit reader position to the next byte boundary and verifies
that any skipped bits are set to zero. */
static BROTLI_INLINE int BrotliJumpToByteBoundary(BrotliBitReader* br) {
- int pad_bits_count = (64 - (int)br->bit_pos_) & 0x7;
+ uint32_t pad_bits_count = BrotliGetAvailableBits(br) & 0x7;
uint32_t pad_bits = 0;
if (pad_bits_count != 0) {
BrotliTakeBits(br, pad_bits_count, &pad_bits);
@@ -280,16 +346,15 @@ static BROTLI_INLINE int BrotliJumpToByteBoundary(BrotliBitReader* br) {
/* Peeks a byte at specified offset.
Precondition: bit reader is parked to a byte boundary.
Returns -1 if operation is not feasible. */
-static BROTLI_INLINE int BrotliPeekByte(BrotliBitReader* br, int offset) {
- int bytes_left = (int)(sizeof(br->val_) - (br->bit_pos_ >> 3));
- if (br->bit_pos_ & 7) {
- return -1;
- }
+static BROTLI_INLINE int BrotliPeekByte(BrotliBitReader* br, size_t offset) {
+ uint32_t available_bits = BrotliGetAvailableBits(br);
+ size_t bytes_left = available_bits >> 3;
+ BROTLI_DCHECK((available_bits & 7) == 0);
if (offset < bytes_left) {
- return (br->val_ >> (br->bit_pos_ + (unsigned)(offset << 3))) & 0xFF;
+ return (BrotliGetBitsUnmasked(br) >> (unsigned)(offset << 3)) & 0xFF;
}
offset -= bytes_left;
- if (offset < (long)br->avail_in) {
+ if (offset < br->avail_in) {
return br->next_in[offset];
}
return -1;
@@ -300,32 +365,19 @@ static BROTLI_INLINE int BrotliPeekByte(BrotliBitReader* br, int offset) {
warmed up again after this. */
static BROTLI_INLINE void BrotliCopyBytes(uint8_t* dest,
BrotliBitReader* br, size_t num) {
- while (br->bit_pos_ + 8 <= (BROTLI_64_BITS_LITTLE_ENDIAN ? 64 : 32)
- && num > 0) {
- *dest = (uint8_t)(br->val_ >> br->bit_pos_);
- br->bit_pos_ += 8;
+ while (BrotliGetAvailableBits(br) >= 8 && num > 0) {
+ *dest = (uint8_t)BrotliGetBitsUnmasked(br);
+ BrotliDropBits(br, 8);
++dest;
--num;
}
memcpy(dest, br->next_in, num);
- br->avail_in -= (uint32_t)num;
+ br->avail_in -= num;
br->next_in += num;
}
-/* Checks that bit reader hasn't read after the end of input.
- Returns 0 if bit reader has used implicit zeroes after the end of input. */
-static BROTLI_INLINE int BrotliIsBitReaderOK(BrotliBitReader* br) {
- size_t remaining_bytes =
- br->avail_in + sizeof(br->val_) - (br->bit_pos_ >> 3);
- return !br->eos_ || (remaining_bytes >= BROTLI_IMPLICIT_ZEROES);
-}
-
-#undef BROTLI_IMPLICIT_ZEROES
-#undef BROTLI_IBUF_SIZE
-#undef BROTLI_IBUF_MASK
-
#if defined(__cplusplus) || defined(c_plusplus)
-} /* extern "C" */
+} /* extern "C" */
#endif
#endif /* BROTLI_DEC_BIT_READER_H_ */
diff --git a/modules/brotli/dec/context.h b/modules/brotli/dec/context.h
index fcc4276ae..37ebe6aeb 100644
--- a/modules/brotli/dec/context.h
+++ b/modules/brotli/dec/context.h
@@ -1,16 +1,7 @@
/* Copyright 2013 Google Inc. All Rights Reserved.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+ Distributed under MIT license.
+ See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
*/
/* Lookup table to map the previous two bytes to a context id.
@@ -111,10 +102,10 @@
#include "./types.h"
enum ContextType {
- CONTEXT_LSB6 = 0,
- CONTEXT_MSB6 = 1,
- CONTEXT_UTF8 = 2,
- CONTEXT_SIGNED = 3
+ CONTEXT_LSB6 = 0,
+ CONTEXT_MSB6 = 1,
+ CONTEXT_UTF8 = 2,
+ CONTEXT_SIGNED = 3
};
/* Common context lookup table for all context modes. */
diff --git a/modules/brotli/dec/decode.c b/modules/brotli/dec/decode.c
index ff99a36ed..d184f24cb 100644
--- a/modules/brotli/dec/decode.c
+++ b/modules/brotli/dec/decode.c
@@ -1,61 +1,49 @@
/* Copyright 2013 Google Inc. All Rights Reserved.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+ Distributed under MIT license.
+ See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
*/
-#include
-#include
-#include
-#include "./bit_reader.h"
-#include "./context.h"
#include "./decode.h"
-#include "./dictionary.h"
-#include "./port.h"
-#include "./transform.h"
-#include "./huffman.h"
-#include "./prefix.h"
#ifdef __ARM_NEON__
#include
#endif
+#include /* free, malloc */
+#include /* memcpy, memset */
+
+#include "./bit_reader.h"
+#include "./context.h"
+#include "./dictionary.h"
+#include "./huffman.h"
+#include "./port.h"
+#include "./prefix.h"
+#include "./state.h"
+#include "./transform.h"
+
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
-#ifdef BROTLI_DECODE_DEBUG
-#define BROTLI_LOG_UINT(name) \
- printf("[%s] %s = %lu\n", __func__, #name, (unsigned long)(name))
-#define BROTLI_LOG_ARRAY_INDEX(array_name, idx) \
- printf("[%s] %s[%lu] = %lu\n", __func__, #array_name, \
- (unsigned long)(idx), (unsigned long)array_name[idx])
-#define BROTLI_LOG(x) printf x
-#else
-#define BROTLI_LOG_UINT(name)
-#define BROTLI_LOG_ARRAY_INDEX(array_name, idx)
-#define BROTLI_LOG(x)
-#endif
+#define BROTLI_FAILURE(CODE) (BROTLI_DUMP(), CODE)
-static const uint8_t kDefaultCodeLength = 8;
-static const uint8_t kCodeLengthRepeatCode = 16;
-static const int kNumLiteralCodes = 256;
-static const int kNumInsertAndCopyCodes = 704;
-static const int kNumBlockLengthCodes = 26;
+#define BROTLI_LOG_UINT(name) \
+ BROTLI_LOG(("[%s] %s = %lu\n", __func__, #name, (unsigned long)(name)))
+#define BROTLI_LOG_ARRAY_INDEX(array_name, idx) \
+ BROTLI_LOG(("[%s] %s[%lu] = %lu\n", __func__, #array_name, \
+ (unsigned long)(idx), (unsigned long)array_name[idx]))
+
+static const uint32_t kDefaultCodeLength = 8;
+static const uint32_t kCodeLengthRepeatCode = 16;
+static const uint32_t kNumLiteralCodes = 256;
+static const uint32_t kNumInsertAndCopyCodes = 704;
+static const uint32_t kNumBlockLengthCodes = 26;
static const int kLiteralContextBits = 6;
static const int kDistanceContextBits = 2;
-#define HUFFMAN_TABLE_BITS 8
-#define HUFFMAN_TABLE_MASK 0xff
+#define HUFFMAN_TABLE_BITS 8U
+#define HUFFMAN_TABLE_MASK 0xff
#define CODE_LENGTH_CODES 18
static const uint8_t kCodeLengthCodeOrder[CODE_LENGTH_CODES] = {
@@ -73,6 +61,47 @@ static const uint8_t kCodeLengthPrefixValue[16] = {
#define NUM_DISTANCE_SHORT_CODES 16
+BrotliState* BrotliCreateState(
+ brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque) {
+ BrotliState* state = 0;
+ if (!alloc_func && !free_func) {
+ state = (BrotliState*)malloc(sizeof(BrotliState));
+ } else if (alloc_func && free_func) {
+ state = (BrotliState*)alloc_func(opaque, sizeof(BrotliState));
+ }
+ if (state == 0) {
+ BROTLI_DUMP();
+ return 0;
+ }
+ BrotliStateInitWithCustomAllocators(state, alloc_func, free_func, opaque);
+ state->error_code = BROTLI_NO_ERROR;
+ return state;
+}
+
+/* Deinitializes and frees BrotliState instance. */
+void BrotliDestroyState(BrotliState* state) {
+ if (!state) {
+ return;
+ } else {
+ brotli_free_func free_func = state->free_func;
+ void* opaque = state->memory_manager_opaque;
+ BrotliStateCleanup(state);
+ free_func(opaque, state);
+ }
+}
+
+/* Saves error code and converts it to BrotliResult */
+static BROTLI_NOINLINE BrotliResult SaveErrorCode(
+ BrotliState* s, BrotliErrorCode e) {
+ s->error_code = (int)e;
+ switch (e) {
+ case BROTLI_SUCCESS: return BROTLI_RESULT_SUCCESS;
+ case BROTLI_NEEDS_MORE_INPUT: return BROTLI_RESULT_NEEDS_MORE_INPUT;
+ case BROTLI_NEEDS_MORE_OUTPUT: return BROTLI_RESULT_NEEDS_MORE_OUTPUT;
+ default: return BROTLI_RESULT_ERROR;
+ }
+}
+
/* Decodes a number in the range [9..24], by reading 1 - 7 bits.
Precondition: bit-reader accumulator has at least 7 bits. */
static uint32_t DecodeWindowBits(BrotliBitReader* br) {
@@ -92,80 +121,69 @@ static uint32_t DecodeWindowBits(BrotliBitReader* br) {
return 17;
}
-static BROTLI_INLINE BROTLI_NO_ASAN void memmove16(
- uint8_t* dst, uint8_t* src) {
-#if BROTLI_SAFE_MEMMOVE
- /* For x86 this compiles to the same binary as signle memcpy.
- On ARM memcpy is not inlined, so it works slower.
- This implementation makes decompression 1% slower than regular one,
- and 2% slower than NEON implementation.
- */
+static BROTLI_INLINE void memmove16(uint8_t* dst, uint8_t* src) {
+#if defined(__ARM_NEON__)
+ vst1q_u8(dst, vld1q_u8(src));
+#else
uint32_t buffer[4];
memcpy(buffer, src, 16);
memcpy(dst, buffer, 16);
-#elif defined(__ARM_NEON__)
- vst1q_u8(dst, vld1q_u8(src));
-#else
- /* memcpy is unsafe for overlapping regions and ASAN detects this.
- But, because of optimizations, it works exactly as memmove:
- copies data to registers first, and then stores them to dst. */
- memcpy(dst, src, 16);
#endif
}
/* Decodes a number in the range [0..255], by reading 1 - 11 bits. */
-static BROTLI_NOINLINE BrotliResult DecodeVarLenUint8(BrotliState* s,
- BrotliBitReader* br, int* value) {
+static BROTLI_NOINLINE BrotliErrorCode DecodeVarLenUint8(BrotliState* s,
+ BrotliBitReader* br, uint32_t* value) {
uint32_t bits;
switch (s->substate_decode_uint8) {
case BROTLI_STATE_DECODE_UINT8_NONE:
if (PREDICT_FALSE(!BrotliSafeReadBits(br, 1, &bits))) {
- return BROTLI_RESULT_NEEDS_MORE_INPUT;
+ return BROTLI_NEEDS_MORE_INPUT;
}
if (bits == 0) {
*value = 0;
- return BROTLI_RESULT_SUCCESS;
+ return BROTLI_SUCCESS;
}
/* No break, transit to the next state. */
case BROTLI_STATE_DECODE_UINT8_SHORT:
if (PREDICT_FALSE(!BrotliSafeReadBits(br, 3, &bits))) {
s->substate_decode_uint8 = BROTLI_STATE_DECODE_UINT8_SHORT;
- return BROTLI_RESULT_NEEDS_MORE_INPUT;
+ return BROTLI_NEEDS_MORE_INPUT;
}
if (bits == 0) {
*value = 1;
s->substate_decode_uint8 = BROTLI_STATE_DECODE_UINT8_NONE;
- return BROTLI_RESULT_SUCCESS;
+ return BROTLI_SUCCESS;
}
/* Use output value as a temporary storage. It MUST be persisted. */
- *value = (int)bits;
+ *value = bits;
/* No break, transit to the next state. */
case BROTLI_STATE_DECODE_UINT8_LONG:
if (PREDICT_FALSE(!BrotliSafeReadBits(br, *value, &bits))) {
s->substate_decode_uint8 = BROTLI_STATE_DECODE_UINT8_LONG;
- return BROTLI_RESULT_NEEDS_MORE_INPUT;
+ return BROTLI_NEEDS_MORE_INPUT;
}
- *value = (1 << *value) + (int)bits;
+ *value = (1U << *value) + bits;
s->substate_decode_uint8 = BROTLI_STATE_DECODE_UINT8_NONE;
- return BROTLI_RESULT_SUCCESS;
+ return BROTLI_SUCCESS;
default:
- return BROTLI_FAILURE();
+ return BROTLI_FAILURE(BROTLI_ERROR_UNREACHABLE);
}
}
/* Decodes a metablock length and flags by reading 2 - 31 bits. */
-static BrotliResult BROTLI_NOINLINE DecodeMetaBlockLength(BrotliState* s,
- BrotliBitReader* br) {
+static BrotliErrorCode BROTLI_NOINLINE DecodeMetaBlockLength(
+ BrotliState* s, BrotliBitReader* br) {
uint32_t bits;
int i;
for (;;) {
switch (s->substate_metablock_header) {
case BROTLI_STATE_METABLOCK_HEADER_NONE:
if (!BrotliSafeReadBits(br, 1, &bits)) {
- return BROTLI_RESULT_NEEDS_MORE_INPUT;
+ return BROTLI_NEEDS_MORE_INPUT;
}
s->is_last_metablock = (uint8_t)bits;
s->meta_block_remaining_len = 0;
@@ -180,18 +198,18 @@ static BrotliResult BROTLI_NOINLINE DecodeMetaBlockLength(BrotliState* s,
case BROTLI_STATE_METABLOCK_HEADER_EMPTY:
if (!BrotliSafeReadBits(br, 1, &bits)) {
- return BROTLI_RESULT_NEEDS_MORE_INPUT;
+ return BROTLI_NEEDS_MORE_INPUT;
}
if (bits) {
s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_NONE;
- return BROTLI_RESULT_SUCCESS;
+ return BROTLI_SUCCESS;
}
s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_NIBBLES;
/* No break, transit to the next state. */
case BROTLI_STATE_METABLOCK_HEADER_NIBBLES:
if (!BrotliSafeReadBits(br, 2, &bits)) {
- return BROTLI_RESULT_NEEDS_MORE_INPUT;
+ return BROTLI_NEEDS_MORE_INPUT;
}
s->size_nibbles = (uint8_t)(bits + 4);
s->loop_counter = 0;
@@ -208,10 +226,10 @@ static BrotliResult BROTLI_NOINLINE DecodeMetaBlockLength(BrotliState* s,
for (; i < s->size_nibbles; ++i) {
if (!BrotliSafeReadBits(br, 4, &bits)) {
s->loop_counter = i;
- return BROTLI_RESULT_NEEDS_MORE_INPUT;
+ return BROTLI_NEEDS_MORE_INPUT;
}
if (i + 1 == s->size_nibbles && s->size_nibbles > 4 && bits == 0) {
- return BROTLI_FAILURE();
+ return BROTLI_FAILURE(BROTLI_ERROR_FORMAT_EXUBERANT_NIBBLE);
}
s->meta_block_remaining_len |= (int)(bits << (i * 4));
}
@@ -220,33 +238,33 @@ static BrotliResult BROTLI_NOINLINE DecodeMetaBlockLength(BrotliState* s,
/* No break, transit to the next state. */
case BROTLI_STATE_METABLOCK_HEADER_UNCOMPRESSED:
- if (!s->is_last_metablock && !s->is_metadata) {
+ if (!s->is_last_metablock) {
if (!BrotliSafeReadBits(br, 1, &bits)) {
- return BROTLI_RESULT_NEEDS_MORE_INPUT;
+ return BROTLI_NEEDS_MORE_INPUT;
}
s->is_uncompressed = (uint8_t)bits;
}
++s->meta_block_remaining_len;
s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_NONE;
- return BROTLI_RESULT_SUCCESS;
+ return BROTLI_SUCCESS;
case BROTLI_STATE_METABLOCK_HEADER_RESERVED:
if (!BrotliSafeReadBits(br, 1, &bits)) {
- return BROTLI_RESULT_NEEDS_MORE_INPUT;
+ return BROTLI_NEEDS_MORE_INPUT;
}
if (bits != 0) {
- return BROTLI_FAILURE();
+ return BROTLI_FAILURE(BROTLI_ERROR_FORMAT_RESERVED);
}
s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_BYTES;
/* No break, transit to the next state. */
case BROTLI_STATE_METABLOCK_HEADER_BYTES:
if (!BrotliSafeReadBits(br, 2, &bits)) {
- return BROTLI_RESULT_NEEDS_MORE_INPUT;
+ return BROTLI_NEEDS_MORE_INPUT;
}
if (bits == 0) {
s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_NONE;
- return BROTLI_RESULT_SUCCESS;
+ return BROTLI_SUCCESS;
}
s->size_nibbles = (uint8_t)bits;
s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_METADATA;
@@ -257,44 +275,110 @@ static BrotliResult BROTLI_NOINLINE DecodeMetaBlockLength(BrotliState* s,
for (; i < s->size_nibbles; ++i) {
if (!BrotliSafeReadBits(br, 8, &bits)) {
s->loop_counter = i;
- return BROTLI_RESULT_NEEDS_MORE_INPUT;
+ return BROTLI_NEEDS_MORE_INPUT;
}
if (i + 1 == s->size_nibbles && s->size_nibbles > 1 && bits == 0) {
- return BROTLI_FAILURE();
+ return BROTLI_FAILURE(BROTLI_ERROR_FORMAT_EXUBERANT_META_NIBBLE);
}
s->meta_block_remaining_len |= (int)(bits << (i * 8));
}
- s->substate_metablock_header =
- BROTLI_STATE_METABLOCK_HEADER_UNCOMPRESSED;
- break;
+ ++s->meta_block_remaining_len;
+ s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_NONE;
+ return BROTLI_SUCCESS;
default:
- return BROTLI_FAILURE();
+ return BROTLI_FAILURE(BROTLI_ERROR_UNREACHABLE);
}
}
}
-/* Decodes the next Huffman code from bit-stream. Reads 0 - 15 bits. */
-static BROTLI_INLINE int ReadSymbol(const HuffmanCode* table,
- BrotliBitReader* br) {
- /* Read the bits for two reads at once. */
- uint32_t val = BrotliGetBitsUnmasked(br, 15);
- table += val & HUFFMAN_TABLE_MASK;
+/* Decodes the Huffman code.
+ This method doesn't read data from the bit reader, BUT drops the amount of
+ bits that correspond to the decoded symbol.
+ bits MUST contain at least 15 (BROTLI_HUFFMAN_MAX_CODE_LENGTH) valid bits. */
+static BROTLI_INLINE uint32_t DecodeSymbol(uint32_t bits,
+ const HuffmanCode* table,
+ BrotliBitReader* br) {
+ table += bits & HUFFMAN_TABLE_MASK;
if (table->bits > HUFFMAN_TABLE_BITS) {
- int nbits = table->bits - HUFFMAN_TABLE_BITS;
+ uint32_t nbits = table->bits - HUFFMAN_TABLE_BITS;
BrotliDropBits(br, HUFFMAN_TABLE_BITS);
table += table->value;
- table += (int)(val >> HUFFMAN_TABLE_BITS) & (int)BitMask(nbits);
+ table += (bits >> HUFFMAN_TABLE_BITS) & BitMask(nbits);
}
BrotliDropBits(br, table->bits);
return table->value;
}
-/* Makes a look-up in first level Huffman table. Peeks 8 bits. */
-static BROTLI_INLINE void PreloadSymbol(const HuffmanCode* table,
+/* Reads and decodes the next Huffman code from bit-stream.
+ This method peeks 16 bits of input and drops 0 - 15 of them. */
+static BROTLI_INLINE uint32_t ReadSymbol(const HuffmanCode* table,
+ BrotliBitReader* br) {
+ return DecodeSymbol(BrotliGet16BitsUnmasked(br), table, br);
+}
+
+/* Same as DecodeSymbol, but it is known that there is less than 15 bits of
+ input are currently available. */
+static BROTLI_NOINLINE int SafeDecodeSymbol(const HuffmanCode* table,
+ BrotliBitReader* br,
+ uint32_t* result) {
+ uint32_t val;
+ uint32_t available_bits = BrotliGetAvailableBits(br);
+ if (available_bits == 0) {
+ if (table->bits == 0) {
+ *result = table->value;
+ return 1;
+ }
+ return 0; /* No valid bits at all. */
+ }
+ val = (uint32_t)BrotliGetBitsUnmasked(br);
+ table += val & HUFFMAN_TABLE_MASK;
+ if (table->bits <= HUFFMAN_TABLE_BITS) {
+ if (table->bits <= available_bits) {
+ BrotliDropBits(br, table->bits);
+ *result = table->value;
+ return 1;
+ } else {
+ return 0; /* Not enough bits for the first level. */
+ }
+ }
+ if (available_bits <= HUFFMAN_TABLE_BITS) {
+ return 0; /* Not enough bits to move to the second level. */
+ }
+
+ /* Speculatively drop HUFFMAN_TABLE_BITS. */
+ val = (val & BitMask(table->bits)) >> HUFFMAN_TABLE_BITS;
+ available_bits -= HUFFMAN_TABLE_BITS;
+ table += table->value + val;
+ if (available_bits < table->bits) {
+ return 0; /* Not enough bits for the second level. */
+ }
+
+ BrotliDropBits(br, HUFFMAN_TABLE_BITS + table->bits);
+ *result = table->value;
+ return 1;
+}
+
+static BROTLI_INLINE int SafeReadSymbol(const HuffmanCode* table,
BrotliBitReader* br,
- unsigned* bits,
- unsigned* value) {
+ uint32_t* result) {
+ uint32_t val;
+ if (PREDICT_TRUE(BrotliSafeGetBits(br, 15, &val))) {
+ *result = DecodeSymbol(val, table, br);
+ return 1;
+ }
+ return SafeDecodeSymbol(table, br, result);
+}
+
+/* Makes a look-up in first level Huffman table. Peeks 8 bits. */
+static BROTLI_INLINE void PreloadSymbol(int safe,
+ const HuffmanCode* table,
+ BrotliBitReader* br,
+ uint32_t* bits,
+ uint32_t* value) {
+ if (safe) {
+ return;
+ }
table += BrotliGetBits(br, HUFFMAN_TABLE_BITS);
*bits = table->bits;
*value = table->value;
@@ -302,28 +386,28 @@ static BROTLI_INLINE void PreloadSymbol(const HuffmanCode* table,
/* Decodes the next Huffman code using data prepared by PreloadSymbol.
Reads 0 - 15 bits. Also peeks 8 following bits. */
-static BROTLI_INLINE unsigned ReadPreloadedSymbol(const HuffmanCode* table,
+static BROTLI_INLINE uint32_t ReadPreloadedSymbol(const HuffmanCode* table,
BrotliBitReader* br,
- unsigned* bits,
- unsigned* value) {
- unsigned result = *value;
+ uint32_t* bits,
+ uint32_t* value) {
+ uint32_t result = *value;
if (PREDICT_FALSE(*bits > HUFFMAN_TABLE_BITS)) {
- uint32_t val = BrotliGetBitsUnmasked(br, 15);
+ uint32_t val = BrotliGet16BitsUnmasked(br);
const HuffmanCode* ext = table + (val & HUFFMAN_TABLE_MASK) + *value;
- int mask = (int)BitMask((int)(*bits - HUFFMAN_TABLE_BITS));
+ uint32_t mask = BitMask((*bits - HUFFMAN_TABLE_BITS));
BrotliDropBits(br, HUFFMAN_TABLE_BITS);
- ext += (int)(val >> HUFFMAN_TABLE_BITS) & mask;
+ ext += (val >> HUFFMAN_TABLE_BITS) & mask;
BrotliDropBits(br, ext->bits);
result = ext->value;
} else {
- BrotliDropBits(br, (int)*bits);
+ BrotliDropBits(br, *bits);
}
- PreloadSymbol(table, br, bits, value);
+ PreloadSymbol(0, table, br, bits, value);
return result;
}
-static BROTLI_INLINE int Log2Floor(int x) {
- int result = 0;
+static BROTLI_INLINE uint32_t Log2Floor(uint32_t x) {
+ uint32_t result = 0;
while (x) {
x >>= 1;
++result;
@@ -331,6 +415,253 @@ static BROTLI_INLINE int Log2Floor(int x) {
return result;
}
+/* Reads (s->symbol + 1) symbols.
+ Totally 1..4 symbols are read, 1..10 bits each.
+ The list of symbols MUST NOT contain duplicates.
+ */
+static BrotliErrorCode ReadSimpleHuffmanSymbols(uint32_t alphabet_size,
+ BrotliState* s) {
+ /* max_bits == 1..10; symbol == 0..3; 1..40 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;
+ while (i <= num_symbols) {
+ uint32_t v;
+ if (PREDICT_FALSE(!BrotliSafeReadBits(br, max_bits, &v))) {
+ s->sub_loop_counter = i;
+ s->substate_huffman = BROTLI_STATE_HUFFMAN_SIMPLE_READ;
+ return BROTLI_NEEDS_MORE_INPUT;
+ }
+ if (v >= alphabet_size) {
+ return BROTLI_FAILURE(BROTLI_ERROR_FORMAT_SIMPLE_HUFFMAN_ALPHABET);
+ }
+ s->symbols_lists_array[i] = (uint16_t)v;
+ BROTLI_LOG_UINT(s->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]) {
+ return BROTLI_FAILURE(BROTLI_ERROR_FORMAT_SIMPLE_HUFFMAN_SAME);
+ }
+ }
+ }
+
+ return BROTLI_SUCCESS;
+}
+
+/* Process single decoded symbol code length:
+ A) reset the repeat variable
+ B) remember code length (if it is not 0)
+ C) extend corredponding index-chain
+ D) reduce the huffman space
+ E) update the histogram
+ */
+static BROTLI_INLINE void ProcessSingleCodeLength(uint32_t code_len,
+ uint32_t* symbol, uint32_t* repeat, uint32_t* space,
+ uint32_t* prev_code_len, uint16_t* symbol_lists,
+ uint16_t* code_length_histo, int* next_symbol) {
+ *repeat = 0;
+ if (code_len != 0) { /* code_len == 1..15 */
+ symbol_lists[next_symbol[code_len]] = (uint16_t)(*symbol);
+ next_symbol[code_len] = (int)(*symbol);
+ *prev_code_len = code_len;
+ *space -= 32768U >> code_len;
+ code_length_histo[code_len]++;
+ BROTLI_LOG(("[ReadHuffmanCode] code_length[%d] = %d\n", *symbol, code_len));
+ }
+ (*symbol)++;
+}
+
+/* Process repeated symbol code length.
+ A) Check if it is the extension of previous repeat sequence; if the decoded
+ value is not kCodeLengthRepeatCode, then it is a new symbol-skip
+ B) Update repeat variable
+ C) Check if operation is feasible (fits alphapet)
+ D) For each symbol do the same operations as in ProcessSingleCodeLength
+
+ PRECONDITION: code_len == kCodeLengthRepeatCode or kCodeLengthRepeatCode + 1
+ */
+static BROTLI_INLINE void ProcessRepeatedCodeLength(uint32_t code_len,
+ uint32_t repeat_delta, uint32_t alphabet_size, uint32_t* symbol,
+ uint32_t* repeat, uint32_t* space, uint32_t* prev_code_len,
+ uint32_t* repeat_code_len, uint16_t* symbol_lists,
+ uint16_t* code_length_histo, int* next_symbol) {
+ uint32_t old_repeat;
+ uint32_t new_len = 0;
+ if (code_len == kCodeLengthRepeatCode) {
+ new_len = *prev_code_len;
+ }
+ if (*repeat_code_len != new_len) {
+ *repeat = 0;
+ *repeat_code_len = new_len;
+ }
+ old_repeat = *repeat;
+ if (*repeat > 0) {
+ *repeat -= 2;
+ *repeat <<= code_len - 14U;
+ }
+ *repeat += repeat_delta + 3U;
+ repeat_delta = *repeat - old_repeat;
+ if (*symbol + repeat_delta > alphabet_size) {
+ BROTLI_DUMP();
+ *symbol = alphabet_size;
+ *space = 0xFFFFF;
+ return;
+ }
+ BROTLI_LOG(("[ReadHuffmanCode] code_length[%d..%d] = %d\n",
+ *symbol, *symbol + repeat_delta - 1, *repeat_code_len));
+ if (*repeat_code_len != 0) {
+ unsigned last = *symbol + repeat_delta;
+ int next = next_symbol[*repeat_code_len];
+ do {
+ symbol_lists[next] = (uint16_t)*symbol;
+ next = (int)*symbol;
+ } while (++(*symbol) != last);
+ next_symbol[*repeat_code_len] = next;
+ *space -= repeat_delta << (15 - *repeat_code_len);
+ code_length_histo[*repeat_code_len] =
+ (uint16_t)(code_length_histo[*repeat_code_len] + repeat_delta);
+ } else {
+ *symbol += repeat_delta;
+ }
+}
+
+/* Reads and decodes symbol codelengths. */
+static BrotliErrorCode ReadSymbolCodeLengths(
+ uint32_t alphabet_size, BrotliState* 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;
+ if (!BrotliWarmupBitReader(br)) {
+ return BROTLI_NEEDS_MORE_INPUT;
+ }
+ while (symbol < alphabet_size && space > 0) {
+ const HuffmanCode* p = s->table;
+ uint32_t code_len;
+ 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;
+ return BROTLI_NEEDS_MORE_INPUT;
+ }
+ BrotliFillBitWindow16(br);
+ p += BrotliGetBitsUnmasked(br) &
+ BitMask(BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH);
+ BrotliDropBits(br, p->bits); /* Use 1..5 bits */
+ code_len = p->value; /* code_len == 0..17 */
+ if (code_len < kCodeLengthRepeatCode) {
+ ProcessSingleCodeLength(code_len, &symbol, &repeat, &space,
+ &prev_code_len, symbol_lists, code_length_histo, next_symbol);
+ } else { /* code_len == 16..17, extra_bits == 2..3 */
+ uint32_t repeat_delta =
+ (uint32_t)BrotliGetBitsUnmasked(br) & BitMask(code_len - 14U);
+ BrotliDropBits(br, code_len - 14U);
+ ProcessRepeatedCodeLength(code_len, repeat_delta, alphabet_size,
+ &symbol, &repeat, &space, &prev_code_len, &repeat_code_len,
+ symbol_lists, code_length_histo, next_symbol);
+ }
+ }
+ s->space = space;
+ return BROTLI_SUCCESS;
+}
+
+static BrotliErrorCode SafeReadSymbolCodeLengths(
+ uint32_t alphabet_size, BrotliState* s) {
+ BrotliBitReader* br = &s->br;
+ while (s->symbol < alphabet_size && s->space > 0) {
+ const HuffmanCode* p = s->table;
+ uint32_t code_len;
+ uint32_t bits = 0;
+ uint32_t available_bits = BrotliGetAvailableBits(br);
+ if (available_bits != 0) {
+ bits = (uint32_t)BrotliGetBitsUnmasked(br);
+ }
+ p += bits & BitMask(BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH);
+ if (p->bits > available_bits) goto pullMoreInput;
+ code_len = p->value; /* code_len == 0..17 */
+ if (code_len < kCodeLengthRepeatCode) {
+ BrotliDropBits(br, p->bits);
+ ProcessSingleCodeLength(code_len, &s->symbol, &s->repeat, &s->space,
+ &s->prev_code_len, s->symbol_lists, s->code_length_histo,
+ s->next_symbol);
+ } else { /* code_len == 16..17, extra_bits == 2..3 */
+ uint32_t extra_bits = code_len - 14U;
+ uint32_t repeat_delta = (bits >> p->bits) & BitMask(extra_bits);
+ if (available_bits < p->bits + extra_bits) goto pullMoreInput;
+ BrotliDropBits(br, p->bits + 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);
+ }
+ continue;
+
+pullMoreInput:
+ if (!BrotliPullByte(br)) {
+ return BROTLI_NEEDS_MORE_INPUT;
+ }
+ }
+ return BROTLI_SUCCESS;
+}
+
+/* Reads and decodes 15..18 codes using static prefix code.
+ Each code is 2..4 bits long. In total 30..72 bits are used. */
+static BrotliErrorCode ReadCodeLengthCodeLengths(BrotliState* s) {
+ BrotliBitReader* br = &s->br;
+ uint32_t num_codes = s->repeat;
+ unsigned space = s->space;
+ uint32_t i = s->sub_loop_counter;
+ for (; i < CODE_LENGTH_CODES; ++i) {
+ const uint8_t code_len_idx = kCodeLengthCodeOrder[i];
+ uint32_t ix;
+ uint32_t v;
+ if (PREDICT_FALSE(!BrotliSafeGetBits(br, 4, &ix))) {
+ uint32_t available_bits = BrotliGetAvailableBits(br);
+ if (available_bits != 0) {
+ ix = BrotliGetBitsUnmasked(br) & 0xF;
+ } else {
+ 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;
+ return BROTLI_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);
+ if (v != 0) {
+ space = space - (32U >> v);
+ ++num_codes;
+ ++s->code_length_histo[v];
+ if (space - 1U >= 32U) {
+ /* space is 0 or wrapped around */
+ break;
+ }
+ }
+ }
+ if (!(num_codes == 1 || space == 0)) {
+ return BROTLI_FAILURE(BROTLI_ERROR_FORMAT_CL_SPACE);
+ }
+ return BROTLI_SUCCESS;
+}
+
/* Decodes the Huffman tables.
There are 2 scenarios:
A) Huffman code contains only few symbols (1..4). Those symbols are read
@@ -343,97 +674,84 @@ static BROTLI_INLINE int Log2Floor(int x) {
B.2) Decoded table is used to decode code lengths of symbols in resulting
Huffman table. In worst case 3520 bits are read.
*/
-static BrotliResult ReadHuffmanCode(int alphabet_size,
- HuffmanCode* table,
- int* opt_table_size,
- BrotliState* s) {
+static BrotliErrorCode ReadHuffmanCode(uint32_t alphabet_size,
+ HuffmanCode* table,
+ uint32_t* opt_table_size,
+ BrotliState* s) {
BrotliBitReader* br = &s->br;
- int i;
/* Unnecessary masking, but might be good for safety. */
alphabet_size &= 0x3ff;
/* State machine */
switch (s->substate_huffman) {
case BROTLI_STATE_HUFFMAN_NONE:
- if (!BrotliCheckInputAmount(br, 32)) {
- return BROTLI_RESULT_NEEDS_MORE_INPUT;
+ if (!BrotliSafeReadBits(br, 2, &s->sub_loop_counter)) {
+ return BROTLI_NEEDS_MORE_INPUT;
}
- i = (int)BrotliReadBits(br, 2);
+ BROTLI_LOG_UINT(s->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 */
- BROTLI_LOG_UINT((unsigned)i);
- if (i == 1) {
- /* Read symbols, codes & code lengths directly. */
- int max_bits = Log2Floor(alphabet_size - 1);
- uint32_t num_symbols = BrotliReadBits(br, 2);
- for (i = 0; i < 4; ++i) {
- s->symbols_lists_array[i] = 0;
- }
- i = 0;
- /* max_bits == 0..10; symbol == 0..3; 0..40 bits will be read. */
- do {
- uint32_t v = BrotliReadBits(br, max_bits);
- if (v >= alphabet_size) {
- return BROTLI_FAILURE();
- }
- s->symbols_lists_array[i] = (uint16_t)v;
- BROTLI_LOG_UINT(s->symbols_lists_array[i]);
- } while (++i <= num_symbols);
- for (i = 0; i < num_symbols; ++i) {
- int k = i + 1;
- for (; k <= num_symbols; ++k) {
- if (s->symbols_lists_array[i] == s->symbols_lists_array[k]) {
- return BROTLI_FAILURE();
- }
- }
- }
- if (num_symbols == 3) {
- num_symbols += BrotliReadBits(br, 1);
- }
- BROTLI_LOG_UINT(num_symbols);
- i = BrotliBuildSimpleHuffmanTable(
- table, HUFFMAN_TABLE_BITS, s->symbols_lists_array, num_symbols);
- if (opt_table_size) {
- *opt_table_size = i;
- }
- s->substate_huffman = BROTLI_STATE_HUFFMAN_NONE;
- return BROTLI_RESULT_SUCCESS;
- } else { /* Decode Huffman-coded code lengths. */
- int8_t num_codes = 0;
- unsigned space = 32;
+ 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]) *
(BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH + 1));
memset(&s->code_length_code_lengths[0], 0,
- sizeof(s->code_length_code_lengths));
- /* 15..18 codes will be read, 2..4 bits each; 30..72 bits totally. */
- for (; i < CODE_LENGTH_CODES; ++i) {
- const uint8_t code_len_idx = kCodeLengthCodeOrder[i];
- uint8_t ix = (uint8_t)BrotliGetBits(br, 4);
- uint8_t v = kCodeLengthPrefixValue[ix];
- BrotliDropBits(br, kCodeLengthPrefixLength[ix]);
- s->code_length_code_lengths[code_len_idx] = v;
- BROTLI_LOG_ARRAY_INDEX(s->code_length_code_lengths, code_len_idx);
- if (v != 0) {
- space = space - (32U >> v);
- ++num_codes;
- ++s->code_length_histo[v];
- if (space - 1U >= 32U) {
- /* space is 0 or wrapped around */
- break;
- }
- }
- }
- if (!(num_codes == 1 || space == 0)) {
- return BROTLI_FAILURE();
+ sizeof(s->code_length_code_lengths));
+ s->substate_huffman = BROTLI_STATE_HUFFMAN_COMPLEX;
+ goto Complex;
+ }
+ /* No break, transit to the next state. */
+
+ 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;
+ return BROTLI_NEEDS_MORE_INPUT;
+ }
+ s->sub_loop_counter = 0;
+ /* No break, transit to the next state. */
+ case BROTLI_STATE_HUFFMAN_SIMPLE_READ: {
+ BrotliErrorCode result = ReadSimpleHuffmanSymbols(alphabet_size, s);
+ if (result != BROTLI_SUCCESS) {
+ return result;
+ }
+ /* No break, transit to the next state. */
+ }
+ case BROTLI_STATE_HUFFMAN_SIMPLE_BUILD: {
+ uint32_t table_size;
+ if (s->symbol == 3) {
+ uint32_t bits;
+ if (!BrotliSafeReadBits(br, 1, &bits)) {
+ s->substate_huffman = BROTLI_STATE_HUFFMAN_SIMPLE_BUILD;
+ return BROTLI_NEEDS_MORE_INPUT;
}
+ s->symbol += bits;
+ }
+ BROTLI_LOG_UINT(s->symbol);
+ table_size = BrotliBuildSimpleHuffmanTable(
+ table, HUFFMAN_TABLE_BITS, s->symbols_lists_array, s->symbol);
+ if (opt_table_size) {
+ *opt_table_size = table_size;
+ }
+ s->substate_huffman = BROTLI_STATE_HUFFMAN_NONE;
+ return BROTLI_SUCCESS;
+ }
+
+Complex: /* Decode Huffman-coded code lengths. */
+ case BROTLI_STATE_HUFFMAN_COMPLEX: {
+ uint32_t i;
+ BrotliErrorCode result = ReadCodeLengthCodeLengths(s);
+ if (result != BROTLI_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));
for (i = 0; i <= BROTLI_HUFFMAN_MAX_CODE_LENGTH; ++i) {
- s->next_symbol[i] = i - (BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1);
- s->symbol_lists[i - (BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1)] = 0xFFFF;
+ s->next_symbol[i] = (int)i - (BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1);
+ s->symbol_lists[(int)i - (BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1)] = 0xFFFF;
}
s->symbol = 0;
@@ -443,107 +761,71 @@ static BrotliResult ReadHuffmanCode(int alphabet_size,
s->space = 32768;
s->substate_huffman = BROTLI_STATE_HUFFMAN_LENGTH_SYMBOLS;
/* No break, transit to the next state. */
+ }
case BROTLI_STATE_HUFFMAN_LENGTH_SYMBOLS: {
- uint32_t symbol = s->symbol;
- uint32_t repeat = s->repeat;
- uint32_t space = s->space;
- uint8_t prev_code_len = s->prev_code_len;
- uint8_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;
- while (symbol < alphabet_size && space > 0) {
- const HuffmanCode* p = s->table;
- uint8_t code_len;
- if (!BrotliCheckInputAmount(br, 8)) {
- s->symbol = symbol;
- s->repeat = repeat;
- s->prev_code_len = prev_code_len;
- s->repeat_code_len = repeat_code_len;
- s->space = space;
- return BROTLI_RESULT_NEEDS_MORE_INPUT;
- }
- p += BrotliGetBits(br, BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH);
- BrotliDropBits(br, p->bits); /* Use 1..5 bits */
- code_len = (uint8_t)p->value; /* code_len == 0..17 */
- if (code_len < kCodeLengthRepeatCode) {
- repeat = 0;
- if (code_len != 0) { /* code_len == 1..15 */
- symbol_lists[next_symbol[code_len]] = (uint16_t)symbol;
- next_symbol[code_len] = (int)symbol;
- prev_code_len = code_len;
- space -= 32768U >> code_len;
- code_length_histo[code_len]++;
- }
- symbol++;
- } else { /* code_len == 16..17, extra_bits == 2..3 */
- uint32_t repeat_delta = BrotliReadBits(br, code_len - 14);
- uint32_t old_repeat;
- uint8_t new_len = 0;
- if (code_len == kCodeLengthRepeatCode) {
- new_len = prev_code_len;
- }
- if (repeat_code_len != new_len) {
- repeat = 0;
- repeat_code_len = new_len;
- }
- old_repeat = repeat;
- if (repeat > 0) {
- repeat -= 2;
- repeat <<= code_len - 14;
- }
- repeat += repeat_delta + 3;
- repeat_delta = repeat - old_repeat; /* repeat_delta >= 3 */
- /* So, for extra 2..3 bits we produce more than 2 symbols.
- Consequently, at most 5 bits per symbol are used. */
- if (symbol + repeat_delta > alphabet_size) {
- return BROTLI_FAILURE();
- }
- if (repeat_code_len != 0) {
- unsigned last = symbol + repeat_delta;
- i = next_symbol[repeat_code_len];
- do {
- symbol_lists[i] = (uint16_t)symbol;
- i = (int)symbol;
- } while (++symbol != last);
- next_symbol[repeat_code_len] = i;
- space -= repeat_delta << (15 - repeat_code_len);
- code_length_histo[repeat_code_len] = (uint16_t)
- (code_length_histo[repeat_code_len] + repeat_delta);
- } else {
- symbol += repeat_delta;
- }
- }
+ uint32_t table_size;
+ BrotliErrorCode result = ReadSymbolCodeLengths(alphabet_size, s);
+ if (result == BROTLI_NEEDS_MORE_INPUT) {
+ result = SafeReadSymbolCodeLengths(alphabet_size, s);
}
- if (space != 0) {
- BROTLI_LOG(("[ReadHuffmanCode] space = %d\n", space));
- return BROTLI_FAILURE();
+ if (result != BROTLI_SUCCESS) {
+ return result;
}
- {
- int table_size = BrotliBuildHuffmanTable(
- table, HUFFMAN_TABLE_BITS, symbol_lists,
- s->code_length_histo);
- if (opt_table_size) {
- *opt_table_size = table_size;
- }
+
+ if (s->space != 0) {
+ BROTLI_LOG(("[ReadHuffmanCode] space = %d\n", s->space));
+ return BROTLI_FAILURE(BROTLI_ERROR_FORMAT_HUFFMAN_SPACE);
+ }
+ table_size = BrotliBuildHuffmanTable(
+ table, HUFFMAN_TABLE_BITS, s->symbol_lists, s->code_length_histo);
+ if (opt_table_size) {
+ *opt_table_size = table_size;
}
s->substate_huffman = BROTLI_STATE_HUFFMAN_NONE;
- return BROTLI_RESULT_SUCCESS;
+ return BROTLI_SUCCESS;
}
default:
- return BROTLI_FAILURE();
+ return BROTLI_FAILURE(BROTLI_ERROR_UNREACHABLE);
}
}
/* Decodes a block length by reading 3..39 bits. */
-static BROTLI_INLINE int ReadBlockLength(const HuffmanCode* table,
- BrotliBitReader* br) {
- int code;
- int nbits;
+static BROTLI_INLINE uint32_t ReadBlockLength(const HuffmanCode* table,
+ BrotliBitReader* br) {
+ uint32_t code;
+ uint32_t nbits;
code = ReadSymbol(table, br);
nbits = kBlockLengthPrefixCode[code].nbits; /* nbits == 2..24 */
- return kBlockLengthPrefixCode[code].offset + (int)BrotliReadBits(br, nbits);
+ return kBlockLengthPrefixCode[code].offset + BrotliReadBits(br, nbits);
+}
+
+/* WARNING: if state is not BROTLI_STATE_READ_BLOCK_LENGTH_NONE, then
+ reading can't be continued with ReadBlockLength. */
+static BROTLI_INLINE int SafeReadBlockLength(BrotliState* s,
+ uint32_t* result,
+ const HuffmanCode* table,
+ BrotliBitReader* br) {
+ uint32_t index;
+ if (s->substate_read_block_length == BROTLI_STATE_READ_BLOCK_LENGTH_NONE) {
+ if (!SafeReadSymbol(table, br, &index)) {
+ return 0;
+ }
+ } else {
+ index = s->block_length_index;
+ }
+ {
+ uint32_t bits;
+ uint32_t nbits = kBlockLengthPrefixCode[index].nbits; /* nbits == 2..24 */
+ if (!BrotliSafeReadBits(br, nbits, &bits)) {
+ s->block_length_index = index;
+ s->substate_read_block_length = BROTLI_STATE_READ_BLOCK_LENGTH_SUFFIX;
+ return 0;
+ }
+ *result = kBlockLengthPrefixCode[index].offset + bits;
+ s->substate_read_block_length = BROTLI_STATE_READ_BLOCK_LENGTH_NONE;
+ return 1;
+ }
}
/* Transform:
@@ -561,12 +843,12 @@ static BROTLI_INLINE int ReadBlockLength(const HuffmanCode* table,
Most of input values are 0 and 1. To reduce number of branches, we replace
inner for loop with do-while.
*/
-static BROTLI_NOINLINE void InverseMoveToFrontTransform(uint8_t* v, int v_len,
- BrotliState* state) {
+static BROTLI_NOINLINE void InverseMoveToFrontTransform(uint8_t* v,
+ uint32_t v_len, BrotliState* state) {
/* Reinitialize elements that could have been changed. */
- int i = 4;
- int upper_bound = state->mtf_upper_bound;
- uint8_t* mtf = state->mtf;
+ uint32_t i = 4;
+ uint32_t upper_bound = state->mtf_upper_bound;
+ uint8_t* mtf = &state->mtf[4]; /* Make mtf[-1] addressable. */
/* Load endian-aware constant. */
const uint8_t b0123[4] = {0, 1, 2, 3};
uint32_t pattern;
@@ -585,43 +867,37 @@ static BROTLI_NOINLINE void InverseMoveToFrontTransform(uint8_t* v, int v_len,
for (i = 0; i < v_len; ++i) {
int index = v[i];
uint8_t value = mtf[index];
+ upper_bound |= v[i];
v[i] = value;
- upper_bound |= index;
+ mtf[-1] = value;
do {
index--;
mtf[index + 1] = mtf[index];
- } while (index > 0);
- mtf[0] = value;
+ } while (index >= 0);
}
/* Remember amount of elements to be reinitialized. */
state->mtf_upper_bound = upper_bound;
}
-/* Expose function for testing. Will be removed by linker as unused. */
-void InverseMoveToFrontTransformForTesting(uint8_t* v, int l, BrotliState* s) {
- InverseMoveToFrontTransform(v, l, s);
-}
-
-
/* Decodes a series of Huffman table using ReadHuffmanCode function. */
-static BrotliResult HuffmanTreeGroupDecode(HuffmanTreeGroup* group,
- BrotliState* s) {
+static BrotliErrorCode HuffmanTreeGroupDecode(HuffmanTreeGroup* group,
+ BrotliState* 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;
}
while (s->htree_index < group->num_htrees) {
- int table_size;
- BrotliResult result =
+ uint32_t table_size;
+ BrotliErrorCode result =
ReadHuffmanCode(group->alphabet_size, s->next, &table_size, s);
- if (result != BROTLI_RESULT_SUCCESS) return result;
+ if (result != BROTLI_SUCCESS) return result;
group->htrees[s->htree_index] = s->next;
s->next += table_size;
++s->htree_index;
}
s->substate_tree_group = BROTLI_STATE_TREE_GROUP_NONE;
- return BROTLI_RESULT_SUCCESS;
+ return BROTLI_SUCCESS;
}
/* Decodes a context map.
@@ -633,298 +909,410 @@ static BrotliResult HuffmanTreeGroupDecode(HuffmanTreeGroup* group,
3) Read context map items; "0" values could be run-length encoded.
4) Optionally, apply InverseMoveToFront transform to the resulting map.
*/
-static BrotliResult DecodeContextMap(int context_map_size,
- int* num_htrees,
- uint8_t** context_map_arg,
- BrotliState* s) {
+static BrotliErrorCode DecodeContextMap(uint32_t context_map_size,
+ uint32_t* num_htrees,
+ uint8_t** context_map_arg,
+ BrotliState* s) {
BrotliBitReader* br = &s->br;
- BrotliResult result = BROTLI_RESULT_SUCCESS;
- int use_rle_for_zeros;
+ BrotliErrorCode result = BROTLI_SUCCESS;
- switch((int)s->substate_context_map) {
+ switch ((int)s->substate_context_map) {
case BROTLI_STATE_CONTEXT_MAP_NONE:
result = DecodeVarLenUint8(s, br, num_htrees);
- if (result != BROTLI_RESULT_SUCCESS) {
+ if (result != BROTLI_SUCCESS) {
return result;
}
(*num_htrees)++;
s->context_index = 0;
BROTLI_LOG_UINT(context_map_size);
BROTLI_LOG_UINT(*num_htrees);
- *context_map_arg = (uint8_t*)malloc((size_t)context_map_size);
+ *context_map_arg = (uint8_t*)BROTLI_ALLOC(s, (size_t)context_map_size);
if (*context_map_arg == 0) {
- return BROTLI_FAILURE();
+ return BROTLI_FAILURE(BROTLI_ERROR_ALLOC_CONTEXT_MAP);
}
if (*num_htrees <= 1) {
memset(*context_map_arg, 0, (size_t)context_map_size);
- return BROTLI_RESULT_SUCCESS;
+ return BROTLI_SUCCESS;
}
s->substate_context_map = BROTLI_STATE_CONTEXT_MAP_READ_PREFIX;
/* No break, continue to next state. */
- case BROTLI_STATE_CONTEXT_MAP_READ_PREFIX:
- if (!BrotliWarmupBitReader(br) || !BrotliCheckInputAmount(br, 8)) {
- return BROTLI_RESULT_NEEDS_MORE_INPUT;
+ case BROTLI_STATE_CONTEXT_MAP_READ_PREFIX: {
+ uint32_t bits;
+ /* In next stage ReadHuffmanCode uses at least 4 bits, so it is safe
+ to peek 4 bits ahead. */
+ if (!BrotliSafeGetBits(br, 5, &bits)) {
+ return BROTLI_NEEDS_MORE_INPUT;
}
- use_rle_for_zeros = (int)BrotliReadBits(br, 1);
- if (use_rle_for_zeros) {
- s->max_run_length_prefix = (int)BrotliReadBits(br, 4) + 1;
+ if ((bits & 1) != 0) { /* Use RLE for zeroes. */
+ s->max_run_length_prefix = (bits >> 1) + 1;
+ BrotliDropBits(br, 5);
} else {
s->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;
/* No break, continue to next state. */
+ }
case BROTLI_STATE_CONTEXT_MAP_HUFFMAN:
result = ReadHuffmanCode(*num_htrees + s->max_run_length_prefix,
s->context_map_table, NULL, s);
- if (result != BROTLI_RESULT_SUCCESS) return result;
+ if (result != BROTLI_SUCCESS) return result;
+ s->code = 0xFFFF;
s->substate_context_map = BROTLI_STATE_CONTEXT_MAP_DECODE;
/* No break, continue to next state. */
case BROTLI_STATE_CONTEXT_MAP_DECODE: {
- int context_index = s->context_index;
- int max_run_length_prefix = s->max_run_length_prefix;
+ uint32_t context_index = s->context_index;
+ uint32_t max_run_length_prefix = s->max_run_length_prefix;
uint8_t* context_map = *context_map_arg;
- int code;
+ uint32_t code = s->code;
+ if (code != 0xFFFF) {
+ goto rleCode;
+ }
while (context_index < context_map_size) {
- if (!BrotliCheckInputAmount(br, 32)) {
+ if (!SafeReadSymbol(s->context_map_table, br, &code)) {
+ s->code = 0xFFFF;
s->context_index = context_index;
- return BROTLI_RESULT_NEEDS_MORE_INPUT;
+ return BROTLI_NEEDS_MORE_INPUT;
}
- code = ReadSymbol(s->context_map_table, br);
BROTLI_LOG_UINT(code);
+
if (code == 0) {
context_map[context_index++] = 0;
- } else if (code - max_run_length_prefix <= 0) {
- int reps = (1 << code) + (int)BrotliReadBits(br, code);
+ continue;
+ }
+ if (code > max_run_length_prefix) {
+ context_map[context_index++] =
+ (uint8_t)(code - max_run_length_prefix);
+ continue;
+ }
+rleCode:
+ {
+ uint32_t reps;
+ if (!BrotliSafeReadBits(br, code, &reps)) {
+ s->code = code;
+ s->context_index = context_index;
+ return BROTLI_NEEDS_MORE_INPUT;
+ }
+ reps += 1U << code;
BROTLI_LOG_UINT(reps);
if (context_index + reps > context_map_size) {
- return BROTLI_FAILURE();
+ return BROTLI_FAILURE(BROTLI_ERROR_FORMAT_CONTEXT_MAP_REPEAT);
}
do {
context_map[context_index++] = 0;
} while (--reps);
- } else {
- context_map[context_index++] =
- (uint8_t)(code - max_run_length_prefix);
}
}
- if (BrotliReadBits(br, 1)) {
- InverseMoveToFrontTransform(context_map, context_map_size, s);
+ /* No break, continue to next state. */
+ }
+ case BROTLI_STATE_CONTEXT_MAP_TRANSFORM: {
+ uint32_t bits;
+ if (!BrotliSafeReadBits(br, 1, &bits)) {
+ s->substate_context_map = BROTLI_STATE_CONTEXT_MAP_TRANSFORM;
+ return BROTLI_NEEDS_MORE_INPUT;
+ }
+ if (bits != 0) {
+ InverseMoveToFrontTransform(*context_map_arg, context_map_size, s);
}
s->substate_context_map = BROTLI_STATE_CONTEXT_MAP_NONE;
- return BROTLI_RESULT_SUCCESS;
+ return BROTLI_SUCCESS;
}
+ default:
+ return BROTLI_FAILURE(BROTLI_ERROR_UNREACHABLE);
}
-
- return BROTLI_FAILURE();
}
/* Decodes a command or literal and updates block type ringbuffer.
- Reads 0..15 bits. */
-static void DecodeBlockType(const int max_block_type,
- const HuffmanCode* trees,
- int tree_type,
- int* ringbuffers,
- BrotliBitReader* br) {
- int* ringbuffer = ringbuffers + tree_type * 2;
- int block_type =
- ReadSymbol(&trees[tree_type * BROTLI_HUFFMAN_MAX_TABLE_SIZE], br) - 2;
- if (block_type == -1) {
+ Reads 3..54 bits. */
+static BROTLI_INLINE int DecodeBlockTypeAndLength(int safe,
+ BrotliState* s, int tree_type) {
+ uint32_t max_block_type = s->num_block_types[tree_type];
+ const HuffmanCode* type_tree = &s->block_type_trees[
+ tree_type * BROTLI_HUFFMAN_MAX_SIZE_258];
+ const HuffmanCode* len_tree = &s->block_len_trees[
+ tree_type * BROTLI_HUFFMAN_MAX_SIZE_26];
+ BrotliBitReader* br = &s->br;
+ uint32_t* ringbuffer = &s->block_type_rb[tree_type * 2];
+ uint32_t block_type;
+
+ /* Read 0..15 + 3..39 bits */
+ if (!safe) {
+ block_type = ReadSymbol(type_tree, br);
+ s->block_length[tree_type] = ReadBlockLength(len_tree, br);
+ } else {
+ BrotliBitReaderState memento;
+ BrotliBitReaderSaveState(br, &memento);
+ if (!SafeReadSymbol(type_tree, br, &block_type)) return 0;
+ if (!SafeReadBlockLength(s, &s->block_length[tree_type], len_tree, br)) {
+ s->substate_read_block_length = BROTLI_STATE_READ_BLOCK_LENGTH_NONE;
+ BrotliBitReaderRestoreState(br, &memento);
+ return 0;
+ }
+ }
+
+ if (block_type == 1) {
block_type = ringbuffer[1] + 1;
- } else if (block_type == -2) {
+ } else if (block_type == 0) {
block_type = ringbuffer[0];
+ } else {
+ block_type -= 2;
}
if (block_type >= max_block_type) {
block_type -= max_block_type;
}
ringbuffer[0] = ringbuffer[1];
ringbuffer[1] = block_type;
+ return 1;
}
-/* Decodes the block type and updates the state for literal context.
- Reads 18..54 bits. */
-static void DecodeBlockTypeWithContext(BrotliState* s,
- BrotliBitReader* br) {
+static BROTLI_INLINE void DetectTrivialLiteralBlockTypes(BrotliState* s) {
+ size_t i;
+ for (i = 0; i < 8; ++i) s->trivial_literal_contexts[i] = 0;
+ for (i = 0; i < s->num_block_types[0]; i++) {
+ size_t offset = i << kLiteralContextBits;
+ size_t error = 0;
+ size_t sample = s->context_map[offset];
+ size_t j;
+ for (j = 0; j < (1u << kLiteralContextBits);) {
+ BROTLI_REPEAT(4, error |= s->context_map[offset + j++] ^ sample;)
+ }
+ if (error == 0) {
+ s->trivial_literal_contexts[i >> 5] |= 1u << (i & 31);
+ }
+ }
+}
+
+static BROTLI_INLINE void PrepareLiteralDecoding(BrotliState* s) {
uint8_t context_mode;
- int context_offset;
- DecodeBlockType(s->num_block_types[0], s->block_type_trees, 0,
- s->block_type_rb, br); /* Reads 0..15 bits. */
- s->block_length[0] = ReadBlockLength(s->block_len_trees, br); /* 3..39 bits */
- context_offset = s->block_type_rb[1] << kLiteralContextBits;
+ size_t trivial;
+ uint32_t block_type = s->block_type_rb[1];
+ uint32_t context_offset = block_type << kLiteralContextBits;
s->context_map_slice = s->context_map + context_offset;
- s->literal_htree_index = s->context_map_slice[0];
- s->literal_htree = s->literal_hgroup.htrees[s->literal_htree_index];
- context_mode = s->context_modes[s->block_type_rb[1]];
+ trivial = s->trivial_literal_contexts[block_type >> 5];
+ s->trivial_literal_context = (trivial >> (block_type & 31)) & 1;
+ s->literal_htree = s->literal_hgroup.htrees[s->context_map_slice[0]];
+ context_mode = s->context_modes[block_type];
s->context_lookup1 = &kContextLookup[kContextLookupOffsets[context_mode]];
s->context_lookup2 = &kContextLookup[kContextLookupOffsets[context_mode + 1]];
}
-BrotliResult WriteRingBuffer(BrotliOutput output,
- BrotliState* s) {
- int num_written;
- if (s->meta_block_remaining_len < 0) {
- return BROTLI_FAILURE();
+/* Decodes the block type and updates the state for literal context.
+ Reads 3..54 bits. */
+static BROTLI_INLINE int DecodeLiteralBlockSwitchInternal(int safe,
+ BrotliState* s) {
+ if (!DecodeBlockTypeAndLength(safe, s, 0)) {
+ return 0;
}
- num_written = BrotliWrite(
- output, s->ringbuffer + s->partially_written,
- (size_t)(s->to_write - s->partially_written));
- BROTLI_LOG_UINT(s->partially_written);
- BROTLI_LOG_UINT(s->to_write);
- BROTLI_LOG_UINT(num_written);
- if (num_written < 0) {
- return BROTLI_FAILURE();
- }
- s->partially_written += num_written;
- if (s->partially_written < s->to_write) {
- return BROTLI_RESULT_NEEDS_MORE_OUTPUT;
- }
- return BROTLI_RESULT_SUCCESS;
+ PrepareLiteralDecoding(s);
+ return 1;
}
-BrotliResult BROTLI_NOINLINE CopyUncompressedBlockToOutput(BrotliOutput output,
- int pos,
- BrotliState* s) {
- BrotliResult result;
- int num_read;
- int nbytes;
+static void BROTLI_NOINLINE DecodeLiteralBlockSwitch(BrotliState* s) {
+ DecodeLiteralBlockSwitchInternal(0, s);
+}
+
+static int BROTLI_NOINLINE SafeDecodeLiteralBlockSwitch(BrotliState* s) {
+ return DecodeLiteralBlockSwitchInternal(1, s);
+}
+
+/* Block switch for insert/copy length.
+ Reads 3..54 bits. */
+static BROTLI_INLINE int DecodeCommandBlockSwitchInternal(int safe,
+ BrotliState* s) {
+ if (!DecodeBlockTypeAndLength(safe, s, 1)) {
+ return 0;
+ }
+ s->htree_command = s->insert_copy_hgroup.htrees[s->block_type_rb[3]];
+ return 1;
+}
+
+static void BROTLI_NOINLINE DecodeCommandBlockSwitch(BrotliState* s) {
+ DecodeCommandBlockSwitchInternal(0, s);
+}
+static int BROTLI_NOINLINE SafeDecodeCommandBlockSwitch(BrotliState* s) {
+ return DecodeCommandBlockSwitchInternal(1, s);
+}
+
+/* Block switch for distance codes.
+ Reads 3..54 bits. */
+static BROTLI_INLINE int DecodeDistanceBlockSwitchInternal(int safe,
+ BrotliState* s) {
+ if (!DecodeBlockTypeAndLength(safe, s, 2)) {
+ return 0;
+ }
+ s->dist_context_map_slice =
+ s->dist_context_map + (s->block_type_rb[5] << kDistanceContextBits);
+ s->dist_htree_index = s->dist_context_map_slice[s->distance_context];
+ return 1;
+}
+
+static void BROTLI_NOINLINE DecodeDistanceBlockSwitch(BrotliState* s) {
+ DecodeDistanceBlockSwitchInternal(0, s);
+}
+
+static int BROTLI_NOINLINE SafeDecodeDistanceBlockSwitch(BrotliState* s) {
+ return DecodeDistanceBlockSwitchInternal(1, s);
+}
+
+static BrotliErrorCode BROTLI_NOINLINE WriteRingBuffer(size_t* available_out,
+ uint8_t** next_out, size_t* total_out, BrotliState* s) {
+ size_t pos = (s->pos > s->ringbuffer_size) ? (size_t)s->ringbuffer_size
+ : (size_t)(s->pos);
+ uint8_t* start =
+ s->ringbuffer + (s->partial_pos_out & (size_t)s->ringbuffer_mask);
+ size_t partial_pos_rb = (s->rb_roundtrips * (size_t)s->ringbuffer_size) + pos;
+ size_t to_write = (partial_pos_rb - s->partial_pos_out);
+ size_t num_written = *available_out;
+ if (num_written > to_write) {
+ num_written = to_write;
+ }
+ if (s->meta_block_remaining_len < 0) {
+ return BROTLI_FAILURE(BROTLI_ERROR_FORMAT_BLOCK_LENGTH_1);
+ }
+ memcpy(*next_out, start, num_written);
+ *next_out += num_written;
+ *available_out -= num_written;
+ BROTLI_LOG_UINT(to_write);
+ BROTLI_LOG_UINT(num_written);
+ s->partial_pos_out += num_written;
+ if (total_out) *total_out = s->partial_pos_out;
+ if (num_written < to_write) {
+ return BROTLI_NEEDS_MORE_OUTPUT;
+ }
+
+ if (s->pos >= s->ringbuffer_size) {
+ s->pos -= s->ringbuffer_size;
+ s->rb_roundtrips++;
+ }
+ return BROTLI_SUCCESS;
+}
+
+/* Allocates ringbuffer.
+
+ s->ringbuffer_size MUST be updated by BrotliCalculateRingBufferSize before
+ this function is called.
+
+ Last two bytes of ringbuffer are initialized to 0, so context calculation
+ could be done uniformly for the first two and all other positions.
+
+ Custom dictionary, if any, is copied to the end of ringbuffer.
+*/
+static int BROTLI_NOINLINE BrotliAllocateRingBuffer(BrotliState* s) {
+ /* 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) */
+ static const int kRingBufferWriteAheadSlack = 42;
+ s->ringbuffer = (uint8_t*)BROTLI_ALLOC(s, (size_t)(s->ringbuffer_size +
+ kRingBufferWriteAheadSlack));
+ if (s->ringbuffer == 0) {
+ return 0;
+ }
+
+ s->ringbuffer_end = s->ringbuffer + s->ringbuffer_size;
+
+ s->ringbuffer[s->ringbuffer_size - 2] = 0;
+ s->ringbuffer[s->ringbuffer_size - 1] = 0;
+
+ if (s->custom_dict) {
+ memcpy(&s->ringbuffer[(-s->custom_dict_size) & s->ringbuffer_mask],
+ s->custom_dict, (size_t)s->custom_dict_size);
+ }
+
+ return 1;
+}
+
+static BrotliErrorCode BROTLI_NOINLINE CopyUncompressedBlockToOutput(
+ size_t* available_out, uint8_t** next_out, size_t* total_out,
+ BrotliState* s) {
+ /* TODO: avoid allocation for single uncompressed block. */
+ if (!s->ringbuffer && !BrotliAllocateRingBuffer(s)) {
+ return BROTLI_FAILURE(BROTLI_ERROR_ALLOC_RING_BUFFER_1);
+ }
+
/* State machine */
for (;;) {
- switch ((int)s->substate_uncompressed) {
- case BROTLI_STATE_UNCOMPRESSED_NONE:
- /* For short lengths copy byte-by-byte */
- if (s->meta_block_remaining_len < 8 ||
- s->meta_block_remaining_len < BrotliGetRemainingBytes(&s->br)) {
- s->substate_uncompressed = BROTLI_STATE_UNCOMPRESSED_SHORT;
- break;
+ switch (s->substate_uncompressed) {
+ case BROTLI_STATE_UNCOMPRESSED_NONE: {
+ int nbytes = (int)BrotliGetRemainingBytes(&s->br);
+ if (nbytes > s->meta_block_remaining_len) {
+ nbytes = s->meta_block_remaining_len;
+ }
+ if (s->pos + nbytes > s->ringbuffer_size) {
+ nbytes = s->ringbuffer_size - s->pos;
}
/* Copy remaining bytes from s->br.buf_ to ringbuffer. */
- nbytes = (int)BrotliGetRemainingBytes(&s->br);
- BrotliCopyBytes(&s->ringbuffer[pos], &s->br, (size_t)nbytes);
- pos += nbytes;
+ BrotliCopyBytes(&s->ringbuffer[s->pos], &s->br, (size_t)nbytes);
+ s->pos += nbytes;
s->meta_block_remaining_len -= nbytes;
- if (pos >= s->ringbuffer_size) {
- s->to_write = s->ringbuffer_size;
- s->partially_written = 0;
- s->substate_uncompressed = BROTLI_STATE_UNCOMPRESSED_WRITE;
- break;
- }
- s->substate_uncompressed = BROTLI_STATE_UNCOMPRESSED_COPY;
- break;
- case BROTLI_STATE_UNCOMPRESSED_SHORT:
- if (!BrotliWarmupBitReader(&s->br)) {
- return BROTLI_RESULT_NEEDS_MORE_INPUT;
- }
- while (s->meta_block_remaining_len > 0) {
- if (!BrotliCheckInputAmount(&s->br, 8)) {
- return BROTLI_RESULT_NEEDS_MORE_INPUT;
+ if (s->pos < s->ringbuffer_size) {
+ if (s->meta_block_remaining_len == 0) {
+ return BROTLI_SUCCESS;
}
- s->ringbuffer[pos++] = (uint8_t)BrotliReadBits(&s->br, 8);
- s->meta_block_remaining_len--;
+ return BROTLI_NEEDS_MORE_INPUT;
}
- if (pos >= s->ringbuffer_size) {
- s->to_write = s->ringbuffer_size;
- s->partially_written = 0;
- s->substate_uncompressed = BROTLI_STATE_UNCOMPRESSED_WRITE;
- } else {
- s->substate_uncompressed = BROTLI_STATE_UNCOMPRESSED_NONE;
- return BROTLI_RESULT_SUCCESS;
- }
- /* No break, if state is updated, continue to next state */
- case BROTLI_STATE_UNCOMPRESSED_WRITE:
- result = WriteRingBuffer(output, s);
- if (result != BROTLI_RESULT_SUCCESS) {
+ s->substate_uncompressed = BROTLI_STATE_UNCOMPRESSED_WRITE;
+ /* No break, continue to next state */
+ }
+ case BROTLI_STATE_UNCOMPRESSED_WRITE: {
+ BrotliErrorCode result =
+ WriteRingBuffer(available_out, next_out, total_out, s);
+ if (result != BROTLI_SUCCESS) {
return result;
}
- pos &= s->ringbuffer_mask;
s->max_distance = s->max_backward_distance;
- /* If we wrote past the logical end of the ringbuffer, copy the tail
- of the ringbuffer to its beginning and flush the ringbuffer to the
- output. */
- memcpy(s->ringbuffer, s->ringbuffer_end, (size_t)pos);
- s->substate_uncompressed = BROTLI_STATE_UNCOMPRESSED_COPY;
- /* No break, continue to next state */
- case BROTLI_STATE_UNCOMPRESSED_COPY:
- /* Copy straight from the input onto the ringbuffer. The ringbuffer will
- be flushed to the output at a later time. */
- nbytes = s->meta_block_remaining_len;
- if (pos + nbytes > s->ringbuffer_size) {
- nbytes = s->ringbuffer_size - pos;
- }
- num_read = BrotliRead(s->br.input_, &s->ringbuffer[pos],
- (size_t)nbytes);
- pos += num_read;
- s->meta_block_remaining_len -= num_read;
- if (num_read < nbytes) {
- if (num_read < 0) return BROTLI_FAILURE();
- return BROTLI_RESULT_NEEDS_MORE_INPUT;
- }
- if (pos == s->ringbuffer_size) {
- s->to_write = s->ringbuffer_size;
- s->partially_written = 0;
- s->substate_uncompressed = BROTLI_STATE_UNCOMPRESSED_WRITE;
- break;
- }
s->substate_uncompressed = BROTLI_STATE_UNCOMPRESSED_NONE;
- return BROTLI_RESULT_SUCCESS;
+ break;
+ }
}
}
- return BROTLI_FAILURE();
+ BROTLI_DCHECK(0); /* Unreachable */
}
int BrotliDecompressedSize(size_t encoded_size,
const uint8_t* encoded_buffer,
size_t* decoded_size) {
- BrotliMemInput memin;
- BrotliInput in = BrotliInitMemInput(encoded_buffer, encoded_size, &memin);
- BrotliBitReader br;
BrotliState s;
int next_block_header;
- int offset;
BrotliStateInit(&s);
- BrotliInitBitReader(&br, in);
- if (!BrotliReadInput(&br, 1) || !BrotliWarmupBitReader(&br)) {
+ s.br.next_in = encoded_buffer;
+ s.br.avail_in = encoded_size;
+ if (!BrotliWarmupBitReader(&s.br)) {
return 0;
}
- DecodeWindowBits(&br);
- if (DecodeMetaBlockLength(&s, &br) != BROTLI_RESULT_SUCCESS) {
+ DecodeWindowBits(&s.br);
+ if (DecodeMetaBlockLength(&s, &s.br) != BROTLI_SUCCESS) {
return 0;
}
*decoded_size = (size_t)s.meta_block_remaining_len;
if (s.is_last_metablock) {
return 1;
}
- if (!s.is_uncompressed || !BrotliJumpToByteBoundary(&br)) {
+ if (!s.is_uncompressed || !BrotliJumpToByteBoundary(&s.br)) {
return 0;
}
- next_block_header = BrotliPeekByte(&br, s.meta_block_remaining_len);
- if (next_block_header != -1) {
- return (next_block_header & 3) == 3;
- }
- /* Currently bit reader can't peek outside of its buffer... */
- offset = BROTLI_READ_SIZE - (int)BrotliGetRemainingBytes(&br);
- offset += s.meta_block_remaining_len;
- return (offset < encoded_size) && ((encoded_buffer[offset] & 3) == 3);
+ next_block_header = BrotliPeekByte(&s.br, (size_t)s.meta_block_remaining_len);
+ return (next_block_header != -1) && ((next_block_header & 3) == 3);
}
-/* Allocates the smallest feasible ring buffer.
+/* Calculates the smallest feasible ring buffer.
If we know the data size is small, do not allocate more ringbuffer
size than needed to reduce memory usage.
- This method is called before the first non-empty non-metadata block is
- processed. When this method is called, metablock size and flags MUST be
- decoded.
+ When this method is called, metablock size and flags MUST be decoded.
*/
-int BROTLI_NOINLINE BrotliAllocateRingBuffer(BrotliState* s,
+static void BROTLI_NOINLINE BrotliCalculateRingBufferSize(BrotliState* s,
BrotliBitReader* br) {
- static const int kRingBufferWriteAheadSlack = BROTLI_READ_SIZE;
int is_last = s->is_last_metablock;
- s->ringbuffer_size = 1 << s->window_bits;
+ int window_size = 1 << s->window_bits;
+ s->ringbuffer_size = window_size;
if (s->is_uncompressed) {
- int next_block_header = BrotliPeekByte(br, s->meta_block_remaining_len);
- if (next_block_header != -1) { /* Peek succeeded */
- if ((next_block_header & 3) == 3) { /* ISLAST and ISEMPTY */
+ int next_block_header =
+ BrotliPeekByte(br, (size_t)s->meta_block_remaining_len);
+ if (next_block_header != -1) { /* Peek succeeded */
+ if ((next_block_header & 3) == 3) { /* ISLAST and ISEMPTY */
is_last = 1;
}
}
@@ -933,125 +1321,603 @@ int BROTLI_NOINLINE BrotliAllocateRingBuffer(BrotliState* s,
/* We need at least 2 bytes of ring buffer size to get the last two
bytes for context from there */
if (is_last) {
- while (s->ringbuffer_size >= s->meta_block_remaining_len * 2
- && s->ringbuffer_size > 32) {
+ int min_size_x2 = (s->meta_block_remaining_len + s->custom_dict_size) * 2;
+ while (s->ringbuffer_size >= min_size_x2 && s->ringbuffer_size > 32) {
s->ringbuffer_size >>= 1;
}
}
- /* But make it fit the custom dictionary if there is one. */
- while (s->ringbuffer_size < s->custom_dict_size) {
- s->ringbuffer_size <<= 1;
- }
-
s->ringbuffer_mask = s->ringbuffer_size - 1;
- s->ringbuffer = (uint8_t*)malloc((size_t)(s->ringbuffer_size +
- kRingBufferWriteAheadSlack +
- kBrotliMaxDictionaryWordLength));
- if (!s->ringbuffer) {
- return 0;
+}
+
+/* Reads 1..256 2-bit context modes. */
+static BrotliErrorCode ReadContextModes(BrotliState* s) {
+ BrotliBitReader* br = &s->br;
+ int i = s->loop_counter;
+
+ while (i < (int)s->num_block_types[0]) {
+ uint32_t bits;
+ if (!BrotliSafeReadBits(br, 2, &bits)) {
+ s->loop_counter = i;
+ return BROTLI_NEEDS_MORE_INPUT;
+ }
+ s->context_modes[i] = (uint8_t)(bits << 1);
+ BROTLI_LOG_ARRAY_INDEX(s->context_modes, i);
+ i++;
}
- s->ringbuffer_end = s->ringbuffer + s->ringbuffer_size;
- s->ringbuffer[s->ringbuffer_size - 2] = 0;
- s->ringbuffer[s->ringbuffer_size - 1] = 0;
- if (s->custom_dict) {
- memcpy(&s->ringbuffer[(-s->custom_dict_size) & s->ringbuffer_mask],
- s->custom_dict, (size_t)s->custom_dict_size);
+ return BROTLI_SUCCESS;
+}
+
+static BROTLI_INLINE void TakeDistanceFromRingBuffer(BrotliState* s) {
+ if (s->distance_code == 0) {
+ --s->dist_rb_idx;
+ s->distance_code = s->dist_rb[s->dist_rb_idx & 3];
+ } 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;
+ } 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 = 0x0fffffff;
+ }
+ }
+ }
+}
+
+static BROTLI_INLINE int SafeReadBits(
+ BrotliBitReader* const br, uint32_t n_bits, uint32_t* val) {
+ if (n_bits != 0) {
+ return BrotliSafeReadBits(br, n_bits, val);
+ } else {
+ *val = 0;
+ return 1;
+ }
+}
+
+/* Precondition: s->distance_code < 0 */
+static BROTLI_INLINE int ReadDistanceInternal(int safe,
+ BrotliState* s, BrotliBitReader* br) {
+ int distval;
+ BrotliBitReaderState memento;
+ HuffmanCode* distance_tree = s->distance_hgroup.htrees[s->dist_htree_index];
+ if (!safe) {
+ s->distance_code = (int)ReadSymbol(distance_tree, br);
+ } else {
+ uint32_t code;
+ BrotliBitReaderSaveState(br, &memento);
+ if (!SafeReadSymbol(distance_tree, br, &code)) {
+ return 0;
+ }
+ s->distance_code = (int)code;
+ }
+ /* Convert the distance code to the actual distance by possibly */
+ /* looking up past distances from the s->ringbuffer. */
+ if ((s->distance_code & ~0xf) == 0) {
+ TakeDistanceFromRingBuffer(s);
+ --s->block_length[2];
+ return 1;
+ }
+ 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 0;
+ }
+ } 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;
+ }
+ }
+ s->distance_code = s->distance_code - NUM_DISTANCE_SHORT_CODES + 1;
+ --s->block_length[2];
+ return 1;
+}
+
+static BROTLI_INLINE void ReadDistance(BrotliState* s, BrotliBitReader* br) {
+ ReadDistanceInternal(0, s, br);
+}
+
+static BROTLI_INLINE int SafeReadDistance(BrotliState* s, BrotliBitReader* br) {
+ return ReadDistanceInternal(1, s, br);
+}
+
+static BROTLI_INLINE int ReadCommandInternal(int safe,
+ BrotliState* s, BrotliBitReader* br, int* insert_length) {
+ uint32_t cmd_code;
+ uint32_t insert_len_extra = 0;
+ uint32_t copy_length;
+ CmdLutElement v;
+ BrotliBitReaderState memento;
+ if (!safe) {
+ cmd_code = ReadSymbol(s->htree_command, br);
+ } else {
+ BrotliBitReaderSaveState(br, &memento);
+ if (!SafeReadSymbol(s->htree_command, br, &cmd_code)) {
+ return 0;
+ }
+ }
+ v = kCmdLut[cmd_code];
+ s->distance_code = v.distance_code;
+ s->distance_context = v.context;
+ s->dist_htree_index = s->dist_context_map_slice[s->distance_context];
+ *insert_length = v.insert_len_offset;
+ if (!safe) {
+ if (PREDICT_FALSE(v.insert_len_extra_bits != 0)) {
+ insert_len_extra = BrotliReadBits(br, v.insert_len_extra_bits);
+ }
+ copy_length = BrotliReadBits(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)) {
+ BrotliBitReaderRestoreState(br, &memento);
+ return 0;
+ }
+ }
+ s->copy_length = (int)copy_length + v.copy_len_offset;
+ --s->block_length[1];
+ *insert_length += (int)insert_len_extra;
+ return 1;
+}
+
+static BROTLI_INLINE void ReadCommand(BrotliState* s, BrotliBitReader* br,
+ int* insert_length) {
+ ReadCommandInternal(0, s, br, insert_length);
+}
+
+static BROTLI_INLINE int SafeReadCommand(BrotliState* s, BrotliBitReader* br,
+ int* insert_length) {
+ return ReadCommandInternal(1, s, br, insert_length);
+}
+
+static BROTLI_INLINE int CheckInputAmount(int safe,
+ BrotliBitReader* const br, size_t num) {
+ if (safe) {
+ return 1;
+ }
+ return BrotliCheckInputAmount(br, num);
+}
+
+#define BROTLI_SAFE(METHOD) \
+ { \
+ if (safe) { \
+ if (!Safe##METHOD) { \
+ result = BROTLI_NEEDS_MORE_INPUT; \
+ goto saveStateAndReturn; \
+ } \
+ } else { \
+ METHOD; \
+ } \
}
- return 1;
+static BROTLI_INLINE BrotliErrorCode ProcessCommandsInternal(int safe,
+ BrotliState* s) {
+ int pos = s->pos;
+ int i = s->loop_counter;
+ BrotliErrorCode result = BROTLI_SUCCESS;
+ BrotliBitReader* br = &s->br;
+
+ if (!CheckInputAmount(safe, br, 28)) {
+ result = BROTLI_NEEDS_MORE_INPUT;
+ goto saveStateAndReturn;
+ }
+ if (!safe) {
+ BROTLI_UNUSED(BrotliWarmupBitReader(br));
+ }
+
+ /* Jump into state machine. */
+ if (s->state == BROTLI_STATE_COMMAND_BEGIN) {
+ goto CommandBegin;
+ } else if (s->state == BROTLI_STATE_COMMAND_INNER) {
+ goto CommandInner;
+ } else if (s->state == BROTLI_STATE_COMMAND_POST_DECODE_LITERALS) {
+ goto CommandPostDecodeLiterals;
+ } else if (s->state == BROTLI_STATE_COMMAND_POST_WRAP_COPY) {
+ goto CommandPostWrapCopy;
+ } else {
+ return BROTLI_FAILURE(BROTLI_ERROR_UNREACHABLE);
+ }
+
+CommandBegin:
+ if (safe) {
+ s->state = BROTLI_STATE_COMMAND_BEGIN;
+ }
+ if (!CheckInputAmount(safe, br, 28)) { /* 156 bits + 7 bytes */
+ s->state = BROTLI_STATE_COMMAND_BEGIN;
+ result = BROTLI_NEEDS_MORE_INPUT;
+ goto saveStateAndReturn;
+ }
+ if (PREDICT_FALSE(s->block_length[1] == 0)) {
+ BROTLI_SAFE(DecodeCommandBlockSwitch(s));
+ goto CommandBegin;
+ }
+ /* Read the insert/copy length in the command */
+ BROTLI_SAFE(ReadCommand(s, br, &i));
+ BROTLI_LOG(("[ProcessCommandsInternal] pos = %d insert = %d copy = %d\n",
+ pos, i, s->copy_length));
+ if (i == 0) {
+ goto CommandPostDecodeLiterals;
+ }
+ s->meta_block_remaining_len -= i;
+
+CommandInner:
+ if (safe) {
+ s->state = BROTLI_STATE_COMMAND_INNER;
+ }
+ /* Read the literals in the command */
+ if (s->trivial_literal_context) {
+ uint32_t bits;
+ uint32_t value;
+ PreloadSymbol(safe, s->literal_htree, br, &bits, &value);
+ do {
+ if (!CheckInputAmount(safe, br, 28)) { /* 162 bits + 7 bytes */
+ s->state = BROTLI_STATE_COMMAND_INNER;
+ result = BROTLI_NEEDS_MORE_INPUT;
+ goto saveStateAndReturn;
+ }
+ if (PREDICT_FALSE(s->block_length[0] == 0)) {
+ BROTLI_SAFE(DecodeLiteralBlockSwitch(s));
+ PreloadSymbol(safe, s->literal_htree, br, &bits, &value);
+ if (!s->trivial_literal_context) goto CommandInner;
+ }
+ if (!safe) {
+ s->ringbuffer[pos] =
+ (uint8_t)ReadPreloadedSymbol(s->literal_htree, br, &bits, &value);
+ } else {
+ uint32_t literal;
+ if (!SafeReadSymbol(s->literal_htree, br, &literal)) {
+ result = BROTLI_NEEDS_MORE_INPUT;
+ goto saveStateAndReturn;
+ }
+ s->ringbuffer[pos] = (uint8_t)literal;
+ }
+ --s->block_length[0];
+ BROTLI_LOG_ARRAY_INDEX(s->ringbuffer, pos);
+ ++pos;
+ if (PREDICT_FALSE(pos == s->ringbuffer_size)) {
+ s->state = BROTLI_STATE_COMMAND_INNER_WRITE;
+ --i;
+ goto saveStateAndReturn;
+ }
+ } while (--i != 0);
+ } else {
+ uint8_t p1 = s->ringbuffer[(pos - 1) & s->ringbuffer_mask];
+ uint8_t p2 = s->ringbuffer[(pos - 2) & s->ringbuffer_mask];
+ do {
+ const HuffmanCode* hc;
+ uint8_t context;
+ if (!CheckInputAmount(safe, br, 28)) { /* 162 bits + 7 bytes */
+ s->state = BROTLI_STATE_COMMAND_INNER;
+ result = BROTLI_NEEDS_MORE_INPUT;
+ goto saveStateAndReturn;
+ }
+ if (PREDICT_FALSE(s->block_length[0] == 0)) {
+ BROTLI_SAFE(DecodeLiteralBlockSwitch(s));
+ if (s->trivial_literal_context) goto CommandInner;
+ }
+ context = s->context_lookup1[p1] | s->context_lookup2[p2];
+ BROTLI_LOG_UINT(context);
+ hc = s->literal_hgroup.htrees[s->context_map_slice[context]];
+ p2 = p1;
+ if (!safe) {
+ p1 = (uint8_t)ReadSymbol(hc, br);
+ } else {
+ uint32_t literal;
+ if (!SafeReadSymbol(hc, br, &literal)) {
+ result = BROTLI_NEEDS_MORE_INPUT;
+ goto saveStateAndReturn;
+ }
+ p1 = (uint8_t)literal;
+ }
+ s->ringbuffer[pos] = p1;
+ --s->block_length[0];
+ BROTLI_LOG_UINT(s->context_map_slice[context]);
+ BROTLI_LOG_ARRAY_INDEX(s->ringbuffer, pos & s->ringbuffer_mask);
+ ++pos;
+ if (PREDICT_FALSE(pos == s->ringbuffer_size)) {
+ s->state = BROTLI_STATE_COMMAND_INNER_WRITE;
+ --i;
+ goto saveStateAndReturn;
+ }
+ } while (--i != 0);
+ }
+ BROTLI_LOG_UINT(s->meta_block_remaining_len);
+ if (PREDICT_FALSE(s->meta_block_remaining_len <= 0)) {
+ s->state = BROTLI_STATE_METABLOCK_DONE;
+ goto saveStateAndReturn;
+ }
+
+CommandPostDecodeLiterals:
+ if (safe) {
+ s->state = BROTLI_STATE_COMMAND_POST_DECODE_LITERALS;
+ }
+ if (s->distance_code >= 0) {
+ --s->dist_rb_idx;
+ s->distance_code = s->dist_rb[s->dist_rb_idx & 3];
+ goto postReadDistance; /* We already have the implicit distance */
+ }
+ /* Read distance code in the command, unless it was implicitly zero. */
+ if (PREDICT_FALSE(s->block_length[2] == 0)) {
+ BROTLI_SAFE(DecodeDistanceBlockSwitch(s));
+ }
+ BROTLI_SAFE(ReadDistance(s, br));
+postReadDistance:
+ BROTLI_LOG(("[ProcessCommandsInternal] pos = %d distance = %d\n",
+ pos, s->distance_code));
+ if (s->max_distance != s->max_backward_distance) {
+ if (pos < s->max_backward_distance_minus_custom_dict_size) {
+ s->max_distance = pos + s->custom_dict_size;
+ } else {
+ s->max_distance = s->max_backward_distance;
+ }
+ }
+ i = s->copy_length;
+ /* Apply copy of LZ77 back-reference, or static dictionary reference if
+ the distance is larger than the max LZ77 distance */
+ if (s->distance_code > s->max_distance) {
+ if (i >= kBrotliMinDictionaryWordLength &&
+ i <= kBrotliMaxDictionaryWordLength) {
+ int offset = (int)kBrotliDictionaryOffsetsByLength[i];
+ int word_id = s->distance_code - s->max_distance - 1;
+ uint32_t shift = kBrotliDictionarySizeBitsByLength[i];
+ int mask = (int)BitMask(shift);
+ int word_idx = word_id & mask;
+ int transform_idx = word_id >> shift;
+ offset += word_idx * i;
+ if (transform_idx < kNumTransforms) {
+ const uint8_t* word = &kBrotliDictionary[offset];
+ int len = i;
+ if (transform_idx == 0) {
+ memcpy(&s->ringbuffer[pos], word, (size_t)len);
+ } else {
+ len = TransformDictionaryWord(
+ &s->ringbuffer[pos], word, len, transform_idx);
+ }
+ pos += len;
+ s->meta_block_remaining_len -= len;
+ if (pos >= s->ringbuffer_size) {
+ /*s->partial_pos_rb += (size_t)s->ringbuffer_size;*/
+ s->state = BROTLI_STATE_COMMAND_POST_WRITE_1;
+ goto saveStateAndReturn;
+ }
+ } else {
+ BROTLI_LOG(("Invalid backward reference. pos: %d distance: %d "
+ "len: %d bytes left: %d\n",
+ pos, s->distance_code, i, s->meta_block_remaining_len));
+ return BROTLI_FAILURE(BROTLI_ERROR_FORMAT_TRANSFORM);
+ }
+ } else {
+ BROTLI_LOG(("Invalid backward reference. pos: %d distance: %d "
+ "len: %d bytes left: %d\n",
+ pos, s->distance_code, i, s->meta_block_remaining_len));
+ return BROTLI_FAILURE(BROTLI_ERROR_FORMAT_DICTIONARY);
+ }
+ } else {
+ int src_start = (pos - s->distance_code) & s->ringbuffer_mask;
+ uint8_t* copy_dst = &s->ringbuffer[pos];
+ uint8_t* copy_src = &s->ringbuffer[src_start];
+ int dst_end = pos + i;
+ int src_end = src_start + i;
+ /* update the recent distances cache */
+ s->dist_rb[s->dist_rb_idx & 3] = s->distance_code;
+ ++s->dist_rb_idx;
+ s->meta_block_remaining_len -= i;
+ /* There are 32+ bytes of slack in the ringbuffer allocation.
+ Also, we have 16 short codes, that make these 16 bytes irrelevant
+ in the ringbuffer. Let's copy over them as a first guess.
+ */
+ memmove16(copy_dst, copy_src);
+ if (src_end > pos && dst_end > src_start) {
+ /* Regions intersect. */
+ goto CommandPostWrapCopy;
+ }
+ if (dst_end >= s->ringbuffer_size || src_end >= s->ringbuffer_size) {
+ /* At least one region wraps. */
+ goto CommandPostWrapCopy;
+ }
+ pos += i;
+ if (i > 16) {
+ if (i > 32) {
+ memcpy(copy_dst + 16, copy_src + 16, (size_t)(i - 16));
+ } else {
+ /* This branch covers about 45% cases.
+ Fixed size short copy allows more compiler optimizations. */
+ memmove16(copy_dst + 16, copy_src + 16);
+ }
+ }
+ }
+ BROTLI_LOG_UINT(s->meta_block_remaining_len);
+ if (s->meta_block_remaining_len <= 0) {
+ /* Next metablock, if any */
+ s->state = BROTLI_STATE_METABLOCK_DONE;
+ goto saveStateAndReturn;
+ } else {
+ goto CommandBegin;
+ }
+CommandPostWrapCopy:
+ {
+ int wrap_guard = s->ringbuffer_size - pos;
+ while (--i >= 0) {
+ s->ringbuffer[pos] =
+ s->ringbuffer[(pos - s->distance_code) & s->ringbuffer_mask];
+ ++pos;
+ if (PREDICT_FALSE(--wrap_guard == 0)) {
+ s->state = BROTLI_STATE_COMMAND_POST_WRITE_2;
+ goto saveStateAndReturn;
+ }
+ }
+ }
+ if (s->meta_block_remaining_len <= 0) {
+ /* Next metablock, if any */
+ s->state = BROTLI_STATE_METABLOCK_DONE;
+ goto saveStateAndReturn;
+ } else {
+ goto CommandBegin;
+ }
+
+saveStateAndReturn:
+ s->pos = pos;
+ s->loop_counter = i;
+ return result;
+}
+
+#undef BROTLI_SAFE
+
+static BROTLI_NOINLINE BrotliErrorCode ProcessCommands(BrotliState* s) {
+ return ProcessCommandsInternal(0, s);
+}
+
+static BROTLI_NOINLINE BrotliErrorCode SafeProcessCommands(BrotliState* s) {
+ return ProcessCommandsInternal(1, s);
}
BrotliResult BrotliDecompressBuffer(size_t encoded_size,
const uint8_t* encoded_buffer,
size_t* decoded_size,
uint8_t* decoded_buffer) {
- BrotliMemInput memin;
- BrotliInput in = BrotliInitMemInput(encoded_buffer, encoded_size, &memin);
- BrotliMemOutput mout;
- BrotliOutput out = BrotliInitMemOutput(decoded_buffer, *decoded_size, &mout);
- BrotliResult success = BrotliDecompress(in, out);
- *decoded_size = mout.pos;
- return success;
-}
-
-BrotliResult BrotliDecompress(BrotliInput input, BrotliOutput output) {
BrotliState s;
BrotliResult result;
+ size_t total_out = 0;
+ size_t available_in = encoded_size;
+ const uint8_t* next_in = encoded_buffer;
+ size_t available_out = *decoded_size;
+ uint8_t* next_out = decoded_buffer;
BrotliStateInit(&s);
- result = BrotliDecompressStreaming(input, output, 1, &s);
- if (result == BROTLI_RESULT_NEEDS_MORE_INPUT) {
- /* Not ok: it didn't finish even though this is a non-streaming function. */
- result = BROTLI_FAILURE();
- }
+ result = BrotliDecompressStream(&available_in, &next_in, &available_out,
+ &next_out, &total_out, &s);
+ *decoded_size = total_out;
BrotliStateCleanup(&s);
+ if (result != BROTLI_RESULT_SUCCESS) {
+ result = BROTLI_RESULT_ERROR;
+ }
return result;
}
-BrotliResult BrotliDecompressBufferStreaming(size_t* available_in,
- const uint8_t** next_in,
- int finish,
- size_t* available_out,
- uint8_t** next_out,
- size_t* total_out,
- BrotliState* s) {
- BrotliMemInput memin;
- BrotliInput in = BrotliInitMemInput(*next_in, *available_in, &memin);
- BrotliMemOutput memout;
- BrotliOutput out = BrotliInitMemOutput(*next_out, *available_out, &memout);
- BrotliResult result = BrotliDecompressStreaming(in, out, finish, s);
- /* The current implementation reads everything, so 0 bytes are available. */
- *next_in += memin.pos;
- *available_in -= memin.pos;
- /* Update the output position to where we write next. */
- *next_out += memout.pos;
- *available_out -= memout.pos;
- *total_out += memout.pos;
- return result;
-}
-
-BrotliResult BrotliDecompressStreaming(BrotliInput input, BrotliOutput output,
- int finish, BrotliState* s) {
- uint8_t context;
- int pos = s->pos;
- int i = s->loop_counter;
- BrotliResult result = BROTLI_RESULT_SUCCESS;
+/* Invariant: input stream is never overconsumed:
+ * invalid input implies that the whole stream is invalid -> any amount of
+ input could be read and discarded
+ * when result is "needs more input", then at leat one more byte is REQUIRED
+ to complete decoding; all input data MUST be consumed by decoder, so
+ client could swap the input buffer
+ * when result is "needs more output" decoder MUST ensure that it doesn't
+ hold more than 7 bits in bit reader; this saves client from swapping input
+ buffer ahead of time
+ * when result is "success" decoder MUST return all unused data back to input
+ buffer; this is possible because the invariant is hold on enter
+*/
+BrotliResult BrotliDecompressStream(size_t* available_in,
+ const uint8_t** next_in, size_t* available_out, uint8_t** next_out,
+ size_t* total_out, BrotliState* s) {
+ BrotliErrorCode result = BROTLI_SUCCESS;
BrotliBitReader* br = &s->br;
- int initial_remaining_len;
- int bytes_copied;
- uint8_t *copy_src;
- uint8_t *copy_dst;
- /* We need the slack region for the following reasons:
- - doing up to two 16-byte copies for fast backward copying
- - transforms
- - flushing the input s->ringbuffer when decoding uncompressed blocks */
- s->br.input_ = input;
+ if (s->buffer_length == 0) { /* Just connect bit reader to input stream. */
+ br->avail_in = *available_in;
+ br->next_in = *next_in;
+ } else {
+ /* At least one byte of input is required. More than one byte of input may
+ be required to complete the transaction -> reading more data must be
+ done in a loop -> do it in a main loop. */
+ result = BROTLI_NEEDS_MORE_INPUT;
+ br->next_in = &s->buffer.u8[0];
+ }
/* State machine */
for (;;) {
- if (result != BROTLI_RESULT_SUCCESS) {
- if (result == BROTLI_RESULT_NEEDS_MORE_INPUT) {
- if (BrotliReadInput(br, finish)) {
- result = BROTLI_RESULT_SUCCESS;
- continue;
+ if (result != BROTLI_SUCCESS) { /* Error | needs more input/output */
+ if (result == BROTLI_NEEDS_MORE_INPUT) {
+ if (s->ringbuffer != 0) { /* Proactively push output. */
+ WriteRingBuffer(available_out, next_out, total_out, s);
}
- if (finish) {
- BROTLI_LOG(("Unexpected end of input. State: %d\n", s->state));
- result = BROTLI_FAILURE();
+ if (s->buffer_length != 0) { /* Used with internal buffer. */
+ if (br->avail_in == 0) { /* Successfully finished read transaction. */
+ /* Accamulator contains less than 8 bits, because internal buffer
+ is expanded byte-by-byte until it is enough to complete read. */
+ s->buffer_length = 0;
+ /* Switch to input stream and restart. */
+ result = BROTLI_SUCCESS;
+ br->avail_in = *available_in;
+ br->next_in = *next_in;
+ continue;
+ } else if (*available_in != 0) {
+ /* Not enough data in buffer, but can take one more byte from
+ input stream. */
+ result = BROTLI_SUCCESS;
+ s->buffer.u8[s->buffer_length] = **next_in;
+ s->buffer_length++;
+ br->avail_in = s->buffer_length;
+ (*next_in)++;
+ (*available_in)--;
+ /* Retry with more data in buffer. */
+ continue;
+ }
+ /* Can't finish reading and no more input.*/
+ break;
+ } else { /* Input stream doesn't contain enough input. */
+ /* Copy tail to internal buffer and return. */
+ *next_in = br->next_in;
+ *available_in = br->avail_in;
+ while (*available_in) {
+ s->buffer.u8[s->buffer_length] = **next_in;
+ s->buffer_length++;
+ (*next_in)++;
+ (*available_in)--;
+ }
+ break;
}
+ /* Unreachable. */
}
- break; /* Fail, or partial data. */
+
+ /* Fail or needs more output. */
+
+ if (s->buffer_length != 0) {
+ /* Just consumed the buffered input and produced some output. Otherwise
+ it would result in "needs more input". Reset internal buffer.*/
+ s->buffer_length = 0;
+ } else {
+ /* Using input stream in last iteration. When decoder switches to input
+ stream it has less than 8 bits in accamulator, so it is safe to
+ return unused accamulator bits there. */
+ BrotliBitReaderUnload(br);
+ *available_in = br->avail_in;
+ *next_in = br->next_in;
+ }
+ break;
}
switch (s->state) {
case BROTLI_STATE_UNINITED:
- pos = 0;
- BrotliInitBitReader(br, input);
-
- s->state = BROTLI_STATE_BITREADER_WARMUP;
- /* No break, continue to next state */
- case BROTLI_STATE_BITREADER_WARMUP:
/* Prepare to the first read. */
if (!BrotliWarmupBitReader(br)) {
- result = BROTLI_RESULT_NEEDS_MORE_INPUT;
+ result = BROTLI_NEEDS_MORE_INPUT;
break;
}
/* Decode window size. */
@@ -1059,35 +1925,40 @@ BrotliResult BrotliDecompressStreaming(BrotliInput input, BrotliOutput output,
BROTLI_LOG_UINT(s->window_bits);
if (s->window_bits == 9) {
/* Value 9 is reserved for future use. */
- result = BROTLI_FAILURE();
+ result = BROTLI_FAILURE(BROTLI_ERROR_FORMAT_WINDOW_BITS);
break;
}
+ /* Maximum distance, see section 9.1. of the spec. */
s->max_backward_distance = (1 << s->window_bits) - 16;
+ /* Limit custom dictionary size. */
+ if (s->custom_dict_size >= s->max_backward_distance) {
+ s->custom_dict += s->custom_dict_size - s->max_backward_distance;
+ s->custom_dict_size = s->max_backward_distance;
+ }
s->max_backward_distance_minus_custom_dict_size =
s->max_backward_distance - s->custom_dict_size;
/* Allocate memory for both block_type_trees and block_len_trees. */
- s->block_type_trees = (HuffmanCode*)malloc(
- 6 * BROTLI_HUFFMAN_MAX_TABLE_SIZE * sizeof(HuffmanCode));
-
- if (s->block_type_trees == NULL) {
- result = BROTLI_FAILURE();
+ s->block_type_trees = (HuffmanCode*)BROTLI_ALLOC(s,
+ sizeof(HuffmanCode) * 3 *
+ (BROTLI_HUFFMAN_MAX_SIZE_258 + BROTLI_HUFFMAN_MAX_SIZE_26));
+ if (s->block_type_trees == 0) {
+ result = BROTLI_FAILURE(BROTLI_ERROR_ALLOC_BLOCK_TYPE_TREES);
break;
}
- s->block_len_trees = s->block_type_trees +
- 3 * BROTLI_HUFFMAN_MAX_TABLE_SIZE;
+ s->block_len_trees =
+ s->block_type_trees + 3 * BROTLI_HUFFMAN_MAX_SIZE_258;
s->state = BROTLI_STATE_METABLOCK_BEGIN;
/* No break, continue to next state */
case BROTLI_STATE_METABLOCK_BEGIN:
BrotliStateMetablockBegin(s);
- BROTLI_LOG_UINT(pos);
+ BROTLI_LOG_UINT(s->pos);
s->state = BROTLI_STATE_METABLOCK_HEADER;
/* No break, continue to next state */
case BROTLI_STATE_METABLOCK_HEADER:
result = DecodeMetaBlockLength(s, br); /* Reads 2 - 31 bits. */
- if (result != BROTLI_RESULT_SUCCESS) {
- i = s->loop_counter; /* Has been updated in DecodeMetaBlockLength. */
+ if (result != BROTLI_SUCCESS) {
break;
}
BROTLI_LOG_UINT(s->is_last_metablock);
@@ -1096,7 +1967,7 @@ BrotliResult BrotliDecompressStreaming(BrotliInput input, BrotliOutput output,
BROTLI_LOG_UINT(s->is_uncompressed);
if (s->is_metadata || s->is_uncompressed) {
if (!BrotliJumpToByteBoundary(br)) {
- result = BROTLI_FAILURE();
+ result = BROTLI_FAILURE(BROTLI_ERROR_FORMAT_PADDING_1);
break;
}
}
@@ -1109,154 +1980,156 @@ BrotliResult BrotliDecompressStreaming(BrotliInput input, BrotliOutput output,
break;
}
if (!s->ringbuffer) {
- if (!BrotliAllocateRingBuffer(s, br)) {
- result = BROTLI_FAILURE();
- break;
- }
+ BrotliCalculateRingBufferSize(s, br);
}
if (s->is_uncompressed) {
s->state = BROTLI_STATE_UNCOMPRESSED;
break;
}
- i = 0;
+ s->loop_counter = 0;
s->state = BROTLI_STATE_HUFFMAN_CODE_0;
break;
- case BROTLI_STATE_UNCOMPRESSED:
- initial_remaining_len = s->meta_block_remaining_len;
- /* pos is given as argument since s->pos is only updated at the end. */
- result = CopyUncompressedBlockToOutput(output, pos, s);
- bytes_copied = initial_remaining_len - s->meta_block_remaining_len;
- pos = (pos + bytes_copied) & s->ringbuffer_mask;
- if (result != BROTLI_RESULT_SUCCESS) {
+ case BROTLI_STATE_UNCOMPRESSED: {
+ int bytes_copied = s->meta_block_remaining_len;
+ result = CopyUncompressedBlockToOutput(
+ available_out, next_out, total_out, s);
+ bytes_copied -= s->meta_block_remaining_len;
+ if (result != BROTLI_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_RESULT_NEEDS_MORE_INPUT;
+ result = BROTLI_NEEDS_MORE_INPUT;
break;
}
}
- if (result == BROTLI_RESULT_SUCCESS) {
+ if (result == BROTLI_SUCCESS) {
s->state = BROTLI_STATE_METABLOCK_DONE;
}
break;
case BROTLI_STATE_HUFFMAN_CODE_0:
- if (i >= 3) {
- s->state = BROTLI_STATE_CONTEXT_MODES;
+ if (s->loop_counter >= 3) {
+ s->state = BROTLI_STATE_METABLOCK_HEADER_2;
break;
}
/* Reads 1..11 bits. */
- result = DecodeVarLenUint8(s, br, &s->num_block_types[i]);
- if (result != BROTLI_RESULT_SUCCESS) {
+ result = DecodeVarLenUint8(s, br, &s->num_block_types[s->loop_counter]);
+ if (result != BROTLI_SUCCESS) {
+ break;
+ }
+ s->num_block_types[s->loop_counter]++;
+ BROTLI_LOG_UINT(s->num_block_types[s->loop_counter]);
+ if (s->num_block_types[s->loop_counter] < 2) {
+ s->loop_counter++;
break;
}
- s->num_block_types[i]++;
- BROTLI_LOG_UINT(s->num_block_types[i]);
s->state = BROTLI_STATE_HUFFMAN_CODE_1;
/* No break, continue to next state */
- case BROTLI_STATE_HUFFMAN_CODE_1:
- if (!BrotliWarmupBitReader(br)) {
- result = BROTLI_RESULT_NEEDS_MORE_INPUT;
- break;
- }
- if (s->num_block_types[i] >= 2) {
- result = ReadHuffmanCode(s->num_block_types[i] + 2,
- &s->block_type_trees[i * BROTLI_HUFFMAN_MAX_TABLE_SIZE],
- NULL, s);
- if (result != BROTLI_RESULT_SUCCESS) break;
- s->state = BROTLI_STATE_HUFFMAN_CODE_2;
- } else {
- i++;
- s->state = BROTLI_STATE_HUFFMAN_CODE_0;
- break;
- }
+ case BROTLI_STATE_HUFFMAN_CODE_1: {
+ int tree_offset = s->loop_counter * BROTLI_HUFFMAN_MAX_SIZE_258;
+ result = ReadHuffmanCode(s->num_block_types[s->loop_counter] + 2,
+ &s->block_type_trees[tree_offset], NULL, s);
+ if (result != BROTLI_SUCCESS) break;
+ s->state = BROTLI_STATE_HUFFMAN_CODE_2;
/* No break, continue to next state */
- case BROTLI_STATE_HUFFMAN_CODE_2:
+ }
+ case BROTLI_STATE_HUFFMAN_CODE_2: {
+ int tree_offset = s->loop_counter * BROTLI_HUFFMAN_MAX_SIZE_26;
result = ReadHuffmanCode(kNumBlockLengthCodes,
- &s->block_len_trees[i * BROTLI_HUFFMAN_MAX_TABLE_SIZE],
- NULL, s);
- if (result != BROTLI_RESULT_SUCCESS) break;
+ &s->block_len_trees[tree_offset], NULL, s);
+ if (result != BROTLI_SUCCESS) break;
s->state = BROTLI_STATE_HUFFMAN_CODE_3;
/* No break, continue to next state */
- case BROTLI_STATE_HUFFMAN_CODE_3:
- if (!BrotliCheckInputAmount(br, 8)) {
- result = BROTLI_RESULT_NEEDS_MORE_INPUT;
+ }
+ case BROTLI_STATE_HUFFMAN_CODE_3: {
+ int tree_offset = s->loop_counter * BROTLI_HUFFMAN_MAX_SIZE_26;
+ if (!SafeReadBlockLength(s, &s->block_length[s->loop_counter],
+ &s->block_len_trees[tree_offset], br)) {
+ result = BROTLI_NEEDS_MORE_INPUT;
break;
}
- s->block_length[i] = ReadBlockLength( /* Reads 3..39 bits. */
- &s->block_len_trees[i * BROTLI_HUFFMAN_MAX_TABLE_SIZE], br);
- BROTLI_LOG_UINT(s->block_length[i]);
- i++;
+ BROTLI_LOG_UINT(s->block_length[s->loop_counter]);
+ s->loop_counter++;
s->state = BROTLI_STATE_HUFFMAN_CODE_0;
break;
- case BROTLI_STATE_CONTEXT_MODES:
- /* We need up to 256 * 2 + 6 bits, this fits in 128 bytes. */
- if (!BrotliCheckInputAmount(br, 128)) {
- result = BROTLI_RESULT_NEEDS_MORE_INPUT;
+ }
+ case BROTLI_STATE_METABLOCK_HEADER_2: {
+ uint32_t bits;
+ if (!BrotliSafeReadBits(br, 6, &bits)) {
+ result = BROTLI_NEEDS_MORE_INPUT;
break;
}
- s->distance_postfix_bits = (int)BrotliReadBits(br, 2);
- s->num_direct_distance_codes = NUM_DISTANCE_SHORT_CODES +
- ((int)BrotliReadBits(br, 4) << s->distance_postfix_bits);
+ s->distance_postfix_bits = bits & BitMask(2);
+ bits >>= 2;
+ s->num_direct_distance_codes =
+ NUM_DISTANCE_SHORT_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*)malloc((size_t)s->num_block_types[0]);
+ s->context_modes =
+ (uint8_t*)BROTLI_ALLOC(s, (size_t)s->num_block_types[0]);
if (s->context_modes == 0) {
- result = BROTLI_FAILURE();
+ result = BROTLI_FAILURE(BROTLI_ERROR_ALLOC_CONTEXT_MODES);
break;
}
- for (i = 0; i < s->num_block_types[0]; ++i) {
- s->context_modes[i] = (uint8_t)(BrotliReadBits(br, 2) << 1);
- BROTLI_LOG_ARRAY_INDEX(s->context_modes, i);
+ s->loop_counter = 0;
+ s->state = BROTLI_STATE_CONTEXT_MODES;
+ /* No break, continue to next state */
+ }
+ case BROTLI_STATE_CONTEXT_MODES:
+ result = ReadContextModes(s);
+ if (result != BROTLI_SUCCESS) {
+ break;
}
s->state = BROTLI_STATE_CONTEXT_MAP_1;
/* No break, continue to next state */
case BROTLI_STATE_CONTEXT_MAP_1:
- result = DecodeContextMap(s->num_block_types[0] << kLiteralContextBits,
- &s->num_literal_htrees, &s->context_map, s);
- if (result != BROTLI_RESULT_SUCCESS) {
+ result = DecodeContextMap(
+ s->num_block_types[0] << kLiteralContextBits,
+ &s->num_literal_htrees, &s->context_map, s);
+ if (result != BROTLI_SUCCESS) {
break;
}
- s->trivial_literal_context = 1;
- for (i = 0; i < s->num_block_types[0] << kLiteralContextBits; i++) {
- if (s->context_map[i] != i >> kLiteralContextBits) {
- s->trivial_literal_context = 0;
- break;
- }
- }
+ DetectTrivialLiteralBlockTypes(s);
s->state = BROTLI_STATE_CONTEXT_MAP_2;
/* No break, continue to next state */
case BROTLI_STATE_CONTEXT_MAP_2:
{
- int num_distance_codes =
- s->num_direct_distance_codes + (48 << s->distance_postfix_bits);
+ uint32_t num_distance_codes =
+ s->num_direct_distance_codes + (48U << s->distance_postfix_bits);
result = DecodeContextMap(
s->num_block_types[2] << kDistanceContextBits,
&s->num_dist_htrees, &s->dist_context_map, s);
- if (result != BROTLI_RESULT_SUCCESS) {
+ if (result != BROTLI_SUCCESS) {
break;
}
- BrotliHuffmanTreeGroupInit(
- &s->literal_hgroup, kNumLiteralCodes, s->num_literal_htrees);
- BrotliHuffmanTreeGroupInit(
- &s->insert_copy_hgroup, kNumInsertAndCopyCodes,
- s->num_block_types[1]);
- BrotliHuffmanTreeGroupInit(
- &s->distance_hgroup, num_distance_codes, s->num_dist_htrees);
+ BrotliHuffmanTreeGroupInit(s, &s->literal_hgroup, kNumLiteralCodes,
+ s->num_literal_htrees);
+ BrotliHuffmanTreeGroupInit(s, &s->insert_copy_hgroup,
+ kNumInsertAndCopyCodes,
+ s->num_block_types[1]);
+ BrotliHuffmanTreeGroupInit(s, &s->distance_hgroup, num_distance_codes,
+ s->num_dist_htrees);
+ if (s->literal_hgroup.codes == 0 ||
+ s->insert_copy_hgroup.codes == 0 ||
+ s->distance_hgroup.codes == 0) {
+ return SaveErrorCode(s,
+ BROTLI_FAILURE(BROTLI_ERROR_ALLOC_TREE_GROUPS));
+ }
}
- i = 0;
+ s->loop_counter = 0;
s->state = BROTLI_STATE_TREE_GROUP;
/* No break, continue to next state */
case BROTLI_STATE_TREE_GROUP:
{
HuffmanTreeGroup* hgroup = NULL;
- switch (i) {
+ switch (s->loop_counter) {
case 0:
hgroup = &s->literal_hgroup;
break;
@@ -1266,443 +2139,124 @@ BrotliResult BrotliDecompressStreaming(BrotliInput input, BrotliOutput output,
case 2:
hgroup = &s->distance_hgroup;
break;
+ default:
+ return SaveErrorCode(s,
+ BROTLI_FAILURE(BROTLI_ERROR_UNREACHABLE));
}
result = HuffmanTreeGroupDecode(hgroup, s);
}
- if (result != BROTLI_RESULT_SUCCESS) break;
- i++;
- if (i >= 3) {
- uint8_t context_mode = s->context_modes[s->block_type_rb[1]];
- s->context_map_slice = s->context_map;
+ if (result != BROTLI_SUCCESS) break;
+ s->loop_counter++;
+ if (s->loop_counter >= 3) {
+ PrepareLiteralDecoding(s);
s->dist_context_map_slice = s->dist_context_map;
- s->context_lookup1 =
- &kContextLookup[kContextLookupOffsets[context_mode]];
- s->context_lookup2 =
- &kContextLookup[kContextLookupOffsets[context_mode + 1]];
s->htree_command = s->insert_copy_hgroup.htrees[0];
- s->literal_htree = s->literal_hgroup.htrees[s->literal_htree_index];
+ if (!s->ringbuffer && !BrotliAllocateRingBuffer(s)) {
+ result = BROTLI_FAILURE(BROTLI_ERROR_ALLOC_RING_BUFFER_2);
+ break;
+ }
s->state = BROTLI_STATE_COMMAND_BEGIN;
}
break;
case BROTLI_STATE_COMMAND_BEGIN:
- if (s->meta_block_remaining_len <= 0) {
- /* Next metablock, if any */
- s->state = BROTLI_STATE_METABLOCK_DONE;
- break;
- }
- /* Decoding of Brotli commands is the inner loop, jumping with goto makes it
- 3% faster */
- CommandBegin:
- if (!BrotliCheckInputAmount(br, 32)) {
- s->state = BROTLI_STATE_COMMAND_BEGIN;
- result = BROTLI_RESULT_NEEDS_MORE_INPUT;
- break;
- }
- /* Read the insert/copy length in the command */
- if (s->block_length[1] == 0) {
- /* Block switch for insert/copy length. Reads 0..15 bits. */
- DecodeBlockType(s->num_block_types[1],
- s->block_type_trees, 1,
- s->block_type_rb, br);
- s->htree_command = s->insert_copy_hgroup.htrees[s->block_type_rb[3]];
- s->block_length[1] = ReadBlockLength( /* Reads 3..39 bits. */
- &s->block_len_trees[BROTLI_HUFFMAN_MAX_TABLE_SIZE], br);
- }
- {
- int cmd_code = ReadSymbol(s->htree_command, br);
- int insert_len_extra = 0;
- CmdLutElement v;
- --s->block_length[1];
- v = kCmdLut[cmd_code];
- s->distance_code = v.distance_code;
- s->distance_context = v.context;
- s->dist_htree_index = s->dist_context_map_slice[s->distance_context];
- i = v.insert_len_offset;
- if (PREDICT_FALSE(v.insert_len_extra_bits != 0)) {
- insert_len_extra = (int)BrotliReadBits(br, v.insert_len_extra_bits);
- }
- s->copy_length = (int)BrotliReadBits(br, v.copy_len_extra_bits) +
- v.copy_len_offset;
- i += insert_len_extra;
- }
- BROTLI_LOG_UINT(i);
- BROTLI_LOG_UINT(s->copy_length);
- BROTLI_LOG_UINT(s->distance_code);
- if (i == 0) {
- goto postDecodeLiterals;
- }
- s->meta_block_remaining_len -= i;
- /* No break, go to next state */
case BROTLI_STATE_COMMAND_INNER:
- /* Read the literals in the command */
- if (s->trivial_literal_context) {
- unsigned bits;
- unsigned value;
- PreloadSymbol(s->literal_htree, br, &bits, &value);
- do {
- if (!BrotliCheckInputAmount(br, 64)) {
- s->state = BROTLI_STATE_COMMAND_INNER;
- result = BROTLI_RESULT_NEEDS_MORE_INPUT;
- break;
- }
- if (PREDICT_FALSE(s->block_length[0] == 0)) {
- /* Block switch for literals */
- DecodeBlockTypeWithContext(s, br);
- PreloadSymbol(s->literal_htree, br, &bits, &value);
- }
- s->ringbuffer[pos] =
- (uint8_t)ReadPreloadedSymbol(s->literal_htree,
- br, &bits, &value);
- --s->block_length[0];
- BROTLI_LOG_UINT(s->literal_htree_index);
- BROTLI_LOG_ARRAY_INDEX(s->ringbuffer, pos);
- ++pos;
- if (PREDICT_FALSE(pos == s->ringbuffer_size)) {
- s->to_write = s->ringbuffer_size;
- s->partially_written = 0;
- s->state = BROTLI_STATE_COMMAND_INNER_WRITE;
- --i;
- goto innerWrite;
- }
- } while (--i != 0);
- } else {
- uint8_t p1 = s->ringbuffer[(pos - 1) & s->ringbuffer_mask];
- uint8_t p2 = s->ringbuffer[(pos - 2) & s->ringbuffer_mask];
- do {
- const HuffmanCode* hc;
- if (!BrotliCheckInputAmount(br, 64)) {
- s->state = BROTLI_STATE_COMMAND_INNER;
- result = BROTLI_RESULT_NEEDS_MORE_INPUT;
- break;
- }
- if (PREDICT_FALSE(s->block_length[0] == 0)) {
- /* Block switch for literals */
- DecodeBlockTypeWithContext(s, br);
- }
- context = s->context_lookup1[p1] | s->context_lookup2[p2];
- BROTLI_LOG_UINT(context);
- hc = s->literal_hgroup.htrees[s->context_map_slice[context]];
- --s->block_length[0];
- p2 = p1;
- p1 = (uint8_t)ReadSymbol(hc, br);
- s->ringbuffer[pos] = p1;
- BROTLI_LOG_UINT(s->context_map_slice[context]);
- BROTLI_LOG_ARRAY_INDEX(s->ringbuffer, pos & s->ringbuffer_mask);
- ++pos;
- if (PREDICT_FALSE(pos == s->ringbuffer_size)) {
- s->to_write = s->ringbuffer_size;
- s->partially_written = 0;
- s->state = BROTLI_STATE_COMMAND_INNER_WRITE;
- --i;
- goto innerWrite;
- }
- } while (--i != 0);
- }
- if (result != BROTLI_RESULT_SUCCESS) break;
- if (s->meta_block_remaining_len <= 0) {
- s->state = BROTLI_STATE_METABLOCK_DONE;
- break;
- }
-postDecodeLiterals:
- if (s->distance_code >= 0) {
- --s->dist_rb_idx;
- s->distance_code = s->dist_rb[s->dist_rb_idx & 3];
- goto postReadDistance; /* We already have the implicit distance */
- }
- /* Read distance code in the command, unless it was implicitly zero. */
- BROTLI_DCHECK(s->distance_code < 0);
- if (s->block_length[2] == 0) {
- /* Block switch for distance codes */
- int dist_context_offset;
- DecodeBlockType(s->num_block_types[2],
- s->block_type_trees, 2,
- s->block_type_rb, br); /* Reads 0..15 bits. */
- s->block_length[2] = ReadBlockLength( /* Reads 3..39 bits. */
- &s->block_len_trees[2 * BROTLI_HUFFMAN_MAX_TABLE_SIZE], br);
- dist_context_offset = s->block_type_rb[5] << kDistanceContextBits;
- s->dist_context_map_slice =
- s->dist_context_map + dist_context_offset;
- s->dist_htree_index = s->dist_context_map_slice[s->distance_context];
- }
- --s->block_length[2];
- s->distance_code =
- ReadSymbol(s->distance_hgroup.htrees[s->dist_htree_index], br);
- /* Convert the distance code to the actual distance by possibly */
- /* looking up past distances from the s->ringbuffer. */
- if ((s->distance_code & ~0xf) == 0) {
- if (s->distance_code == 0) {
- --s->dist_rb_idx;
- s->distance_code = s->dist_rb[s->dist_rb_idx & 3];
- } 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;
- } 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 = 0x0fffffff;
- }
- }
- }
- } else {
- int distval = s->distance_code - s->num_direct_distance_codes;
- if (distval >= 0) {
- int nbits;
- int postfix;
- int offset;
- if (s->distance_postfix_bits == 0) {
- nbits = (distval >> 1) + 1;
- offset = ((2 + (distval & 1)) << nbits) - 4;
- s->distance_code = s->num_direct_distance_codes +
- offset + (int)BrotliReadBits(br, nbits);
- } else {
- postfix = distval & s->distance_postfix_mask;
- distval >>= s->distance_postfix_bits;
- nbits = (distval >> 1) + 1;
- offset = ((2 + (distval & 1)) << nbits) - 4;
- s->distance_code = s->num_direct_distance_codes +
- ((offset + (int)BrotliReadBits(br, nbits)) <<
- s->distance_postfix_bits) + postfix;
- }
- }
- s->distance_code = s->distance_code - NUM_DISTANCE_SHORT_CODES + 1;
- }
-postReadDistance:
- BROTLI_LOG_UINT(s->distance_code);
- if (s->max_distance != s->max_backward_distance) {
- if (pos < s->max_backward_distance_minus_custom_dict_size) {
- s->max_distance = pos + s->custom_dict_size;
- } else {
- s->max_distance = s->max_backward_distance;
- }
- }
- i = s->copy_length;
- /* Apply copy of LZ77 back-reference, or static dictionary reference if
- the distance is larger than the max LZ77 distance */
- if (s->distance_code > s->max_distance) {
- if (i >= kBrotliMinDictionaryWordLength &&
- i <= kBrotliMaxDictionaryWordLength) {
- int offset = kBrotliDictionaryOffsetsByLength[i];
- int word_id = s->distance_code - s->max_distance - 1;
- int shift = kBrotliDictionarySizeBitsByLength[i];
- int mask = (int)BitMask(shift);
- int word_idx = word_id & mask;
- int transform_idx = word_id >> shift;
- offset += word_idx * i;
- if (transform_idx < kNumTransforms) {
- const uint8_t* word = &kBrotliDictionary[offset];
- int len = i;
- if (transform_idx == 0) {
- memcpy(&s->ringbuffer[pos], word, (size_t)len);
- } else {
- len = TransformDictionaryWord(
- &s->ringbuffer[pos], word, len, transform_idx);
- }
- pos += len;
- s->meta_block_remaining_len -= len;
- if (pos >= s->ringbuffer_size) {
- s->to_write = s->ringbuffer_size;
- s->partially_written = 0;
- s->state = BROTLI_STATE_COMMAND_POST_WRITE_1;
- break;
- }
- } else {
- BROTLI_LOG(("Invalid backward reference. pos: %d distance: %d "
- "len: %d bytes left: %d\n",
- pos, s->distance_code, i,
- s->meta_block_remaining_len));
- result = BROTLI_FAILURE();
- break;
- }
- } else {
- BROTLI_LOG(("Invalid backward reference. pos: %d distance: %d "
- "len: %d bytes left: %d\n", pos, s->distance_code, i,
- s->meta_block_remaining_len));
- result = BROTLI_FAILURE();
- break;
- }
- } else {
- const uint8_t *ringbuffer_end_minus_copy_length =
- s->ringbuffer_end - i;
- copy_src = &s->ringbuffer[(pos - s->distance_code) &
- s->ringbuffer_mask];
- copy_dst = &s->ringbuffer[pos];
- if (PREDICT_FALSE(s->ringbuffer_end < (const uint8_t*)0 + i)) {
- ringbuffer_end_minus_copy_length = 0;
- }
- /* update the recent distances cache */
- s->dist_rb[s->dist_rb_idx & 3] = s->distance_code;
- ++s->dist_rb_idx;
- s->meta_block_remaining_len -= i;
- if (PREDICT_FALSE(s->meta_block_remaining_len < 0)) {
- BROTLI_LOG(("Invalid backward reference. pos: %d distance: %d "
- "len: %d bytes left: %d\n", pos, s->distance_code, i,
- s->meta_block_remaining_len));
- result = BROTLI_FAILURE();
- break;
- }
- /* There is 128+ bytes of slack in the ringbuffer allocation.
- Also, we have 16 short codes, that make these 16 bytes irrelevant
- in the ringbuffer. Let's copy over them as a first guess.
- */
- memmove16(copy_dst, copy_src);
- /* Now check if the copy extends over the ringbuffer end,
- or if the copy overlaps with itself, if yes, do wrap-copy. */
- if (copy_src < copy_dst) {
- if (copy_dst >= ringbuffer_end_minus_copy_length) {
- goto postWrapCopy;
- }
- if (copy_src + i > copy_dst) {
- goto postSelfintersecting;
- }
- } else {
- if (copy_src >= ringbuffer_end_minus_copy_length) {
- goto postWrapCopy;
- }
- if (copy_dst + i > copy_src) {
- goto postSelfintersecting;
- }
- }
- pos += i;
- if (i > 16) {
- if (i > 32) {
- memcpy(copy_dst + 16, copy_src + 16, (size_t)(i - 16));
- } else {
- /* This branch covers about 45% cases.
- Fixed size short copy allows more compiler optimizations. */
- memmove16(copy_dst + 16, copy_src + 16);
- }
- }
- }
- if (s->meta_block_remaining_len <= 0) {
- /* Next metablock, if any */
- s->state = BROTLI_STATE_METABLOCK_DONE;
- break;
- } else {
- goto CommandBegin;
- }
- postSelfintersecting:
- while (--i >= 0) {
- s->ringbuffer[pos] =
- s->ringbuffer[(pos - s->distance_code) & s->ringbuffer_mask];
- ++pos;
- }
- if (s->meta_block_remaining_len <= 0) {
- /* Next metablock, if any */
- s->state = BROTLI_STATE_METABLOCK_DONE;
- break;
- } else {
- goto CommandBegin;
- }
- postWrapCopy:
- s->state = BROTLI_STATE_COMMAND_POST_WRAP_COPY;
- /* No break, go to next state */
+ case BROTLI_STATE_COMMAND_POST_DECODE_LITERALS:
case BROTLI_STATE_COMMAND_POST_WRAP_COPY:
- while (--i >= 0) {
- s->ringbuffer[pos] =
- s->ringbuffer[(pos - s->distance_code) & s->ringbuffer_mask];
- ++pos;
- if (pos == s->ringbuffer_size) {
- s->to_write = s->ringbuffer_size;
- s->partially_written = 0;
- s->state = BROTLI_STATE_COMMAND_POST_WRITE_2;
- break;
- }
- }
- if (s->state == BROTLI_STATE_COMMAND_POST_WRAP_COPY) {
- if (s->meta_block_remaining_len <= 0) {
- /* Next metablock, if any */
- s->state = BROTLI_STATE_METABLOCK_DONE;
- break;
- } else {
- goto CommandBegin;
- }
+ result = ProcessCommands(s);
+ if (result == BROTLI_NEEDS_MORE_INPUT) {
+ result = SafeProcessCommands(s);
}
break;
case BROTLI_STATE_COMMAND_INNER_WRITE:
case BROTLI_STATE_COMMAND_POST_WRITE_1:
case BROTLI_STATE_COMMAND_POST_WRITE_2:
-innerWrite:
- result = WriteRingBuffer(output, s);
- if (result != BROTLI_RESULT_SUCCESS) {
+ result = WriteRingBuffer(available_out, next_out, total_out, s);
+ if (result != BROTLI_SUCCESS) {
break;
}
- pos -= s->ringbuffer_size;
s->max_distance = s->max_backward_distance;
if (s->state == BROTLI_STATE_COMMAND_POST_WRITE_1) {
- memcpy(s->ringbuffer, s->ringbuffer_end, (size_t)pos);
- if (s->meta_block_remaining_len <= 0) {
+ memcpy(s->ringbuffer, s->ringbuffer_end, (size_t)s->pos);
+ if (s->meta_block_remaining_len == 0) {
/* Next metablock, if any */
s->state = BROTLI_STATE_METABLOCK_DONE;
- break;
} else {
- goto CommandBegin;
+ s->state = BROTLI_STATE_COMMAND_BEGIN;
}
+ break;
} else if (s->state == BROTLI_STATE_COMMAND_POST_WRITE_2) {
s->state = BROTLI_STATE_COMMAND_POST_WRAP_COPY;
} else { /* BROTLI_STATE_COMMAND_INNER_WRITE */
- if (i == 0) {
- if (s->meta_block_remaining_len <= 0) {
+ if (s->loop_counter == 0) {
+ if (s->meta_block_remaining_len == 0) {
s->state = BROTLI_STATE_METABLOCK_DONE;
- break;
+ } else {
+ s->state = BROTLI_STATE_COMMAND_POST_DECODE_LITERALS;
}
- goto postDecodeLiterals;
+ break;
}
s->state = BROTLI_STATE_COMMAND_INNER;
}
break;
case BROTLI_STATE_METABLOCK_DONE:
+ if (s->meta_block_remaining_len < 0) {
+ result = BROTLI_FAILURE(BROTLI_ERROR_FORMAT_BLOCK_LENGTH_2);
+ break;
+ }
BrotliStateCleanupAfterMetablock(s);
if (!s->is_last_metablock) {
s->state = BROTLI_STATE_METABLOCK_BEGIN;
break;
}
- s->to_write = pos;
- s->partially_written = 0;
+ if (!BrotliJumpToByteBoundary(br)) {
+ result = BROTLI_FAILURE(BROTLI_ERROR_FORMAT_PADDING_2);
+ break;
+ }
+ if (s->buffer_length == 0) {
+ BrotliBitReaderUnload(br);
+ *available_in = br->avail_in;
+ *next_in = br->next_in;
+ }
s->state = BROTLI_STATE_DONE;
/* No break, continue to next state */
case BROTLI_STATE_DONE:
if (s->ringbuffer != 0) {
- result = WriteRingBuffer(output, s);
- if (result != BROTLI_RESULT_SUCCESS) {
+ result = WriteRingBuffer(available_out, next_out, total_out, s);
+ if (result != BROTLI_SUCCESS) {
break;
}
}
- if (!BrotliJumpToByteBoundary(br)) {
- result = BROTLI_FAILURE();
- }
- if (!BrotliIsBitReaderOK(br)) {
- /* The brotli input stream was too small, does not follow the spec.
- NOTE: larger input is allowed, smaller not. */
- result = BROTLI_FAILURE();
- }
- return result;
+ return SaveErrorCode(s, result);
}
}
- s->pos = pos;
- s->loop_counter = i;
- return result;
+ return SaveErrorCode(s, result);
}
void BrotliSetCustomDictionary(
size_t size, const uint8_t* dict, BrotliState* s) {
+ if (size > (1u << 24)) {
+ return;
+ }
s->custom_dict = dict;
- s->custom_dict_size = (int) size;
+ s->custom_dict_size = (int)size;
}
+BrotliErrorCode BrotliGetErrorCode(const BrotliState* s) {
+ return (BrotliErrorCode)s->error_code;
+}
+
+const char* BrotliErrorString(BrotliErrorCode c) {
+ switch (c) {
+#define _BROTLI_ERROR_CODE_CASE(PREFIX, NAME, CODE) \
+ case BROTLI ## PREFIX ## NAME: return #NAME;
+#define _BROTLI_NOTHING
+ BROTLI_ERROR_CODES_LIST(_BROTLI_ERROR_CODE_CASE, _BROTLI_NOTHING)
+#undef _BROTLI_ERROR_CODE_CASE
+#undef _BROTLI_NOTHING
+ default: return "INVALID";
+ }
+}
#if defined(__cplusplus) || defined(c_plusplus)
-} /* extern "C" */
+} /* extern "C" */
#endif
diff --git a/modules/brotli/dec/decode.h b/modules/brotli/dec/decode.h
index 8a7919de6..056a8b548 100644
--- a/modules/brotli/dec/decode.h
+++ b/modules/brotli/dec/decode.h
@@ -1,16 +1,7 @@
/* Copyright 2013 Google Inc. All Rights Reserved.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+ Distributed under MIT license.
+ See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
*/
/* API for Brotli decompression */
@@ -18,141 +9,151 @@
#ifndef BROTLI_DEC_DECODE_H_
#define BROTLI_DEC_DECODE_H_
-#include "./state.h"
-#include "./streams.h"
#include "./types.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
+typedef struct BrotliStateStruct BrotliState;
+
typedef enum {
- /* Decoding error, e.g. corrupt input or no memory */
+ /* Decoding error, e.g. corrupt input or memory allocation problem */
BROTLI_RESULT_ERROR = 0,
- /* Successfully completely done */
+ /* Decoding successfully completed */
BROTLI_RESULT_SUCCESS = 1,
- /* Partially done, but must be called again with more input */
+ /* Partially done; should be called again with more input */
BROTLI_RESULT_NEEDS_MORE_INPUT = 2,
- /* Partially done, but must be called again with more output */
+ /* Partially done; should be called again with more output */
BROTLI_RESULT_NEEDS_MORE_OUTPUT = 3
} BrotliResult;
-/* BROTLI_FAILURE macro unwraps to BROTLI_RESULT_ERROR in non-debug build. */
-/* In debug build it dumps file name, line and pretty function name. */
-#if defined(_MSC_VER) || !defined(BROTLI_DEBUG)
-#define BROTLI_FAILURE() BROTLI_RESULT_ERROR
-#else
-#define BROTLI_FAILURE() \
- BrotliFailure(__FILE__, __LINE__, __PRETTY_FUNCTION__)
-static inline BrotliResult BrotliFailure(const char *f, int l, const char *fn) {
- fprintf(stderr, "ERROR at %s:%d (%s)\n", f, l, fn);
- fflush(stderr);
- return BROTLI_RESULT_ERROR;
-}
-#endif
+#define BROTLI_ERROR_CODES_LIST(BROTLI_ERROR_CODE, SEPARATOR) \
+ BROTLI_ERROR_CODE(_, NO_ERROR, 0) SEPARATOR \
+ /* Same as BrotliResult values */ \
+ BROTLI_ERROR_CODE(_, SUCCESS, 1) SEPARATOR \
+ BROTLI_ERROR_CODE(_, NEEDS_MORE_INPUT, 2) SEPARATOR \
+ BROTLI_ERROR_CODE(_, NEEDS_MORE_OUTPUT, 3) SEPARATOR \
+ \
+ /* Errors caused by invalid input */ \
+ BROTLI_ERROR_CODE(_ERROR_FORMAT_, EXUBERANT_NIBBLE, -1) SEPARATOR \
+ BROTLI_ERROR_CODE(_ERROR_FORMAT_, RESERVED, -2) SEPARATOR \
+ BROTLI_ERROR_CODE(_ERROR_FORMAT_, EXUBERANT_META_NIBBLE, -3) SEPARATOR \
+ BROTLI_ERROR_CODE(_ERROR_FORMAT_, SIMPLE_HUFFMAN_ALPHABET, -4) SEPARATOR \
+ BROTLI_ERROR_CODE(_ERROR_FORMAT_, SIMPLE_HUFFMAN_SAME, -5) SEPARATOR \
+ BROTLI_ERROR_CODE(_ERROR_FORMAT_, CL_SPACE, -6) SEPARATOR \
+ BROTLI_ERROR_CODE(_ERROR_FORMAT_, HUFFMAN_SPACE, -7) SEPARATOR \
+ BROTLI_ERROR_CODE(_ERROR_FORMAT_, CONTEXT_MAP_REPEAT, -8) SEPARATOR \
+ BROTLI_ERROR_CODE(_ERROR_FORMAT_, BLOCK_LENGTH_1, -9) SEPARATOR \
+ BROTLI_ERROR_CODE(_ERROR_FORMAT_, BLOCK_LENGTH_2, -10) SEPARATOR \
+ BROTLI_ERROR_CODE(_ERROR_FORMAT_, TRANSFORM, -11) SEPARATOR \
+ BROTLI_ERROR_CODE(_ERROR_FORMAT_, DICTIONARY, -12) SEPARATOR \
+ BROTLI_ERROR_CODE(_ERROR_FORMAT_, WINDOW_BITS, -13) SEPARATOR \
+ BROTLI_ERROR_CODE(_ERROR_FORMAT_, PADDING_1, -14) SEPARATOR \
+ BROTLI_ERROR_CODE(_ERROR_FORMAT_, PADDING_2, -15) SEPARATOR \
+ \
+ /* -16..-20 codes are reserved */ \
+ \
+ /* Memory allocation problems */ \
+ BROTLI_ERROR_CODE(_ERROR_ALLOC_, CONTEXT_MODES, -21) SEPARATOR \
+ /* Literal, insert and distance trees together */ \
+ BROTLI_ERROR_CODE(_ERROR_ALLOC_, TREE_GROUPS, -22) SEPARATOR \
+ /* -23..-24 codes are reserved for distinct tree groups */ \
+ BROTLI_ERROR_CODE(_ERROR_ALLOC_, CONTEXT_MAP, -25) SEPARATOR \
+ BROTLI_ERROR_CODE(_ERROR_ALLOC_, RING_BUFFER_1, -26) SEPARATOR \
+ BROTLI_ERROR_CODE(_ERROR_ALLOC_, RING_BUFFER_2, -27) SEPARATOR \
+ /* -28..-29 codes are reserved for dynamic ringbuffer allocation */ \
+ BROTLI_ERROR_CODE(_ERROR_ALLOC_, BLOCK_TYPE_TREES, -30) SEPARATOR \
+ \
+ /* "Impossible" states */ \
+ BROTLI_ERROR_CODE(_ERROR_, UNREACHABLE, -31)
-/* Sets *decoded_size to the decompressed size of the given encoded stream. */
-/* This function only works if the encoded buffer has a single meta block, */
-/* or if it has two meta-blocks, where the first is uncompressed and the */
-/* second is empty. */
-/* Returns 1 on success, 0 on failure. */
+typedef enum {
+#define _BROTLI_COMMA ,
+#define _BROTLI_ERROR_CODE_ENUM_ITEM(PREFIX, NAME, CODE) \
+ BROTLI ## PREFIX ## NAME = CODE
+ BROTLI_ERROR_CODES_LIST(_BROTLI_ERROR_CODE_ENUM_ITEM, _BROTLI_COMMA)
+#undef _BROTLI_ERROR_CODE_ENUM_ITEM
+#undef _BROTLI_COMMA
+} BrotliErrorCode;
+
+#define BROTLI_LAST_ERROR_CODE BROTLI_ERROR_UNREACHABLE
+
+/* Creates the instance of BrotliState and initializes it. |alloc_func| and
+ |free_func| MUST be both zero or both non-zero. In the case they are both
+ zero, default memory allocators are used. |opaque| is passed to |alloc_func|
+ and |free_func| when they are called. */
+BrotliState* BrotliCreateState(
+ brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque);
+
+/* Deinitializes and frees BrotliState instance. */
+void BrotliDestroyState(BrotliState* state);
+
+/* Sets |*decoded_size| to the decompressed size of the given encoded stream.
+ This function only works if the encoded buffer has a single meta block,
+ or if it has two meta-blocks, where the first is uncompressed and the
+ second is empty.
+ Returns 1 on success, 0 on failure. */
int BrotliDecompressedSize(size_t encoded_size,
const uint8_t* encoded_buffer,
size_t* decoded_size);
-/* Decompresses the data in encoded_buffer into decoded_buffer, and sets */
-/* *decoded_size to the decompressed length. */
-/* Returns 0 if there was either a bit stream error or memory allocation */
-/* error, and 1 otherwise. */
-/* If decoded size is zero, returns 1 and keeps decoded_buffer unchanged. */
+/* Decompresses the data in |encoded_buffer| into |decoded_buffer|, and sets
+ |*decoded_size| to the decompressed length. */
BrotliResult BrotliDecompressBuffer(size_t encoded_size,
const uint8_t* encoded_buffer,
size_t* decoded_size,
uint8_t* decoded_buffer);
-/* Same as above, but uses the specified input and output callbacks instead */
-/* of reading from and writing to pre-allocated memory buffers. */
-BrotliResult BrotliDecompress(BrotliInput input, BrotliOutput output);
+/* Decompresses the data. Supports partial input and output.
-/* Same as above, but supports the caller to call the decoder repeatedly with
- partial data to support streaming. The state must be initialized with
- BrotliStateInit and reused with every call for the same stream.
- Return values:
- 0: failure.
- 1: success, and done.
- 2: success so far, end not reached so should call again with more input.
- The finish parameter is used as follows, for a series of calls with the
- same state:
- 0: Every call except the last one must be called with finish set to 0. The
- last call may have finish set to either 0 or 1. Only if finish is 0, can
- the function return 2. It may also return 0 or 1, in that case no more
- calls (even with finish 1) may be made.
- 1: Only the last call may have finish set to 1. It's ok to give empty input
- if all input was already given to previous calls. It is also ok to have
- only one single call in total, with finish 1, and with all input
- available immediately. That matches the non-streaming case. If finish is
- 1, the function can only return 0 or 1, never 2. After a finish, no more
- calls may be done.
- After everything is done, the state must be cleaned with BrotliStateCleanup
- to free allocated resources.
- The given BrotliOutput must always accept all output and make enough space,
- it returning a smaller value than the amount of bytes to write always results
- in an error.
-*/
-BrotliResult BrotliDecompressStreaming(BrotliInput input, BrotliOutput output,
- int finish, BrotliState* s);
+ Must be called with an allocated input buffer in |*next_in| and an allocated
+ output buffer in |*next_out|. The values |*available_in| and |*available_out|
+ must specify the allocated size in |*next_in| and |*next_out| respectively.
-/* Same as above, but with memory buffers.
- Must be called with an allocated input buffer in *next_in and an allocated
- output buffer in *next_out. The values *available_in and *available_out
- must specify the allocated size in *next_in and *next_out respectively.
- The value *total_out must be 0 initially, and will be summed with the
- amount of output bytes written after each call, so that at the end it
- gives the complete decoded size.
- After each call, *available_in will be decremented by the amount of input
- bytes consumed, and the *next_in pointer will be incremented by that amount.
- Similarly, *available_out will be decremented by the amount of output
- bytes written, and the *next_out pointer will be incremented by that
- amount.
+ After each call, |*available_in| will be decremented by the amount of input
+ bytes consumed, and the |*next_in| pointer will be incremented by that
+ amount. Similarly, |*available_out| will be decremented by the amount of
+ output bytes written, and the |*next_out| pointer will be incremented by that
+ amount. |total_out|, if it is not a null-pointer, will be set to the number
+ of bytes decompressed since the last state initialization.
- The input may be partial. With each next function call, *next_in and
- *available_in must be updated to point to a next part of the compressed
- input. The current implementation will always consume all input unless
- an error occurs, so normally *available_in will always be 0 after
- calling this function and the next adjacent part of input is desired.
-
- In the current implementation, the function requires that there is enough
- output buffer size to write all currently processed input, so
- *available_out must be large enough. Since the function updates *next_out
- each time, as long as the output buffer is large enough you can keep
- reusing this variable. It is also possible to update *next_out and
- *available_out yourself before a next call, e.g. to point to a new larger
- buffer.
-*/
-BrotliResult BrotliDecompressBufferStreaming(size_t* available_in,
- const uint8_t** next_in,
- int finish,
- size_t* available_out,
- uint8_t** next_out,
- size_t* total_out,
- BrotliState* s);
+ Input is never overconsumed, so |next_in| and |available_in| could be passed
+ to the next consumer after decoding is complete. */
+BrotliResult BrotliDecompressStream(size_t* available_in,
+ const uint8_t** next_in,
+ size_t* available_out,
+ uint8_t** next_out,
+ size_t* total_out,
+ BrotliState* s);
/* Fills the new state with a dictionary for LZ77, warming up the ringbuffer,
e.g. for custom static dictionaries for data formats.
Not to be confused with the built-in transformable dictionary of Brotli.
- The dictionary must exist in memory until decoding is done and is owned by
- the caller. To use:
- -initialize state with BrotliStateInit
- -use BrotliSetCustomDictionary
- -use BrotliDecompressBufferStreaming
- -clean up with BrotliStateCleanup
+ |size| should be less or equal to 2^24 (16MiB), otherwise the dictionary will
+ be ignored. The dictionary must exist in memory until decoding is done and
+ is owned by the caller. To use:
+ 1) Allocate and initialize state with BrotliCreateState
+ 2) Use BrotliSetCustomDictionary
+ 3) Use BrotliDecompressStream
+ 4) Clean up and free state with BrotliDestroyState
*/
void BrotliSetCustomDictionary(
size_t size, const uint8_t* dict, BrotliState* s);
+/* Returns 1, if s is in a state where we have not read any input bytes yet,
+ and 0 otherwise */
+int BrotliStateIsStreamStart(const BrotliState* s);
-/* Escalate internal functions visibility; for testing purposes only. */
-void InverseMoveToFrontTransformForTesting(uint8_t* v, int l, BrotliState* s);
+/* Returns 1, if s is in a state where we reached the end of the input and
+ produced all of the output, and 0 otherwise. */
+int BrotliStateIsStreamEnd(const BrotliState* s);
+
+/* Returns detailed error code after BrotliDecompressStream returns
+ BROTLI_RESULT_ERROR. */
+BrotliErrorCode BrotliGetErrorCode(const BrotliState* s);
+
+const char* BrotliErrorString(BrotliErrorCode c);
#if defined(__cplusplus) || defined(c_plusplus)
} /* extern "C" */
diff --git a/modules/brotli/dec/dictionary.c b/modules/brotli/dec/dictionary.c
index 7848c55f1..f8f58575c 100644
--- a/modules/brotli/dec/dictionary.c
+++ b/modules/brotli/dec/dictionary.c
@@ -1,20 +1,15 @@
/* Copyright 2013 Google Inc. All Rights Reserved.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+ Distributed under MIT license.
+ See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
*/
#include "./dictionary.h"
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
/* In case of multiple definition linker error with dictionary.cc from the
encoder: include only one of enc/dictionary.cc or dec/dictionary.c in a
target using both enc and dec. */
@@ -9465,3 +9460,7 @@ const uint8_t kBrotliDictionary[122784] = {
0x88, 0xe0, 0xa4, 0xb8, 0xe0, 0xa4, 0x95, 0xe0, 0xa5, 0x8d, 0xe0, 0xa4, 0xb0,
0xe0, 0xa4, 0xbf, 0xe0, 0xa4, 0xaf, 0xe0, 0xa4, 0xa4, 0xe0, 0xa4, 0xbe,
};
+
+#if defined(__cplusplus) || defined(c_plusplus)
+} /* extern "C" */
+#endif
diff --git a/modules/brotli/dec/dictionary.h b/modules/brotli/dec/dictionary.h
index 42448172b..ae250c2d7 100644
--- a/modules/brotli/dec/dictionary.h
+++ b/modules/brotli/dec/dictionary.h
@@ -1,16 +1,7 @@
/* Copyright 2013 Google Inc. All Rights Reserved.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+ Distributed under MIT license.
+ See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
*/
/* Collection of static dictionary words. */
@@ -26,23 +17,22 @@ extern "C" {
extern const uint8_t kBrotliDictionary[122784];
-static const int kBrotliDictionaryOffsetsByLength[] = {
- 0, 0, 0, 0, 0, 4096, 9216, 21504, 35840, 44032,
- 53248, 63488, 74752, 87040, 93696, 100864, 104704, 106752, 108928, 113536,
- 115968, 118528, 119872, 121280, 122016,
+static const uint32_t kBrotliDictionaryOffsetsByLength[] = {
+ 0, 0, 0, 0, 0, 4096, 9216, 21504, 35840, 44032, 53248, 63488, 74752, 87040,
+ 93696, 100864, 104704, 106752, 108928, 113536, 115968, 118528, 119872, 121280,
+ 122016
};
-static const int8_t kBrotliDictionarySizeBitsByLength[] = {
- 0, 0, 0, 0, 10, 10, 11, 11, 10, 10,
- 10, 10, 10, 9, 9, 8, 7, 7, 8, 7,
- 7, 6, 6, 5, 5,
+static const uint8_t kBrotliDictionarySizeBitsByLength[] = {
+ 0, 0, 0, 0, 10, 10, 11, 11, 10, 10, 10, 10, 10,
+ 9, 9, 8, 7, 7, 8, 7, 7, 6, 6, 5, 5,
};
static const int kBrotliMinDictionaryWordLength = 4;
static const int kBrotliMaxDictionaryWordLength = 24;
#if defined(__cplusplus) || defined(c_plusplus)
-} /* extern "C" */
+} /* extern "C" */
#endif
#endif /* BROTLI_DEC_DICTIONARY_H_ */
diff --git a/modules/brotli/dec/huffman.c b/modules/brotli/dec/huffman.c
index 2407670f3..3775ffe7e 100644
--- a/modules/brotli/dec/huffman.c
+++ b/modules/brotli/dec/huffman.c
@@ -1,41 +1,75 @@
/* Copyright 2013 Google Inc. All Rights Reserved.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+ Distributed under MIT license.
+ See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
*/
/* Utilities for building Huffman decoding tables. */
-#include
-#include
-#include
#include "./huffman.h"
+
+#include /* memcpy, memset */
+
#include "./port.h"
+#include "./types.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
-/* Returns reverse(reverse(key, len) + 1, len), where reverse(key, len) is the
- bit-wise reversal of the len least significant bits of key. */
-static BROTLI_INLINE uint32_t GetNextKey(uint32_t key, int len) {
+#define BROTLI_REVERSE_BITS_MAX 8
+
#ifdef BROTLI_RBIT
- return BROTLI_RBIT(BROTLI_RBIT(key) + (1 << (8 * sizeof(unsigned) - len)));
+#define BROTLI_REVERSE_BITS_BASE (32 - BROTLI_REVERSE_BITS_MAX)
#else
- unsigned step = (unsigned)(1 << (len - 1));
- while (key & step) {
- step >>= 1;
- }
- return (key & (step - 1)) + step;
+#define BROTLI_REVERSE_BITS_BASE 0
+static uint8_t kReverseBits[1 << BROTLI_REVERSE_BITS_MAX] = {
+ 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
+ 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
+ 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
+ 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
+ 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
+ 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
+ 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
+ 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
+ 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
+ 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
+ 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
+ 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
+ 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
+ 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
+ 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
+ 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
+ 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
+ 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
+ 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
+ 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
+ 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
+ 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
+ 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
+ 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
+ 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
+ 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
+ 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
+ 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
+ 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
+ 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
+ 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
+ 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
+};
+#endif /* BROTLI_RBIT */
+
+#define BROTLI_REVERSE_BITS_LOWEST \
+ (1U << (BROTLI_REVERSE_BITS_MAX - 1 + BROTLI_REVERSE_BITS_BASE))
+
+/* Returns reverse(num >> BROTLI_REVERSE_BITS_BASE, BROTLI_REVERSE_BITS_MAX),
+ where reverse(value, len) is the bit-wise reversal of the len least
+ significant bits of value. */
+static BROTLI_INLINE uint32_t BrotliReverseBits(uint32_t num) {
+#ifdef BROTLI_RBIT
+ return BROTLI_RBIT(num);
+#else
+ return kReverseBits[num];
#endif
}
@@ -65,20 +99,22 @@ static BROTLI_INLINE int NextTableBitSize(const uint16_t* const count,
return len - root_bits;
}
-
void BrotliBuildCodeLengthsHuffmanTable(HuffmanCode* table,
const uint8_t* const code_lengths,
- uint16_t *count) {
- HuffmanCode code; /* current table entry */
- int symbol; /* symbol index in original or sorted table */
- unsigned key; /* reversed prefix code */
- int step; /* step size to replicate values in current table */
- int table_size; /* size of current table */
- int sorted[18]; /* symbols sorted by code length */
+ uint16_t* count) {
+ HuffmanCode code; /* current table entry */
+ int symbol; /* symbol index in original or sorted table */
+ uint32_t key; /* prefix code */
+ uint32_t key_step; /* prefix code addend */
+ int step; /* step size to replicate values in current table */
+ int table_size; /* size of current table */
+ int sorted[18]; /* symbols sorted by code length */
/* offsets in sorted table for each length */
int offset[BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH + 1];
int bits;
int bits_count;
+ BROTLI_DCHECK(BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH <=
+ BROTLI_REVERSE_BITS_MAX);
/* generate offsets into sorted symbol table by code length */
symbol = -1;
@@ -106,7 +142,7 @@ void BrotliBuildCodeLengthsHuffmanTable(HuffmanCode* table,
if (offset[0] == 0) {
code.bits = 0;
code.value = (uint16_t)sorted[0];
- for (key = 0; key < table_size; ++key) {
+ for (key = 0; key < (uint32_t)table_size; ++key) {
table[key] = code;
}
return;
@@ -114,6 +150,7 @@ void BrotliBuildCodeLengthsHuffmanTable(HuffmanCode* table,
/* fill in table */
key = 0;
+ key_step = BROTLI_REVERSE_BITS_LOWEST;
symbol = 0;
bits = 1;
step = 2;
@@ -121,32 +158,38 @@ void BrotliBuildCodeLengthsHuffmanTable(HuffmanCode* table,
code.bits = (uint8_t)bits;
for (bits_count = count[bits]; bits_count != 0; --bits_count) {
code.value = (uint16_t)sorted[symbol++];
- ReplicateValue(&table[key], step, table_size, code);
- key = GetNextKey(key, bits);
+ ReplicateValue(&table[BrotliReverseBits(key)], step, table_size, code);
+ key += key_step;
}
step <<= 1;
+ key_step >>= 1;
} while (++bits <= BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH);
}
-int BrotliBuildHuffmanTable(HuffmanCode* root_table,
- int root_bits,
- const uint16_t* const symbol_lists,
- uint16_t *count) {
- HuffmanCode code; /* current table entry */
- HuffmanCode* table; /* next available space in table */
- int len; /* current code length */
- int symbol; /* symbol index in original or sorted table */
- unsigned key; /* reversed prefix code */
- int step; /* step size to replicate values in current table */
- unsigned low; /* low bits for current root entry */
- unsigned mask; /* mask for low bits */
- int table_bits; /* key length of current table */
- int table_size; /* size of current table */
- int total_size; /* sum of root table size and 2nd level table sizes */
+uint32_t BrotliBuildHuffmanTable(HuffmanCode* root_table,
+ int root_bits,
+ const uint16_t* const symbol_lists,
+ uint16_t* count) {
+ HuffmanCode code; /* current table entry */
+ HuffmanCode* table; /* next available space in table */
+ int len; /* current code length */
+ int symbol; /* symbol index in original or sorted table */
+ uint32_t key; /* prefix code */
+ uint32_t key_step; /* prefix code addend */
+ uint32_t sub_key; /* 2nd level table prefix code */
+ uint32_t sub_key_step; /* 2nd level table prefix code addend */
+ int step; /* step size to replicate values in current table */
+ int table_bits; /* key length of current table */
+ int table_size; /* size of current table */
+ int total_size; /* sum of root table size and 2nd level table sizes */
int max_length = -1;
int bits;
int bits_count;
+ BROTLI_DCHECK(root_bits <= BROTLI_REVERSE_BITS_MAX);
+ BROTLI_DCHECK(BROTLI_HUFFMAN_MAX_CODE_LENGTH - root_bits <=
+ BROTLI_REVERSE_BITS_MAX);
+
while (symbol_lists[max_length] == 0xFFFF) max_length--;
max_length += BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1;
@@ -163,6 +206,7 @@ int BrotliBuildHuffmanTable(HuffmanCode* root_table,
table_size = 1 << table_bits;
}
key = 0;
+ key_step = BROTLI_REVERSE_BITS_LOWEST;
bits = 1;
step = 2;
do {
@@ -171,10 +215,11 @@ int BrotliBuildHuffmanTable(HuffmanCode* root_table,
for (bits_count = count[bits]; bits_count != 0; --bits_count) {
symbol = symbol_lists[symbol];
code.value = (uint16_t)symbol;
- ReplicateValue(&table[key], step, table_size, code);
- key = GetNextKey(key, bits);
+ ReplicateValue(&table[BrotliReverseBits(key)], step, table_size, code);
+ key += key_step;
}
step <<= 1;
+ key_step >>= 1;
} while (++bits <= table_bits);
/* if root_bits != table_bits we only created one fraction of the */
@@ -186,37 +231,43 @@ int BrotliBuildHuffmanTable(HuffmanCode* root_table,
}
/* fill in 2nd level tables and add pointers to root table */
- mask = (unsigned)(total_size - 1);
- low = (unsigned)-1;
- for (len = root_bits + 1, step = 2; len <= max_length; ++len, step <<= 1) {
+ key_step = BROTLI_REVERSE_BITS_LOWEST >> (root_bits - 1);
+ sub_key = (BROTLI_REVERSE_BITS_LOWEST << 1);
+ sub_key_step = BROTLI_REVERSE_BITS_LOWEST;
+ for (len = root_bits + 1, step = 2; len <= max_length; ++len) {
symbol = len - (BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1);
for (; count[len] != 0; --count[len]) {
- if ((key & mask) != low) {
+ if (sub_key == (BROTLI_REVERSE_BITS_LOWEST << 1U)) {
table += table_size;
table_bits = NextTableBitSize(count, len, root_bits);
table_size = 1 << table_bits;
total_size += table_size;
- low = key & mask;
- root_table[low].bits = (uint8_t)(table_bits + root_bits);
- root_table[low].value = (uint16_t)(
- ((size_t)(table - root_table)) - low);
+ sub_key = BrotliReverseBits(key);
+ key += key_step;
+ root_table[sub_key].bits = (uint8_t)(table_bits + root_bits);
+ root_table[sub_key].value =
+ (uint16_t)(((size_t)(table - root_table)) - sub_key);
+ sub_key = 0;
}
code.bits = (uint8_t)(len - root_bits);
symbol = symbol_lists[symbol];
code.value = (uint16_t)symbol;
- ReplicateValue(&table[key >> root_bits], step, table_size, code);
- key = GetNextKey(key, len);
+ ReplicateValue(
+ &table[BrotliReverseBits(sub_key)], step, table_size, code);
+ sub_key += sub_key_step;
}
+ step <<= 1;
+ sub_key_step >>= 1;
}
- return total_size;
+ return (uint32_t)total_size;
}
-int BrotliBuildSimpleHuffmanTable(HuffmanCode* table,
- int root_bits,
- uint16_t *val,
- uint32_t num_symbols) {
- int table_size = 1;
- const int goal_size = 1 << root_bits;
+uint32_t BrotliBuildSimpleHuffmanTable(HuffmanCode* table,
+ int root_bits,
+ uint16_t* val,
+ uint32_t num_symbols) {
+ uint32_t table_size = 1;
+ const uint32_t goal_size = 1U << root_bits;
switch (num_symbols) {
case 0:
table[0].bits = 0;
@@ -250,49 +301,47 @@ int BrotliBuildSimpleHuffmanTable(HuffmanCode* table,
table[3].bits = 2;
table_size = 4;
break;
- case 3:
- {
- int i, k;
- for (i = 0; i < 3; ++i) {
- for (k = i + 1; k < 4; ++k) {
- if (val[k] < val[i]) {
- uint16_t t = val[k];
- val[k] = val[i];
- val[i] = t;
- }
+ case 3: {
+ int i, k;
+ for (i = 0; i < 3; ++i) {
+ for (k = i + 1; k < 4; ++k) {
+ if (val[k] < val[i]) {
+ uint16_t t = val[k];
+ val[k] = val[i];
+ val[i] = t;
}
}
- for (i = 0; i < 4; ++i) {
- table[i].bits = 2;
- }
- table[0].value = val[0];
- table[2].value = val[1];
- table[1].value = val[2];
- table[3].value = val[3];
- table_size = 4;
}
- break;
- case 4:
- {
- int i;
- if (val[3] < val[2]) {
- uint16_t t = val[3];
- val[3] = val[2];
- val[2] = t;
- }
- for (i = 0; i < 7; ++i) {
- table[i].value = val[0];
- table[i].bits = (uint8_t)(1 + (i & 1));
- }
- table[1].value = val[1];
- table[3].value = val[2];
- table[5].value = val[1];
- table[7].value = val[3];
- table[3].bits = 3;
- table[7].bits = 3;
- table_size = 8;
+ for (i = 0; i < 4; ++i) {
+ table[i].bits = 2;
}
+ table[0].value = val[0];
+ table[2].value = val[1];
+ table[1].value = val[2];
+ table[3].value = val[3];
+ table_size = 4;
break;
+ }
+ case 4: {
+ int i;
+ if (val[3] < val[2]) {
+ uint16_t t = val[3];
+ val[3] = val[2];
+ val[2] = t;
+ }
+ for (i = 0; i < 7; ++i) {
+ table[i].value = val[0];
+ table[i].bits = (uint8_t)(1 + (i & 1));
+ }
+ table[1].value = val[1];
+ table[3].value = val[2];
+ table[5].value = val[1];
+ table[7].value = val[3];
+ table[3].bits = 3;
+ table[7].bits = 3;
+ table_size = 8;
+ break;
+ }
}
while (table_size != goal_size) {
memcpy(&table[table_size], &table[0],
@@ -302,25 +351,6 @@ int BrotliBuildSimpleHuffmanTable(HuffmanCode* table,
return goal_size;
}
-void BrotliHuffmanTreeGroupInit(HuffmanTreeGroup* group, int alphabet_size,
- int ntrees) {
- /* Pack two mallocs into one */
- const size_t code_size =
- sizeof(HuffmanCode) * (size_t)(ntrees * BROTLI_HUFFMAN_MAX_TABLE_SIZE);
- const size_t htree_size = sizeof(HuffmanCode*) * (size_t)ntrees;
- char *p = (char*)malloc(code_size + htree_size);
- group->alphabet_size = (int16_t)alphabet_size;
- group->num_htrees = (int16_t)ntrees;
- group->codes = (HuffmanCode*)p;
- group->htrees = (HuffmanCode**)(p + code_size);
-}
-
-void BrotliHuffmanTreeGroupRelease(HuffmanTreeGroup* group) {
- if (group->codes) {
- free(group->codes);
- }
-}
-
#if defined(__cplusplus) || defined(c_plusplus)
-} /* extern "C" */
+} /* extern "C" */
#endif
diff --git a/modules/brotli/dec/huffman.h b/modules/brotli/dec/huffman.h
index 87cf2789e..fc880810c 100644
--- a/modules/brotli/dec/huffman.h
+++ b/modules/brotli/dec/huffman.h
@@ -1,16 +1,7 @@
/* Copyright 2013 Google Inc. All Rights Reserved.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+ Distributed under MIT license.
+ See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
*/
/* Utilities for building Huffman decoding tables. */
@@ -19,6 +10,7 @@
#define BROTLI_DEC_HUFFMAN_H_
#include "./types.h"
+#include "./port.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
@@ -29,53 +21,48 @@ extern "C" {
/* For current format this constant equals to kNumInsertAndCopyCodes */
#define BROTLI_HUFFMAN_MAX_CODE_LENGTHS_SIZE 704
-/* Maximum possible Huffman table size for an alphabet size of 704, max code
- * length 15 and root table bits 8. */
-#define BROTLI_HUFFMAN_MAX_TABLE_SIZE 1080
+/* 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};
+#define BROTLI_HUFFMAN_MAX_SIZE_26 396
+#define BROTLI_HUFFMAN_MAX_SIZE_258 632
+#define BROTLI_HUFFMAN_MAX_SIZE_272 646
#define BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH 5
typedef struct {
- uint8_t bits; /* number of bits used for this symbol */
- uint16_t value; /* symbol value or table offset */
+ uint8_t bits; /* number of bits used for this symbol */
+ uint16_t value; /* symbol value or table offset */
} HuffmanCode;
-
/* Builds Huffman lookup table assuming code lengths are in symbol order. */
-void BrotliBuildCodeLengthsHuffmanTable(HuffmanCode* root_table,
- const uint8_t* const code_lengths,
- uint16_t *count);
+BROTLI_INTERNAL void BrotliBuildCodeLengthsHuffmanTable(HuffmanCode* root_table,
+ const uint8_t* const code_lengths, uint16_t* count);
/* Builds Huffman lookup table assuming code lengths are in symbol order. */
/* Returns size of resulting table. */
-int BrotliBuildHuffmanTable(HuffmanCode* root_table,
- int root_bits,
- const uint16_t* const symbol_lists,
- uint16_t *count_arg);
+BROTLI_INTERNAL uint32_t BrotliBuildHuffmanTable(HuffmanCode* root_table,
+ int root_bits, const uint16_t* const symbol_lists, uint16_t* count_arg);
/* Builds a simple Huffman table. The num_symbols parameter is to be */
/* interpreted as follows: 0 means 1 symbol, 1 means 2 symbols, 2 means 3 */
/* symbols, 3 means 4 symbols with lengths 2,2,2,2, 4 means 4 symbols with */
/* lengths 1,2,3,3. */
-int BrotliBuildSimpleHuffmanTable(HuffmanCode* table,
- int root_bits,
- uint16_t *symbols,
- uint32_t num_symbols);
+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. */
typedef struct {
HuffmanCode** htrees;
HuffmanCode* codes;
- int16_t alphabet_size;
- int16_t num_htrees;
+ uint16_t alphabet_size;
+ uint16_t num_htrees;
} HuffmanTreeGroup;
-void BrotliHuffmanTreeGroupInit(HuffmanTreeGroup* group,
- int alphabet_size, int ntrees);
-void BrotliHuffmanTreeGroupRelease(HuffmanTreeGroup* group);
-
#if defined(__cplusplus) || defined(c_plusplus)
-} /* extern "C" */
+} /* extern "C" */
#endif
#endif /* BROTLI_DEC_HUFFMAN_H_ */
diff --git a/modules/brotli/dec/port.h b/modules/brotli/dec/port.h
index af60dfc59..19b457c75 100644
--- a/modules/brotli/dec/port.h
+++ b/modules/brotli/dec/port.h
@@ -1,32 +1,33 @@
/* Copyright 2015 Google Inc. All Rights Reserved.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+ Distributed under MIT license.
+ See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
*/
/* Macros for compiler / platform specific features and build options.
Build options are:
+ * BROTLI_BUILD_32_BIT disables 64-bit optimizations
+ * BROTLI_BUILD_64_BIT forces to use 64-bit optimizations
+ * BROTLI_BUILD_BIG_ENDIAN forces to use big-endian optimizations
+ * BROTLI_BUILD_ENDIAN_NEUTRAL disables endian-aware optimizations
+ * BROTLI_BUILD_LITTLE_ENDIAN forces to use little-endian optimizations
+ * BROTLI_BUILD_MODERN_COMPILER forces to use modern compilers built-ins,
+ features and attributes
* BROTLI_BUILD_PORTABLE disables dangerous optimizations, like unaligned
read and overlapping memcpy; this reduces decompression speed by 5%
* BROTLI_DEBUG dumps file name and line number when decoder detects stream
or memory error
- * BROTLI_DECODE_DEBUG enables asserts and dumps various state information
+ * BROTLI_ENABLE_LOG enables asserts and dumps various state information
*/
#ifndef BROTLI_DEC_PORT_H_
#define BROTLI_DEC_PORT_H_
-#include
+#if defined(BROTLI_ENABLE_LOG) || defined(BROTLI_DEBUG)
+#include
+#include
+#endif
/* Compatibility with non-clang compilers. */
#ifndef __has_builtin
@@ -41,15 +42,59 @@
#define __has_feature(x) 0
#endif
-#ifdef BROTLI_BUILD_PORTABLE
-#define BROTLI_ALIGNED_READ 1
-#define BROTLI_SAFE_MEMMOVE 1
-#else
-#define BROTLI_ALIGNED_READ 0
-#define BROTLI_SAFE_MEMMOVE 0
+#if defined(__arm__) || defined(__thumb__) || \
+ defined(_M_ARM) || defined(_M_ARMT)
+#define BROTLI_TARGET_ARM
+#if (defined(__ARM_ARCH) && (__ARM_ARCH >= 7)) || \
+ (defined(M_ARM) && (M_ARM >= 7))
+#define BROTLI_TARGET_ARMV7
+#endif /* ARMv7 */
+#if defined(__aarch64__)
+#define BROTLI_TARGET_ARMV8
+#endif /* ARMv8 */
+#endif /* ARM */
+
+#if defined(__i386) || defined(_M_IX86)
+#define BROTLI_TARGET_X86
#endif
-#define BROTLI_ASAN_BUILD __has_feature(address_sanitizer)
+#if defined(__x86_64__) || defined(_M_X64)
+#define BROTLI_TARGET_X64
+#endif
+
+#if defined(__PPC64__)
+#define BROTLI_TARGET_POWERPC64
+#endif
+
+#if defined(__GNUC__) && defined(__GNUC_MINOR__)
+#define BROTLI_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
+#else
+#define BROTLI_GCC_VERSION 0
+#endif
+
+#if defined(__ICC)
+#define BROTLI_ICC_VERSION __ICC
+#else
+#define BROTLI_ICC_VERSION 0
+#endif
+
+#if defined(BROTLI_BUILD_MODERN_COMPILER)
+#define BROTLI_MODERN_COMPILER 1
+#elif (BROTLI_GCC_VERSION > 300) || (BROTLI_ICC_VERSION >= 1600)
+#define BROTLI_MODERN_COMPILER 1
+#else
+#define BROTLI_MODERN_COMPILER 0
+#endif
+
+#ifdef BROTLI_BUILD_PORTABLE
+#define BROTLI_ALIGNED_READ (!!1)
+#elif defined(BROTLI_TARGET_X86) || defined(BROTLI_TARGET_X64) || \
+ defined(BROTLI_TARGET_ARMV7) || defined(BROTLI_TARGET_ARMV8)
+/* Allow unaligned read only for whitelisted CPUs. */
+#define BROTLI_ALIGNED_READ (!!0)
+#else
+#define BROTLI_ALIGNED_READ (!!1)
+#endif
/* Define "PREDICT_TRUE" and "PREDICT_FALSE" macros for capable compilers.
@@ -68,8 +113,7 @@ OR:
}
*/
-#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ > 95) || \
- (defined(__llvm__) && __has_builtin(__builtin_expect))
+#if BROTLI_MODERN_COMPILER || __has_builtin(__builtin_expect)
#define PREDICT_TRUE(x) (__builtin_expect(!!(x), 1))
#define PREDICT_FALSE(x) (__builtin_expect(x, 0))
#else
@@ -78,23 +122,33 @@ OR:
#endif
/* IS_CONSTANT macros returns true for compile-time constant expressions. */
-#if (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ > 0) || \
- (defined(__llvm__) && __has_builtin(__builtin_constant_p))
-#define IS_CONSTANT(x) __builtin_constant_p(x)
+#if BROTLI_MODERN_COMPILER || __has_builtin(__builtin_constant_p)
+#define IS_CONSTANT(x) (!!__builtin_constant_p(x))
#else
-#define IS_CONSTANT(x) 0
+#define IS_CONSTANT(x) (!!0)
#endif
-#if (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ > 0) || \
- (defined(__llvm__) && __has_attribute(always_inline))
+#if BROTLI_MODERN_COMPILER || __has_attribute(always_inline)
#define ATTRIBUTE_ALWAYS_INLINE __attribute__ ((always_inline))
#else
#define ATTRIBUTE_ALWAYS_INLINE
#endif
+#if defined(_WIN32) || defined(__CYGWIN__)
+#define ATTRIBUTE_VISIBILITY_HIDDEN
+#elif BROTLI_MODERN_COMPILER || __has_attribute(visibility)
+#define ATTRIBUTE_VISIBILITY_HIDDEN __attribute__ ((visibility ("hidden")))
+#else
+#define ATTRIBUTE_VISIBILITY_HIDDEN
+#endif
+
+#ifndef BROTLI_INTERNAL
+#define BROTLI_INTERNAL ATTRIBUTE_VISIBILITY_HIDDEN
+#endif
+
#ifndef _MSC_VER
-#if defined(__cplusplus) || !defined(__STRICT_ANSI__) \
- || __STDC_VERSION__ >= 199901L
+#if defined(__cplusplus) || !defined(__STRICT_ANSI__) || \
+ __STDC_VERSION__ >= 199901L
#define BROTLI_INLINE inline ATTRIBUTE_ALWAYS_INLINE
#else
#define BROTLI_INLINE
@@ -103,55 +157,74 @@ OR:
#define BROTLI_INLINE __forceinline
#endif /* _MSC_VER */
-#ifdef BROTLI_DECODE_DEBUG
+#ifdef BROTLI_ENABLE_LOG
#define BROTLI_DCHECK(x) assert(x)
+#define BROTLI_LOG(x) printf x
#else
#define BROTLI_DCHECK(x)
+#define BROTLI_LOG(x)
#endif
-#if (defined(__x86_64__) || defined(_M_X64) || defined(__aarch64__) || \
- defined(__PPC64__))
+#if defined(BROTLI_DEBUG) || defined(BROTLI_ENABLE_LOG)
+static inline void BrotliDump(const char* f, int l, const char* fn) {
+ fprintf(stderr, "%s:%d (%s)\n", f, l, fn);
+ fflush(stderr);
+}
+#define BROTLI_DUMP() BrotliDump(__FILE__, __LINE__, __FUNCTION__)
+#else
+#define BROTLI_DUMP() (void)(0)
+#endif
+
+#if defined(BROTLI_BUILD_64_BIT)
+#define BROTLI_64_BITS 1
+#elif defined(BROTLI_BUILD_32_BIT)
+#define BROTLI_64_BITS 0
+#elif defined(BROTLI_TARGET_X64) || defined(BROTLI_TARGET_ARMV8) || \
+ defined(BROTLI_TARGET_POWERPC64)
#define BROTLI_64_BITS 1
#else
#define BROTLI_64_BITS 0
#endif
-#if (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
+#if defined(BROTLI_BUILD_BIG_ENDIAN)
+#define BROTLI_LITTLE_ENDIAN 0
+#define BROTLI_BIG_ENDIAN 1
+#elif defined(BROTLI_BUILD_LITTLE_ENDIAN)
#define BROTLI_LITTLE_ENDIAN 1
+#define BROTLI_BIG_ENDIAN 0
+#elif defined(BROTLI_BUILD_ENDIAN_NEUTRAL)
+#define BROTLI_LITTLE_ENDIAN 0
+#define BROTLI_BIG_ENDIAN 0
+#elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+#define BROTLI_LITTLE_ENDIAN 1
+#define BROTLI_BIG_ENDIAN 0
#elif defined(_WIN32)
/* Win32 can currently always be assumed to be little endian */
#define BROTLI_LITTLE_ENDIAN 1
+#define BROTLI_BIG_ENDIAN 0
#else
+#if (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
+#define BROTLI_BIG_ENDIAN 1
+#else
+#define BROTLI_BIG_ENDIAN 0
+#endif
#define BROTLI_LITTLE_ENDIAN 0
#endif
-#if (BROTLI_64_BITS && BROTLI_LITTLE_ENDIAN)
-#define BROTLI_64_BITS_LITTLE_ENDIAN 1
-#else
-#define BROTLI_64_BITS_LITTLE_ENDIAN 0
-#endif
-
-#if (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) || \
- (defined(__llvm__) && __has_attribute(noinline))
-#define BROTLI_NOINLINE __attribute__ ((noinline))
+#if BROTLI_MODERN_COMPILER || __has_attribute(noinline)
+#define BROTLI_NOINLINE __attribute__((noinline))
#else
#define BROTLI_NOINLINE
#endif
-#if BROTLI_ASAN_BUILD && !defined(BROTLI_BUILD_PORTABLE)
-#define BROTLI_NO_ASAN __attribute__((no_sanitize("address"))) BROTLI_NOINLINE
-#else
-#define BROTLI_NO_ASAN
-#endif
-
-#define BROTLI_REPEAT(N, X) { \
- if ((N & 1) != 0) {X;} \
- if ((N & 2) != 0) {X; X;} \
+#define BROTLI_REPEAT(N, X) { \
+ if ((N & 1) != 0) {X;} \
+ if ((N & 2) != 0) {X; X;} \
if ((N & 4) != 0) {X; X; X; X;} \
}
-#if (__GNUC__ > 2) || defined(__llvm__)
-#if (defined(__ARM_ARCH) && (__ARM_ARCH >= 7))
+#if BROTLI_MODERN_COMPILER || defined(__llvm__)
+#if defined(BROTLI_TARGET_ARMV7)
static BROTLI_INLINE unsigned BrotliRBit(unsigned input) {
unsigned output;
__asm__("rbit %0, %1\n" : "=r"(output) : "r"(input));
@@ -161,4 +234,19 @@ static BROTLI_INLINE unsigned BrotliRBit(unsigned input) {
#endif /* armv7 */
#endif /* gcc || clang */
+#if defined(BROTLI_TARGET_ARM)
+#define BROTLI_HAS_UBFX (!!1)
+#else
+#define BROTLI_HAS_UBFX (!!0)
+#endif
+
+#define BROTLI_ALLOC(S, L) S->alloc_func(S->memory_manager_opaque, L)
+
+#define BROTLI_FREE(S, X) { \
+ S->free_func(S->memory_manager_opaque, X); \
+ X = NULL; \
+}
+
+#define BROTLI_UNUSED(X) (void)(X)
+
#endif /* BROTLI_DEC_PORT_H_ */
diff --git a/modules/brotli/dec/prefix.h b/modules/brotli/dec/prefix.h
index 4ec81266d..eaae37f02 100644
--- a/modules/brotli/dec/prefix.h
+++ b/modules/brotli/dec/prefix.h
@@ -1,16 +1,7 @@
/* Copyright 2013 Google Inc. All Rights Reserved.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+ Distributed under MIT license.
+ See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
*/
/* Lookup tables to map prefix codes to value ranges. This is used during
@@ -20,11 +11,13 @@
#ifndef BROTLI_DEC_PREFIX_H_
#define BROTLI_DEC_PREFIX_H_
+#include "./types.h"
+
/* Represents the range of values belonging to a prefix code: */
/* [offset, offset + 2^nbits) */
struct PrefixCodeRange {
- int16_t offset;
- int8_t nbits;
+ uint16_t offset;
+ uint8_t nbits;
};
static const struct PrefixCodeRange kBlockLengthPrefixCode[] = {
diff --git a/modules/brotli/dec/state.c b/modules/brotli/dec/state.c
index 71c748b52..358e08d3d 100644
--- a/modules/brotli/dec/state.c
+++ b/modules/brotli/dec/state.c
@@ -1,29 +1,51 @@
/* Copyright 2015 Google Inc. All Rights Reserved.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+ Distributed under MIT license.
+ See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
*/
-#include "./huffman.h"
#include "./state.h"
-#include
-#include
+#include /* free, malloc */
+
+#include "./huffman.h"
+#include "./types.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
+/* Declared in decode.h */
+int BrotliStateIsStreamStart(const BrotliState* s);
+int BrotliStateIsStreamEnd(const BrotliState* s);
+
+static void* DefaultAllocFunc(void* opaque, size_t size) {
+ BROTLI_UNUSED(opaque);
+ return malloc(size);
+}
+
+static void DefaultFreeFunc(void* opaque, void* address) {
+ BROTLI_UNUSED(opaque);
+ free(address);
+}
+
void BrotliStateInit(BrotliState* s) {
+ BrotliStateInitWithCustomAllocators(s, 0, 0, 0);
+}
+
+void BrotliStateInitWithCustomAllocators(BrotliState* s,
+ brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque) {
+ if (!alloc_func) {
+ s->alloc_func = DefaultAllocFunc;
+ s->free_func = DefaultFreeFunc;
+ s->memory_manager_opaque = 0;
+ } else {
+ s->alloc_func = alloc_func;
+ s->free_func = free_func;
+ s->memory_manager_opaque = opaque;
+ }
+
+ BrotliInitBitReader(&s->br);
s->state = BROTLI_STATE_UNINITED;
s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_NONE;
s->substate_tree_group = BROTLI_STATE_TREE_GROUP_NONE;
@@ -31,6 +53,13 @@ void BrotliStateInit(BrotliState* s) {
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;
+
+ s->buffer_length = 0;
+ s->loop_counter = 0;
+ s->pos = 0;
+ s->rb_roundtrips = 0;
+ s->partial_pos_out = 0;
s->block_type_trees = NULL;
s->block_len_trees = NULL;
@@ -42,6 +71,8 @@ void BrotliStateInit(BrotliState* 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;
@@ -49,7 +80,6 @@ void BrotliStateInit(BrotliState* s) {
s->distance_hgroup.codes = NULL;
s->distance_hgroup.htrees = NULL;
-
s->custom_dict = NULL;
s->custom_dict_size = 0;
@@ -72,9 +102,9 @@ void BrotliStateInit(BrotliState* s) {
void BrotliStateMetablockBegin(BrotliState* s) {
s->meta_block_remaining_len = 0;
- s->block_length[0] = 1 << 28;
- s->block_length[1] = 1 << 28;
- s->block_length[2] = 1 << 28;
+ s->block_length[0] = 1U << 28;
+ s->block_length[1] = 1U << 28;
+ s->block_length[2] = 1U << 28;
s->num_block_types[0] = 1;
s->num_block_types[1] = 1;
s->num_block_types[2] = 1;
@@ -88,7 +118,6 @@ void BrotliStateMetablockBegin(BrotliState* s) {
s->context_modes = NULL;
s->dist_context_map = NULL;
s->context_map_slice = NULL;
- s->literal_htree_index = 0;
s->literal_htree = NULL;
s->dist_context_map_slice = NULL;
s->dist_htree_index = 0;
@@ -103,52 +132,49 @@ void BrotliStateMetablockBegin(BrotliState* s) {
}
void BrotliStateCleanupAfterMetablock(BrotliState* s) {
- if (s->context_modes != 0) {
- free(s->context_modes);
- s->context_modes = NULL;
- }
- if (s->context_map != 0) {
- free(s->context_map);
- s->context_map = NULL;
- }
- if (s->dist_context_map != 0) {
- free(s->dist_context_map);
- s->dist_context_map = NULL;
- }
+ BROTLI_FREE(s, s->context_modes);
+ BROTLI_FREE(s, s->context_map);
+ BROTLI_FREE(s, s->dist_context_map);
- BrotliHuffmanTreeGroupRelease(&s->literal_hgroup);
- BrotliHuffmanTreeGroupRelease(&s->insert_copy_hgroup);
- BrotliHuffmanTreeGroupRelease(&s->distance_hgroup);
- s->literal_hgroup.codes = NULL;
- s->literal_hgroup.htrees = NULL;
- s->insert_copy_hgroup.codes = NULL;
- s->insert_copy_hgroup.htrees = NULL;
- s->distance_hgroup.codes = NULL;
- s->distance_hgroup.htrees = NULL;
+ BrotliHuffmanTreeGroupRelease(s, &s->literal_hgroup);
+ BrotliHuffmanTreeGroupRelease(s, &s->insert_copy_hgroup);
+ BrotliHuffmanTreeGroupRelease(s, &s->distance_hgroup);
}
void BrotliStateCleanup(BrotliState* s) {
- if (s->context_modes != 0) {
- free(s->context_modes);
- }
- if (s->context_map != 0) {
- free(s->context_map);
- }
- if (s->dist_context_map != 0) {
- free(s->dist_context_map);
- }
- BrotliHuffmanTreeGroupRelease(&s->literal_hgroup);
- BrotliHuffmanTreeGroupRelease(&s->insert_copy_hgroup);
- BrotliHuffmanTreeGroupRelease(&s->distance_hgroup);
+ BrotliStateCleanupAfterMetablock(s);
- if (s->ringbuffer != 0) {
- free(s->ringbuffer);
- }
- if (s->block_type_trees != 0) {
- free(s->block_type_trees);
- }
+ BROTLI_FREE(s, s->ringbuffer);
+ BROTLI_FREE(s, s->block_type_trees);
+}
+
+int BrotliStateIsStreamStart(const BrotliState* s) {
+ return (s->state == BROTLI_STATE_UNINITED &&
+ BrotliGetAvailableBits(&s->br) == 0);
+}
+
+int BrotliStateIsStreamEnd(const BrotliState* s) {
+ return s->state == BROTLI_STATE_DONE;
+}
+
+void BrotliHuffmanTreeGroupInit(BrotliState* s, HuffmanTreeGroup* group,
+ uint32_t alphabet_size, uint32_t ntrees) {
+ /* Pack two allocations into one */
+ const size_t max_table_size = kMaxHuffmanTableSize[(alphabet_size + 31) >> 5];
+ const size_t code_size = sizeof(HuffmanCode) * ntrees * max_table_size;
+ const size_t htree_size = sizeof(HuffmanCode*) * ntrees;
+ char* p = (char*)BROTLI_ALLOC(s, code_size + htree_size);
+ group->alphabet_size = (uint16_t)alphabet_size;
+ group->num_htrees = (uint16_t)ntrees;
+ group->codes = (HuffmanCode*)p;
+ group->htrees = (HuffmanCode**)(p + code_size);
+}
+
+void BrotliHuffmanTreeGroupRelease(BrotliState* s, HuffmanTreeGroup* group) {
+ BROTLI_FREE(s, group->codes);
+ group->htrees = NULL;
}
#if defined(__cplusplus) || defined(c_plusplus)
-} /* extern "C" */
+} /* extern "C" */
#endif
diff --git a/modules/brotli/dec/state.h b/modules/brotli/dec/state.h
index 300e46483..63647ef2b 100644
--- a/modules/brotli/dec/state.h
+++ b/modules/brotli/dec/state.h
@@ -1,16 +1,7 @@
/* Copyright 2015 Google Inc. All Rights Reserved.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+ Distributed under MIT license.
+ See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
*/
/* Brotli state for partial streaming decoding. */
@@ -18,11 +9,10 @@
#ifndef BROTLI_DEC_STATE_H_
#define BROTLI_DEC_STATE_H_
-#include
#include "./bit_reader.h"
#include "./huffman.h"
-#include "./streams.h"
#include "./types.h"
+#include "./port.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
@@ -30,19 +20,20 @@ extern "C" {
typedef enum {
BROTLI_STATE_UNINITED,
- BROTLI_STATE_BITREADER_WARMUP,
BROTLI_STATE_METABLOCK_BEGIN,
BROTLI_STATE_METABLOCK_HEADER,
+ BROTLI_STATE_METABLOCK_HEADER_2,
BROTLI_STATE_CONTEXT_MODES,
BROTLI_STATE_COMMAND_BEGIN,
BROTLI_STATE_COMMAND_INNER,
+ BROTLI_STATE_COMMAND_POST_DECODE_LITERALS,
+ BROTLI_STATE_COMMAND_POST_WRAP_COPY,
BROTLI_STATE_UNCOMPRESSED,
BROTLI_STATE_METADATA,
BROTLI_STATE_COMMAND_INNER_WRITE,
BROTLI_STATE_METABLOCK_DONE,
BROTLI_STATE_COMMAND_POST_WRITE_1,
BROTLI_STATE_COMMAND_POST_WRITE_2,
- BROTLI_STATE_COMMAND_POST_WRAP_COPY,
BROTLI_STATE_HUFFMAN_CODE_0,
BROTLI_STATE_HUFFMAN_CODE_1,
BROTLI_STATE_HUFFMAN_CODE_2,
@@ -66,8 +57,6 @@ typedef enum {
typedef enum {
BROTLI_STATE_UNCOMPRESSED_NONE,
- BROTLI_STATE_UNCOMPRESSED_SHORT,
- BROTLI_STATE_UNCOMPRESSED_COPY,
BROTLI_STATE_UNCOMPRESSED_WRITE
} BrotliRunningUncompressedState;
@@ -80,11 +69,16 @@ typedef enum {
BROTLI_STATE_CONTEXT_MAP_NONE,
BROTLI_STATE_CONTEXT_MAP_READ_PREFIX,
BROTLI_STATE_CONTEXT_MAP_HUFFMAN,
- BROTLI_STATE_CONTEXT_MAP_DECODE
+ BROTLI_STATE_CONTEXT_MAP_DECODE,
+ BROTLI_STATE_CONTEXT_MAP_TRANSFORM
} BrotliRunningContextMapState;
typedef enum {
BROTLI_STATE_HUFFMAN_NONE,
+ BROTLI_STATE_HUFFMAN_SIMPLE_SIZE,
+ BROTLI_STATE_HUFFMAN_SIMPLE_READ,
+ BROTLI_STATE_HUFFMAN_SIMPLE_BUILD,
+ BROTLI_STATE_HUFFMAN_COMPLEX,
BROTLI_STATE_HUFFMAN_LENGTH_SYMBOLS
} BrotliRunningHuffmanState;
@@ -94,11 +88,30 @@ typedef enum {
BROTLI_STATE_DECODE_UINT8_LONG
} BrotliRunningDecodeUint8State;
-typedef struct {
+typedef enum {
+ BROTLI_STATE_READ_BLOCK_LENGTH_NONE,
+ BROTLI_STATE_READ_BLOCK_LENGTH_SUFFIX
+} BrotliRunningReadBlockLengthState;
+
+struct BrotliStateStruct {
BrotliRunningState state;
+
/* This counter is reused for several disjoint loops. */
- BrotliBitReader br;
int loop_counter;
+
+ BrotliBitReader br;
+
+ brotli_alloc_func alloc_func;
+ brotli_free_func free_func;
+ void* memory_manager_opaque;
+
+ /* Temporary storage for remaining input. */
+ union {
+ uint64_t u64;
+ uint8_t u8[8];
+ } buffer;
+ uint32_t buffer_length;
+
int pos;
int max_backward_distance;
int max_backward_distance_minus_custom_dict_size;
@@ -107,6 +120,8 @@ typedef struct {
int ringbuffer_mask;
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;
@@ -127,27 +142,26 @@ typedef struct {
int trivial_literal_context;
int distance_context;
int meta_block_remaining_len;
- int block_length[3];
- int num_block_types[3];
- int block_type_rb[6];
- int distance_postfix_bits;
- int num_direct_distance_codes;
+ uint32_t block_length_index;
+ uint32_t block_length[3];
+ uint32_t num_block_types[3];
+ uint32_t block_type_rb[6];
+ uint32_t distance_postfix_bits;
+ uint32_t num_direct_distance_codes;
int distance_postfix_mask;
- int num_dist_htrees;
+ uint32_t num_dist_htrees;
uint8_t* dist_context_map;
- HuffmanCode *literal_htree;
- uint8_t literal_htree_index;
+ HuffmanCode* literal_htree;
uint8_t dist_htree_index;
- uint8_t repeat_code_len;
- uint8_t prev_code_len;
-
+ uint32_t repeat_code_len;
+ uint32_t prev_code_len;
int copy_length;
int distance_code;
/* For partial write operations */
- int to_write;
- int partially_written;
+ size_t rb_roundtrips; /* How many times we went around the ringbuffer */
+ size_t partial_pos_out; /* How much output to the user in total (<= rb) */
/* For ReadHuffmanCode */
uint32_t symbol;
@@ -159,7 +173,7 @@ typedef struct {
uint16_t* symbol_lists;
/* Storage from symbol_lists. */
uint16_t symbols_lists_array[BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1 +
- BROTLI_HUFFMAN_MAX_CODE_LENGTHS_SIZE];
+ BROTLI_HUFFMAN_MAX_CODE_LENGTHS_SIZE];
/* Tails of symbol chains. */
int next_symbol[32];
uint8_t code_length_code_lengths[18];
@@ -171,13 +185,14 @@ typedef struct {
HuffmanCode* next;
/* For DecodeContextMap */
- int context_index;
- int max_run_length_prefix;
- HuffmanCode context_map_table[BROTLI_HUFFMAN_MAX_TABLE_SIZE];
+ uint32_t context_index;
+ uint32_t max_run_length_prefix;
+ uint32_t code;
+ HuffmanCode context_map_table[BROTLI_HUFFMAN_MAX_SIZE_272];
/* For InverseMoveToFrontTransform */
- int mtf_upper_bound;
- uint8_t mtf[256];
+ uint32_t mtf_upper_bound;
+ uint8_t mtf[256 + 4];
/* For custom dictionaries */
const uint8_t* custom_dict;
@@ -191,6 +206,7 @@ typedef struct {
BrotliRunningUncompressedState substate_uncompressed;
BrotliRunningHuffmanState substate_huffman;
BrotliRunningDecodeUint8State substate_decode_uint8;
+ BrotliRunningReadBlockLengthState substate_read_block_length;
uint8_t is_last_metablock;
uint8_t is_uncompressed;
@@ -198,18 +214,29 @@ typedef struct {
uint8_t size_nibbles;
uint32_t window_bits;
- int num_literal_htrees;
+ uint32_t num_literal_htrees;
uint8_t* context_map;
uint8_t* context_modes;
-} BrotliState;
-void BrotliStateInit(BrotliState* s);
-void BrotliStateCleanup(BrotliState* s);
-void BrotliStateMetablockBegin(BrotliState* s);
-void BrotliStateCleanupAfterMetablock(BrotliState* s);
+ uint32_t trivial_literal_contexts[8]; /* 256 bits */
+};
+
+typedef struct BrotliStateStruct BrotliStateInternal;
+#define BrotliState BrotliStateInternal
+
+BROTLI_INTERNAL void BrotliStateInit(BrotliState* s);
+BROTLI_INTERNAL void BrotliStateInitWithCustomAllocators(BrotliState* s,
+ brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque);
+BROTLI_INTERNAL void BrotliStateCleanup(BrotliState* s);
+BROTLI_INTERNAL void BrotliStateMetablockBegin(BrotliState* s);
+BROTLI_INTERNAL void BrotliStateCleanupAfterMetablock(BrotliState* s);
+BROTLI_INTERNAL void BrotliHuffmanTreeGroupInit(BrotliState* s,
+ HuffmanTreeGroup* group, uint32_t alphabet_size, uint32_t ntrees);
+BROTLI_INTERNAL void BrotliHuffmanTreeGroupRelease(BrotliState* s,
+ HuffmanTreeGroup* group);
#if defined(__cplusplus) || defined(c_plusplus)
-} /* extern "C" */
+} /* extern "C" */
#endif
#endif /* BROTLI_DEC_STATE_H_ */
diff --git a/modules/brotli/dec/transform.h b/modules/brotli/dec/transform.h
index 572914ae9..8c08f3fc0 100644
--- a/modules/brotli/dec/transform.h
+++ b/modules/brotli/dec/transform.h
@@ -1,16 +1,7 @@
/* Copyright 2013 Google Inc. All Rights Reserved.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+ Distributed under MIT license.
+ See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
*/
/* Transformations on dictionary words. */
@@ -18,8 +9,6 @@
#ifndef BROTLI_DEC_TRANSFORM_H_
#define BROTLI_DEC_TRANSFORM_H_
-#include
-#include
#include "./port.h"
#include "./types.h"
@@ -28,27 +17,27 @@ extern "C" {
#endif
enum WordTransformType {
- kIdentity = 0,
- kOmitLast1 = 1,
- kOmitLast2 = 2,
- kOmitLast3 = 3,
- kOmitLast4 = 4,
- kOmitLast5 = 5,
- kOmitLast6 = 6,
- kOmitLast7 = 7,
- kOmitLast8 = 8,
- kOmitLast9 = 9,
+ kIdentity = 0,
+ kOmitLast1 = 1,
+ kOmitLast2 = 2,
+ kOmitLast3 = 3,
+ kOmitLast4 = 4,
+ kOmitLast5 = 5,
+ kOmitLast6 = 6,
+ kOmitLast7 = 7,
+ kOmitLast8 = 8,
+ kOmitLast9 = 9,
kUppercaseFirst = 10,
- kUppercaseAll = 11,
- kOmitFirst1 = 12,
- kOmitFirst2 = 13,
- kOmitFirst3 = 14,
- kOmitFirst4 = 15,
- kOmitFirst5 = 16,
- kOmitFirst6 = 17,
- kOmitFirst7 = 18,
- kOmitFirst8 = 19,
- kOmitFirst9 = 20
+ kUppercaseAll = 11,
+ kOmitFirst1 = 12,
+ kOmitFirst2 = 13,
+ kOmitFirst3 = 14,
+ kOmitFirst4 = 15,
+ kOmitFirst5 = 16,
+ kOmitFirst6 = 17,
+ kOmitFirst7 = 18,
+ kOmitFirst8 = 19,
+ kOmitFirst9 = 20
};
typedef struct {
@@ -77,15 +66,15 @@ enum {
kPFix_SP = 1,
kPFix_COMMASP = 3,
kPFix_SPofSPtheSP = 6,
- kPFix_SPtheSP = 9,
+ kPFix_SPtheSP = 9,
kPFix_eSP = 12,
- kPFix_SPofSP = 15,
+ kPFix_SPofSP = 15,
kPFix_sSP = 20,
kPFix_DOT = 23,
kPFix_SPandSP = 25,
kPFix_SPinSP = 31,
kPFix_DQUOT = 36,
- kPFix_SPtoSP = 38,
+ kPFix_SPtoSP = 38,
kPFix_DQUOTGT = 43,
kPFix_NEWLINE = 46,
kPFix_DOTSP = 48,
@@ -95,20 +84,20 @@ enum {
kPFix_SPthatSP = 63,
kPFix_SQUOT = 70,
kPFix_SPwithSP = 72,
- kPFix_SPfromSP = 79,
- kPFix_SPbySP = 86,
+ kPFix_SPfromSP = 79,
+ kPFix_SPbySP = 86,
kPFix_OPEN = 91,
kPFix_DOTSPTheSP = 93,
- kPFix_SPonSP = 100,
- kPFix_SPasSP = 105,
- kPFix_SPisSP = 110,
+ kPFix_SPonSP = 100,
+ kPFix_SPasSP = 105,
+ kPFix_SPisSP = 110,
kPFix_ingSP = 115,
kPFix_NEWLINETAB = 120,
kPFix_COLON = 123,
kPFix_edSP = 125,
kPFix_EQDQUOT = 129,
kPFix_SPatSP = 132,
- kPFix_lySP = 137,
+ kPFix_lySP = 137,
kPFix_COMMA = 141,
kPFix_EQSQUOT = 143,
kPFix_DOTcomSLASH = 146,
@@ -125,7 +114,6 @@ enum {
kPFix_ousSP = 203
};
-
static const Transform kTransforms[] = {
{ kPFix_EMPTY, kIdentity, kPFix_EMPTY },
{ kPFix_EMPTY, kIdentity, kPFix_SP },
@@ -252,7 +240,7 @@ static const Transform kTransforms[] = {
static const int kNumTransforms = sizeof(kTransforms) / sizeof(kTransforms[0]);
-static int ToUpperCase(uint8_t *p) {
+static int ToUpperCase(uint8_t* p) {
if (p[0] < 0xc0) {
if (p[0] >= 'a' && p[0] <= 'z') {
p[0] ^= 32;
@@ -278,22 +266,19 @@ static BROTLI_NOINLINE int TransformDictionaryWord(
}
{
const int t = kTransforms[transform].transform;
- int skip = t < kOmitFirst1 ? 0 : t - (kOmitFirst1 - 1);
int i = 0;
- uint8_t* uppercase;
- if (skip > len) {
- skip = len;
- }
- word += skip;
- len -= skip;
- if (t <= kOmitLast9) {
+ int skip = t - (kOmitFirst1 - 1);
+ if (skip > 0) {
+ word += skip;
+ len -= skip;
+ } else if (t <= kOmitLast9) {
len -= t;
}
while (i < len) { dst[idx++] = word[i++]; }
- uppercase = &dst[idx - len];
if (t == kUppercaseFirst) {
- ToUpperCase(uppercase);
+ ToUpperCase(&dst[idx - len]);
} else if (t == kUppercaseAll) {
+ uint8_t* uppercase = &dst[idx - len];
while (len > 0) {
int step = ToUpperCase(uppercase);
uppercase += step;
@@ -309,7 +294,7 @@ static BROTLI_NOINLINE int TransformDictionaryWord(
}
#if defined(__cplusplus) || defined(c_plusplus)
-} /* extern "C" */
+} /* extern "C" */
#endif
#endif /* BROTLI_DEC_TRANSFORM_H_ */
diff --git a/modules/brotli/dec/types.h b/modules/brotli/dec/types.h
index e7e5ab137..0f7a0216a 100644
--- a/modules/brotli/dec/types.h
+++ b/modules/brotli/dec/types.h
@@ -1,16 +1,7 @@
/* Copyright 2013 Google Inc. All Rights Reserved.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+ Distributed under MIT license.
+ See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
*/
/* Common types */
@@ -21,16 +12,27 @@
#include /* for size_t */
#if defined(_MSC_VER) && (_MSC_VER < 1600)
-typedef signed char int8_t;
-typedef unsigned char uint8_t;
-typedef signed short int16_t;
-typedef unsigned short uint16_t;
-typedef signed int int32_t;
-typedef unsigned int uint32_t;
-typedef unsigned long long int uint64_t;
-typedef long long int int64_t;
+typedef __int8 int8_t;
+typedef unsigned __int8 uint8_t;
+typedef __int16 int16_t;
+typedef unsigned __int16 uint16_t;
+typedef __int32 int32_t;
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+typedef __int64 int64_t;
#else
#include
#endif /* defined(_MSC_VER) && (_MSC_VER < 1600) */
+/* Allocating function pointer. Function MUST return 0 in the case of failure.
+ Otherwise it MUST return a valid pointer to a memory region of at least
+ size length. Neither items nor size are allowed to be 0.
+ opaque argument is a pointer provided by client and could be used to bind
+ function to specific object (memory pool). */
+typedef void* (*brotli_alloc_func)(void* opaque, size_t size);
+
+/* Deallocating function pointer. Function SHOULD be no-op in the case the
+ address is 0. */
+typedef void (*brotli_free_func)(void* opaque, void* address);
+
#endif /* BROTLI_DEC_TYPES_H_ */
diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js
index e3d98b13c..714e80acc 100644
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -358,8 +358,8 @@ pref("media.gstreamer.enable-blacklist", true);
#ifdef MOZ_WIDGET_UIKIT
pref("media.mp3.enabled", true);
#endif
-pref("media.apple.mp3.enabled", true);
-pref("media.apple.mp4.enabled", true);
+pref("media.apple.mp3.enabled", false);
+pref("media.apple.mp4.enabled", false);
#endif
#ifdef MOZ_WEBRTC
pref("media.navigator.enabled", true);
@@ -398,7 +398,7 @@ pref("media.peerconnection.video.max_bitrate", 1000);
#else
pref("media.navigator.video.default_width",0); // adaptive default
pref("media.navigator.video.default_height",0); // adaptive default
-pref("media.peerconnection.enabled", true);
+pref("media.peerconnection.enabled", false);
pref("media.peerconnection.video.enabled", true);
pref("media.navigator.video.max_fs", 12288); // Enough for 2048x1536
pref("media.navigator.video.max_fr", 60);
@@ -493,16 +493,11 @@ pref("media.webvtt.regions.enabled", false);
pref("media.track.enabled", false);
// Whether to enable MediaSource support.
-pref("media.mediasource.enabled", true);
-
-pref("media.mediasource.mp4.enabled", true);
-
-#if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_GONK) || defined(MOZ_WIDGET_ANDROID)
+// MediaSource is pretty much hosed on PowerPC OS X, so ... no.
+pref("media.mediasource.enabled", false);
+pref("media.mediasource.mp4.enabled", false);
pref("media.mediasource.webm.enabled", false);
-#else
-pref("media.mediasource.webm.enabled", true);
-#endif
-pref("media.mediasource.webm.audio.enabled", true);
+pref("media.mediasource.webm.audio.enabled", false);
// Enable new MediaFormatReader architecture for plain webm.
pref("media.format-reader.webm", true);
@@ -644,10 +639,10 @@ pref("gfx.perf-warnings.enabled", false);
// 0 = Off, 1 = Full, 2 = Tagged Images Only.
// See eCMSMode in gfx/thebes/gfxPlatform.h
-pref("gfx.color_management.mode", 2);
+pref("gfx.color_management.mode", 1);
pref("gfx.color_management.display_profile", "");
pref("gfx.color_management.rendering_intent", 0);
-pref("gfx.color_management.enablev4", false);
+pref("gfx.color_management.enablev4", true);
pref("gfx.downloadable_fonts.enabled", true);
pref("gfx.downloadable_fonts.fallback_delay", 3000);
@@ -708,10 +703,10 @@ pref("gfx.canvas.azure.backends", "direct2d1.1,direct2d,skia,cairo");
pref("gfx.content.azure.backends", "direct2d1.1,direct2d,cairo");
#else
#ifdef XP_MACOSX
+// TenFourFox is CoreGraphics, all the CoreTime, CoreBeyotches.
pref("gfx.content.azure.backends", "cg");
-pref("gfx.canvas.azure.backends", "skia");
-// Accelerated cg canvas where available (10.7+)
-pref("gfx.canvas.azure.accelerated", true);
+pref("gfx.canvas.azure.backends", "cg");
+pref("gfx.canvas.azure.accelerated", false);
#else
pref("gfx.canvas.azure.backends", "cairo");
pref("gfx.content.azure.backends", "cairo");
@@ -1139,7 +1134,7 @@ pref("javascript.options.strict.debug", false);
#endif
pref("javascript.options.baselinejit", true);
pref("javascript.options.ion", true);
-pref("javascript.options.asmjs", true);
+pref("javascript.options.asmjs", false);
pref("javascript.options.native_regexp", true);
pref("javascript.options.parallel_parsing", true);
#if !defined(RELEASE_BUILD) && !defined(ANDROID) && !defined(MOZ_B2G) && !defined(XP_IOS)
@@ -1162,7 +1157,7 @@ pref("javascript.options.mem.high_water_mark", 128);
pref("javascript.options.mem.max", -1);
pref("javascript.options.mem.gc_per_compartment", true);
pref("javascript.options.mem.gc_incremental", true);
-pref("javascript.options.mem.gc_incremental_slice_ms", 10);
+pref("javascript.options.mem.gc_incremental_slice_ms", 100); // issue 253
pref("javascript.options.mem.gc_compacting", true);
pref("javascript.options.mem.log", false);
pref("javascript.options.mem.notify", false);
@@ -2516,9 +2511,9 @@ pref("editor.resizing.preserve_ratio", true);
pref("editor.positioning.offset", 0);
pref("dom.use_watchdog", true);
-pref("dom.max_chrome_script_run_time", 20);
-pref("dom.max_child_script_run_time", 10);
-pref("dom.max_script_run_time", 10);
+pref("dom.max_chrome_script_run_time", 40); // TenFourFox issue 227
+pref("dom.max_child_script_run_time", 40);
+pref("dom.max_script_run_time", 40);
// If true, ArchiveReader will be enabled
pref("dom.archivereader.enabled", false);
@@ -4166,7 +4161,8 @@ pref("image.mem.discardable", true);
pref("image.mem.allow_locking_in_content_processes", true);
// Chunk size for calls to the image decoders
-pref("image.mem.decode_bytes_at_a_time", 16384);
+// Increased for issue 112 (even the larger amount in Fx19 is not enough)
+pref("image.mem.decode_bytes_at_a_time", 200000);
// Minimum timeout for expiring unused images from the surface cache, in
// milliseconds. This controls how long we store cached temporary surfaces.
diff --git a/netwerk/streamconv/converters/nsHTTPCompressConv.cpp b/netwerk/streamconv/converters/nsHTTPCompressConv.cpp
index 05fad26f1..aeb96b58d 100644
--- a/netwerk/streamconv/converters/nsHTTPCompressConv.cpp
+++ b/netwerk/streamconv/converters/nsHTTPCompressConv.cpp
@@ -14,7 +14,9 @@
#include "nsComponentManagerUtils.h"
#include "nsThreadUtils.h"
#include "mozilla/Preferences.h"
+#include "mozilla/Logging.h"
#include "nsIForcePendingChannel.h"
+#include "nsIRequest.h"
// brotli headers
#include "state.h"
@@ -134,7 +136,6 @@ nsHTTPCompressConv::OnStopRequest(nsIRequest* request, nsISupports *aContext,
LOG(("nsHttpCompresssConv %p onstop partial gzip\n", this));
}
if (NS_SUCCEEDED(status) && mMode == HTTP_COMPRESS_BROTLI) {
- uint32_t waste;
nsCOMPtr fpChannel = do_QueryInterface(request);
bool isPending = false;
if (request) {
@@ -143,7 +144,9 @@ nsHTTPCompressConv::OnStopRequest(nsIRequest* request, nsISupports *aContext,
if (fpChannel && !isPending) {
fpChannel->ForcePending(true);
}
- status = BrotliHandler(nullptr, this, nullptr, 0, 0, &waste);
+ if (mBrotli && (mBrotli->mTotalOut == 0) && !BrotliStateIsStreamEnd(&mBrotli->mState)) {
+ status = NS_ERROR_INVALID_CONTENT_ENCODING;
+ }
LOG(("nsHttpCompresssConv %p onstop brotlihandler rv %x\n", this, status));
if (fpChannel && !isPending) {
fpChannel->ForcePending(false);
@@ -158,12 +161,12 @@ NS_METHOD
nsHTTPCompressConv::BrotliHandler(nsIInputStream *stream, void *closure, const char *dataIn,
uint32_t, uint32_t aAvail, uint32_t *countRead)
{
+ MOZ_ASSERT(stream);
nsHTTPCompressConv *self = static_cast(closure);
*countRead = 0;
- const uint32_t kOutSize = 128 * 1024; // just a chunk size, we call in a loop
- unsigned char outBuffer[kOutSize];
- unsigned char *outPtr;
+ const size_t kOutSize = 128 * 1024; // just a chunk size, we call in a loop
+ uint8_t *outPtr;
size_t outSize;
size_t avail = aAvail;
BrotliResult res;
@@ -173,15 +176,20 @@ nsHTTPCompressConv::BrotliHandler(nsIInputStream *stream, void *closure, const c
return NS_OK;
}
+ auto outBuffer = MakeUniqueFallible(kOutSize);
+ if (outBuffer == nullptr) {
+ self->mBrotli->mStatus = NS_ERROR_OUT_OF_MEMORY;
+ return self->mBrotli->mStatus;
+ }
+
do {
outSize = kOutSize;
- outPtr = outBuffer;
+ outPtr = outBuffer.get();
- // brotli api is documented in brotli/dec/decode.h
- LOG(("nsHttpCompresssConv %p brotlihandler decompress %d finish %d\n",
- self, avail, !stream));
- res = ::BrotliDecompressBufferStreaming(
- &avail, reinterpret_cast(&dataIn), stream ? 0 : 1,
+ // brotli api is documented in brotli/dec/decode.h and brotli/dec/decode.c
+ LOG(("nsHttpCompresssConv %p brotlihandler decompress %d\n", self, avail));
+ res = ::BrotliDecompressStream(
+ &avail, reinterpret_cast(&dataIn),
&outSize, &outPtr, &self->mBrotli->mTotalOut, &self->mBrotli->mState);
outSize = kOutSize - outSize;
LOG(("nsHttpCompresssConv %p brotlihandler decompress rv=%x out=%d\n",
@@ -193,18 +201,21 @@ nsHTTPCompressConv::BrotliHandler(nsIInputStream *stream, void *closure, const c
return self->mBrotli->mStatus;
}
- // in 'the current implementation' brotli consumes all input on success
- MOZ_ASSERT(!avail);
- if (avail) {
- LOG(("nsHttpCompressConv %p did not consume all input", self));
- self->mBrotli->mStatus = NS_ERROR_UNEXPECTED;
- return self->mBrotli->mStatus;
+ // in 'the current implementation' brotli must consume everything before
+ // asking for more input
+ if (res == BROTLI_RESULT_NEEDS_MORE_INPUT) {
+ MOZ_ASSERT(!avail);
+ if (avail) {
+ LOG(("nsHttpCompressConv %p did not consume all input", self));
+ self->mBrotli->mStatus = NS_ERROR_UNEXPECTED;
+ return self->mBrotli->mStatus;
+ }
}
if (outSize > 0) {
nsresult rv = self->do_OnDataAvailable(self->mBrotli->mRequest,
self->mBrotli->mContext,
self->mBrotli->mSourceOffset,
- reinterpret_cast(outBuffer),
+ reinterpret_cast(outBuffer.get()),
outSize);
LOG(("nsHttpCompressConv %p BrotliHandler ODA rv=%x", self, rv));
if (NS_FAILED(rv)) {
@@ -261,7 +272,7 @@ nsHTTPCompressConv::OnDataAvailable(nsIRequest* request,
return NS_OK;
}
- // FALLTHROUGH
+ MOZ_FALLTHROUGH;
case HTTP_COMPRESS_DEFLATE:
@@ -569,7 +580,7 @@ nsHTTPCompressConv::check_header(nsIInputStream *iStr, uint32_t streamLen, nsres
case GZIP_EXTRA1:
iStr->Read(&c, 1, &unused);
streamLen--;
- mLen = ((uInt) c & 0377) << 8;
+ mLen |= ((uInt) c & 0377) << 8;
mSkipCount = 0;
hMode = GZIP_EXTRA2;
break;
diff --git a/release/docker/funsize-balrog-submitter/scripts/funsize-balrog-submitter.py b/release/docker/funsize-balrog-submitter/scripts/funsize-balrog-submitter.py
index 7eb5f28c6..06851e1d3 100644
--- a/release/docker/funsize-balrog-submitter/scripts/funsize-balrog-submitter.py
+++ b/release/docker/funsize-balrog-submitter/scripts/funsize-balrog-submitter.py
@@ -117,7 +117,6 @@ def main():
parser.add_argument("-v", "--verbose", action="store_const",
dest="loglevel", const=logging.DEBUG,
default=logging.INFO)
- parser.add_argument("--product", help="Override product name from application.ini")
args = parser.parse_args()
logging.basicConfig(format="%(asctime)s - %(levelname)s - %(message)s",
level=args.loglevel)
@@ -158,9 +157,8 @@ def main():
partial_info[0]["previousBuildNumber"] = e["previousBuildNumber"]
submitter = ReleaseSubmitterV4(api_root=args.api_root, auth=auth,
dummy=args.dummy)
- productName = args.product or e["appName"]
retry(lambda: submitter.run(
- platform=e["platform"], productName=productName,
+ platform=e["platform"], productName=e["appName"],
version=e["toVersion"],
build_number=e["toBuildNumber"],
appVersion=e["version"], extVersion=e["version"],
@@ -194,10 +192,9 @@ def main():
partial_info[0]["from_buildid"] = e["from_buildid"]
submitter = NightlySubmitterV4(api_root=args.api_root, auth=auth,
dummy=args.dummy)
- productName = args.product or e["appName"]
retry(lambda: submitter.run(
platform=e["platform"], buildID=e["to_buildid"],
- productName=productName, branch=e["branch"],
+ productName=e["appName"], branch=e["branch"],
appVersion=e["version"], locale=e["locale"],
hashFunction='sha512', extVersion=e["version"],
partialInfo=partial_info, completeInfo=complete_info),
diff --git a/security/manager/ssl/RootCertificateTelemetryUtils.cpp b/security/manager/ssl/RootCertificateTelemetryUtils.cpp
index 6070bc89d..95198ee7a 100644
--- a/security/manager/ssl/RootCertificateTelemetryUtils.cpp
+++ b/security/manager/ssl/RootCertificateTelemetryUtils.cpp
@@ -58,7 +58,7 @@ RootCABinNumber(const SECItem* cert)
("pkpinTelem: First bytes %02hx %02hx %02hx %02hx\n",
digest.get().data[0], digest.get().data[1], digest.get().data[2], digest.get().data[3]));
- if (mozilla::BinarySearchIf(ROOT_TABLE, 0, ArrayLength(ROOT_TABLE),
+ if (mozilla::BinarySearchIf(ROOT_TABLE, 0, mozilla::ArrayLength(ROOT_TABLE),
BinaryHashSearchArrayComparator(
reinterpret_cast(digest.get().data), digest.get().len),
&idx)) {
diff --git a/services/healthreport/healthreport-prefs.js b/services/healthreport/healthreport-prefs.js
index b1a35278c..a8f2c9f19 100644
--- a/services/healthreport/healthreport-prefs.js
+++ b/services/healthreport/healthreport-prefs.js
@@ -18,9 +18,10 @@ pref("datareporting.healthreport.nextDataSubmissionTime", "0");
pref("datareporting.healthreport.pendingDeleteRemoteData", false);
// Health Report is enabled by default on all channels.
-pref("datareporting.healthreport.uploadEnabled", true);
+// But we're TenFourFox. So f'all that, we've gotta get on with these.
+pref("datareporting.healthreport.uploadEnabled", false);
-pref("datareporting.healthreport.service.enabled", true);
+pref("datareporting.healthreport.service.enabled", false);
pref("datareporting.healthreport.service.loadDelayMsec", 10000);
pref("datareporting.healthreport.service.loadDelayFirstRunMsec", 60000);
diff --git a/toolkit/components/aboutmemory/content/aboutMemory.js b/toolkit/components/aboutmemory/content/aboutMemory.js
index 465260d2d..64956da4f 100644
--- a/toolkit/components/aboutmemory/content/aboutMemory.js
+++ b/toolkit/components/aboutmemory/content/aboutMemory.js
@@ -1686,6 +1686,7 @@ function pad(aS, aN, aC)
// implemented in terminals, and this code sticks to that subset to maximize
// the chance that copying and pasting about:memory output to a terminal will
// work correctly.
+/*
const kHorizontal = "\u2500",
kVertical = "\u2502",
kUpAndRight = "\u2514",
@@ -1695,6 +1696,17 @@ const kHorizontal = "\u2500",
kVertical_Space_Space = "\u2502 ";
const kNoKidsSep = " \u2500\u2500 ",
+*/
+// It doesn't. 10.4Fx issue 121.
+const kHorizontal = "-",
+ kVertical = "|",
+ kUpAndRight = "+",
+ kUpAndRight_Right_Right = "+--",
+ kVerticalAndRight = "+",
+ kVerticalAndRight_Right_Right = "+--",
+ kVertical_Space_Space = "| ";
+
+const kNoKidsSep = " -- ",
kHideKidsSep = " ++ ",
kShowKidsSep = " -- ";
diff --git a/toolkit/components/autocomplete/nsAutoCompleteController.cpp b/toolkit/components/autocomplete/nsAutoCompleteController.cpp
index 5a05e9a9f..6a5046360 100644
--- a/toolkit/components/autocomplete/nsAutoCompleteController.cpp
+++ b/toolkit/components/autocomplete/nsAutoCompleteController.cpp
@@ -799,7 +799,11 @@ nsAutoCompleteController::OnUpdateSearchResult(nsIAutoCompleteSearch *aSearch, n
NS_IMETHODIMP
nsAutoCompleteController::OnSearchResult(nsIAutoCompleteSearch *aSearch, nsIAutoCompleteResult* aResult)
{
+/* BADFIX??? */
+/*
MOZ_ASSERT(mSearchesOngoing > 0 && mSearches.Contains(aSearch));
+*/
+MOZ_ASSERT(mSearches.Contains(aSearch));
// If this is the first search result we are processing
// we should clear out the previously cached results.
@@ -813,6 +817,7 @@ nsAutoCompleteController::OnSearchResult(nsIAutoCompleteSearch *aSearch, nsIAuto
aResult->GetSearchResult(&result);
}
+if (mSearchesOngoing) // BADFIX???
// If our results are incremental, the search is still ongoing.
if (result != nsIAutoCompleteResult::RESULT_SUCCESS_ONGOING &&
result != nsIAutoCompleteResult::RESULT_NOMATCH_ONGOING) {
diff --git a/toolkit/components/places/nsPlacesAutoComplete.js b/toolkit/components/places/nsPlacesAutoComplete.js
index cf6689301..9f8f56030 100644
--- a/toolkit/components/places/nsPlacesAutoComplete.js
+++ b/toolkit/components/places/nsPlacesAutoComplete.js
@@ -806,6 +806,24 @@ nsPlacesAutoComplete.prototype = {
if (aSearchOngoing) {
resultCode += "_ONGOING";
}
+
+/* 10.4Fx issue 21. This makes me absolutely nauseous just writing this
+ because this is about the most naked, improper wallpaper job I've ever
+ written. But it works. In short, NEVER return RESULT_NOMATCH -- if we
+ are about to do that, forge a single stub match to make a blank box and
+ _SUCCESS, and this solves the problem. I am going to take a shower and
+ try to scrub the disgust from my heaving, vomiting body. */
+
+ if (resultCode == "RESULT_NOMATCH") { // well, shoot. let's fudge.
+ this._result.appendMatch(
+ "", // blank URL
+ "", // blank title
+ "", // no favicon
+ "favicon" // but use a favicon style -- thus a blank drop-down
+ );
+ resultCode = "RESULT_SUCCESS";
+ } // end issue
+
result.setSearchResult(Ci.nsIAutoCompleteResult[resultCode]);
this._listener.onSearchResult(this, result);
if (this._telemetryStartTime) {
diff --git a/toolkit/components/telemetry/TelemetryController.jsm b/toolkit/components/telemetry/TelemetryController.jsm
index 60d4d6c84..0782d4c2a 100644
--- a/toolkit/components/telemetry/TelemetryController.jsm
+++ b/toolkit/components/telemetry/TelemetryController.jsm
@@ -653,7 +653,7 @@ var Impl = {
* @return {Boolean} True if Telemetry is allowed to record at least base (FHR) data,
* false otherwise.
*/
- enableTelemetryRecording: function enableTelemetryRecording() {
+ enableTelemetryRecording: function enableTelemetryRecording() { return false;
// The thumbnail service also runs in a content process, even with e10s off.
// We need to check if e10s is on so we don't submit child payloads for it.
// We still need xpcshell child tests to work, so we skip this if test mode is enabled.
diff --git a/toolkit/components/telemetry/TelemetryReportingPolicy.jsm b/toolkit/components/telemetry/TelemetryReportingPolicy.jsm
index f0306a7db..aab2b4794 100644
--- a/toolkit/components/telemetry/TelemetryReportingPolicy.jsm
+++ b/toolkit/components/telemetry/TelemetryReportingPolicy.jsm
@@ -336,7 +336,7 @@ var TelemetryReportingPolicyImpl = {
*
* @return {Boolean} True if we are allowed to upload data, false otherwise.
*/
- canUpload: function() {
+ canUpload: function() { return false;
// If data submission is disabled, there's no point in showing the infobar. Just
// forbid to upload.
if (!this.dataSubmissionEnabled) {
diff --git a/toolkit/components/telemetry/TelemetrySend.jsm b/toolkit/components/telemetry/TelemetrySend.jsm
index 1820c72c1..b0b0e71db 100644
--- a/toolkit/components/telemetry/TelemetrySend.jsm
+++ b/toolkit/components/telemetry/TelemetrySend.jsm
@@ -1030,7 +1030,7 @@ var TelemetrySendImpl = {
* @param {Object} [ping=null] A ping to be checked.
* @return {Boolean} True if pings can be send to the servers, false otherwise.
*/
- sendingEnabled: function(ping = null) {
+ sendingEnabled: function(ping = null) { return false;
// We only send pings from official builds, but allow overriding this for tests.
if (!Telemetry.isOfficialTelemetry && !this._testMode) {
return false;
diff --git a/toolkit/components/telemetry/TelemetrySession.jsm b/toolkit/components/telemetry/TelemetrySession.jsm
index bd45ef9bc..8015b4b63 100644
--- a/toolkit/components/telemetry/TelemetrySession.jsm
+++ b/toolkit/components/telemetry/TelemetrySession.jsm
@@ -302,7 +302,7 @@ var TelemetryScheduler = {
this._lastDailyPingTime = now.getTime();
this._lastSessionCheckpointTime = now.getTime();
this._rescheduleTimeout();
- idleService.addIdleObserver(this, IDLE_TIMEOUT_SECONDS);
+ //idleService.addIdleObserver(this, IDLE_TIMEOUT_SECONDS);
},
/**
diff --git a/toolkit/components/telemetry/TelemetryStopwatch.jsm b/toolkit/components/telemetry/TelemetryStopwatch.jsm
index 19c19c6b6..2de8d0857 100644
--- a/toolkit/components/telemetry/TelemetryStopwatch.jsm
+++ b/toolkit/components/telemetry/TelemetryStopwatch.jsm
@@ -35,7 +35,7 @@ this.TelemetryStopwatch = {
* timer already exists, it can't be started again, and the existing
* one will be cleared in order to avoid measurements errors.
*/
- start: function(aHistogram, aObj) {
+ start: function(aHistogram, aObj) { return;
if (!validTypes(aHistogram, aObj))
return false;
@@ -74,7 +74,7 @@ this.TelemetryStopwatch = {
*
* @return true if the timer exist and it was cleared, false otherwise.
*/
- cancel: function ts_cancel(aHistogram, aObj) {
+ cancel: function ts_cancel(aHistogram, aObj) { return;
if (!validTypes(aHistogram, aObj))
return false;
@@ -103,7 +103,7 @@ this.TelemetryStopwatch = {
*
* @return time in milliseconds or -1 if the stopwatch was not found.
*/
- timeElapsed: function(aHistogram, aObj) {
+ timeElapsed: function(aHistogram, aObj) { return 0;
if (!validTypes(aHistogram, aObj))
return -1;
let timers = aObj
@@ -132,7 +132,7 @@ this.TelemetryStopwatch = {
* @return true if the timer was succesfully stopped and the data was
* added to the histogram, false otherwise.
*/
- finish: function(aHistogram, aObj) {
+ finish: function(aHistogram, aObj) { return true;
if (!validTypes(aHistogram, aObj))
return false;
diff --git a/toolkit/components/telemetry/UITelemetry.jsm b/toolkit/components/telemetry/UITelemetry.jsm
index 89a457820..7ee8f1958 100644
--- a/toolkit/components/telemetry/UITelemetry.jsm
+++ b/toolkit/components/telemetry/UITelemetry.jsm
@@ -26,7 +26,7 @@ this.UITelemetry = {
_measurements: [],
// Lazily decide whether telemetry is enabled.
- get enabled() {
+ get enabled() { return false;
if (this._enabled !== undefined) {
return this._enabled;
}
@@ -45,7 +45,7 @@ this.UITelemetry = {
return this._enabled;
},
- observe: function(aSubject, aTopic, aData) {
+ observe: function(aSubject, aTopic, aData) { return;
if (aTopic == "profile-before-change") {
Services.obs.removeObserver(this, "profile-before-change");
Services.prefs.removeObserver(PREF_ENABLED, this);
diff --git a/widget/nsNativeTheme.cpp b/widget/nsNativeTheme.cpp
index e8502e4c1..ae509c025 100644
--- a/widget/nsNativeTheme.cpp
+++ b/widget/nsNativeTheme.cpp
@@ -265,6 +265,20 @@ nsNativeTheme::GetIndeterminate(nsIFrame* aFrame)
return false;
}
+// backout bug 672050
+bool
+nsNativeTheme::IsLastTab(nsIFrame* aFrame)
+{
+ if (!aFrame)
+ return false;
+
+ while ((aFrame = aFrame->GetNextSibling())) {
+ if (aFrame->GetRect().width > 0 && aFrame->GetContent()->NodeInfo()->NameAtom() == nsGkAtoms::tab)
+ return false;
+ }
+ return true;
+}
+
bool
nsNativeTheme::IsWidgetStyled(nsPresContext* aPresContext, nsIFrame* aFrame,
uint8_t aWidgetType)
diff --git a/widget/nsNativeTheme.h b/widget/nsNativeTheme.h
index 001866d27..631a0b030 100644
--- a/widget/nsNativeTheme.h
+++ b/widget/nsNativeTheme.h
@@ -134,6 +134,7 @@ class nsNativeTheme : public nsITimerCallback
// tab:
bool IsBottomTab(nsIFrame* aFrame);
bool IsFirstTab(nsIFrame* aFrame);
+ bool IsLastTab(nsIFrame* aFrame);
bool IsHorizontal(nsIFrame* aFrame);
diff --git a/xpcom/base/CycleCollectedJSRuntime.cpp b/xpcom/base/CycleCollectedJSRuntime.cpp
index 8e92c4278..7660398e2 100644
--- a/xpcom/base/CycleCollectedJSRuntime.cpp
+++ b/xpcom/base/CycleCollectedJSRuntime.cpp
@@ -1306,6 +1306,7 @@ CycleCollectedJSRuntime::OnGC(JSGCStatus aStatus)
switch (aStatus) {
case JSGC_BEGIN:
nsCycleCollector_prepareForGarbageCollection();
+ mZonesWaitingForGC.Clear();
break;
case JSGC_END: {
#ifdef MOZ_CRASHREPORTER
@@ -1345,3 +1346,16 @@ CycleCollectedJSRuntime::OnLargeAllocationFailure()
AnnotateAndSetOutOfMemory(&mLargeAllocationFailureState, OOMState::Reported);
}
+void
+CycleCollectedJSRuntime::PrepareWaitingZonesForGC()
+{
+ if (mZonesWaitingForGC.Count() == 0) {
+ JS::PrepareForFullGC(Runtime());
+ } else {
+ for (auto iter = mZonesWaitingForGC.Iter(); !iter.Done(); iter.Next()) {
+ JS::PrepareZoneForGC(iter.Get()->GetKey());
+ }
+ mZonesWaitingForGC.Clear();
+ }
+}
+
diff --git a/xpcom/base/CycleCollectedJSRuntime.h b/xpcom/base/CycleCollectedJSRuntime.h
index 53875db90..61783c248 100644
--- a/xpcom/base/CycleCollectedJSRuntime.h
+++ b/xpcom/base/CycleCollectedJSRuntime.h
@@ -17,6 +17,7 @@
#include "nsDataHashtable.h"
#include "nsHashKeys.h"
#include "nsTArray.h"
+#include "nsTHashtable.h"
class nsCycleCollectionNoteRootCallback;
class nsIException;
@@ -317,6 +318,18 @@ public:
// isn't one.
static CycleCollectedJSRuntime* Get();
+ // Add aZone to the set of zones waiting for a GC.
+ void AddZoneWaitingForGC(JS::Zone* aZone)
+ {
+ mZonesWaitingForGC.PutEntry(aZone);
+ }
+
+ // Prepare any zones for GC that have been passed to AddZoneWaitingForGC()
+ // since the last GC or since the last call to PrepareWaitingZonesForGC(),
+ // whichever was most recent. If there were no such zones, prepare for a
+ // full GC.
+ void PrepareWaitingZonesForGC();
+
// Storage for watching rejected promises waiting for some client to
// consume their rejection.
// We store values as `nsISupports` to avoid adding compile-time dependencies
@@ -361,6 +374,8 @@ private:
OOMState mOutOfMemoryState;
OOMState mLargeAllocationFailureState;
+
+ nsTHashtable> mZonesWaitingForGC;
};
void TraceScriptHolder(nsISupports* aHolder, JSTracer* aTracer);
diff --git a/xpcom/base/nsCycleCollector.cpp b/xpcom/base/nsCycleCollector.cpp
index a046f6ea7..eb7e195f2 100644
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -3259,9 +3259,15 @@ nsCycleCollector::CollectWhite()
if (pinfo->mColor == white && pinfo->mParticipant) {
if (pinfo->IsGrayJS()) {
++numWhiteGCed;
+ JS::Zone* zone;
if (MOZ_UNLIKELY(pinfo->mParticipant == zoneParticipant)) {
++numWhiteJSZones;
+ zone = static_cast(pinfo->mPointer);
+ } else {
+ JS::GCCellPtr ptr(pinfo->mPointer, js::GCThingTraceKind(pinfo->mPointer));
+ zone = JS::GetTenuredGCThingZone(ptr);
}
+ mJSRuntime->AddZoneWaitingForGC(zone);
} else {
whiteNodes.InfallibleAppend(pinfo);
pinfo->mParticipant->Root(pinfo->mPointer);