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);