diff --git a/accessible/src/html/nsHTMLTableAccessible.cpp b/accessible/src/html/nsHTMLTableAccessible.cpp index 2ead768140e6..bfcd7fdf35fa 100644 --- a/accessible/src/html/nsHTMLTableAccessible.cpp +++ b/accessible/src/html/nsHTMLTableAccessible.cpp @@ -895,8 +895,16 @@ nsHTMLTableAccessible::GetCellAt(PRInt32 aRow, PRInt32 aColumn, nsCOMPtr cellContent(do_QueryInterface(cellElement)); nsAccessible *cell = GetAccService()->GetAccessibleInWeakShell(cellContent, mWeakShell); - if (cell) - CallQueryInterface(cell, aTableCellAccessible); + + if (!cell) { + return NS_ERROR_INVALID_ARG; + } + + if (cell != this) { + // XXX bug 576838: crazy tables (like table6 in tables/test_table2.html) may + // return itself as a cell what makes Orca hang. + NS_ADDREF(*aTableCellAccessible = cell); + } return NS_OK; } diff --git a/accessible/tests/mochitest/table/test_table_2.html b/accessible/tests/mochitest/table/test_table_2.html index bd52a1688822..4b82b33abe6f 100644 --- a/accessible/tests/mochitest/table/test_table_2.html +++ b/accessible/tests/mochitest/table/test_table_2.html @@ -76,6 +76,11 @@ function doTest() accNotCreated = (!isAccessible("t5_cell")); ok(accNotCreated, "wrongly created table cell accessible"); + // test crazy table + var table6 = getAccessible("table6", [nsIAccessibleTable]); + ok(!table6.getCellAt(0, 0), + "We don't expect cell accessible for crazy table 6!"); + SimpleTest.finish(); } SimpleTest.waitForExplicitFinish(); @@ -138,6 +143,11 @@ addA11yLoadEvent(doTest); +
+ + Bad checkbox +
+ diff --git a/browser/app/profile/extensions/testpilot@labs.mozilla.com/chrome.manifest b/browser/app/profile/extensions/testpilot@labs.mozilla.com/chrome.manifest index b045815f5cfe..d9ed10944f6c 100644 --- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/chrome.manifest +++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/chrome.manifest @@ -4,6 +4,8 @@ skin testpilot skin skin/all/ skin testpilot-os skin skin/linux/ os=Linux skin testpilot-os skin skin/mac/ os=Darwin skin testpilot-os skin skin/win/ os=WINNT +overlay chrome://browser/content/macBrowserOverlay.xul chrome://testpilot/content/tp-browser.xul application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} appversion<4.0b1pre +overlay chrome://browser/content/macBrowserOverlay.xul chrome://testpilot/content/feedback-browser.xul application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} appversion>=4.0b1pre overlay chrome://browser/content/browser.xul chrome://testpilot/content/tp-browser.xul application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} appversion<=3.6.* overlay chrome://browser/content/browser.xul chrome://testpilot/content/feedback-browser.xul application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} appversion>3.7a1pre # For the menubar on Mac diff --git a/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/all-studies-window.js b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/all-studies-window.js index 6b6f318fceba..322cef5dabed 100644 --- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/all-studies-window.js +++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/all-studies-window.js @@ -286,11 +286,9 @@ var TestPilotXulWindow = { let openInTab = (task.taskType == TaskConstants.TYPE_LEGACY); this.addDescription(textVbox, task.title, task.summary); - if (task.showMoreInfoLink) { - this.addXulLink( - textVbox, this._stringBundle.getString("testpilot.moreInfo"), - task.defaultUrl, openInTab); - } + this.addXulLink( + textVbox, this._stringBundle.getString("testpilot.moreInfo"), + task.defaultUrl, openInTab); // Create the rightmost status area, depending on status: let statusVbox = document.createElement("vbox"); diff --git a/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/browser.css b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/browser.css index b048ae1dafdc..cf9fdb103045 100644 --- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/browser.css +++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/browser.css @@ -7,8 +7,10 @@ #feedback-menu-button .toolbarbutton-text { display: -moz-box; margin: 0; + color: -moz-dialogtext; + text-shadow: none; } - + #feedback-menu-button .toolbarbutton-icon { display: none; } diff --git a/browser/app/profile/extensions/testpilot@labs.mozilla.com/modules/tasks.js b/browser/app/profile/extensions/testpilot@labs.mozilla.com/modules/tasks.js index 30bbead9357f..9e551fc7b00f 100644 --- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/modules/tasks.js +++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/modules/tasks.js @@ -166,10 +166,6 @@ var TestPilotTask = { return url + this._id; }, - get showMoreInfoLink() { - return true; - }, - // event handlers: onExperimentStartup: function TestPilotTask_onExperimentStartup() { @@ -1021,10 +1017,6 @@ TestPilotWebSurvey.prototype = { return this.infoPageUrl; }, - get showMoreInfoLink() { - return false; - }, - onDetailPageOpened: function TPWS_onDetailPageOpened() { /* Once you view the URL of the survey, we'll assume you've taken it. * There's no reliable way to tell whether you have or not, so let's diff --git a/browser/base/content/browser-places.js b/browser/base/content/browser-places.js index ea952d0175f9..e20d34ecfc38 100644 --- a/browser/base/content/browser-places.js +++ b/browser/base/content/browser-places.js @@ -480,9 +480,9 @@ var PlacesCommandHook = { var tabList = []; var seenURIs = {}; - var browsers = gBrowser.browsers; - for (var i = 0; i < browsers.length; ++i) { - let uri = browsers[i].currentURI; + let tabs = gBrowser.visibleTabs; + for (let i = 0; i < tabs.length; ++i) { + let uri = tabs[i].linkedBrowser.currentURI; // skip redundant entries if (uri.spec in seenURIs) diff --git a/browser/base/content/browser-tabPreviews.js b/browser/base/content/browser-tabPreviews.js index 785101305f93..4cc9c3ce4465 100644 --- a/browser/base/content/browser-tabPreviews.js +++ b/browser/base/content/browser-tabPreviews.js @@ -215,12 +215,13 @@ var ctrlTab = { if (this._tabList) return this._tabList; - var list = Array.slice(gBrowser.tabs); + let list = gBrowser.visibleTabs; if (this._closing) this.detachTab(this._closing, list); - for (let i = 0; i < gBrowser.tabContainer.selectedIndex; i++) + // Rotate the list until the selected tab is first + while (!list[0].selected) list.push(list.shift()); if (this.recentlyUsedLimit != 0) { @@ -462,11 +463,12 @@ var ctrlTab = { } else if (!event.shiftKey) { event.preventDefault(); event.stopPropagation(); - if (gBrowser.tabs.length > 2) { + let tabs = gBrowser.visibleTabs; + if (tabs.length > 2) { this.open(); - } else if (gBrowser.tabs.length == 2) { - gBrowser.selectedTab = gBrowser.selectedTab.nextSibling || - gBrowser.selectedTab.previousSibling; + } else if (tabs.length == 2) { + let index = gBrowser.selectedTab == tabs[0] ? 1 : 0; + gBrowser.selectedTab = tabs[index]; } } } @@ -664,7 +666,7 @@ var allTabs = { Array.forEach(this.previews, function (preview) { var tab = preview._tab; var matches = 0; - if (filter.length) { + if (filter.length && !tab.hidden) { let tabstring = tab.linkedBrowser.currentURI.spec; try { tabstring = decodeURI(tabstring); @@ -673,7 +675,7 @@ var allTabs = { for (let i = 0; i < filter.length; i++) matches += tabstring.indexOf(filter[i]) > -1; } - if (matches < filter.length) { + if (matches < filter.length || tab.hidden) { preview.hidden = true; } else { diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 27b471d3852e..667be1088938 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -6772,14 +6772,12 @@ var gBookmarkAllTabsHandler = { this._updateCommandState(); }, - _updateCommandState: function BATH__updateCommandState(aTabClose) { - var numTabs = gBrowser.tabs.length; + _updateCommandState: function BATH__updateCommandState() { + let remainingTabs = gBrowser.visibleTabs.filter(function(tab) { + return gBrowser._removingTabs.indexOf(tab) == -1; + }); - // The TabClose event is fired before the tab is removed from the DOM - if (aTabClose) - numTabs--; - - if (numTabs > 1) + if (remainingTabs.length > 1) this._command.removeAttribute("disabled"); else this._command.setAttribute("disabled", "true"); @@ -6791,7 +6789,7 @@ var gBookmarkAllTabsHandler = { // nsIDOMEventListener handleEvent: function(aEvent) { - this._updateCommandState(aEvent.type == "TabClose"); + this._updateCommandState(); } }; @@ -7799,7 +7797,7 @@ var TabContextMenu = { updateContextMenu: function updateContextMenu(aPopupMenu) { this.contextTab = document.popupNode.localName == "tab" ? document.popupNode : gBrowser.selectedTab; - var disabled = gBrowser.tabs.length == 1; + let disabled = gBrowser.visibleTabs.length == 1; // Enable the "Close Tab" menuitem when the window doesn't close with the last tab. document.getElementById("context_closeTab").disabled = @@ -7821,7 +7819,7 @@ var TabContextMenu = { // Disable "Close other Tabs" if there is only one unpinned tab and // hide it when the user rightclicked on a pinned tab. - var unpinnedTabs = gBrowser.tabs.length - gBrowser._numPinnedTabs; + let unpinnedTabs = gBrowser.visibleTabs.length - gBrowser._numPinnedTabs; document.getElementById("context_closeOtherTabs").disabled = unpinnedTabs <= 1; document.getElementById("context_closeOtherTabs").hidden = this.contextTab.pinned; } diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index eb4eb2c9df34..5feadcf21e03 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -88,6 +88,8 @@ this.tabContainer.childNodes; + Components.classes["@mozilla.org/docshell/urifixup;1"] .getService(Components.interfaces.nsIURIFixup); @@ -758,6 +760,7 @@ newBrowser.docShell.isActive = true; this.mCurrentBrowser = newBrowser; this.mCurrentTab = this.selectedTab; + this.mCurrentTab.hidden = false; if (updatePageReport) this.mCurrentBrowser.updatePageReport(); @@ -1016,7 +1019,8 @@ // == 1 false YES // == 1 true NO // > 1 false/true NO - var owner = (aURIs.length > 1) || aLoadInBackground ? null : this.selectedTab; + var multiple = aURIs.length > 1; + var owner = multiple || aLoadInBackground ? null : this.selectedTab; var firstTabAdded = null; if (aReplace) { @@ -1028,7 +1032,7 @@ } } else - firstTabAdded = this.addTab(aURIs[0], {ownerTab: owner, skipAnimation: true}); + firstTabAdded = this.addTab(aURIs[0], {ownerTab: owner, skipAnimation: multiple}); var tabNum = this.tabContainer.selectedIndex; for (let i = 1; i < aURIs.length; ++i) { @@ -1243,7 +1247,7 @@ var tabsToClose = this.tabs.length; if (!aAll) - tabsToClose -= 1 + gBrowser._numPinnedTabs; + tabsToClose = this.visibleTabs.length - (1 + this._numPinnedTabs); if (tabsToClose <= 1) return true; @@ -1294,11 +1298,12 @@ return; if (this.warnAboutClosingTabs(false)) { + let tabs = this.visibleTabs; this.selectedTab = aTab; - for (let i = this.tabs.length - 1; i >= 0; --i) { - if (this.tabs[i] != aTab && !this.tabs[i].pinned) - this.removeTab(this.tabs[i]); + for (let i = tabs.length - 1; i >= 0; --i) { + if (tabs[i] != aTab && !tabs[i].pinned) + this.removeTab(tabs[i]); } } ]]> @@ -1323,10 +1328,19 @@ -1) { + this._endRemoveTab(aTab); + return; + } + + var isLastTab = (this.tabs.length - this._removingTabs.length == 1); + if (!this._beginRemoveTab(aTab, false, null, true)) return; @@ -1564,18 +1578,29 @@ return; } - var tab = aTab; + let removing = this._removingTabs; + function keepRemaining(tab) { + // A tab remains only if it's not being removed nor blurred + return removing.indexOf(tab) == -1 && tab != aTab; + } + // Switch to a visible tab unless there aren't any remaining + let remainingTabs = this.visibleTabs.filter(keepRemaining); + if (remainingTabs.length == 0) + remainingTabs = Array.filter(this.tabs, keepRemaining); + + // Try to find a remaining tab that comes after the given tab + var tab = aTab; do { tab = tab.nextSibling; - } while (tab && this._removingTabs.indexOf(tab) != -1); + } while (tab && remainingTabs.indexOf(tab) == -1); if (!tab) { tab = aTab; do { tab = tab.previousSibling; - } while (tab && this._removingTabs.indexOf(tab) != -1); + } while (tab && remainingTabs.indexOf(tab) == -1); } this.selectedTab = tab; @@ -1651,10 +1676,11 @@ + + + + + + + = 0 && - aIndex < this.tabs.length && - aIndex != this.tabContainer.selectedIndex) - this.selectedTab = this.tabs[aIndex]; + if (aIndex >= 0 && aIndex < tabs.length) + this.selectedTab = tabs[aIndex]; if (aEvent) { aEvent.preventDefault(); @@ -1805,7 +1842,7 @@ - - return !tab.pinned; - + @@ -2404,7 +2441,7 @@ var tabs = document.getBindingParent(this); tabs.removeAttribute("overflow"); - tabs.tabbrowser._removingTabs.forEach(tabs.tabbrowser._endRemoveTab, + tabs.tabbrowser._removingTabs.forEach(tabs.tabbrowser.removeTab, tabs.tabbrowser); tabs._positionPinnedTabs(); @@ -2569,7 +2606,8 @@ if (this.childNodes.length == 1 && this._closeWindowWithLastTab) this.setAttribute("closebuttons", "noclose"); else { - let tab = this.childNodes.item(this.tabbrowser._numPinnedTabs); + // Grab the last tab for size comparison + let tab = this.tabbrowser.visibleTabs.pop(); if (tab && tab.getBoundingClientRect().width > this.mTabClipWidth) this.setAttribute("closebuttons", "alltabs"); else @@ -3366,7 +3404,7 @@ + * Aza Raskin + * Michael Yoshitaka Erlewine + * + * This file incorporates work from: + * jQuery JavaScript Library v1.4.2: http://code.jquery.com/jquery-1.4.2.js + * This incorporated work is covered by the following copyright and + * permission notice: + * Copyright 2010, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +// ********** +// Title: iq.js +// Various helper functions, in the vein of jQuery. + +// ---------- +// Function: iQ +// Returns an iQClass object which represents an individual element or a group +// of elements. It works pretty much like jQuery(), with a few exceptions, +// most notably that you can't use strings with complex html, +// just simple tags like '
'. +function iQ(selector, context) { + // The iQ object is actually just the init constructor 'enhanced' + return new iQClass(selector, context); +}; + +// A simple way to check for HTML strings or ID strings +// (both of which we optimize for) +let quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/; + +// Match a standalone tag +let rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/; + +// ########## +// Class: iQClass +// The actual class of iQ result objects, representing an individual element +// or a group of elements. +// +// ---------- +// Function: iQClass +// You don't call this directly; this is what's called by iQ(). +let iQClass = function(selector, context) { + + // Handle $(""), $(null), or $(undefined) + if (!selector) { + return this; + } + + // Handle $(DOMElement) + if (selector.nodeType) { + this.context = selector; + this[0] = selector; + this.length = 1; + return this; + } + + // The body element only exists once, optimize finding it + if (selector === "body" && !context) { + this.context = document; + this[0] = document.body; + this.selector = "body"; + this.length = 1; + return this; + } + + // Handle HTML strings + if (typeof selector === "string") { + // Are we dealing with HTML string or an ID? + + let match = quickExpr.exec(selector); + + // Verify a match, and that no context was specified for #id + if (match && (match[1] || !context)) { + + // HANDLE $(html) -> $(array) + if (match[1]) { + let doc = (context ? context.ownerDocument || context : document); + + // If a single string is passed in and it's a single tag + // just do a createElement and skip the rest + let ret = rsingleTag.exec(selector); + + if (ret) { + if (Utils.isPlainObject(context)) { + Utils.assert(false, 'does not support HTML creation with context'); + } else { + selector = [doc.createElement(ret[1])]; + } + + } else { + Utils.assert(false, 'does not support complex HTML creation'); + } + + return Utils.merge(this, selector); + + // HANDLE $("#id") + } else { + let elem = document.getElementById(match[2]); + + if (elem) { + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE $("TAG") + } else if (!context && /^\w+$/.test(selector)) { + this.selector = selector; + this.context = document; + selector = document.getElementsByTagName(selector); + return Utils.merge(this, selector); + + // HANDLE $(expr, $(...)) + } else if (!context || context.iq) { + return (context || iQ(document)).find(selector); + + // HANDLE $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return iQ(context).find(selector); + } + + // HANDLE $(function) + // Shortcut for document ready + } else if (typeof selector == "function") { + Utils.log('iQ does not support ready functions'); + return null; + } + + if (typeof selector.selector !== "undefined") { + this.selector = selector.selector; + this.context = selector.context; + } + + let ret = this || []; + if (selector != null) { + // The window, strings (and functions) also have 'length' + if (selector.length == null || typeof selector == "string" || selector.setInterval) { + Array.push(ret, selector); + } else { + Utils.merge(ret, selector); + } + } + return ret; +}; + +iQClass.prototype = { + + // Start with an empty selector + selector: "", + + // The default length of a iQ object is 0 + length: 0, + + // ---------- + // Function: each + // Execute a callback for every element in the matched set. + each: function(callback) { + if (typeof callback != "function") { + Utils.assert(false, "each's argument must be a function"); + return null; + } + for (let i = 0; this[i] != null; i++) { + callback(this[i]); + } + return this; + }, + + // ---------- + // Function: addClass + // Adds the given class(es) to the receiver. + addClass: function(value) { + Utils.assertThrow(typeof value == "string" && value, + 'requires a valid string argument'); + + let length = this.length; + for (let i = 0; i < length; i++) { + let elem = this[i]; + if (elem.nodeType === 1) { + value.split(/\s+/).forEach(function(className) { + elem.classList.add(className); + }); + } + } + + return this; + }, + + // ---------- + // Function: removeClass + // Removes the given class(es) from the receiver. + removeClass: function(value) { + if (typeof value != "string" || !value) { + Utils.assert(false, 'does not support function argument'); + return null; + } + + let length = this.length; + for (let i = 0; i < length; i++) { + let elem = this[i]; + if (elem.nodeType === 1 && elem.className) { + value.split(/\s+/).forEach(function(className) { + elem.classList.remove(className); + }); + } + } + + return this; + }, + + // ---------- + // Function: hasClass + // Returns true is the receiver has the given css class. + hasClass: function(singleClassName) { + let length = this.length; + for (let i = 0; i < length; i++) { + if (this[i].classList.contains(singleClassName)) { + return true; + } + } + return false; + }, + + // ---------- + // Function: find + // Searches the receiver and its children, returning a new iQ object with + // elements that match the given selector. + find: function(selector) { + let ret = []; + let length = 0; + + let l = this.length; + for (let i = 0; i < l; i++) { + length = ret.length; + try { + Utils.merge(ret, this[i].querySelectorAll(selector)); + } catch(e) { + Utils.log('iQ.find error (bad selector)', e); + } + + if (i > 0) { + // Make sure that the results are unique + for (let n = length; n < ret.length; n++) { + for (let r = 0; r < length; r++) { + if (ret[r] === ret[n]) { + ret.splice(n--, 1); + break; + } + } + } + } + } + + return iQ(ret); + }, + + // ---------- + // Function: remove + // Removes the receiver from the DOM. + remove: function() { + for (let i = 0; this[i] != null; i++) { + let elem = this[i]; + if (elem.parentNode) { + elem.parentNode.removeChild(elem); + } + } + return this; + }, + + // ---------- + // Function: empty + // Removes all of the reciever's children and HTML content from the DOM. + empty: function() { + for (let i = 0; this[i] != null; i++) { + let elem = this[i]; + while (elem.firstChild) { + elem.removeChild(elem.firstChild); + } + } + return this; + }, + + // ---------- + // Function: width + // Returns the width of the receiver. + width: function() { + let bounds = this.bounds(); + return bounds.width; + }, + + // ---------- + // Function: height + // Returns the height of the receiver. + height: function() { + let bounds = this.bounds(); + return bounds.height; + }, + + // ---------- + // Function: position + // Returns an object with the receiver's position in left and top + // properties. + position: function() { + let bounds = this.bounds(); + return new Point(bounds.left, bounds.top); + }, + + // ---------- + // Function: bounds + // Returns a with the receiver's bounds. + bounds: function() { + Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)'); + let rect = this[0].getBoundingClientRect(); + return new Rect(Math.floor(rect.left), Math.floor(rect.top), + Math.floor(rect.width), Math.floor(rect.height)); + }, + + // ---------- + // Function: data + // Pass in both key and value to attach some data to the receiver; + // pass in just key to retrieve it. + data: function(key, value) { + let data = null; + if (typeof value === "undefined") { + Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)'); + data = this[0].iQData; + if (data) + return data[key]; + else + return null; + } + + for (let i = 0; this[i] != null; i++) { + let elem = this[i]; + data = elem.iQData; + + if (!data) + data = elem.iQData = {}; + + data[key] = value; + } + + return this; + }, + + // ---------- + // Function: html + // Given a value, sets the receiver's innerHTML to it; otherwise returns + // what's already there. + html: function(value) { + Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)'); + if (typeof value === "undefined") + return this[0].innerHTML; + + this[0].innerHTML = value; + return this; + }, + + // ---------- + // Function: text + // Given a value, sets the receiver's textContent to it; otherwise returns + // what's already there. + text: function(value) { + Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)'); + if (typeof value === "undefined") { + return this[0].textContent; + } + + return this.empty().append((this[0] && this[0].ownerDocument || document).createTextNode(value)); + }, + + // ---------- + // Function: val + // Given a value, sets the receiver's value to it; otherwise returns what's already there. + val: function(value) { + Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)'); + if (typeof value === "undefined") { + return this[0].value; + } + + this[0].value = value; + return this; + }, + + // ---------- + // Function: appendTo + // Appends the receiver to the result of iQ(selector). + appendTo: function(selector) { + Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)'); + iQ(selector).append(this); + return this; + }, + + // ---------- + // Function: append + // Appends the result of iQ(selector) to the receiver. + append: function(selector) { + let object = iQ(selector); + Utils.assert(object.length == 1 && this.length == 1, + 'does not yet support multi-objects (or null objects)'); + this[0].appendChild(object[0]); + return this; + }, + + // ---------- + // Function: attr + // Sets or gets an attribute on the element(s). + attr: function(key, value) { + Utils.assert(typeof key === 'string', 'string key'); + if (typeof value === "undefined") { + Utils.assert(this.length == 1, 'retrieval does not support multi-objects (or null objects)'); + return this[0].getAttribute(key); + } + + for (let i = 0; this[i] != null; i++) + this[i].setAttribute(key, value); + + return this; + }, + + // ---------- + // Function: css + // Sets or gets CSS properties on the receiver. When setting certain numerical properties, + // will automatically add "px". A property can be removed by setting it to null. + // + // Possible call patterns: + // a: object, b: undefined - sets with properties from a + // a: string, b: undefined - gets property specified by a + // a: string, b: string/number - sets property specified by a to b + css: function(a, b) { + let properties = null; + + if (typeof a === 'string') { + let key = a; + if (typeof b === "undefined") { + Utils.assert(this.length == 1, 'retrieval does not support multi-objects (or null objects)'); + + return window.getComputedStyle(this[0], null).getPropertyValue(key); + } + properties = {}; + properties[key] = b; + } else { + properties = a; + } + + let pixels = { + 'left': true, + 'top': true, + 'right': true, + 'bottom': true, + 'width': true, + 'height': true + }; + + for (let i = 0; this[i] != null; i++) { + let elem = this[i]; + for (let key in properties) { + let value = properties[key]; + if (pixels[key] && typeof value != 'string') + value += 'px'; + + if (value == null) { + elem.style.removeProperty(key); + } else if (key.indexOf('-') != -1) + elem.style.setProperty(key, value, ''); + else + elem.style[key] = value; + } + } + + return this; + }, + + // ---------- + // Function: animate + // Uses CSS transitions to animate the element. + // + // Parameters: + // css - an object map of the CSS properties to change + // options - an object with various properites (see below) + // + // Possible "options" properties: + // duration - how long to animate, in milliseconds + // easing - easing function to use. Possibilities include + // "tabviewBounce", "easeInQuad". Default is "ease". + // complete - function to call once the animation is done, takes nothing + // in, but "this" is set to the element that was animated. + animate: function(css, options) { + Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)'); + + if (!options) + options = {}; + + let easings = { + tabviewBounce: "cubic-bezier(0.0, 0.63, .6, 1.0)", + // TODO: change 1.0 above to 1.29 after bug 575672 is fixed + + easeInQuad: 'ease-in', // TODO: make it a real easeInQuad, or decide we don't care + fast: 'cubic-bezier(0.7,0,1,1)' + }; + + let duration = (options.duration || 400); + let easing = (easings[options.easing] || 'ease'); + + // The latest versions of Firefox do not animate from a non-explicitly + // set css properties. So for each element to be animated, go through + // and explicitly define 'em. + let rupper = /([A-Z])/g; + this.each(function(elem) { + let cStyle = window.getComputedStyle(elem, null); + for (let prop in css) { + prop = prop.replace(rupper, "-$1").toLowerCase(); + iQ(elem).css(prop, cStyle.getPropertyValue(prop)); + } + }); + + this.css({ + '-moz-transition-property': 'all', // TODO: just animate the properties we're changing + '-moz-transition-duration': (duration / 1000) + 's', + '-moz-transition-timing-function': easing + }); + + this.css(css); + + let self = this; + setTimeout(function() { + self.css({ + '-moz-transition-property': 'none', + '-moz-transition-duration': '', + '-moz-transition-timing-function': '' + }); + + if (typeof options.complete == "function") + options.complete.apply(self); + }, duration); + + return this; + }, + + // ---------- + // Function: fadeOut + // Animates the receiver to full transparency. Calls callback on completion. + fadeOut: function(callback) { + Utils.assert(typeof callback == "function" || typeof callback === "undefined", + 'does not yet support duration'); + + this.animate({ + opacity: 0 + }, { + duration: 400, + complete: function() { + iQ(this).css({display: 'none'}); + if (typeof callback == "function") + callback.apply(this); + } + }); + + return this; + }, + + // ---------- + // Function: fadeIn + // Animates the receiver to full opacity. + fadeIn: function() { + this.css({display: ''}); + this.animate({ + opacity: 1 + }, { + duration: 400 + }); + + return this; + }, + + // ---------- + // Function: hide + // Hides the receiver. + hide: function() { + this.css({display: 'none', opacity: 0}); + return this; + }, + + // ---------- + // Function: show + // Shows the receiver. + show: function() { + this.css({display: '', opacity: 1}); + return this; + }, + + // ---------- + // Function: bind + // Binds the given function to the given event type. Also wraps the function + // in a try/catch block that does a Utils.log on any errors. + bind: function(type, func) { + let handler = function(event) func.apply(this, [event]); + + for (let i = 0; this[i] != null; i++) { + let elem = this[i]; + if (!elem.iQEventData) + elem.iQEventData = {}; + + if (!elem.iQEventData[type]) + elem.iQEventData[type] = []; + + elem.iQEventData[type].push({ + original: func, + modified: handler + }); + + elem.addEventListener(type, handler, false); + } + + return this; + }, + + // ---------- + // Function: one + // Binds the given function to the given event type, but only for one call; + // automatically unbinds after the event fires once. + one: function(type, func) { + Utils.assert(typeof func == "function", 'does not support eventData argument'); + + let handler = function(e) { + iQ(this).unbind(type, handler); + return func.apply(this, [e]); + }; + + return this.bind(type, handler); + }, + + // ---------- + // Function: unbind + // Unbinds the given function from the given event type. + unbind: function(type, func) { + Utils.assert(typeof func == "function", 'Must provide a function'); + + for (let i = 0; this[i] != null; i++) { + let elem = this[i]; + let handler = func; + if (elem.iQEventData && elem.iQEventData[type]) { + let count = elem.iQEventData[type].length; + for (let a = 0; a < count; a++) { + let pair = elem.iQEventData[type][a]; + if (pair.original == func) { + handler = pair.modified; + elem.iQEventData[type].splice(a, 1); + break; + } + } + } + + elem.removeEventListener(type, handler, false); + } + + return this; + } +}; + +// ---------- +// Create various event aliases +let events = [ + 'keyup', + 'keydown', + 'mouseup', + 'mousedown', + 'mouseover', + 'mouseout', + 'mousemove', + 'click', + 'resize', + 'change', + 'blur', + 'focus' +]; + +events.forEach(function(event) { + iQClass.prototype[event] = function(func) { + return this.bind(event, func); + }; +}); diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm new file mode 100644 index 000000000000..0a0bcdef3e27 --- /dev/null +++ b/browser/base/content/tabview/modules/utils.jsm @@ -0,0 +1,728 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is utils.js. + * + * The Initial Developer of the Original Code is the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Aza Raskin + * Ian Gilman + * Michael Yoshitaka Erlewine + * + * This file incorporates work from: + * jQuery JavaScript Library v1.4.2: http://code.jquery.com/jquery-1.4.2.js + * This incorporated work is covered by the following copyright and + * permission notice: + * Copyright 2010, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +// ********** +// Title: utils.js + +let EXPORTED_SYMBOLS = ["Point", "Rect", "Range", "Subscribable", "Utils"]; + +// ######### +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/Services.jsm"); + +// ########## +// Class: Point +// A simple point. +// +// Constructor: Point +// If a is a Point, creates a copy of it. Otherwise, expects a to be x, +// and creates a Point with it along with y. If either a or y are omitted, +// 0 is used in their place. +function Point(a, y) { + if (Utils.isPoint(a)) { + this.x = a.x; + this.y = a.y; + } else { + this.x = (Utils.isNumber(a) ? a : 0); + this.y = (Utils.isNumber(y) ? y : 0); + } +}; + +Point.prototype = { + // ---------- + // Function: distance + // Returns the distance from this point to the given . + distance: function(point) { + var ax = this.x - point.x; + var ay = this.y - point.y; + return Math.sqrt((ax * ax) + (ay * ay)); + } +}; + +// ########## +// Class: Rect +// A simple rectangle. Note that in addition to the left and width, it also has +// a right property; changing one affects the others appropriately. Same for the +// vertical properties. +// +// Constructor: Rect +// If a is a Rect, creates a copy of it. Otherwise, expects a to be left, +// and creates a Rect with it along with top, width, and height. +function Rect(a, top, width, height) { + // Note: perhaps 'a' should really be called 'rectOrLeft' + if (Utils.isRect(a)) { + this.left = a.left; + this.top = a.top; + this.width = a.width; + this.height = a.height; + } else { + this.left = a; + this.top = top; + this.width = width; + this.height = height; + } +}; + +Rect.prototype = { + + get right() this.left + this.width, + set right(value) { + this.width = value - this.left; + }, + + get bottom() this.top + this.height, + set bottom(value) { + this.height = value - this.top; + }, + + // ---------- + // Variable: xRange + // Gives you a new for the horizontal dimension. + get xRange() new Range(this.left, this.right), + + // ---------- + // Variable: yRange + // Gives you a new for the vertical dimension. + get yRange() new Range(this.top, this.bottom), + + // ---------- + // Function: intersects + // Returns true if this rectangle intersects the given . + intersects: function(rect) { + return (rect.right > this.left && + rect.left < this.right && + rect.bottom > this.top && + rect.top < this.bottom); + }, + + // ---------- + // Function: intersection + // Returns a new with the intersection of this rectangle and the give , + // or null if they don't intersect. + intersection: function(rect) { + var box = new Rect(Math.max(rect.left, this.left), Math.max(rect.top, this.top), 0, 0); + box.right = Math.min(rect.right, this.right); + box.bottom = Math.min(rect.bottom, this.bottom); + if (box.width > 0 && box.height > 0) + return box; + + return null; + }, + + // ---------- + // Function: contains + // Returns a boolean denoting if the is contained inside + // of the bounding rect. + // + // Paramaters + // - A + contains: function(rect) { + return (rect.left > this.left && + rect.right < this.right && + rect.top > this.top && + rect.bottom < this.bottom); + }, + + // ---------- + // Function: center + // Returns a new with the center location of this rectangle. + center: function() { + return new Point(this.left + (this.width / 2), this.top + (this.height / 2)); + }, + + // ---------- + // Function: size + // Returns a new with the dimensions of this rectangle. + size: function() { + return new Point(this.width, this.height); + }, + + // ---------- + // Function: position + // Returns a new with the top left of this rectangle. + position: function() { + return new Point(this.left, this.top); + }, + + // ---------- + // Function: area + // Returns the area of this rectangle. + area: function() { + return this.width * this.height; + }, + + // ---------- + // Function: inset + // Makes the rect smaller (if the arguments are positive) as if a margin is added all around + // the initial rect, with the margin widths (symmetric) being specified by the arguments. + // + // Paramaters + // - A or two arguments: x and y + inset: function(a, b) { + if (Utils.isPoint(a)) { + b = a.y; + a = a.x; + } + + this.left += a; + this.width -= a * 2; + this.top += b; + this.height -= b * 2; + }, + + // ---------- + // Function: offset + // Moves (translates) the rect by the given vector. + // + // Paramaters + // - A or two arguments: x and y + offset: function(a, b) { + if (Utils.isPoint(a)) { + this.left += a.x; + this.top += a.y; + } else { + this.left += a; + this.top += b; + } + }, + + // ---------- + // Function: equals + // Returns true if this rectangle is identical to the given . + equals: function(rect) { + return (rect.left == this.left && + rect.top == this.top && + rect.width == this.width && + rect.height == this.height); + }, + + // ---------- + // Function: union + // Returns a new with the union of this rectangle and the given . + union: function(a) { + var newLeft = Math.min(a.left, this.left); + var newTop = Math.min(a.top, this.top); + var newWidth = Math.max(a.right, this.right) - newLeft; + var newHeight = Math.max(a.bottom, this.bottom) - newTop; + var newRect = new Rect(newLeft, newTop, newWidth, newHeight); + + return newRect; + }, + + // ---------- + // Function: copy + // Copies the values of the given into this rectangle. + copy: function(a) { + this.left = a.left; + this.top = a.top; + this.width = a.width; + this.height = a.height; + }, + + // ---------- + // Function: css + // Returns an object with the dimensions of this rectangle, suitable for + // passing into iQ's css method. You could of course just pass the rectangle + // straight in, but this is cleaner, as it removes all the extraneous + // properties. If you give a to without this, it will + // ignore the extraneous properties, but result in CSS warnings. + css: function() { + return { + left: this.left, + top: this.top, + width: this.width, + height: this.height + }; + } +}; + +// ########## +// Class: Range +// A physical interval, with a min and max. +// +// Constructor: Range +// Creates a Range with the given min and max +function Range(min, max) { + if (Utils.isRange(min) && !max) { // if the one variable given is a range, copy it. + this.min = min.min; + this.max = min.max; + } else { + this.min = min || 0; + this.max = max || 0; + } +}; + +Range.prototype = { + // Variable: extent + // Equivalent to max-min + get extent() { + return (this.max - this.min); + }, + + set extent(extent) { + this.max = extent - this.min; + }, + + // ---------- + // Function: contains + // Whether the contains the given or value or not. + // + // Paramaters + // - a number or + contains: function(value) { + if (Utils.isNumber(value)) + return value >= this.min && value <= this.max; + if (Utils.isRange(value)) + return value.min >= this.min && value.max <= this.max; + return false; + }, + + // ---------- + // Function: proportion + // Maps the given value to the range [0,1], so that it returns 0 if the value is <= the min, + // returns 1 if the value >= the max, and returns an interpolated "proportion" in (min, max). + // + // Paramaters + // - a number + // - (bool) smooth? If true, a smooth tanh-based function will be used instead of the linear. + proportion: function(value, smooth) { + if (value <= this.min) + return 0; + if (this.max <= value) + return 1; + + var proportion = (value - this.min) / this.extent; + + if (smooth) { + // The ease function ".5+.5*Math.tanh(4*x-2)" is a pretty + // little graph. It goes from near 0 at x=0 to near 1 at x=1 + // smoothly and beautifully. + // http://www.wolframalpha.com/input/?i=.5+%2B+.5+*+tanh%28%284+*+x%29+-+2%29 + function tanh(x) { + var e = Math.exp(x); + return (e - 1/e) / (e + 1/e); + } + return .5 - .5 * tanh(2 - 4 * proportion); + } + + return proportion; + }, + + // ---------- + // Function: scale + // Takes the given value in [0,1] and maps it to the associated value on the Range. + // + // Paramaters + // - a number in [0,1] + scale: function(value) { + if (value > 1) + value = 1; + if (value < 0) + value = 0; + return this.min + this.extent * value; + } +}; + +// ########## +// Class: Subscribable +// A mix-in for allowing objects to collect subscribers for custom events. +function Subscribable() { + this.subscribers = null; +}; + +Subscribable.prototype = { + // ---------- + // Function: addSubscriber + // The given callback will be called when the Subscribable fires the given event. + // The refObject is used to facilitate removal if necessary. + addSubscriber: function(refObject, eventName, callback) { + try { + Utils.assertThrow(refObject, "refObject"); + Utils.assertThrow(typeof callback == "function", "callback must be a function"); + Utils.assertThrow(eventName && typeof eventName == "string", + "eventName must be a non-empty string"); + } catch(e) { + Utils.log(e); + return; + } + + if (!this.subscribers) + this.subscribers = {}; + + if (!this.subscribers[eventName]) + this.subscribers[eventName] = []; + + var subs = this.subscribers[eventName]; + var existing = subs.filter(function(element) { + return element.refObject == refObject; + }); + + if (existing.length) { + Utils.assert(existing.length == 1, 'should only ever be one'); + existing[0].callback = callback; + } else { + subs.push({ + refObject: refObject, + callback: callback + }); + } + }, + + // ---------- + // Function: removeSubscriber + // Removes the callback associated with refObject for the given event. + removeSubscriber: function(refObject, eventName) { + try { + Utils.assertThrow(refObject, "refObject"); + Utils.assertThrow(eventName && typeof eventName == "string", + "eventName must be a non-empty string"); + } catch(e) { + Utils.log(e); + return; + } + + if (!this.subscribers || !this.subscribers[eventName]) + return; + + this.subscribers[eventName] = this.subscribers[eventName].filter(function(element) { + return element.refObject != refObject; + }); + }, + + // ---------- + // Function: _sendToSubscribers + // Internal routine. Used by the Subscribable to fire events. + _sendToSubscribers: function(eventName, eventInfo) { + try { + Utils.assertThrow(eventName && typeof eventName == "string", + "eventName must be a non-empty string"); + } catch(e) { + Utils.log(e); + return; + } + + if (!this.subscribers || !this.subscribers[eventName]) + return; + + var subsCopy = this.subscribers[eventName].concat(); + subsCopy.forEach(function(object) { + try { + object.callback(this, eventInfo); + } catch(e) { + Utils.log(e); + } + }, this); + } +}; + +// ########## +// Class: Utils +// Singelton with common utility functions. +let Utils = { + // ___ Logging + + // ---------- + // Function: log + // Prints the given arguments to the JavaScript error console as a message. + // Pass as many arguments as you want, it'll print them all. + log: function() { + var text = this.expandArgumentsForLog(arguments); + Services.console.logStringMessage(text); + }, + + // ---------- + // Function: error + // Prints the given arguments to the JavaScript error console as an error. + // Pass as many arguments as you want, it'll print them all. + error: function() { + var text = this.expandArgumentsForLog(arguments); + Cu.reportError("tabview error: " + text); + }, + + // ---------- + // Function: trace + // Prints the given arguments to the JavaScript error console as a message, + // along with a full stack trace. + // Pass as many arguments as you want, it'll print them all. + trace: function() { + var text = this.expandArgumentsForLog(arguments); + // cut off the first two lines of the stack trace, because they're just this function. + let stack = Error().stack.replace(/^.*?\n.*?\n/, ""); + // if the caller was assert, cut out the line for the assert function as well. + if (this.trace.caller.name == 'Utils_assert') + stack = stack.replace(/^.*?\n/, ""); + this.log('trace: ' + text + '\n' + stack); + }, + + // ---------- + // Function: assert + // Prints a stack trace along with label (as a console message) if condition is false. + assert: function Utils_assert(condition, label) { + if (!condition) { + let text; + if (typeof label != 'string') + text = 'badly formed assert'; + else + text = "tabview assert: " + label; + + this.trace(text); + } + }, + + // ---------- + // Function: assertThrow + // Throws label as an exception if condition is false. + assertThrow: function(condition, label) { + if (!condition) { + let text; + if (typeof label != 'string') + text = 'badly formed assert'; + else + text = "tabview assert: " + label; + + // cut off the first two lines of the stack trace, because they're just this function. + text += Error().stack.replace(/^.*?\n.*?\n/, ""); + + throw text; + } + }, + + // ---------- + // Function: expandObject + // Prints the given object to a string, including all of its properties. + expandObject: function(obj) { + var s = obj + ' = {'; + for (let prop in obj) { + let value; + try { + value = obj[prop]; + } catch(e) { + value = '[!!error retrieving property]'; + } + + s += prop + ': '; + if (typeof value == 'string') + s += '\'' + value + '\''; + else if (typeof value == 'function') + s += 'function'; + else + s += value; + + s += ', '; + } + return s + '}'; + }, + + // ---------- + // Function: expandArgumentsForLog + // Expands all of the given args (an array) into a single string. + expandArgumentsForLog: function(args) { + var that = this; + return Array.map(args, function(arg) { + return typeof arg == 'object' ? that.expandObject(arg) : arg; + }).join('; '); + }, + + // ___ Misc + + // ---------- + // Function: isRightClick + // Given a DOM mouse event, returns true if it was for the right mouse button. + isRightClick: function(event) { + return event.button == 2; + }, + + // ---------- + // Function: isDOMElement + // Returns true if the given object is a DOM element. + isDOMElement: function(object) { + return object instanceof Ci.nsIDOMElement; + }, + + // ---------- + // Function: isNumber + // Returns true if the argument is a valid number. + isNumber: function(n) { + return typeof n == 'number' && !isNaN(n); + }, + + // ---------- + // Function: isRect + // Returns true if the given object (r) looks like a . + isRect: function(r) { + return (r && + this.isNumber(r.left) && + this.isNumber(r.top) && + this.isNumber(r.width) && + this.isNumber(r.height)); + }, + + // ---------- + // Function: isRange + // Returns true if the given object (r) looks like a . + isRange: function(r) { + return (r && + this.isNumber(r.min) && + this.isNumber(r.max)); + }, + + // ---------- + // Function: isPoint + // Returns true if the given object (p) looks like a . + isPoint: function(p) { + return (p && this.isNumber(p.x) && this.isNumber(p.y)); + }, + + // ---------- + // Function: isPlainObject + // Check to see if an object is a plain object (created using "{}" or "new Object"). + isPlainObject: function(obj) { + // Must be an Object. + // Make sure that DOM nodes and window objects don't pass through, as well + if (!obj || Object.prototype.toString.call(obj) !== "[object Object]" || + obj.nodeType || obj.setInterval) { + return false; + } + + // Not own constructor property must be Object + const hasOwnProperty = Object.prototype.hasOwnProperty; + + if (obj.constructor && + !hasOwnProperty.call(obj, "constructor") && + !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf")) { + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + + var key; + for (key in obj) {} + + return key === undefined || hasOwnProperty.call(obj, key); + }, + + // ---------- + // Function: isEmptyObject + // Returns true if the given object has no members. + isEmptyObject: function(obj) { + for (let name in obj) + return false; + return true; + }, + + // ---------- + // Function: copy + // Returns a copy of the argument. Note that this is a shallow copy; if the argument + // has properties that are themselves objects, those properties will be copied by reference. + copy: function(value) { + if (value && typeof value == 'object') { + if (Array.isArray(value)) + return this.extend([], value); + return this.extend({}, value); + } + return value; + }, + + // ---------- + // Function: merge + // Merge two array-like objects into the first and return it. + merge: function(first, second) { + Array.forEach(second, function(el) Array.push(first, el)); + return first; + }, + + // ---------- + // Function: extend + // Pass several objects in and it will combine them all into the first object and return it. + extend: function() { + + // copy reference to target object + let target = arguments[0] || {}; + // Deep copy is not supported + if (typeof target === "boolean") { + this.assert(false, "The first argument of extend cannot be a boolean." + + "Deep copy is not supported."); + return target; + } + + // Back when this was in iQ + iQ.fn, so you could extend iQ objects with it. + // This is no longer supported. + let length = arguments.length; + if (length === 1) { + this.assert(false, "Extending the iQ prototype using extend is not supported."); + return target; + } + + // Handle case when target is a string or something + if (typeof target != "object" && typeof target != "function") { + target = {}; + } + + for (let i = 1; i < length; i++) { + // Only deal with non-null/undefined values + let options = arguments[i]; + if (options != null) { + // Extend the base object + for (let name in options) { + let copy = options[name]; + + // Prevent never-ending loop + if (target === copy) + continue; + + if (copy !== undefined) + target[name] = copy; + } + } + } + + // Return the modified object + return target; + } +}; diff --git a/browser/base/content/test/Makefile.in b/browser/base/content/test/Makefile.in index 86f0f3e7c77d..7dffbb2dc1db 100644 --- a/browser/base/content/test/Makefile.in +++ b/browser/base/content/test/Makefile.in @@ -68,7 +68,6 @@ _TEST_FILES = \ gZipOfflineChild.html^headers^ \ gZipOfflineChild.cacheManifest \ gZipOfflineChild.cacheManifest^headers^ \ - test_bug452451.html \ $(NULL) ifeq (,$(filter gtk2,$(MOZ_WIDGET_TOOLKIT))) @@ -167,6 +166,7 @@ _BROWSER_FILES = \ browser_selectTabAtIndex.js \ browser_tabfocus.js \ browser_tabs_owner.js \ + browser_visibleTabs.js \ discovery.html \ moz.png \ test_bug435035.html \ diff --git a/browser/base/content/test/browser_bug380960.js b/browser/base/content/test/browser_bug380960.js index 4ba0c87c87e4..a754935a7436 100644 --- a/browser/base/content/test/browser_bug380960.js +++ b/browser/base/content/test/browser_bug380960.js @@ -5,6 +5,11 @@ function test() { gBrowser.removeTab(tab); is(tab.parentNode, null, "tab removed immediately"); + tab = gBrowser.addTab("about:blank", { skipAnimation: true }); + gBrowser.removeTab(tab, { animate: true }); + gBrowser.removeTab(tab); + is(tab.parentNode, null, "tab removed immediately when calling removeTab again after the animation was kicked off"); + waitForExplicitFinish(); Services.prefs.setBoolPref("browser.tabs.animate", true); diff --git a/browser/base/content/test/browser_visibleTabs.js b/browser/base/content/test/browser_visibleTabs.js new file mode 100644 index 000000000000..c105bc8f8c8c --- /dev/null +++ b/browser/base/content/test/browser_visibleTabs.js @@ -0,0 +1,126 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is tabbrowser visibleTabs test. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Edward Lee + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +function test() { + // There should be one tab when we start the test + let [origTab] = gBrowser.visibleTabs; + + // Add a tab that will get pinned + let pinned = gBrowser.addTab(); + gBrowser.pinTab(pinned); + + let testTab = gBrowser.addTab(); + + let visible = gBrowser.visibleTabs; + is(visible.length, 3, "3 tabs should be open"); + is(visible[0], pinned, "the pinned tab is first"); + is(visible[1], origTab, "original tab is next"); + is(visible[2], testTab, "last created tab is last"); + + // Only show the test tab (but also get pinned and selected) + is(gBrowser.selectedTab, origTab, "sanity check that we're on the original tab"); + gBrowser.showOnlyTheseTabs([testTab]); + is(gBrowser.visibleTabs.length, 3, "all 3 tabs are still visible"); + + // Select the test tab and only show that (and pinned) + gBrowser.selectedTab = testTab; + gBrowser.showOnlyTheseTabs([testTab]); + + visible = gBrowser.visibleTabs; + is(visible.length, 2, "2 tabs should be visible including the pinned"); + is(visible[0], pinned, "first is pinned"); + is(visible[1], testTab, "next is the test tab"); + is(gBrowser.tabs.length, 3, "3 tabs should still be open"); + + gBrowser.selectTabAtIndex(0); + is(gBrowser.selectedTab, pinned, "first tab is pinned"); + gBrowser.selectTabAtIndex(1); + is(gBrowser.selectedTab, testTab, "second tab is the test tab"); + gBrowser.selectTabAtIndex(2); + is(gBrowser.selectedTab, testTab, "no third tab, so no change"); + gBrowser.selectTabAtIndex(0); + is(gBrowser.selectedTab, pinned, "switch back to the pinned"); + gBrowser.selectTabAtIndex(2); + is(gBrowser.selectedTab, pinned, "no third tab, so no change"); + gBrowser.selectTabAtIndex(-1); + is(gBrowser.selectedTab, testTab, "last tab is the test tab"); + + gBrowser.tabContainer.advanceSelectedTab(1, true); + is(gBrowser.selectedTab, pinned, "wrapped around the end to pinned"); + gBrowser.tabContainer.advanceSelectedTab(1, true); + is(gBrowser.selectedTab, testTab, "next to test tab"); + gBrowser.tabContainer.advanceSelectedTab(1, true); + is(gBrowser.selectedTab, pinned, "next to pinned again"); + + gBrowser.tabContainer.advanceSelectedTab(-1, true); + is(gBrowser.selectedTab, testTab, "going backwards to last tab"); + gBrowser.tabContainer.advanceSelectedTab(-1, true); + is(gBrowser.selectedTab, pinned, "next to pinned"); + gBrowser.tabContainer.advanceSelectedTab(-1, true); + is(gBrowser.selectedTab, testTab, "next to test tab again"); + + // Try showing all tabs + gBrowser.showOnlyTheseTabs(Array.slice(gBrowser.tabs)); + is(gBrowser.visibleTabs.length, 3, "all 3 tabs are visible again"); + + // Select the pinned tab and show the testTab to make sure selection updates + gBrowser.selectedTab = pinned; + gBrowser.showOnlyTheseTabs([testTab]); + is(gBrowser.tabs[1], origTab, "make sure origTab is in the middle"); + is(origTab.hidden, true, "make sure it's hidden"); + gBrowser.removeTab(pinned); + is(gBrowser.selectedTab, testTab, "making sure origTab was skipped"); + is(gBrowser.visibleTabs.length, 1, "only testTab is there"); + + // Only show one of the non-pinned tabs (but testTab is selected) + gBrowser.showOnlyTheseTabs([origTab]); + is(gBrowser.visibleTabs.length, 2, "got 2 tabs"); + + // Now really only show one of the tabs + gBrowser.showOnlyTheseTabs([testTab]); + visible = gBrowser.visibleTabs; + is(visible.length, 1, "only the original tab is visible"); + is(visible[0], testTab, "it's the original tab"); + is(gBrowser.tabs.length, 2, "still have 2 open tabs"); + + // Close the last visible tab and make sure we still get a visible tab + gBrowser.removeTab(testTab); + is(gBrowser.visibleTabs.length, 1, "only orig is left and visible"); + is(gBrowser.tabs.length, 1, "sanity check that it matches"); + is(gBrowser.selectedTab, origTab, "got the orig tab"); + is(origTab.hidden, false, "and it's not hidden -- visible!"); +} diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js index 0ca39bf8437d..dd0b172e7670 100644 --- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -52,6 +52,11 @@ const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); +XPCOMUtils.defineLazyGetter(this, "NetUtil", function() { + Cu.import("resource://gre/modules/NetUtil.jsm"); + return NetUtil; +}); + const PREF_EM_NEW_ADDONS_LIST = "extensions.newAddons"; const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser"; const PREF_PLUGINS_UPDATEURL = "plugins.update.url"; @@ -840,16 +845,18 @@ BrowserGlue.prototype = { var dirService = Cc["@mozilla.org/file/directory_service;1"]. getService(Ci.nsIProperties); - var bookmarksFile = null; + var bookmarksURI = null; if (restoreDefaultBookmarks) { // User wants to restore bookmarks.html file from default profile folder - bookmarksFile = dirService.get("profDef", Ci.nsILocalFile); - bookmarksFile.append("bookmarks.html"); + bookmarksURI = NetUtil.newURI("resource:///defaults/profile/bookmarks.html"); + } + else { + var bookmarksFile = dirService.get("BMarks", Ci.nsILocalFile); + if (bookmarksFile.exists()) + bookmarksURI = NetUtil.newURI(bookmarksFile); } - else - bookmarksFile = dirService.get("BMarks", Ci.nsILocalFile); - if (bookmarksFile.exists()) { + if (bookmarksURI) { // Add an import observer. It will ensure that smart bookmarks are // created once the operation is complete. Services.obs.addObserver(this, "bookmarks-restore-success", false); @@ -859,7 +866,7 @@ BrowserGlue.prototype = { try { var importer = Cc["@mozilla.org/browser/places/import-export-service;1"]. getService(Ci.nsIPlacesImportExportService); - importer.importHTMLFromFile(bookmarksFile, true /* overwrite existing */); + importer.importHTMLFromURI(bookmarksURI, true /* overwrite existing */); } catch (err) { // Report the error, but ignore it. Cu.reportError("Bookmarks.html file could be corrupt. " + err); diff --git a/browser/components/places/content/controller.js b/browser/components/places/content/controller.js index afdd501447be..04f3dc57d7ad 100644 --- a/browser/components/places/content/controller.js +++ b/browser/components/places/content/controller.js @@ -50,7 +50,7 @@ const RELOAD_ACTION_NOTHING = 0; const RELOAD_ACTION_INSERT = 1; // Removing items from the view, select the first item after the last selected const RELOAD_ACTION_REMOVE = 2; -// Moving items within a view, don't treat the dropped items as additional +// Moving items within a view, don't treat the dropped items as additional // rows. const RELOAD_ACTION_MOVE = 3; @@ -67,7 +67,7 @@ const REMOVE_PAGES_MAX_SINGLEREMOVES = 10; /** * Represents an insertion point within a container where we can insert - * items. + * items. * @param aItemId * The identifier of the parent container * @param aIndex @@ -139,7 +139,7 @@ PlacesController.prototype = { return true; } - // All other Places Commands are prefixed with "placesCmd_" ... this + // All other Places Commands are prefixed with "placesCmd_" ... this // filters out other commands that we do _not_ support (see 329587). const CMD_PREFIX = "placesCmd_"; return (aCommand.substr(0, CMD_PREFIX.length) == CMD_PREFIX); @@ -178,7 +178,7 @@ PlacesController.prototype = { if (this._view.selType != "single") { let rootNode = this._view.result.root; if (rootNode.containerOpen && rootNode.childCount > 0) - return true; + return true; } return false; case "placesCmd_open": @@ -308,9 +308,9 @@ PlacesController.prototype = { onEvent: function PC_onEvent(eventName) { }, - + /** - * Determine whether or not the selection can be removed, either by the + * Determine whether or not the selection can be removed, either by the * delete or cut operations based on whether or not any of its contents * are non-removable. We don't need to worry about recursion here since it * is a policy decision that a removable item not be placed inside a non- @@ -373,14 +373,14 @@ PlacesController.prototype = { var root = this._view.result.root; for (var i = 0; i < nodes.length; ++i) { if (nodes[i] == root) - return true; + return true; } return false; }, /** - * Looks at the data on the clipboard to see if it is paste-able. + * Looks at the data on the clipboard to see if it is paste-able. * Paste-able data is: * - in a format that the view can receive * @returns true if: - clipboard data is of a TYPE_X_MOZ_PLACE_* flavor, @@ -427,7 +427,7 @@ PlacesController.prototype = { } }, - /** + /** * Gathers information about the selected nodes according to the following * rules: * "link" node is a URI @@ -442,7 +442,7 @@ PlacesController.prototype = { * * @returns an array of objects corresponding the selected nodes. Each * object has each of the properties above set if its corresponding - * node matches the rule. In addition, the annotations names for each + * node matches the rule. In addition, the annotations names for each * node are set on its corresponding object as properties. * Notes: * 1) This can be slow, so don't call it anywhere performance critical! @@ -456,7 +456,7 @@ PlacesController.prototype = { if (nodes.length == 0) nodes.push(root); // See the second note above - for (var i=0; i < nodes.length; i++) { + for (var i = 0; i < nodes.length; i++) { var nodeData = {}; var node = nodes[i]; var nodeType = node.type; @@ -514,16 +514,16 @@ PlacesController.prototype = { // annotations if (uri) { - var names = PlacesUtils.annotations.getPageAnnotationNames(uri); - for (var j = 0; j < names.length; ++j) + let names = PlacesUtils.annotations.getPageAnnotationNames(uri); + for (let j = 0; j < names.length; ++j) nodeData[names[j]] = true; } // For items also include the item-specific annotations if (node.itemId != -1) { - names = PlacesUtils.annotations - .getItemAnnotationNames(node.itemId); - for (j = 0; j < names.length; ++j) + let names = PlacesUtils.annotations + .getItemAnnotationNames(node.itemId); + for (let j = 0; j < names.length; ++j) nodeData[names[j]] = true; } metadata.push(nodeData); @@ -532,14 +532,14 @@ PlacesController.prototype = { return metadata; }, - /** + /** * Determines if a context-menu item should be shown * @param aMenuItem - * the context menu item + * the context menu item * @param aMetaData * meta data about the selection * @returns true if the conditions (see buildContextMenu) are satisfied - * and the item can be displayed, false otherwise. + * and the item can be displayed, false otherwise. */ _shouldShowMenuItem: function PC__shouldShowMenuItem(aMenuItem, aMetaData) { var selectiontype = aMenuItem.getAttribute("selectiontype"); @@ -551,8 +551,8 @@ PlacesController.prototype = { var forceHideAttr = aMenuItem.getAttribute("forcehideselection"); if (forceHideAttr) { var forceHideRules = forceHideAttr.split("|"); - for (var i = 0; i < aMetaData.length; ++i) { - for (var j=0; j < forceHideRules.length; ++j) { + for (let i = 0; i < aMetaData.length; ++i) { + for (let j = 0; j < forceHideRules.length; ++j) { if (forceHideRules[j] in aMetaData[i]) return false; } @@ -682,7 +682,7 @@ PlacesController.prototype = { }, /** - * Select all links in the current view. + * Select all links in the current view. */ selectAll: function PC_selectAll() { this._view.selectAll(); @@ -691,7 +691,7 @@ PlacesController.prototype = { /** * Opens the bookmark properties for the selected URI Node. */ - showBookmarkPropertiesForSelection: + showBookmarkPropertiesForSelection: function PC_showBookmarkPropertiesForSelection() { var node = this._view.selectedNode; if (!node) @@ -717,7 +717,7 @@ PlacesController.prototype = { * receive a string instead of an nsIURI object. */ _assertURINotString: function PC__assertURINotString(value) { - NS_ASSERT((typeof(value) == "object") && !(value instanceof String), + NS_ASSERT((typeof(value) == "object") && !(value instanceof String), "This method should be passed a URI as a nsIURI object, not as a string."); }, @@ -759,16 +759,15 @@ PlacesController.prototype = { var messageKey = "tabs.openWarningMultipleBranded"; var openKey = "tabs.openButtonMultiple"; - var strings = document.getElementById("placeBundle"); const BRANDING_BUNDLE_URI = "chrome://branding/locale/brand.properties"; var brandShortName = Cc["@mozilla.org/intl/stringbundle;1"]. getService(Ci.nsIStringBundleService). createBundle(BRANDING_BUNDLE_URI). GetStringFromName("brandShortName"); - + var buttonPressed = promptService.confirmEx(window, PlacesUIUtils.getString("tabs.openWarningTitle"), - PlacesUIUtils.getFormattedString(messageKey, + PlacesUIUtils.getFormattedString(messageKey, [numTabsToOpen, brandShortName]), (promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0) + (promptService.BUTTON_TITLE_CANCEL * promptService.BUTTON_POS_1), @@ -788,7 +787,7 @@ PlacesController.prototype = { }, /** - * Opens the links in the selected folder, or the selected links in new tabs. + * Opens the links in the selected folder, or the selected links in new tabs. */ openSelectionInTabs: function PC_openLinksInTabs(aEvent) { var node = this._view.selectedNode; @@ -828,7 +827,7 @@ PlacesController.prototype = { /** * Create a new Bookmark folder somewhere. Prompts the user for the name - * of the folder. + * of the folder. */ newFolder: function PC_newFolder() { var ip = this._view.insertionPoint; @@ -880,17 +879,17 @@ PlacesController.prototype = { /** * Walk the list of folders we're removing in this delete operation, and - * see if the selected node specified is already implicitly being removed - * because it is a child of that folder. + * see if the selected node specified is already implicitly being removed + * because it is a child of that folder. * @param node - * Node to check for containment. + * Node to check for containment. * @param pastFolders * List of folders the calling function has already traversed - * @returns true if the node should be skipped, false otherwise. + * @returns true if the node should be skipped, false otherwise. */ _shouldSkipNode: function PC_shouldSkipNode(node, pastFolders) { /** - * Determines if a node is contained by another node within a resultset. + * Determines if a node is contained by another node within a resultset. * @param node * The node to check for containment for * @param parent @@ -906,7 +905,7 @@ PlacesController.prototype = { } return false; } - + for (var j = 0; j < pastFolders.length; ++j) { if (isContainedBy(node, pastFolders[j])) return true; @@ -915,10 +914,10 @@ PlacesController.prototype = { }, /** - * Creates a set of transactions for the removal of a range of items. + * Creates a set of transactions for the removal of a range of items. * A range is an array of adjacent nodes in a view. * @param [in] range - * An array of nodes to remove. Should all be adjacent. + * An array of nodes to remove. Should all be adjacent. * @param [out] transactions * An array of transactions. * @param [optional] removedFolders @@ -1090,7 +1089,7 @@ PlacesController.prototype = { var root = this._view.result.root; - if (PlacesUtils.nodeIsFolder(root)) + if (PlacesUtils.nodeIsFolder(root)) this._removeRowsFromBookmarks(aTxnName); else if (PlacesUtils.nodeIsQuery(root)) { var queryType = PlacesUtils.asQuery(root).queryOptions.queryType; @@ -1136,7 +1135,7 @@ PlacesController.prototype = { addData(PlacesUtils.TYPE_HTML, index, overrideURI); } - // This order is _important_! It controls how this and other + // This order is _important_! It controls how this and other // applications select data to be inserted based on type. addData(PlacesUtils.TYPE_X_MOZ_PLACE, i); @@ -1166,8 +1165,8 @@ PlacesController.prototype = { try { let nodes = this._view.selectedNodes; - let xferable = Cc["@mozilla.org/widget/transferable;1"]. - createInstance(Ci.nsITransferable); + let xferable = Cc["@mozilla.org/widget/transferable;1"]. + createInstance(Ci.nsITransferable); let foundFolder = false, foundLink = false; let copiedFolders = []; let placeString, mozURLString, htmlString, unicodeString; @@ -1179,11 +1178,11 @@ PlacesController.prototype = { continue; if (PlacesUtils.nodeIsFolder(node)) copiedFolders.push(node); - + function generateChunk(type, overrideURI) { let suffix = i < (nodes.length - 1) ? PlacesUtils.endl : ""; let uri = overrideURI; - + if (PlacesUtils.nodeIsLivemarkContainer(node)) uri = PlacesUtils.livemarks.getFeedURI(node.itemId).spec @@ -1194,8 +1193,8 @@ PlacesController.prototype = { htmlString += (PlacesUtils.wrapNode(node, PlacesUtils.TYPE_HTML, uri) + suffix); - var placeSuffix = i < (nodes.length - 1) ? "," : ""; - var resolveShortcuts = !PlacesControllerDragHelper.canMoveNode(node); + let placeSuffix = i < (nodes.length - 1) ? "," : ""; + let resolveShortcuts = !PlacesControllerDragHelper.canMoveNode(node); return PlacesUtils.wrapNode(node, type, overrideURI, resolveShortcuts) + placeSuffix; } @@ -1207,7 +1206,7 @@ PlacesController.prototype = { xferable.addDataFlavor(type); xferable.setTransferData(type, PlacesUIUtils._wrapString(data), data.length * 2); } - // This order is _important_! It controls how this and other applications + // This order is _important_! It controls how this and other applications // select data to be inserted based on type. if (placeString) addData(PlacesUtils.TYPE_X_MOZ_PLACE, placeString); @@ -1241,11 +1240,11 @@ PlacesController.prototype = { */ paste: function PC_paste() { // Strategy: - // - // There can be data of various types (folder, separator, link) on the + // + // There can be data of various types (folder, separator, link) on the // clipboard. We need to get all of that data and build edit transactions - // for them. This means asking the clipboard once for each type and - // aggregating the results. + // for them. This means asking the clipboard once for each type and + // aggregating the results. /** * Constructs a transferable that can receive data of specific types. @@ -1255,10 +1254,9 @@ PlacesController.prototype = { * @returns The transferable. */ function makeXferable(types) { - var xferable = - Cc["@mozilla.org/widget/transferable;1"]. - createInstance(Ci.nsITransferable); - for (var i = 0; i < types.length; ++i) + var xferable = Cc["@mozilla.org/widget/transferable;1"]. + createInstance(Ci.nsITransferable); + for (var i = 0; i < types.length; ++i) xferable.addDataFlavor(types[i]); return xferable; } @@ -1291,10 +1289,10 @@ PlacesController.prototype = { if (ip.isTag) { var uri = PlacesUtils._uri(items[i].uri); txn = PlacesUIUtils.ptm.tagURI(uri, [ip.itemId]); - } + } else { // adjusted to make sure that items are given the correct index - // transactions insert differently if index == -1 + // transactions insert differently if index == -1 // transaction will enqueue the item. if (ip.index > -1) index = ip.index + i; @@ -1307,18 +1305,18 @@ PlacesController.prototype = { } catch (e) { // getAnyTransferData will throw if there is no data of the specified - // type on the clipboard. + // type on the clipboard. // unwrapNodes will throw if the data that is present is malformed in - // some way. + // some way. // In either case, don't fail horribly, just return no data. } return []; } // Get transactions to paste any folders, separators or links that might - // be on the clipboard, aggregate them and execute them. + // be on the clipboard, aggregate them and execute them. var transactions = getTransactions([PlacesUtils.TYPE_X_MOZ_PLACE, - PlacesUtils.TYPE_X_MOZ_URL, + PlacesUtils.TYPE_X_MOZ_URL, PlacesUtils.TYPE_UNICODE]); var txn = PlacesUIUtils.ptm.aggregateTransactions("Paste", transactions); PlacesUIUtils.ptm.doTransaction(txn); @@ -1336,8 +1334,8 @@ PlacesController.prototype = { /** * Handles drag and drop operations for views. Note that this is view agnostic! * You should not use PlacesController._view within these methods, since - * the view that the item(s) have been dropped on was not necessarily active. - * Drop functions are passed the view that is being dropped on. + * the view that the item(s) have been dropped on was not necessarily active. + * Drop functions are passed the view that is being dropped on. */ let PlacesControllerDragHelper = { /** @@ -1416,7 +1414,8 @@ let PlacesControllerDragHelper = { let dragged; try { dragged = PlacesUtils.unwrapNodes(data, flavor)[0]; - } catch (e) { + } + catch (e) { return false; } @@ -1442,10 +1441,10 @@ let PlacesControllerDragHelper = { return true; }, - + /** * Determines if a node can be moved. - * + * * @param aNode * A nsINavHistoryResultNode node. * @returns True if the node can be moved, false otherwise. @@ -1477,7 +1476,7 @@ let PlacesControllerDragHelper = { /** * Determines if a container node can be moved. - * + * * @param aId * A bookmark folder id. * @param [optional] aParentId diff --git a/browser/components/places/content/sidebarUtils.js b/browser/components/places/content/sidebarUtils.js index 13ac90b2801d..2a97cb75960d 100644 --- a/browser/components/places/content/sidebarUtils.js +++ b/browser/components/places/content/sidebarUtils.js @@ -132,6 +132,6 @@ var SidebarUtils = { }, clearURLFromStatusBar: function SU_clearURLFromStatusBar() { - window.top.XULBrowserWindow.setOverLink("", null); + window.top.XULBrowserWindow.setOverLink("", null); } }; diff --git a/browser/components/places/content/tree.xml b/browser/components/places/content/tree.xml index ad46c1e753cc..eed2cb61f66f 100644 --- a/browser/components/places/content/tree.xml +++ b/browser/components/places/content/tree.xml @@ -780,4 +780,3 @@ - diff --git a/browser/components/places/content/treeView.js b/browser/components/places/content/treeView.js index a4d97fbeffe3..970824a89762 100644 --- a/browser/components/places/content/treeView.js +++ b/browser/components/places/content/treeView.js @@ -37,11 +37,21 @@ * * ***** END LICENSE BLOCK ***** */ +function PlacesTreeView(aFlatList, aOnOpenFlatContainer) { + this._tree = null; + this._result = null; + this._selection = null; + this._rootNode = null; + this._rows = []; + this._flatList = aFlatList; + this._openContainerCallback = aOnOpenFlatContainer; +} + PlacesTreeView.prototype = { _makeAtom: function PTV__makeAtom(aString) { - return Cc["@mozilla.org/atom-service;1"]. - getService(Ci.nsIAtomService). - getAtom(aString); + return Cc["@mozilla.org/atom-service;1"]. + getService(Ci.nsIAtomService). + getAtom(aString); }, _atoms: [], @@ -122,8 +132,7 @@ PlacesTreeView.prototype = { if (!(aContainer instanceof Ci.nsINavHistoryQueryResultNode)) return aContainer._plainContainer = false; - let resultType = aContainer.queryOptions.resultType; - switch (resultType) { + switch (aContainer.queryOptions.resultType) { case Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_QUERY: case Ci.nsINavHistoryQueryOptions.RESULTS_AS_SITE_QUERY: case Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_SITE_QUERY: @@ -602,8 +611,7 @@ PlacesTreeView.prototype = { return; // Bail out for hidden separators. - if (PlacesUtils.nodeIsSeparator(aNode) && - this._result.sortingMode != Ci.nsINavHistoryQueryOptions.SORT_BY_NONE) + if (PlacesUtils.nodeIsSeparator(aNode) && this.isSorted()) return; let parentRow; @@ -632,7 +640,7 @@ PlacesTreeView.prototype = { // children themselves that would be in the way. let cc = aParentNode.childCount; let separatorsAreHidden = PlacesUtils.nodeIsSeparator(aNode) && - this._result.sortingMode != Ci.nsINavHistoryQueryOptions.SORT_BY_NONE; + this.isSorted(); for (let i = aNewIndex + 1; i < cc; i++) { let node = aParentNode.getChild(i); if (!separatorsAreHidden || PlacesUtils.nodeIsSeparator(node)) { @@ -679,8 +687,7 @@ PlacesTreeView.prototype = { throw Cr.NS_ERROR_NOT_IMPLEMENTED; // Bail out for hidden separators. - if (PlacesUtils.nodeIsSeparator(aNode) && - this._result.sortingMode != Ci.nsINavHistoryQueryOptions.SORT_BY_NONE) + if (PlacesUtils.nodeIsSeparator(aNode) && this.isSorted()) return; let parentRow = aParentNode == this._rootNode ? @@ -728,8 +735,7 @@ PlacesTreeView.prototype = { return; // Bail out for hidden separators. - if (PlacesUtils.nodeIsSeparator(aNode) && - this._result.sortingMode != Ci.nsINavHistoryQueryOptions.SORT_BY_NONE) + if (PlacesUtils.nodeIsSeparator(aNode) && this.isSorted()) return; let oldRow = this._getRowForNode(aNode, true); @@ -1204,7 +1210,7 @@ PlacesTreeView.prototype = { else { // Use the last-selected node's container. container = lastSelected.parent; - + // During its Drag & Drop operation, the tree code closes-and-opens // containers very often (part of the XUL "spring-loaded folders" // implementation). And in certain cases, we may reach a closed @@ -1309,8 +1315,7 @@ PlacesTreeView.prototype = { getCellText: function PTV_getCellText(aRow, aColumn) { let node = this._getNodeForRow(aRow); - let columnType = this._getColumnType(aColumn); - switch (columnType) { + switch (this._getColumnType(aColumn)) { case this.COLUMN_TYPE_TITLE: // normally, this is just the title, but we don't want empty items in // the tree view so return a special string if the title is empty. @@ -1428,8 +1433,7 @@ PlacesTreeView.prototype = { let newSort; let newSortingAnnotation = ""; const NHQO = Ci.nsINavHistoryQueryOptions; - let columnType = this._getColumnType(aColumn); - switch (columnType) { + switch (this._getColumnType(aColumn)) { case this.COLUMN_TYPE_TITLE: if (oldSort == NHQO.SORT_BY_TITLE_ASCENDING) newSort = NHQO.SORT_BY_TITLE_DESCENDING; @@ -1576,13 +1580,3 @@ PlacesTreeView.prototype = { performActionOnRow: function(aAction, aRow) { }, performActionOnCell: function(aAction, aRow, aColumn) { } }; - -function PlacesTreeView(aFlatList, aOnOpenFlatContainer) { - this._tree = null; - this._result = null; - this._selection = null; - this._rootNode = null; - this._rows = []; - this._flatList = aFlatList; - this._openContainerCallback = aOnOpenFlatContainer; -} diff --git a/browser/components/places/src/PlacesUIUtils.jsm b/browser/components/places/src/PlacesUIUtils.jsm index 6841f0bcfb26..5aa0db777125 100644 --- a/browser/components/places/src/PlacesUIUtils.jsm +++ b/browser/components/places/src/PlacesUIUtils.jsm @@ -72,7 +72,7 @@ var PlacesUIUtils = { * @returns A URI object for the spec. */ createFixedURI: function PUIU_createFixedURI(aSpec) { - return URIFixup.createFixupURI(aSpec, 0); + return URIFixup.createFixupURI(aSpec, Ci.nsIURIFixup.FIXUP_FLAG_NONE); }, /** @@ -128,7 +128,7 @@ var PlacesUIUtils = { */ _getBookmarkItemCopyTransaction: function PUIU__getBookmarkItemCopyTransaction(aData, aContainer, aIndex, - aExcludeAnnotations) { + aExcludeAnnotations) { var itemURL = PlacesUtils._uri(aData.uri); var itemTitle = aData.title; var keyword = aData.keyword || null; @@ -278,7 +278,7 @@ var PlacesUIUtils = { * the move/insert. */ makeTransaction: function PUIU_makeTransaction(data, type, container, - index, copy) { + index, copy) { switch (data.type) { case PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER: if (copy) @@ -289,7 +289,7 @@ var PlacesUIUtils = { case PlacesUtils.TYPE_X_MOZ_PLACE: if (data.id == -1) // Not bookmarked. return this._getURIItemCopyTransaction(data, container, index); - + if (copy) return this._getBookmarkItemCopyTransaction(data, container, index); // Otherwise move the item. @@ -361,14 +361,14 @@ var PlacesUIUtils = { * bookmarks root folder. */ showAddBookmarkUI: function PUIU_showAddBookmarkUI(aURI, - aTitle, - aDescription, - aDefaultInsertionPoint, - aShowPicker, - aLoadInSidebar, - aKeyword, - aPostData, - aCharSet) { + aTitle, + aDescription, + aDefaultInsertionPoint, + aShowPicker, + aLoadInSidebar, + aKeyword, + aPostData, + aCharSet) { var info = { action: "add", type: "bookmark" @@ -417,9 +417,9 @@ var PlacesUIUtils = { */ showMinimalAddBookmarkUI: function PUIU_showMinimalAddBookmarkUI(aURI, aTitle, aDescription, - aDefaultInsertionPoint, aShowPicker, - aLoadInSidebar, aKeyword, aPostData, - aCharSet) { + aDefaultInsertionPoint, aShowPicker, + aLoadInSidebar, aKeyword, aPostData, + aCharSet) { var info = { action: "add", type: "bookmark", @@ -485,11 +485,11 @@ var PlacesUIUtils = { * bookmarks root folder. */ showAddLivemarkUI: function PUIU_showAddLivemarkURI(aFeedURI, - aSiteURI, - aTitle, - aDescription, - aDefaultInsertionPoint, - aShowPicker) { + aSiteURI, + aTitle, + aDescription, + aDefaultInsertionPoint, + aShowPicker) { var info = { action: "add", type: "livemark" @@ -525,8 +525,8 @@ var PlacesUIUtils = { */ showMinimalAddLivemarkUI: function PUIU_showMinimalAddLivemarkURI(aFeedURI, aSiteURI, aTitle, - aDescription, aDefaultInsertionPoint, - aShowPicker) { + aDescription, aDefaultInsertionPoint, + aShowPicker) { var info = { action: "add", type: "livemark", @@ -706,7 +706,7 @@ var PlacesUIUtils = { /** * By calling this before visiting an URL, the visit will be associated to a * TRANSITION_BOOKMARK transition. - * This is used when visiting pages from the bookmarks menu, + * This is used when visiting pages from the bookmarks menu, * personal toolbar, and bookmarks from within the places organizer. * If this is not called visits will be marked as TRANSITION_LINK. */ @@ -730,6 +730,8 @@ var PlacesUIUtils = { * bookmarked (see bug 224521). * @param aURINode * a URI node + * @param aWindow + * a window on which a potential error alert is shown on. * @return true if it's safe to open the node in the browser, false otherwise. * */ @@ -890,7 +892,7 @@ var PlacesUIUtils = { openNodeWithEvent: function PUIU_openNodeWithEvent(aNode, aEvent) { this.openNodeIn(aNode, this._getCurrentActiveWin().whereToOpenLink(aEvent)); }, - + /** * Loads the node's URL in the appropriate tab or window or as a * web panel. @@ -927,12 +929,12 @@ var PlacesUIUtils = { * Used to avoid nsIURI overhead in frequently called UI functions. * * @param aUrlString the url to guess the scheme from. - * + * * @return guessed scheme for this url string. * * @note this is not supposed be perfect, so use it only for UI purposes. */ - guessUrlSchemeForUI: function PUU_guessUrlSchemeForUI(aUrlString) { + guessUrlSchemeForUI: function PUIU_guessUrlSchemeForUI(aUrlString) { return aUrlString.substr(0, aUrlString.indexOf(":")); }, @@ -961,7 +963,7 @@ var PlacesUIUtils = { return title || this.getString("noTitle"); }, - get leftPaneQueries() { + get leftPaneQueries() { // build the map this.leftPaneFolderId; return this.leftPaneQueries; @@ -1239,7 +1241,7 @@ var PlacesUIUtils = { queryName = name; } } - return queryName; + return queryName; } }; @@ -1380,7 +1382,7 @@ XPCOMUtils.defineLazyGetter(PlacesUIUtils, "ptm", function() { /** * Transaction for editing a the description of a bookmark or a folder. - * + * * @param aItemId * id of the item to edit. * @param aDescription diff --git a/browser/components/sessionstore/src/nsSessionStore.js b/browser/components/sessionstore/src/nsSessionStore.js index 4f73426fe4fe..27fa9aae0843 100644 --- a/browser/components/sessionstore/src/nsSessionStore.js +++ b/browser/components/sessionstore/src/nsSessionStore.js @@ -1212,6 +1212,7 @@ SessionStoreService.prototype = { if (aTab.pinned) tabData.pinned = true; + tabData.hidden = aTab.hidden; var disallow = []; for (var i = 0; i < CAPABILITIES.length; i++) @@ -1947,6 +1948,7 @@ SessionStoreService.prototype = { tabbrowser.pinTab(tabs[t]); else tabbrowser.unpinTab(tabs[t]); + tabs[t].hidden = winData.tabs[t].hidden; } // when overwriting tabs, remove all superflous ones @@ -2031,6 +2033,7 @@ SessionStoreService.prototype = { tabbrowser.pinTab(tab); else tabbrowser.unpinTab(tab); + tab.hidden = tabData.hidden; tabData._tabStillLoading = true; if (!tabData.entries || tabData.entries.length == 0) { diff --git a/browser/components/sessionstore/test/browser/Makefile.in b/browser/components/sessionstore/test/browser/Makefile.in index 366617c3a74c..4103b7d83efd 100644 --- a/browser/components/sessionstore/test/browser/Makefile.in +++ b/browser/components/sessionstore/test/browser/Makefile.in @@ -115,6 +115,7 @@ _BROWSER_TEST_FILES = \ browser_524745.js \ browser_528776.js \ browser_579879.js \ + browser_586147.js \ $(NULL) libs:: $(_BROWSER_TEST_FILES) diff --git a/browser/components/sessionstore/test/browser/browser_586147.js b/browser/components/sessionstore/test/browser/browser_586147.js new file mode 100644 index 000000000000..ea71bb4a982e --- /dev/null +++ b/browser/components/sessionstore/test/browser/browser_586147.js @@ -0,0 +1,84 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is sessionstore test code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Edward Lee + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +function observeOneRestore(callback) { + let topic = "sessionstore-browser-state-restored"; + Services.obs.addObserver(function() { + Services.obs.removeObserver(arguments.callee, topic, false); + callback(); + }, topic, false); +}; + +function test() { + let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore); + waitForExplicitFinish(); + + // There should be one tab when we start the test + let [origTab] = gBrowser.visibleTabs; + let hiddenTab = gBrowser.addTab(); + + is(gBrowser.visibleTabs.length, 2, "should have 2 tabs before hiding"); + gBrowser.showOnlyTheseTabs([origTab]); + is(gBrowser.visibleTabs.length, 1, "only 1 after hiding"); + ok(hiddenTab.hidden, "sanity check that it's hidden"); + + let extraTab = gBrowser.addTab(); + let state = ss.getBrowserState(); + let stateObj = JSON.parse(state); + let tabs = stateObj.windows[0].tabs; + is(tabs.length, 3, "just checking that browser state is correct"); + ok(!tabs[0].hidden, "first tab is visible"); + ok(tabs[1].hidden, "second is hidden"); + ok(!tabs[2].hidden, "third is visible"); + + // Make the third tab hidden and then restore the modified state object + tabs[2].hidden = true; + + observeOneRestore(function() { + let testWindow = Services.wm.getEnumerator("navigator:browser").getNext(); + is(testWindow.gBrowser.visibleTabs.length, 1, "only restored 1 visible tab"); + let tabs = testWindow.gBrowser.tabs; + ok(!tabs[0].hidden, "first is still visible"); + ok(tabs[1].hidden, "second tab is still hidden"); + ok(tabs[2].hidden, "third tab is now hidden"); + + // Restore the original state and clean up now that we're done + gBrowser.removeTab(hiddenTab); + gBrowser.removeTab(extraTab); + finish(); + }); + ss.setBrowserState(JSON.stringify(stateObj)); +} diff --git a/browser/installer/removed-files.in b/browser/installer/removed-files.in index 2873a1cadef8..793257b3c65f 100644 --- a/browser/installer/removed-files.in +++ b/browser/installer/removed-files.in @@ -11,9 +11,9 @@ @DLL_PREFIX@zlib@DLL_SUFFIX@ LICENSE browserconfig.properties -chrome.manifest chrome/US.jar chrome/app-chrome.manifest +chrome/browser.manifest chrome/chrome.rdf chrome/chromelist.txt chrome/classic.jar @@ -21,14 +21,18 @@ chrome/classic.manifest chrome/comm.jar chrome/comm.manifest chrome/en-win.jar +chrome/en-US.manifest chrome/help.jar chrome/installed-chrome.txt chrome/m3ffxtbr.jar chrome/m3ffxtbr.manifest chrome/overlayinfo/ +chrome/pippki.manifest chrome/reporter.jar chrome/reporter.manifest +chrome/toolkit.manifest component.reg +components/browser.manifest components/components.list components/@DLL_PREFIX@browserdirprovider@DLL_SUFFIX@ components/@DLL_PREFIX@brwsrdir@DLL_SUFFIX@ diff --git a/browser/themes/gnomestripe/browser/browser.css b/browser/themes/gnomestripe/browser/browser.css index dc5f13b68f55..8d6d5650b2ed 100644 --- a/browser/themes/gnomestripe/browser/browser.css +++ b/browser/themes/gnomestripe/browser/browser.css @@ -878,7 +878,7 @@ toolbar[iconsize="small"] #fullscreen-button { -moz-border-start: 1px solid ThreeDShadow; } -#identity-box:focus { +#identity-box:-moz-focusring { outline: 1px dotted -moz-DialogText; } diff --git a/browser/themes/pinstripe/browser/browser.css b/browser/themes/pinstripe/browser/browser.css index 65826320a769..4cb6e09b9f44 100644 --- a/browser/themes/pinstripe/browser/browser.css +++ b/browser/themes/pinstripe/browser/browser.css @@ -699,7 +699,7 @@ toolbar[iconsize="small"][mode="icons"] #forward-button:-moz-locale-dir(rtl) { } -#identity-box:focus:not(:active):not([open="true"]) { +#identity-box:-moz-focusring { -moz-box-shadow: 0 0 3px 1px -moz-mac-focusring inset, 0 0 3px 2px -moz-mac-focusring; } diff --git a/browser/themes/winstripe/browser/browser.css b/browser/themes/winstripe/browser/browser.css index e7c2bef61674..aa38b376b469 100644 --- a/browser/themes/winstripe/browser/browser.css +++ b/browser/themes/winstripe/browser/browser.css @@ -60,11 +60,8 @@ #navigator-toolbox { -moz-appearance: none; - border-top: none; background-color: transparent; -} - -#navigator-toolbox[tabsontop="true"] { + border-top: none; border-bottom: 1px solid ThreeDShadow; } @@ -81,7 +78,11 @@ } #nav-bar:not(:-moz-lwtheme), -#navigator-toolbox[tabsontop="true"] > #nav-bar { +#nav-bar[collapsed="true"] + toolbar:not(:-moz-lwtheme), +#nav-bar[collapsed="true"] + #customToolbars + #PersonalToolbar:not(:-moz-lwtheme), +#navigator-toolbox[tabsontop="true"] > #nav-bar, +#navigator-toolbox[tabsontop="true"]:not([customizing]) > #nav-bar[collapsed="true"] + toolbar, +#navigator-toolbox[tabsontop="true"]:not([customizing]) > #nav-bar[collapsed="true"] + #customToolbars + #PersonalToolbar { background-image: -moz-linear-gradient(@toolbarHighlight@, rgba(255,255,255,0)); } @@ -844,13 +845,13 @@ toolbar:not([iconsize="small"])[mode="icons"] #forward-button:not([disabled="tru background-color: hsl(92,45%,52%); } -#identity-box:focus { +#identity-box:-moz-focusring { outline: 1px dotted -moz-DialogText; outline-offset: -3px; } -#identity-box.verifiedDomain:focus , -#identity-box.verifiedIdentity:focus { +#identity-box.verifiedDomain:-moz-focusring, +#identity-box.verifiedIdentity:-moz-focusring { outline-color: white; } @@ -1115,10 +1116,6 @@ richlistitem[type="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-i -moz-box-shadow: 0 -1px ThreeDShadow inset; } -#TabsToolbar[tabsontop="false"] { - border-bottom: 1px solid ThreeDShadow !important; -} - .tabbrowser-tabs:-moz-system-metric(touch-enabled) { min-height: .73cm; } diff --git a/caps/tests/mochitest/Makefile.in b/caps/tests/mochitest/Makefile.in index 15c38f825410..4be86f8b1328 100644 --- a/caps/tests/mochitest/Makefile.in +++ b/caps/tests/mochitest/Makefile.in @@ -51,5 +51,9 @@ _TEST_FILES = test_bug423375.html \ test_bug470804.html \ $(NULL) +test_bug292789.html : % : %.in + $(PYTHON) $(topsrcdir)/config/Preprocessor.py \ + $(AUTOMATION_PPARGS) $(DEFINES) $(ACDEFINES) $< > $@ + libs:: $(_TEST_FILES) $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir) diff --git a/caps/tests/mochitest/test_bug292789.html b/caps/tests/mochitest/test_bug292789.html.in similarity index 91% rename from caps/tests/mochitest/test_bug292789.html rename to caps/tests/mochitest/test_bug292789.html.in index 2cb1619a6b3a..a80f630f2251 100644 --- a/caps/tests/mochitest/test_bug292789.html +++ b/caps/tests/mochitest/test_bug292789.html.in @@ -38,7 +38,11 @@ function testScriptSrc(aCallback) { ** moved the resource **/ var resjs = document.createElement("script"); +#ifdef MOZ_CHROME_FILE_FORMAT_JAR resjs.src = "jar:resource://gre/chrome/toolkit.jar!/content/mozapps/xpinstall/xpinstallConfirm.js"; +#else + resjs.src = "resource://gre/chrome/toolkit/content/mozapps/xpinstall/xpinstallConfirm.js"; +#endif resjs.onload = scriptOnload; document.getElementById("content").appendChild(resjs); @@ -55,7 +59,11 @@ function testScriptSrc(aCallback) { /** tests **/ var img_global = "chrome://global/skin/icons/Error.png"; var img_mozapps = "chrome://mozapps/skin/passwordmgr/key.png"; +#ifdef MOZ_CHROME_FILE_FORMAT_JAR var res_mozapps = "jar:resource://gre/chrome/toolkit.jar!/skin/classic/mozapps/passwordmgr/key.png"; +#else +var res_mozapps = "resource://gre/chrome/toolkit/skin/classic/mozapps/passwordmgr/key.png"; +#endif var imgTests = [[img_global, "success"], [img_mozapps, "fail"], diff --git a/config/system-headers b/config/system-headers index b9b29b8811ac..de25c54cfe80 100644 --- a/config/system-headers +++ b/config/system-headers @@ -1036,4 +1036,7 @@ event.h #ifdef MOZ_ENABLE_LIBPROXY proxy.h #endif +#if MOZ_PLATFORM_MAEMO==6 +contentaction/contentaction.h +#endif diff --git a/content/base/public/nsIDocument.h b/content/base/public/nsIDocument.h index c68cc10e12ca..e02db7b53010 100644 --- a/content/base/public/nsIDocument.h +++ b/content/base/public/nsIDocument.h @@ -437,7 +437,7 @@ public: nsIViewManager* aViewManager, nsStyleSet* aStyleSet, nsIPresShell** aInstancePtrResult) = 0; - void DeleteShell() { mPresShell = nsnull; } + virtual void DeleteShell() = 0; nsIPresShell* GetShell() const { diff --git a/content/base/src/nsAttrValue.cpp b/content/base/src/nsAttrValue.cpp index 703b6220f4ab..239fd0d9eb4e 100644 --- a/content/base/src/nsAttrValue.cpp +++ b/content/base/src/nsAttrValue.cpp @@ -249,7 +249,7 @@ nsAttrValue::SetTo(const nsAttrValue& aOther) case eAtomArray: { if (!EnsureEmptyAtomArray() || - !GetAtomArrayValue()->AppendObjects(*otherCont->mAtomArray)) { + !GetAtomArrayValue()->AppendElements(*otherCont->mAtomArray)) { Reset(); return; } @@ -503,7 +503,7 @@ nsAttrValue::GetEnumString(nsAString& aResult, PRBool aRealTag) const NS_NOTREACHED("couldn't find value in EnumTable"); } -PRInt32 +PRUint32 nsAttrValue::GetAtomCount() const { ValueType type = Type(); @@ -513,7 +513,7 @@ nsAttrValue::GetAtomCount() const } if (type == eAtomArray) { - return GetAtomArrayValue()->Count(); + return GetAtomArrayValue()->Length(); } return 0; @@ -531,7 +531,7 @@ nsAttrValue::AtomAt(PRInt32 aIndex) const NS_ASSERTION(Type() == eAtomArray, "GetAtomCount must be confused"); - return GetAtomArrayValue()->ObjectAt(aIndex); + return GetAtomArrayValue()->ElementAt(aIndex); } PRUint32 @@ -592,9 +592,11 @@ nsAttrValue::HashValue() const case eAtomArray: { PRUint32 retval = 0; - PRInt32 i, count = cont->mAtomArray->Count(); - for (i = 0; i < count; ++i) { - retval ^= NS_PTR_TO_INT32(cont->mAtomArray->ObjectAt(i)); + PRUint32 count = cont->mAtomArray->Length(); + for (nsCOMPtr *cur = cont->mAtomArray->Elements(), + *end = cur + count; + cur != end; ++cur) { + retval ^= NS_PTR_TO_INT32(cur->get()); } return retval; } @@ -690,18 +692,10 @@ nsAttrValue::Equals(const nsAttrValue& aOther) const // For classlists we could be insensitive to order, however // classlists are never mapped attributes so they are never compared. - PRInt32 count = thisCont->mAtomArray->Count(); - if (count != otherCont->mAtomArray->Count()) { + if (!(*thisCont->mAtomArray == *otherCont->mAtomArray)) { return PR_FALSE; } - PRInt32 i; - for (i = 0; i < count; ++i) { - if (thisCont->mAtomArray->ObjectAt(i) != - otherCont->mAtomArray->ObjectAt(i)) { - return PR_FALSE; - } - } needsStringComparison = PR_TRUE; break; } @@ -827,19 +821,21 @@ nsAttrValue::Contains(nsIAtom* aValue, nsCaseTreatment aCaseSensitive) const default: { if (Type() == eAtomArray) { - nsCOMArray* array = GetAtomArrayValue(); + AtomArray* array = GetAtomArrayValue(); if (aCaseSensitive == eCaseMatters) { - return array->IndexOf(aValue) >= 0; + return array->IndexOf(aValue) != AtomArray::NoIndex; } nsDependentAtomString val1(aValue); - for (PRInt32 i = 0, count = array->Count(); i < count; ++i) { + for (nsCOMPtr *cur = array->Elements(), + *end = cur + array->Length(); + cur != end; ++cur) { // For performance reasons, don't do a full on unicode case // insensitive string comparison. This is only used for quirks mode // anyway. if (nsContentUtils::EqualsIgnoreASCIICase(val1, - nsDependentAtomString(array->ObjectAt(i)))) { + nsDependentAtomString(*cur))) { return PR_TRUE; } } @@ -913,9 +909,9 @@ nsAttrValue::ParseAtomArray(const nsAString& aValue) return; } - nsCOMArray* array = GetAtomArrayValue(); + AtomArray* array = GetAtomArrayValue(); - if (!array->AppendObject(classAtom)) { + if (!array->AppendElement(classAtom)) { Reset(); return; } @@ -930,7 +926,7 @@ nsAttrValue::ParseAtomArray(const nsAString& aValue) classAtom = do_GetAtom(Substring(start, iter)); - if (!array->AppendObject(classAtom)) { + if (!array->AppendElement(classAtom)) { Reset(); return; } @@ -1362,7 +1358,7 @@ nsAttrValue::EnsureEmptyAtomArray() return PR_FALSE; } - nsCOMArray* array = new nsCOMArray; + AtomArray* array = new AtomArray; if (!array) { Reset(); return PR_FALSE; diff --git a/content/base/src/nsAttrValue.h b/content/base/src/nsAttrValue.h index 407c348b114d..c8a2107aa1b3 100644 --- a/content/base/src/nsAttrValue.h +++ b/content/base/src/nsAttrValue.h @@ -50,6 +50,7 @@ #include "nsColor.h" #include "nsCaseTreatment.h" #include "nsMargin.h" +#include "nsCOMPtr.h" typedef PRUptrdiff PtrBits; class nsAString; @@ -57,7 +58,7 @@ class nsIAtom; class nsICSSStyleRule; class nsISVGValue; class nsIDocument; -template class nsCOMArray; +template class nsTArray; template class nsTPtrArray; #define NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM 12 @@ -92,6 +93,8 @@ public: class nsAttrValue { public: + typedef nsTArray< nsCOMPtr > AtomArray; + nsAttrValue(); nsAttrValue(const nsAttrValue& aOther); explicit nsAttrValue(const nsAString& aValue); @@ -151,7 +154,7 @@ public: PRBool GetColorValue(nscolor& aColor) const; inline PRInt16 GetEnumValue() const; inline float GetPercentValue() const; - inline nsCOMArray* GetAtomArrayValue() const; + inline AtomArray* GetAtomArrayValue() const; inline nsICSSStyleRule* GetCSSStyleRuleValue() const; #ifdef MOZ_SVG inline nsISVGValue* GetSVGValue() const; @@ -170,7 +173,7 @@ public: // Methods to get access to atoms we may have // Returns the number of atoms we have; 0 if we have none. It's OK // to call this without checking the type first; it handles that. - PRInt32 GetAtomCount() const; + PRUint32 GetAtomCount() const; // Returns the atom at aIndex (0-based). Do not call this with // aIndex >= GetAtomCount(). nsIAtom* AtomAt(PRInt32 aIndex) const; @@ -333,7 +336,7 @@ private: PRUint32 mEnumValue; PRInt32 mPercent; nsICSSStyleRule* mCSSStyleRule; - nsCOMArray* mAtomArray; + AtomArray* mAtomArray; #ifdef MOZ_SVG nsISVGValue* mSVGValue; #endif @@ -426,7 +429,7 @@ nsAttrValue::GetPercentValue() const / 100.0f; } -inline nsCOMArray* +inline nsAttrValue::AtomArray* nsAttrValue::GetAtomArrayValue() const { NS_PRECONDITION(Type() == eAtomArray, "wrong type"); diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index 5892f477a04f..809c7eeb88f4 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -6004,7 +6004,7 @@ nsContentUtils::ReparentClonedObjectToScope(JSContext* cx, } struct ClassMatchingInfo { - nsCOMArray mClasses; + nsAttrValue::AtomArray mClasses; nsCaseTreatment mCaseTreatment; }; @@ -6020,14 +6020,14 @@ MatchClassNames(nsIContent* aContent, PRInt32 aNamespaceID, nsIAtom* aAtom, // need to match *all* of the classes ClassMatchingInfo* info = static_cast(aData); - PRInt32 length = info->mClasses.Count(); + PRUint32 length = info->mClasses.Length(); if (!length) { // If we actually had no classes, don't match. return PR_FALSE; } - PRInt32 i; + PRUint32 i; for (i = 0; i < length; ++i) { - if (!classAttr->Contains(info->mClasses.ObjectAt(i), + if (!classAttr->Contains(info->mClasses[i], info->mCaseTreatment)) { return PR_FALSE; } @@ -6054,9 +6054,9 @@ AllocClassMatchingInfo(nsINode* aRootNode, NS_ENSURE_TRUE(info, nsnull); if (attrValue.Type() == nsAttrValue::eAtomArray) { - info->mClasses.AppendObjects(*(attrValue.GetAtomArrayValue())); + info->mClasses.SwapElements(*(attrValue.GetAtomArrayValue())); } else if (attrValue.Type() == nsAttrValue::eAtom) { - info->mClasses.AppendObject(attrValue.GetAtomValue()); + info->mClasses.AppendElement(attrValue.GetAtomValue()); } info->mCaseTreatment = diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index c2869c5860d2..6d9736a2ea74 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -783,6 +783,36 @@ nsExternalResourceMap::Traverse(nsCycleCollectionTraversalCallback* aCallback) c mMap.EnumerateRead(ExternalResourceTraverser, aCallback); } +static PLDHashOperator +ExternalResourceHider(nsIURI* aKey, + nsExternalResourceMap::ExternalResource* aData, + void* aClosure) +{ + aData->mViewer->Hide(); + return PL_DHASH_NEXT; +} + +void +nsExternalResourceMap::HideViewers() +{ + mMap.EnumerateRead(ExternalResourceHider, nsnull); +} + +static PLDHashOperator +ExternalResourceShower(nsIURI* aKey, + nsExternalResourceMap::ExternalResource* aData, + void* aClosure) +{ + aData->mViewer->Show(); + return PL_DHASH_NEXT; +} + +void +nsExternalResourceMap::ShowViewers() +{ + mMap.EnumerateRead(ExternalResourceShower, nsnull); +} + nsresult nsExternalResourceMap::AddExternalResource(nsIURI* aURI, nsIDocumentViewer* aViewer, @@ -811,6 +841,9 @@ nsExternalResourceMap::AddExternalResource(nsIURI* aURI, } else { doc->SetDisplayDocument(aDisplayDocument); + // Make sure that hiding our viewer will tear down its presentation. + aViewer->SetSticky(PR_FALSE); + rv = aViewer->Init(nsnull, nsIntRect(0, 0, 0, 0)); if (NS_SUCCEEDED(rv)) { rv = aViewer->Open(nsnull, nsnull); @@ -3039,11 +3072,20 @@ nsDocument::doCreateShell(nsPresContext* aContext, // Note: we don't hold a ref to the shell (it holds a ref to us) mPresShell = shell; + mExternalResourceMap.ShowViewers(); + shell.swap(*aInstancePtrResult); return NS_OK; } +void +nsDocument::DeleteShell() +{ + mExternalResourceMap.HideViewers(); + mPresShell = nsnull; +} + static void SubDocClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry) { @@ -7815,4 +7857,3 @@ nsIDocument::CreateStaticClone(nsISupports* aCloneContainer) mCreatingStaticClone = PR_FALSE; return clonedDoc.forget(); } - diff --git a/content/base/src/nsDocument.h b/content/base/src/nsDocument.h index 3510efbb08be..34501bb019eb 100644 --- a/content/base/src/nsDocument.h +++ b/content/base/src/nsDocument.h @@ -375,6 +375,12 @@ public: nsCOMPtr mLoadGroup; }; + // Hide all our viewers + void HideViewers(); + + // Show all our viewers + void ShowViewers(); + protected: class PendingLoad : public ExternalResourceLoad, public nsIStreamListener @@ -588,6 +594,7 @@ public: nsIViewManager* aViewManager, nsStyleSet* aStyleSet, nsIPresShell** aInstancePtrResult); + virtual void DeleteShell(); virtual nsresult SetSubDocumentFor(nsIContent *aContent, nsIDocument* aSubDoc); diff --git a/content/html/content/src/nsHTMLDNSPrefetch.cpp b/content/html/content/src/nsHTMLDNSPrefetch.cpp index dc7d98eb7c2c..914efe3b6090 100644 --- a/content/html/content/src/nsHTMLDNSPrefetch.cpp +++ b/content/html/content/src/nsHTMLDNSPrefetch.cpp @@ -106,6 +106,11 @@ nsHTMLDNSPrefetch::Initialize() rv = CallGetService(kDNSServiceCID, &sDNSService); if (NS_FAILED(rv)) return rv; +#ifdef MOZ_IPC + if (mozilla::net::IsNeckoChild()) + mozilla::net::NeckoChild::InitNeckoChild(); +#endif + sInitialized = PR_TRUE; return NS_OK; } diff --git a/content/html/document/src/nsHTMLFragmentContentSink.cpp b/content/html/document/src/nsHTMLFragmentContentSink.cpp index 823ca70b9cd7..918d33ec4f2e 100644 --- a/content/html/document/src/nsHTMLFragmentContentSink.cpp +++ b/content/html/document/src/nsHTMLFragmentContentSink.cpp @@ -1100,6 +1100,10 @@ nsHTMLParanoidFragmentSink::CloseContainer(const nsHTMLTag aTag) { nsresult rv = NS_OK; + if (mIgnoreNextCloseHead && aTag == eHTMLTag_head) { + mIgnoreNextCloseHead = PR_FALSE; + return NS_OK; + } if (mSkip) { mSkip = PR_FALSE; return rv; @@ -1229,7 +1233,10 @@ nsHTMLParanoidFragmentSink::AddLeaf(const nsIParserNode& aNode) nsresult rv = NS_OK; - if (mSkip) { + // We need to explicitly skip adding leaf nodes in the paranoid sink, + // otherwise things like the textnode under get appended to + // the fragment itself, and won't be popped off in CloseContainer. + if (mSkip || mIgnoreNextCloseHead) { return rv; } diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 0bdd603408b4..0d898f88bc83 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -53,6 +53,8 @@ #include "nsServiceManagerUtils.h" #include "nsThreadUtils.h" #include "nsChromeRegistryChrome.h" +#include "nsExternalHelperAppService.h" +#include "nsCExternalHandlerService.h" #ifdef ANDROID #include "AndroidBridge.h" @@ -470,6 +472,17 @@ ContentParent::RecvSetURITitle(const IPC::URI& uri, return true; } +bool +ContentParent::RecvLoadURIExternal(const IPC::URI& uri) +{ + nsCOMPtr<nsIExternalProtocolService> extProtService(do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID)); + if (!extProtService) + return true; + nsCOMPtr<nsIURI> ourURI(uri); + extProtService->LoadURI(ourURI, nsnull); + return true; +} + /* void onDispatchedEvent (in nsIThreadInternal thread); */ NS_IMETHODIMP ContentParent::OnDispatchedEvent(nsIThreadInternal *thread) diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index a183d87e40e7..d6daab20264a 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -162,6 +162,8 @@ private: ; + virtual bool RecvLoadURIExternal(const IPC::URI& uri); + mozilla::Monitor mMonitor; GeckoChildProcessHost* mSubprocess; diff --git a/dom/ipc/Makefile.in b/dom/ipc/Makefile.in index 3dc4680c4f0f..38d62de76484 100644 --- a/dom/ipc/Makefile.in +++ b/dom/ipc/Makefile.in @@ -82,6 +82,7 @@ LOCAL_INCLUDES += \ -I$(srcdir)/../../toolkit/components/places/src \ -I$(srcdir)/../src/geolocation \ -I$(topsrcdir)/chrome/src \ + -I$(topsrcdir)/uriloader/exthandler \ $(NULL) CXXFLAGS += $(TK_CFLAGS) diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index e38ff064878e..8e878aad3914 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -81,6 +81,8 @@ parent: async VisitURI(URI uri, URI referrer, PRUint32 flags); async SetURITitle(URI uri, nsString title); + async LoadURIExternal(URI uri); + // PrefService messages sync GetPrefType(nsCString prefName) returns (PRInt32 retValue, nsresult rv); sync GetBoolPref(nsCString prefName) returns (PRBool retValue, nsresult rv); diff --git a/editor/libeditor/html/tests/test_bug520189.html b/editor/libeditor/html/tests/test_bug520189.html index 8bf41534ba09..d62a9db220f1 100644 --- a/editor/libeditor/html/tests/test_bug520189.html +++ b/editor/libeditor/html/tests/test_bug520189.html @@ -52,6 +52,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=520182 <div id="jj" contenteditable="true"></div> <iframe id="kk" src="about:blank"></iframe> <div id="ll" contenteditable="true"></div> + <iframe id="mm" src="about:blank"></iframe> + <div id="nn" contenteditable="true"></div> </div> <pre id="test"> <script type="application/javascript"> @@ -73,6 +75,7 @@ const invalidStyle3Payload = "foo<style>@import 'xxx.css';</style>baz"; const invalidStyle4Payload = "foo<span style=\"@import 'xxx.css';\">bar</span>baz"; const invalidStyle5Payload = "foo<span style=\"@font-face{font-family:xxx;src:'xxx.ttf';}\">bar</span>baz"; const invalidStyle6Payload = "foo<span style=\"@namespace xxx url(http://example.com/);\">bar</span>baz"; +const invalidStyle7Payload = "<html><head><title>xxxfoo"; const nestedStylePayload = "foo#bar2{-moz-binding:url('data:text/xml,');baz"; const validImgSrc1Payload = "foobaz"; const validImgSrc2Payload = "foobaz"; @@ -333,6 +336,27 @@ var tests = [ payload: invalidStyle6Payload, rootElement: function() document.getElementById("ll"), checkResult: function(html) isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image") + }, + { + id: "mm", + isIFrame: true, + insertHTML: true, + payload: invalidStyle7Payload, + rootElement: function() document.getElementById("mm").contentDocument.documentElement, + checkResult: function(html) { + is(html.indexOf("xxx"), -1, "Should not have retained the title text"); + isnot(html.indexOf("foo"), -1, "Should have retained the body text"); + } + }, + { + id: "nn", + insertHTML: true, + payload: invalidStyle7Payload, + rootElement: function() document.getElementById("nn"), + checkResult: function(html) { + is(html.indexOf("xxx"), -1, "Should not have retained the title text"); + isnot(html.indexOf("foo"), -1, "Should have retained the body text"); + } } ]; @@ -357,28 +381,37 @@ function runTest(test) { } else elem.focus(); - netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + if ("insertHTML" in test) { + if ("isIFrame" in test) { + elem.contentDocument.execCommand("inserthtml", false, test.payload); + } else { + getSelection().collapse(elem, 0); + document.execCommand("inserthtml", false, test.payload); + } + } else { + netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); - var clipboard = Components.classes["@mozilla.org/widget/clipboard;1"] - .getService(Components.interfaces.nsIClipboard); + var clipboard = Components.classes["@mozilla.org/widget/clipboard;1"] + .getService(Components.interfaces.nsIClipboard); - var trans = Components.classes["@mozilla.org/widget/transferable;1"] - .createInstance(Components.interfaces.nsITransferable); - var data = Components.classes["@mozilla.org/supports-string;1"] - .createInstance(Components.interfaces.nsISupportsString); - data.data = test.payload; - trans.addDataFlavor("text/html"); - trans.setTransferData("text/html", data, data.data.length * 2); - clipboard.setData(trans, null, Components.interfaces.nsIClipboard.kGlobalClipboard); + var trans = Components.classes["@mozilla.org/widget/transferable;1"] + .createInstance(Components.interfaces.nsITransferable); + var data = Components.classes["@mozilla.org/supports-string;1"] + .createInstance(Components.interfaces.nsISupportsString); + data.data = test.payload; + trans.addDataFlavor("text/html"); + trans.setTransferData("text/html", data, data.data.length * 2); + clipboard.setData(trans, null, Components.interfaces.nsIClipboard.kGlobalClipboard); - var mainWindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) - .getInterface(Components.interfaces.nsIWebNavigation) - .QueryInterface(Components.interfaces.nsIDocShellTreeItem) - .rootTreeItem - .QueryInterface(Components.interfaces.nsIInterfaceRequestor) - .getInterface(Components.interfaces.nsIDOMWindow); + var mainWindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsIWebNavigation) + .QueryInterface(Components.interfaces.nsIDocShellTreeItem) + .rootTreeItem + .QueryInterface(Components.interfaces.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsIDOMWindow); - mainWindow.goDoCommand("cmd_paste"); + mainWindow.goDoCommand("cmd_paste"); + } if ("checkResult" in test) { if ("isIFrame" in test) { diff --git a/embedding/android/AndroidManifest.xml.in b/embedding/android/AndroidManifest.xml.in index 976a922280c8..b6b13161de18 100644 --- a/embedding/android/AndroidManifest.xml.in +++ b/embedding/android/AndroidManifest.xml.in @@ -14,7 +14,8 @@ + android:icon="@drawable/icon" + android:debuggable="true"> SetAllowDNSPrefetch(!!aValue); } + break; case nsIWebBrowserSetup::SETUP_USE_GLOBAL_HISTORY: { NS_ENSURE_STATE(mDocShell); @@ -1230,10 +1232,15 @@ NS_IMETHODIMP nsWebBrowser::Create() NS_ENSURE_SUCCESS(rv, rv); } mDocShellAsNav->SetSessionHistory(mInitInfo->sessionHistory); - - // Hook up global history. Do not fail if we can't - just warn. - rv = EnableGlobalHistory(mShouldEnableHistory); - NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "EnableGlobalHistory() failed"); + +#ifdef MOZ_IPC + if (XRE_GetProcessType() == GeckoProcessType_Default) +#endif + { + // Hook up global history. Do not fail if we can't - just warn. + rv = EnableGlobalHistory(mShouldEnableHistory); + NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "EnableGlobalHistory() failed"); + } NS_ENSURE_SUCCESS(mDocShellAsWin->Create(), NS_ERROR_FAILURE); diff --git a/gfx/cairo/cairo/src/cairo-d2d-private-fx.h b/gfx/cairo/cairo/src/cairo-d2d-private-fx.h new file mode 100644 index 000000000000..15945339ff3a --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-d2d-private-fx.h @@ -0,0 +1,546 @@ +#if 0 +// +// FX Version: fx_4_0 +// Child effect (requires effect pool): false +// +// 1 local buffer(s) +// +cbuffer cb0 +{ + float4 QuadDesc; // Offset: 0, size: 16 + float4 TexCoords; // Offset: 16, size: 16 +} + +// +// 2 local object(s) +// +Texture2D tex; +SamplerState sSampler +{ + Texture = tex; + AddressU = uint(CLAMP /* 3 */); + AddressV = uint(CLAMP /* 3 */); +}; + +// +// 1 technique(s) +// +technique10 SampleTexture +{ + pass P0 + { + VertexShader = asm { + // + // Generated by Microsoft (R) HLSL Shader Compiler 9.27.952.3022 + // + // + // Buffer Definitions: + // + // cbuffer cb0 + // { + // + // float4 QuadDesc; // Offset: 0 Size: 16 + // float4 TexCoords; // Offset: 16 Size: 16 + // + // } + // + // + // Resource Bindings: + // + // Name Type Format Dim Slot Elements + // ------------------------------ ---------- ------- ----------- ---- -------- + // cb0 cbuffer NA NA 0 1 + // + // + // + // Input signature: + // + // Name Index Mask Register SysValue Format Used + // -------------------- ----- ------ -------- -------- ------ ------ + // POSITION 0 xyz 0 NONE float xy + // + // + // Output signature: + // + // Name Index Mask Register SysValue Format Used + // -------------------- ----- ------ -------- -------- ------ ------ + // SV_Position 0 xyzw 0 POS float xyzw + // TEXCOORD 0 xy 1 NONE float xy + // + // + // Constant buffer to DX9 shader constant mappings: + // + // Target Reg Buffer Start Reg # of Regs Data Conversion + // ---------- ------- --------- --------- ---------------------- + // c1 cb0 0 2 ( FLT, FLT, FLT, FLT) + // + // + // Runtime generated constant mappings: + // + // Target Reg Constant Description + // ---------- -------------------------------------------------- + // c0 Vertex Shader position offset + // + // + // Level9 shader bytecode: + // + vs_2_x + def c3, 0, 1, 0, 0 + dcl_texcoord v0 + mad oT0.xy, v0, c2.zwzw, c2 + mad r0.x, v0.x, c1.z, c1.x + mad r0.y, v0.y, c1.w, c1.y + add oPos.xy, r0, c0 + mov oPos.zw, c3.xyxy + + // approximately 5 instruction slots used + vs_4_0 + dcl_constantbuffer cb0[2], immediateIndexed + dcl_input v0.xy + dcl_output_siv o0.xyzw, position + dcl_output o1.xy + mad o0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx + mov o0.zw, l(0,0,0,1.000000) + mad o1.xy, v0.xyxx, cb0[1].zwzz, cb0[1].xyxx + ret + // Approximately 4 instruction slots used + + }; + GeometryShader = NULL; + PixelShader = asm { + // + // Generated by Microsoft (R) HLSL Shader Compiler 9.27.952.3022 + // + // + // Resource Bindings: + // + // Name Type Format Dim Slot Elements + // ------------------------------ ---------- ------- ----------- ---- -------- + // sSampler sampler NA NA 0 1 + // tex texture float4 2d 0 1 + // + // + // + // Input signature: + // + // Name Index Mask Register SysValue Format Used + // -------------------- ----- ------ -------- -------- ------ ------ + // SV_Position 0 xyzw 0 POS float + // TEXCOORD 0 xy 1 NONE float xy + // + // + // Output signature: + // + // Name Index Mask Register SysValue Format Used + // -------------------- ----- ------ -------- -------- ------ ------ + // SV_Target 0 xyzw 0 TARGET float xyzw + // + // + // Sampler/Resource to DX9 shader sampler mappings: + // + // Target Sampler Source Sampler Source Resource + // -------------- --------------- ---------------- + // s0 s0 t0 + // + // + // Level9 shader bytecode: + // + ps_2_x + dcl t0.xy + dcl_2d s0 + texld r0, t0, s0 + mov oC0, r0 + + // approximately 2 instruction slots used (1 texture, 1 arithmetic) + ps_4_0 + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + sample o0.xyzw, v1.xyxx, t0.xyzw, s0 + ret + // Approximately 2 instruction slots used + + }; + } + +} + +#endif + +const BYTE g_main[] = +{ + 68, 88, 66, 67, 235, 24, + 238, 6, 37, 230, 191, 228, + 58, 61, 41, 219, 70, 130, + 61, 51, 1, 0, 0, 0, + 187, 8, 0, 0, 1, 0, + 0, 0, 36, 0, 0, 0, + 70, 88, 49, 48, 143, 8, + 0, 0, 1, 16, 255, 254, + 1, 0, 0, 0, 2, 0, + 0, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 79, 7, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 0, + 0, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 99, 98, + 48, 0, 102, 108, 111, 97, + 116, 52, 0, 8, 0, 0, + 0, 1, 0, 0, 0, 0, + 0, 0, 0, 16, 0, 0, + 0, 16, 0, 0, 0, 16, + 0, 0, 0, 10, 33, 0, + 0, 81, 117, 97, 100, 68, + 101, 115, 99, 0, 84, 101, + 120, 67, 111, 111, 114, 100, + 115, 0, 84, 101, 120, 116, + 117, 114, 101, 50, 68, 0, + 62, 0, 0, 0, 2, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 12, 0, 0, 0, 116, 101, + 120, 0, 83, 97, 109, 112, + 108, 101, 114, 83, 116, 97, + 116, 101, 0, 104, 0, 0, + 0, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 21, 0, 0, + 0, 115, 83, 97, 109, 112, + 108, 101, 114, 0, 1, 0, + 0, 0, 2, 0, 0, 0, + 3, 0, 0, 0, 1, 0, + 0, 0, 2, 0, 0, 0, + 3, 0, 0, 0, 83, 97, + 109, 112, 108, 101, 84, 101, + 120, 116, 117, 114, 101, 0, + 80, 48, 0, 172, 3, 0, + 0, 68, 88, 66, 67, 247, + 105, 31, 113, 120, 95, 58, + 12, 207, 141, 45, 76, 175, + 59, 223, 25, 1, 0, 0, + 0, 172, 3, 0, 0, 6, + 0, 0, 0, 56, 0, 0, + 0, 248, 0, 0, 0, 188, + 1, 0, 0, 56, 2, 0, + 0, 32, 3, 0, 0, 84, + 3, 0, 0, 65, 111, 110, + 57, 184, 0, 0, 0, 184, + 0, 0, 0, 0, 2, 254, + 255, 132, 0, 0, 0, 52, + 0, 0, 0, 1, 0, 36, + 0, 0, 0, 48, 0, 0, + 0, 48, 0, 0, 0, 36, + 0, 1, 0, 48, 0, 0, + 0, 0, 0, 2, 0, 1, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 2, 254, + 255, 81, 0, 0, 5, 3, + 0, 15, 160, 0, 0, 0, + 0, 0, 0, 128, 63, 0, + 0, 0, 0, 0, 0, 0, + 0, 31, 0, 0, 2, 5, + 0, 0, 128, 0, 0, 15, + 144, 4, 0, 0, 4, 0, + 0, 3, 224, 0, 0, 228, + 144, 2, 0, 238, 160, 2, + 0, 228, 160, 4, 0, 0, + 4, 0, 0, 1, 128, 0, + 0, 0, 144, 1, 0, 170, + 160, 1, 0, 0, 160, 4, + 0, 0, 4, 0, 0, 2, + 128, 0, 0, 85, 144, 1, + 0, 255, 160, 1, 0, 85, + 160, 2, 0, 0, 3, 0, + 0, 3, 192, 0, 0, 228, + 128, 0, 0, 228, 160, 1, + 0, 0, 2, 0, 0, 12, + 192, 3, 0, 68, 160, 255, + 255, 0, 0, 83, 72, 68, + 82, 188, 0, 0, 0, 64, + 0, 1, 0, 47, 0, 0, + 0, 89, 0, 0, 4, 70, + 142, 32, 0, 0, 0, 0, + 0, 2, 0, 0, 0, 95, + 0, 0, 3, 50, 16, 16, + 0, 0, 0, 0, 0, 103, + 0, 0, 4, 242, 32, 16, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 101, 0, 0, + 3, 50, 32, 16, 0, 1, + 0, 0, 0, 50, 0, 0, + 11, 50, 32, 16, 0, 0, + 0, 0, 0, 70, 16, 16, + 0, 0, 0, 0, 0, 230, + 138, 32, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 70, + 128, 32, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 54, + 0, 0, 8, 194, 32, 16, + 0, 0, 0, 0, 0, 2, + 64, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 128, + 63, 50, 0, 0, 11, 50, + 32, 16, 0, 1, 0, 0, + 0, 70, 16, 16, 0, 0, + 0, 0, 0, 230, 138, 32, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 70, 128, 32, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 62, 0, 0, + 1, 83, 84, 65, 84, 116, + 0, 0, 0, 4, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 82, + 68, 69, 70, 224, 0, 0, + 0, 1, 0, 0, 0, 64, + 0, 0, 0, 1, 0, 0, + 0, 28, 0, 0, 0, 0, + 4, 254, 255, 0, 129, 0, + 0, 174, 0, 0, 0, 60, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, + 0, 99, 98, 48, 0, 60, + 0, 0, 0, 2, 0, 0, + 0, 88, 0, 0, 0, 32, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 136, + 0, 0, 0, 0, 0, 0, + 0, 16, 0, 0, 0, 2, + 0, 0, 0, 148, 0, 0, + 0, 0, 0, 0, 0, 164, + 0, 0, 0, 16, 0, 0, + 0, 16, 0, 0, 0, 2, + 0, 0, 0, 148, 0, 0, + 0, 0, 0, 0, 0, 81, + 117, 97, 100, 68, 101, 115, + 99, 0, 171, 171, 171, 1, + 0, 3, 0, 1, 0, 4, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 84, 101, 120, + 67, 111, 111, 114, 100, 115, + 0, 77, 105, 99, 114, 111, + 115, 111, 102, 116, 32, 40, + 82, 41, 32, 72, 76, 83, + 76, 32, 83, 104, 97, 100, + 101, 114, 32, 67, 111, 109, + 112, 105, 108, 101, 114, 32, + 57, 46, 50, 55, 46, 57, + 53, 50, 46, 51, 48, 50, + 50, 0, 171, 73, 83, 71, + 78, 44, 0, 0, 0, 1, + 0, 0, 0, 8, 0, 0, + 0, 32, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 3, 0, 0, 0, 0, + 0, 0, 0, 7, 3, 0, + 0, 80, 79, 83, 73, 84, + 73, 79, 78, 0, 171, 171, + 171, 79, 83, 71, 78, 80, + 0, 0, 0, 2, 0, 0, + 0, 8, 0, 0, 0, 56, + 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, + 0, 15, 0, 0, 0, 68, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 3, + 0, 0, 0, 1, 0, 0, + 0, 3, 12, 0, 0, 83, + 86, 95, 80, 111, 115, 105, + 116, 105, 111, 110, 0, 84, + 69, 88, 67, 79, 79, 82, + 68, 0, 171, 171, 171, 195, + 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 2, + 0, 0, 0, 0, 0, 0, + 0, 188, 2, 0, 0, 68, + 88, 66, 67, 90, 17, 243, + 62, 104, 14, 0, 40, 49, + 70, 150, 92, 77, 1, 115, + 141, 1, 0, 0, 0, 188, + 2, 0, 0, 6, 0, 0, + 0, 56, 0, 0, 0, 164, + 0, 0, 0, 16, 1, 0, + 0, 140, 1, 0, 0, 48, + 2, 0, 0, 136, 2, 0, + 0, 65, 111, 110, 57, 100, + 0, 0, 0, 100, 0, 0, + 0, 0, 2, 255, 255, 60, + 0, 0, 0, 40, 0, 0, + 0, 0, 0, 40, 0, 0, + 0, 40, 0, 0, 0, 40, + 0, 1, 0, 36, 0, 0, + 0, 40, 0, 0, 0, 0, + 0, 1, 2, 255, 255, 31, + 0, 0, 2, 0, 0, 0, + 128, 0, 0, 3, 176, 31, + 0, 0, 2, 0, 0, 0, + 144, 0, 8, 15, 160, 66, + 0, 0, 3, 0, 0, 15, + 128, 0, 0, 228, 176, 0, + 8, 228, 160, 1, 0, 0, + 2, 0, 8, 15, 128, 0, + 0, 228, 128, 255, 255, 0, + 0, 83, 72, 68, 82, 100, + 0, 0, 0, 64, 0, 0, + 0, 25, 0, 0, 0, 90, + 0, 0, 3, 0, 96, 16, + 0, 0, 0, 0, 0, 88, + 24, 0, 4, 0, 112, 16, + 0, 0, 0, 0, 0, 85, + 85, 0, 0, 98, 16, 0, + 3, 50, 16, 16, 0, 1, + 0, 0, 0, 101, 0, 0, + 3, 242, 32, 16, 0, 0, + 0, 0, 0, 69, 0, 0, + 9, 242, 32, 16, 0, 0, + 0, 0, 0, 70, 16, 16, + 0, 1, 0, 0, 0, 70, + 126, 16, 0, 0, 0, 0, + 0, 0, 96, 16, 0, 0, + 0, 0, 0, 62, 0, 0, + 1, 83, 84, 65, 84, 116, + 0, 0, 0, 2, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 82, + 68, 69, 70, 156, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2, 0, 0, + 0, 28, 0, 0, 0, 0, + 4, 255, 255, 0, 129, 0, + 0, 105, 0, 0, 0, 92, + 0, 0, 0, 3, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, + 0, 101, 0, 0, 0, 2, + 0, 0, 0, 5, 0, 0, + 0, 4, 0, 0, 0, 255, + 255, 255, 255, 0, 0, 0, + 0, 1, 0, 0, 0, 12, + 0, 0, 0, 115, 83, 97, + 109, 112, 108, 101, 114, 0, + 116, 101, 120, 0, 77, 105, + 99, 114, 111, 115, 111, 102, + 116, 32, 40, 82, 41, 32, + 72, 76, 83, 76, 32, 83, + 104, 97, 100, 101, 114, 32, + 67, 111, 109, 112, 105, 108, + 101, 114, 32, 57, 46, 50, + 55, 46, 57, 53, 50, 46, + 51, 48, 50, 50, 0, 171, + 171, 73, 83, 71, 78, 80, + 0, 0, 0, 2, 0, 0, + 0, 8, 0, 0, 0, 56, + 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, + 0, 15, 0, 0, 0, 68, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 3, + 0, 0, 0, 1, 0, 0, + 0, 3, 3, 0, 0, 83, + 86, 95, 80, 111, 115, 105, + 116, 105, 111, 110, 0, 84, + 69, 88, 67, 79, 79, 82, + 68, 0, 171, 171, 171, 79, + 83, 71, 78, 44, 0, 0, + 0, 1, 0, 0, 0, 8, + 0, 0, 0, 32, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, 0, 0, + 0, 0, 0, 0, 0, 15, + 0, 0, 0, 83, 86, 95, + 84, 97, 114, 103, 101, 116, + 0, 171, 171, 135, 4, 0, + 0, 0, 0, 0, 0, 4, + 0, 0, 0, 32, 0, 0, + 0, 0, 0, 0, 0, 2, + 0, 0, 0, 255, 255, 255, + 255, 0, 0, 0, 0, 43, + 0, 0, 0, 15, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 52, 0, 0, + 0, 15, 0, 0, 0, 0, + 0, 0, 0, 16, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 100, 0, 0, 0, 72, + 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, + 0, 0, 0, 145, 0, 0, + 0, 117, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, + 255, 3, 0, 0, 0, 55, + 0, 0, 0, 0, 0, 0, + 0, 2, 0, 0, 0, 100, + 0, 0, 0, 46, 0, 0, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 154, 0, 0, + 0, 47, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, + 0, 166, 0, 0, 0, 0, + 0, 0, 0, 178, 0, 0, + 0, 1, 0, 0, 0, 0, + 0, 0, 0, 192, 0, 0, + 0, 3, 0, 0, 0, 0, + 0, 0, 0, 6, 0, 0, + 0, 0, 0, 0, 0, 7, + 0, 0, 0, 115, 4, 0, + 0, 8, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, + 0, 123, 4, 0, 0, 7, + 0, 0, 0, 0, 0, 0, + 0, 7, 0, 0, 0, 71, + 7, 0, 0 +}; diff --git a/gfx/cairo/cairo/src/cairo-d2d-private.fx b/gfx/cairo/cairo/src/cairo-d2d-private.fx new file mode 100644 index 000000000000..8f3236a4cda3 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-d2d-private.fx @@ -0,0 +1,57 @@ +// We store vertex coordinates and the quad shape in a constant buffer, this is +// easy to update and allows us to use a single call to set the x, y, w, h of +// the quad. +// The QuadDesc and TexCoords both work as follows: +// The x component is the quad left point, the y component is the top point +// the z component is the width, and the w component is the height. The quad +// are specified in viewport coordinates, i.e. { -1.0f, 1.0f, 2.0f, -2.0f } +// would cover the entire viewport (which runs from <-1.0f, 1.0f> left to right +// and <-1.0f, 1.0f> -bottom- to top. The TexCoords desc is specified in texture +// space <0, 1.0f> left to right and top to bottom. The input vertices of the +// shader stage always form a rectangle from {0, 0} - {1, 1} +cbuffer cb0 +{ + float4 QuadDesc; + float4 TexCoords; +} + +struct VS_OUTPUT +{ + float4 Position : SV_Position; + float2 TexCoord : TEXCOORD0; +}; + +Texture2D tex; + +sampler sSampler = sampler_state { + Texture = tex; + AddressU = Clamp; + AddressV = Clamp; +}; + +VS_OUTPUT SampleTextureVS(float3 pos : POSITION) +{ + VS_OUTPUT Output; + Output.Position.w = 1.0f; + Output.Position.x = pos.x * QuadDesc.z + QuadDesc.x; + Output.Position.y = pos.y * QuadDesc.w + QuadDesc.y; + Output.Position.z = 0; + Output.TexCoord.x = pos.x * TexCoords.z + TexCoords.x; + Output.TexCoord.y = pos.y * TexCoords.w + TexCoords.y; + return Output; +} + +float4 SampleTexturePS( VS_OUTPUT In) : SV_Target +{ + return tex.Sample(sSampler, In.TexCoord); +}; + +technique10 SampleTexture +{ + pass P0 + { + SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS())); + SetGeometryShader(NULL); + SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleTexturePS())); + } +} diff --git a/gfx/cairo/cairo/src/cairo-d2d-private.h b/gfx/cairo/cairo/src/cairo-d2d-private.h index 3244bb42dbf9..da60e4168a1a 100644 --- a/gfx/cairo/cairo/src/cairo-d2d-private.h +++ b/gfx/cairo/cairo/src/cairo-d2d-private.h @@ -49,10 +49,28 @@ extern "C" { } #include "cairo-win32-refptr.h" +#include "cairo-d2d-private-fx.h" +#include "cairo-win32.h" /* describes the type of the currently applied clip so that we can pop it */ struct d2d_clip; +#define MAX_OPERATORS CAIRO_OPERATOR_HSL_LUMINOSITY + 1 + +struct _cairo_d2d_device +{ + cairo_device_t base; + + HMODULE mD3D10_1; + RefPtr mD3D10Device; + RefPtr mSampleEffect; + RefPtr mInputLayout; + RefPtr mQuadBuffer; + RefPtr mRasterizerState; + RefPtr mBlendStates[MAX_OPERATORS]; +}; +typedef struct _cairo_d2d_device cairo_d2d_device_t; + struct _cairo_d2d_surface { _cairo_d2d_surface() : d2d_clip(NULL), clipping(false), isDrawing(false), textRenderingInit(true) @@ -61,6 +79,10 @@ struct _cairo_d2d_surface { } cairo_surface_t base; + /* Device used by this surface + * NOTE: In upstream cairo this is in the surface base class */ + cairo_d2d_device_t *device; + /** Render target of the texture we render to */ RefPtr rt; /** Surface containing our backstore */ @@ -103,6 +125,10 @@ struct _cairo_d2d_surface { /** Indicates if text rendering is initialized */ bool textRenderingInit; + RefPtr buffer_rt_view; + RefPtr buffer_sr_view; + + //cairo_surface_clipper_t clipper; }; typedef struct _cairo_d2d_surface cairo_d2d_surface_t; @@ -124,104 +150,14 @@ typedef HRESULT (WINAPI*D3D10CreateDevice1Func)( ID3D10Device1 **ppDevice ); -class D2DSurfFactory -{ -public: - static ID2D1Factory *Instance() - { - if (!mFactoryInstance) { - D2D1CreateFactoryFunc createD2DFactory = (D2D1CreateFactoryFunc) - GetProcAddress(LoadLibraryW(L"d2d1.dll"), "D2D1CreateFactory"); - if (createD2DFactory) { - D2D1_FACTORY_OPTIONS options; -#ifdef DEBUG - options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION; -#else - options.debugLevel = D2D1_DEBUG_LEVEL_NONE; -#endif - createD2DFactory( - D2D1_FACTORY_TYPE_SINGLE_THREADED, - __uuidof(ID2D1Factory), - &options, - (void**)&mFactoryInstance); - } - } - return mFactoryInstance; - } -private: - static ID2D1Factory *mFactoryInstance; -}; - -/** - * On usage of D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS: - * documentation on D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS - * can be misleading. In fact, that flag gives no such indication. I pointed this - * out to Bas in my email. However, Microsoft is in fact using this flag to - * indicate "light weight" DX applications. By light weight they are essentially - * referring to applications that are not games. The idea is that when you create - * a DX game, the driver assumes that you will pretty much have a single instance - * and therefore it doesn't try to hold back when it comes to GPU resource - * allocation as long as it can crank out performance. In other words, the - * priority in regular DX applications is to make that one application run as fast - * as you can. For "light weight" applications, including D2D applications, the - * priorities are a bit different. Now you are no longer going to have a single - * (or very few) instances. You can have a lot of them (say, for example, a - * separate DX context/device per browser tab). In such cases, the GPU resource - * allocation scheme changes. - */ -class D3D10Factory -{ -public: - static ID3D10Device1 *Device() - { - if (!mDeviceInstance) { - D3D10CreateDevice1Func createD3DDevice = (D3D10CreateDevice1Func) - GetProcAddress(LoadLibraryA("d3d10_1.dll"), "D3D10CreateDevice1"); - if (createD3DDevice) { - HRESULT hr = createD3DDevice( - NULL, - D3D10_DRIVER_TYPE_HARDWARE, - NULL, - D3D10_CREATE_DEVICE_BGRA_SUPPORT | - D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, - D3D10_FEATURE_LEVEL_10_1, - D3D10_1_SDK_VERSION, - &mDeviceInstance); - if (FAILED(hr)) { - HRESULT hr = createD3DDevice( - NULL, - D3D10_DRIVER_TYPE_HARDWARE, - NULL, - D3D10_CREATE_DEVICE_BGRA_SUPPORT | - D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, - D3D10_FEATURE_LEVEL_10_0, - D3D10_1_SDK_VERSION, - &mDeviceInstance); - if (FAILED(hr)) { - /* TODO: D3D10Level9 might be slower than GDI */ - HRESULT hr = createD3DDevice( - NULL, - D3D10_DRIVER_TYPE_HARDWARE, - NULL, - D3D10_CREATE_DEVICE_BGRA_SUPPORT | - D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, - D3D10_FEATURE_LEVEL_9_3, - D3D10_1_SDK_VERSION, - &mDeviceInstance); - - } - } - if (SUCCEEDED(hr)) { - mDeviceInstance->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_LINESTRIP); - } - } - } - return mDeviceInstance; - } -private: - static ID3D10Device1 *mDeviceInstance; -}; - +typedef HRESULT (WINAPI*D3D10CreateEffectFromMemoryFunc)( + void *pData, + SIZE_T DataLength, + UINT FXFlags, + ID3D10Device *pDevice, + ID3D10EffectPool *pEffectPool, + ID3D10Effect **ppEffect +); RefPtr _cairo_d2d_create_brush_for_pattern(cairo_d2d_surface_t *d2dsurf, @@ -229,8 +165,15 @@ _cairo_d2d_create_brush_for_pattern(cairo_d2d_surface_t *d2dsurf, bool unique = false); void _cairo_d2d_begin_draw_state(cairo_d2d_surface_t *d2dsurf); + cairo_status_t _cairo_d2d_set_clip(cairo_d2d_surface_t *d2dsurf, cairo_clip_t *clip); +cairo_int_status_t _cairo_d2d_blend_temp_surface(cairo_d2d_surface_t *surf, cairo_operator_t op, ID2D1RenderTarget *rt, cairo_clip_t *clip, const cairo_rectangle_int_t *bounds = NULL); + +RefPtr _cairo_d2d_get_temp_rt(cairo_d2d_surface_t *surf, cairo_clip_t *clip); + +cairo_operator_t _cairo_d2d_simplify_operator(cairo_operator_t op, const cairo_pattern_t *source); + #endif /* CAIRO_HAS_D2D_SURFACE */ #endif /* CAIRO_D2D_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-d2d-surface.cpp b/gfx/cairo/cairo/src/cairo-d2d-surface.cpp index aa720ad8e898..a6d2ff55abef 100644 --- a/gfx/cairo/cairo/src/cairo-d2d-surface.cpp +++ b/gfx/cairo/cairo/src/cairo-d2d-surface.cpp @@ -47,11 +47,328 @@ extern "C" { // Required for using placement new. #include -ID2D1Factory *D2DSurfFactory::mFactoryInstance = NULL; -ID3D10Device1 *D3D10Factory::mDeviceInstance = NULL; - #define CAIRO_INT_STATUS_SUCCESS (cairo_int_status_t)CAIRO_STATUS_SUCCESS +struct Vertex +{ + float position[2]; +}; + +// This factory is not device dependent, we can store it. But will clear it +// if there are no devices left needing it. +static ID2D1Factory *sD2DFactory = NULL; +static HMODULE sD2DModule; + +static void +_cairo_d2d_release_factory() +{ + int refcnt = sD2DFactory->Release(); + if (!refcnt) { + // Once the last reference goes, free the library. + sD2DFactory = NULL; + FreeLibrary(sD2DModule); + } +} + +/** + * Set a blending mode for an operator. This will also return a boolean that + * reports if for this blend mode the entire surface needs to be blended. This + * is true whenever the DEST blend is not ONE when src alpha is 0. + */ +static cairo_int_status_t +_cairo_d2d_set_operator(cairo_d2d_device_t *device, + cairo_operator_t op) +{ + assert(op < MAX_OPERATORS); + if (op >= MAX_OPERATORS) { + // Eep! Someone forgot to update MAX_OPERATORS probably. + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (device->mBlendStates[op]) { + device->mD3D10Device->OMSetBlendState(device->mBlendStates[op], NULL, 0xffffffff); + return CAIRO_INT_STATUS_SUCCESS; + } + + D3D10_BLEND_DESC desc; + memset(&desc, 0, sizeof(desc)); + desc.BlendEnable[0] = TRUE; + desc.AlphaToCoverageEnable = FALSE; + desc.RenderTargetWriteMask[0] = D3D10_COLOR_WRITE_ENABLE_ALL; + + switch (op) { + case CAIRO_OPERATOR_OVER: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ONE; + break; + case CAIRO_OPERATOR_ADD: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ONE; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ONE; + break; + case CAIRO_OPERATOR_IN: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ZERO; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_DEST_ALPHA; + break; + case CAIRO_OPERATOR_OUT: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ZERO; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; + break; + case CAIRO_OPERATOR_ATOP: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_DEST_ALPHA; + break; + case CAIRO_OPERATOR_DEST: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ONE; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ZERO; + break; + case CAIRO_OPERATOR_DEST_OVER: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ONE; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; + break; + case CAIRO_OPERATOR_DEST_IN: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_SRC_ALPHA; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ZERO; + break; + case CAIRO_OPERATOR_DEST_OUT: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ZERO; + break; + case CAIRO_OPERATOR_DEST_ATOP: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_SRC_ALPHA; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; + break; + case CAIRO_OPERATOR_XOR: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; + break; + default: + return CAIRO_INT_STATUS_UNSUPPORTED; + }; + device->mD3D10Device->CreateBlendState(&desc, &device->mBlendStates[op]); + + device->mD3D10Device->OMSetBlendState(device->mBlendStates[op], NULL, 0xffffffff); + return CAIRO_INT_STATUS_SUCCESS; +} + +cairo_device_t * +cairo_d2d_create_device() +{ + D3D10_RASTERIZER_DESC rastDesc; + D3D10_INPUT_ELEMENT_DESC layout[] = + { + { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, + }; + D3D10_PASS_DESC passDesc; + ID3D10EffectTechnique *technique; + Vertex vertices[] = { {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0} }; + CD3D10_BUFFER_DESC bufferDesc(sizeof(vertices), D3D10_BIND_VERTEX_BUFFER); + D3D10_SUBRESOURCE_DATA data; + + cairo_d2d_device_t *device = new cairo_d2d_device_t; + device->mD3D10_1 = LoadLibraryA("d3d10_1.dll"); + D3D10CreateDevice1Func createD3DDevice = (D3D10CreateDevice1Func) + GetProcAddress(device->mD3D10_1, "D3D10CreateDevice1"); + D3D10CreateEffectFromMemoryFunc createEffect = (D3D10CreateEffectFromMemoryFunc) + GetProcAddress(device->mD3D10_1, "D3D10CreateEffectFromMemory"); + D2D1CreateFactoryFunc createD2DFactory; + + if (!createD3DDevice || !createEffect) { + goto FAILED; + } + + /** + * On usage of D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS: + * documentation on D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS + * can be misleading. In fact, that flag gives no such indication. I pointed this + * out to Bas in my email. However, Microsoft is in fact using this flag to + * indicate "light weight" DX applications. By light weight they are essentially + * referring to applications that are not games. The idea is that when you create + * a DX game, the driver assumes that you will pretty much have a single instance + * and therefore it doesn't try to hold back when it comes to GPU resource + * allocation as long as it can crank out performance. In other words, the + * priority in regular DX applications is to make that one application run as fast + * as you can. For "light weight" applications, including D2D applications, the + * priorities are a bit different. Now you are no longer going to have a single + * (or very few) instances. You can have a lot of them (say, for example, a + * separate DX context/device per browser tab). In such cases, the GPU resource + * allocation scheme changes. + */ + HRESULT hr = createD3DDevice( + NULL, + D3D10_DRIVER_TYPE_HARDWARE, + NULL, + D3D10_CREATE_DEVICE_BGRA_SUPPORT | + D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, + D3D10_FEATURE_LEVEL_10_1, + D3D10_1_SDK_VERSION, + &device->mD3D10Device); + if (FAILED(hr)) { + hr = createD3DDevice( + NULL, + D3D10_DRIVER_TYPE_HARDWARE, + NULL, + D3D10_CREATE_DEVICE_BGRA_SUPPORT | + D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, + D3D10_FEATURE_LEVEL_10_0, + D3D10_1_SDK_VERSION, + &device->mD3D10Device); + if (FAILED(hr)) { + /* This is not guaranteed to be too fast! */ + hr = createD3DDevice( + NULL, + D3D10_DRIVER_TYPE_HARDWARE, + NULL, + D3D10_CREATE_DEVICE_BGRA_SUPPORT | + D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, + D3D10_FEATURE_LEVEL_9_3, + D3D10_1_SDK_VERSION, + &device->mD3D10Device); + + } + } + if (FAILED(hr)) { + goto FAILED; + } + + if (!sD2DFactory) { + sD2DModule = LoadLibraryW(L"d2d1.dll"); + createD2DFactory = (D2D1CreateFactoryFunc) + GetProcAddress(sD2DModule, "D2D1CreateFactory"); + if (!createD2DFactory) { + goto FAILED; + } + D2D1_FACTORY_OPTIONS options; +#ifdef DEBUG + options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION; +#else + options.debugLevel = D2D1_DEBUG_LEVEL_NONE; +#endif + hr = createD2DFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, + __uuidof(ID2D1Factory), + &options, + (void**)&sD2DFactory); + if (FAILED(hr)) { + goto FAILED; + } + } else { + sD2DFactory->AddRef(); + } + + device->mD3D10Device->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_LINESTRIP); + createEffect((void*)g_main, sizeof(g_main), 0, device->mD3D10Device, NULL, &device->mSampleEffect); + + technique = device->mSampleEffect->GetTechniqueByName("SampleTexture"); + technique->GetPassByIndex(0)->GetDesc(&passDesc); + + + hr = device->mD3D10Device->CreateInputLayout(layout, + sizeof(layout) / sizeof(D3D10_INPUT_ELEMENT_DESC), + passDesc.pIAInputSignature, + passDesc.IAInputSignatureSize, + &device->mInputLayout); + if (FAILED(hr)) { + goto FAILED; + } + + data.pSysMem = (void*)vertices; + hr = device->mD3D10Device->CreateBuffer(&bufferDesc, &data, &device->mQuadBuffer); + if (FAILED(hr)) { + goto FAILED; + } + + memset(&rastDesc, 0, sizeof(rastDesc)); + rastDesc.CullMode = D3D10_CULL_NONE; + rastDesc.FillMode = D3D10_FILL_SOLID; + hr = device->mD3D10Device->CreateRasterizerState(&rastDesc, &device->mRasterizerState); + if (FAILED(hr)) { + goto FAILED; + } + device->base.refcount = 1; + + return &device->base; +FAILED: + delete &device->base; + return NULL; +} + +int +cairo_release_device(cairo_device_t *device) +{ + int newrefcnt = --device->refcount; + if (!newrefcnt) { + // Call the correct destructor + cairo_d2d_device_t *d2d_device = reinterpret_cast(device); + FreeLibrary(d2d_device->mD3D10_1); + delete d2d_device; + _cairo_d2d_release_factory(); + } + return newrefcnt; +} + +int +cairo_addref_device(cairo_device_t *device) +{ + return ++device->refcount; +} + +void +cairo_d2d_finish_device(cairo_device_t *device) +{ + cairo_d2d_device_t *d2d_device = reinterpret_cast(device); + // Here it becomes interesting, this flush method is generally called when + // interop is going on between our device and another device. The + // synchronisation between these devices is not always that great. The + // device flush method may flush the device's command queue, but it gives + // no guarantee that the device will actually be done with those commands, + // and so the surface may still not be complete when the external device + // chooses to use it. The EVENT query will actually tell us when the GPU + // is completely done with our commands. + D3D10_QUERY_DESC queryDesc; + queryDesc.MiscFlags = 0; + queryDesc.Query = D3D10_QUERY_EVENT; + RefPtr query; + + d2d_device->mD3D10Device->CreateQuery(&queryDesc, &query); + + // QUERY_EVENT does not use Begin(). It's disabled. + query->End(); + + BOOL done = FALSE; + while (!done) { + // This will return S_OK and done = FALSE when the GPU is not done, and + // S_OK and done = TRUE when the GPU is done. Any other return value + // means we need to break out or risk an infinite loop. + if (FAILED(query->GetData(&done, sizeof(BOOL), 0))) { + break; + } + } +} + +static void +_cairo_d2d_setup_for_blend(cairo_d2d_device_t *device) +{ + device->mD3D10Device->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + device->mD3D10Device->IASetInputLayout(device->mInputLayout); + + UINT stride = sizeof(Vertex); + UINT offset = 0; + ID3D10Buffer *buff = device->mQuadBuffer; + device->mD3D10Device->IASetVertexBuffers(0, 1, &buff, &stride, &offset); + + device->mD3D10Device->RSSetState(device->mRasterizerState); +} + // Contains our cache usage - perhaps this should be made threadsafe. static int cache_usage = 0; @@ -347,6 +664,15 @@ _cairo_d2d_color_from_cairo_color(const cairo_color_t &color) (FLOAT)color.alpha); } +static void +_cairo_d2d_round_out_to_int_rect(cairo_rectangle_int_t *rect, double x1, double y1, double x2, double y2) +{ + rect->x = (int)floor(x1); + rect->y = (int)floor(y1); + rect->width = (int)ceil(x2) - rect->x; + rect->height = (int)ceil(y2) - rect->y; +} + /** * Gets the surface buffer texture for window surfaces whose backbuffer * is not directly usable as a bitmap. @@ -366,7 +692,7 @@ _cairo_d2d_get_buffer_texture(cairo_d2d_surface_t *surface) softDesc.MipLevels = 1; softDesc.Usage = D3D10_USAGE_DEFAULT; softDesc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; - D3D10Factory::Device()->CreateTexture2D(&softDesc, NULL, &surface->bufferTexture); + surface->device->mD3D10Device->CreateTexture2D(&softDesc, NULL, &surface->bufferTexture); } return surface->bufferTexture; } @@ -414,7 +740,7 @@ void cairo_d2d_present_backbuffer(cairo_surface_t *surface) _cairo_d2d_flush(d2dsurf); if (d2dsurf->dxgiChain) { d2dsurf->dxgiChain->Present(0, 0); - D3D10Factory::Device()->Flush(); + d2dsurf->device->mD3D10Device->Flush(); } } @@ -743,16 +1069,16 @@ _cairo_d2d_create_strokestyle_for_stroke_style(const cairo_stroke_style_t *style } RefPtr strokeStyle; - D2DSurfFactory::Instance()->CreateStrokeStyle(D2D1::StrokeStyleProperties(line_cap, - line_cap, - line_cap, - line_join, - (FLOAT)style->miter_limit, - dashStyle, - (FLOAT)style->dash_offset), - dashes, - style->num_dashes, - &strokeStyle); + sD2DFactory->CreateStrokeStyle(D2D1::StrokeStyleProperties(line_cap, + line_cap, + line_cap, + line_join, + (FLOAT)style->miter_limit, + dashStyle, + (FLOAT)style->dash_offset), + dashes, + style->num_dashes, + &strokeStyle); delete [] dashes; return strokeStyle; } @@ -769,6 +1095,18 @@ cairo_user_data_key_t bitmap_key_extend; cairo_user_data_key_t bitmap_key_snapshot; struct cached_bitmap { + cached_bitmap() + { + sD2DFactory->AddRef(); + } + + ~cached_bitmap() + { + // Clear bitmap out first because it depends on the factory. + bitmap = NULL; + _cairo_d2d_release_factory(); + } + /** The cached bitmap */ RefPtr bitmap; /** The cached bitmap is dirty and needs its data refreshed */ @@ -1724,7 +2062,7 @@ _cairo_d2d_create_path_geometry_for_path(cairo_path_fixed_t *path, D2D1_FIGURE_BEGIN type) { RefPtr d2dpath; - D2DSurfFactory::Instance()->CreatePathGeometry(&d2dpath); + sD2DFactory->CreatePathGeometry(&d2dpath); RefPtr sink; d2dpath->Open(&sink); D2D1_FILL_MODE fillMode = D2D1_FILL_MODE_WINDING; @@ -1844,8 +2182,8 @@ _cairo_d2d_clear (cairo_d2d_surface_t *d2dsurf, return CAIRO_INT_STATUS_SUCCESS; } -static cairo_operator_t _cairo_d2d_simplify_operator(cairo_operator_t op, - const cairo_pattern_t *source) +cairo_operator_t _cairo_d2d_simplify_operator(cairo_operator_t op, + const cairo_pattern_t *source) { if (op == CAIRO_OPERATOR_SOURCE) { /** Operator over is easier for D2D! If the source if opaque, change */ @@ -1935,7 +2273,7 @@ _cairo_d2d_create_similar(void *surface, RefPtr texture; RefPtr dxgiSurface; - hr = D3D10Factory::Device()->CreateTexture2D(&desc, NULL, &texture); + hr = d2dsurf->device->mD3D10Device->CreateTexture2D(&desc, NULL, &texture); if (FAILED(hr)) { goto FAIL_CREATESIMILAR; } @@ -1952,14 +2290,14 @@ _cairo_d2d_create_similar(void *surface, D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE : D2D1_RENDER_TARGET_USAGE_NONE; - hr = D2DSurfFactory::Instance()->CreateDxgiSurfaceRenderTarget(dxgiSurface, - D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, - D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, - alpha), - dpiX, - dpiY, - usage), - &newSurf->rt); + hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(dxgiSurface, + D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, + D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, + alpha), + dpiX, + dpiY, + usage), + &newSurf->rt); if (FAILED(hr)) { goto FAIL_CREATESIMILAR; @@ -1982,6 +2320,9 @@ _cairo_d2d_create_similar(void *surface, _d2d_clear_surface(newSurf); + newSurf->device = d2dsurf->device; + cairo_addref_device(&newSurf->device->base); + return reinterpret_cast(newSurf); FAIL_CREATESIMILAR: @@ -1998,6 +2339,7 @@ _cairo_d2d_finish(void *surface) reset_clip(d2dsurf); + cairo_release_device(&d2dsurf->device->base); d2dsurf->~cairo_d2d_surface_t(); return CAIRO_STATUS_SUCCESS; } @@ -2032,12 +2374,12 @@ _cairo_d2d_acquire_source_image(void *abstract_surface, softDesc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE | D3D10_CPU_ACCESS_READ; softDesc.Usage = D3D10_USAGE_STAGING; softDesc.BindFlags = 0; - hr = D3D10Factory::Device()->CreateTexture2D(&softDesc, NULL, &softTexture); + hr = d2dsurf->device->mD3D10Device->CreateTexture2D(&softDesc, NULL, &softTexture); if (FAILED(hr)) { return CAIRO_STATUS_NO_MEMORY; } - D3D10Factory::Device()->CopyResource(softTexture, d2dsurf->surface); + d2dsurf->device->mD3D10Device->CopyResource(softTexture, d2dsurf->surface); D3D10_MAPPED_TEXTURE2D data; hr = softTexture->Map(0, D3D10_MAP_READ_WRITE, 0, &data); @@ -2105,11 +2447,11 @@ _cairo_d2d_acquire_dest_image(void *abstract_surface, softDesc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE | D3D10_CPU_ACCESS_READ; softDesc.Usage = D3D10_USAGE_STAGING; softDesc.BindFlags = 0; - hr = D3D10Factory::Device()->CreateTexture2D(&softDesc, NULL, &softTexture); + hr = d2dsurf->device->mD3D10Device->CreateTexture2D(&softDesc, NULL, &softTexture); if (FAILED(hr)) { return CAIRO_STATUS_NO_MEMORY; } - D3D10Factory::Device()->CopyResource(softTexture, d2dsurf->surface); + d2dsurf->device->mD3D10Device->CopyResource(softTexture, d2dsurf->surface); D3D10_MAPPED_TEXTURE2D data; hr = softTexture->Map(0, D3D10_MAP_READ_WRITE, 0, &data); @@ -2149,7 +2491,7 @@ _cairo_d2d_release_dest_image(void *abstract_surface, cairo_surface_destroy(&image->base); softTexture->Unmap(0); - D3D10Factory::Device()->CopyResource(d2dsurf->surface, softTexture); + d2dsurf->device->mD3D10Device->CopyResource(d2dsurf->surface, softTexture); softTexture->Release(); } @@ -2168,7 +2510,342 @@ _cairo_d2d_flush(void *surface) return CAIRO_STATUS_SUCCESS; } +static cairo_int_status_t +_cairo_d2d_copy_surface(cairo_d2d_surface_t *dst, + cairo_d2d_surface_t *src, + cairo_point_int_t *translation, + cairo_region_t *region) +{ + RefPtr dstSurface; + dst->surface->QueryInterface(&dstSurface); + RefPtr srcSurface; + src->surface->QueryInterface(&srcSurface); + DXGI_SURFACE_DESC srcDesc, dstDesc; + srcSurface->GetDesc(&srcDesc); + dstSurface->GetDesc(&dstDesc); + + cairo_rectangle_int_t clip_rect; + clip_rect.x = 0; + clip_rect.y = 0; + clip_rect.width = dstDesc.Width; + clip_rect.height = dstDesc.Height; + + cairo_int_status_t rv = CAIRO_INT_STATUS_SUCCESS; + + _cairo_d2d_flush(dst); + ID3D10Resource *srcResource = src->surface; + if (src->surface.get() == dst->surface.get()) { + // Self-copy + srcResource = _cairo_d2d_get_buffer_texture(dst); + src->device->mD3D10Device->CopyResource(srcResource, src->surface); + } else { + // Need to flush the source too if it's a different surface. + _cairo_d2d_flush(src); + } + + // One copy for each rectangle in the final clipping region. + for (int i = 0; i < cairo_region_num_rectangles(region); i++) { + D3D10_BOX rect; + cairo_rectangle_int_t area_to_copy; + + cairo_region_get_rectangle(region, i, &area_to_copy); + + cairo_rectangle_int_t transformed_rect = { area_to_copy.x + translation->x, + area_to_copy.y + translation->y, + area_to_copy.width, area_to_copy.height }; + cairo_rectangle_int_t surface_rect = { 0, 0, srcDesc.Width, srcDesc.Height }; + + + if (!_cairo_rectangle_contains(&surface_rect, &transformed_rect)) { + /* We cannot do any sort of extend, in the future a little bit of extra code could + * allow us to support EXTEND_NONE. + */ + rv = CAIRO_INT_STATUS_UNSUPPORTED; + break; + } + + rect.front = 0; + rect.back = 1; + rect.left = transformed_rect.x; + rect.top = transformed_rect.y; + rect.right = transformed_rect.x + transformed_rect.width; + rect.bottom = transformed_rect.y + transformed_rect.height; + + src->device->mD3D10Device->CopySubresourceRegion(dst->surface, + 0, + area_to_copy.x, + area_to_copy.y, + 0, + srcResource, + 0, + &rect); + } + + return rv; +} + +/** + * This function will text if we can use GPU mem cpy to execute an operation with + * a surface pattern. If box is NULL it will operate on the entire dst surface. + */ +static cairo_int_status_t +_cairo_d2d_try_copy(cairo_d2d_surface_t *dst, + cairo_surface_t *src, + cairo_box_t *box, + const cairo_matrix_t *matrix, + cairo_clip_t *clip, + cairo_operator_t op) +{ + if (op != CAIRO_OPERATOR_SOURCE && + !(op == CAIRO_OPERATOR_OVER && src->content == CAIRO_CONTENT_COLOR)) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + cairo_point_int_t translation; + if ((box && !box_is_integer(box)) || + !_cairo_matrix_is_integer_translation(matrix, &translation.x, &translation.y)) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + /* For now we do only D2D sources */ + if (src->type != CAIRO_SURFACE_TYPE_D2D) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + cairo_rectangle_int_t rect; + if (box) { + _cairo_box_round_to_rectangle(box, &rect); + } else { + rect.x = rect.y = 0; + rect.width = dst->rt->GetPixelSize().width; + rect.height = dst->rt->GetPixelSize().height; + } + + cairo_d2d_surface_t *d2dsrc = reinterpret_cast(src); + + if (d2dsrc->device != dst->device) { + // This doesn't work between different devices. + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + /* Region we need to clip this operation to */ + cairo_region_t *clipping_region = NULL; + cairo_region_t *region; + if (clip) { + _cairo_clip_get_region(clip, &clipping_region); + + if (!clipping_region) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + region = cairo_region_copy(clipping_region); + + cairo_region_intersect_rectangle(region, &rect); + + if (cairo_region_is_empty(region)) { + // Nothing to do. + return CAIRO_INT_STATUS_SUCCESS; + } + } else { + region = cairo_region_create_rectangle(&rect); + // Areas outside of the surface do not matter. + cairo_rectangle_int_t surface_rect = { 0, 0, + dst->rt->GetPixelSize().width, + dst->rt->GetPixelSize().height }; + cairo_region_intersect_rectangle(region, &surface_rect); + } + + cairo_int_status_t rv = _cairo_d2d_copy_surface(dst, d2dsrc, &translation, region); + + cairo_region_destroy(region); + + return rv; +} + +RefPtr _cairo_d2d_get_temp_rt(cairo_d2d_surface_t *surf, cairo_clip_t *clip) +{ + RefPtr texture = _cairo_d2d_get_buffer_texture(surf); + RefPtr new_rt; + RefPtr dxgiSurface; + texture->QueryInterface(&dxgiSurface); + HRESULT hr; + + _cairo_d2d_flush(surf); + + if (!surf) { + return NULL; + } + + D2D1_RENDER_TARGET_PROPERTIES props = + D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, + D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED)); + hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(dxgiSurface, + props, + &new_rt); + + if (FAILED(hr)) { + return NULL; + } + + new_rt->BeginDraw(); + new_rt->Clear(D2D1::ColorF(0, 0)); + + // Since this is a fresh surface there's no point in doing clever things to + // keep the clip path around until a certain depth. So we just do a straight- + // forward push of all clip paths in the tree, similar to what the normal + // clip code does, but a little less clever. + if (clip) { + cairo_clip_path_t *path = clip->path; + while (path) { + cairo_box_t clip_box; + if (_cairo_path_fixed_is_box(&path->path, &clip_box)) { + // If this does not have a region it could be none-pixel aligned. + D2D1_ANTIALIAS_MODE aaMode = D2D1_ANTIALIAS_MODE_PER_PRIMITIVE; + if (box_is_integer(&clip_box)) { + aaMode = D2D1_ANTIALIAS_MODE_ALIASED; + } + new_rt->PushAxisAlignedClip(D2D1::RectF(_cairo_fixed_to_float(clip_box.p1.x), + _cairo_fixed_to_float(clip_box.p1.y), + _cairo_fixed_to_float(clip_box.p2.x), + _cairo_fixed_to_float(clip_box.p2.y)), + aaMode); + } else { + HRESULT hr; + RefPtr geom = _cairo_d2d_create_path_geometry_for_path (&path->path, + path->fill_rule, + D2D1_FIGURE_BEGIN_FILLED); + RefPtr layer; + + hr = new_rt->CreateLayer (&layer); + + D2D1_LAYER_OPTIONS options = D2D1_LAYER_OPTIONS_NONE; + + new_rt->PushLayer(D2D1::LayerParameters( + D2D1::InfiniteRect(), + geom, + D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, + D2D1::IdentityMatrix(), + 1.0, + 0, + options), + layer); + } + path = path->prev; + } + } + return new_rt; +} + +cairo_int_status_t _cairo_d2d_blend_temp_surface(cairo_d2d_surface_t *surf, cairo_operator_t op, ID2D1RenderTarget *rt, cairo_clip_t *clip, const cairo_rectangle_int_t *bounds) +{ + int numPaths = 0; + if (clip) { + cairo_clip_path_t *path = clip->path; + while (path) { + numPaths++; + path = path->prev; + } + + cairo_clip_path_t **paths = new cairo_clip_path_t*[numPaths]; + + numPaths = 0; + path = clip->path; + while (path) { + paths[numPaths++] = path; + path = path->prev; + } + + for (int i = numPaths - 1; i >= 0; i--) { + if (paths[i]->flags & CAIRO_CLIP_PATH_IS_BOX) { + rt->PopAxisAlignedClip(); + } else { + rt->PopLayer(); + } + } + delete [] paths; + } + rt->EndDraw(); + HRESULT hr; + + RefPtr srcTexture = _cairo_d2d_get_buffer_texture(surf); + RefPtr dstTexture; + + surf->surface->QueryInterface(&dstTexture); + ID3D10Device *device = surf->device->mD3D10Device; + + if (!surf->buffer_rt_view) { + hr = device->CreateRenderTargetView(dstTexture, NULL, &surf->buffer_rt_view); + if (FAILED(hr)) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } + + if (!surf->buffer_sr_view) { + hr = device->CreateShaderResourceView(srcTexture, NULL, &surf->buffer_sr_view); + if (FAILED(hr)) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } + + cairo_int_status_t status; + + status = _cairo_d2d_set_operator(surf->device, op); + + if (unlikely(status)) { + return status; + } + + D3D10_TEXTURE2D_DESC tDesc; + dstTexture->GetDesc(&tDesc); + D3D10_VIEWPORT vp; + vp.Height = tDesc.Height; + vp.MinDepth = 0; + vp.MaxDepth = 1.0; + vp.TopLeftX = 0; + vp.TopLeftY = 0; + vp.Width = tDesc.Width; + device->RSSetViewports(1, &vp); + + ID3D10Effect *effect = surf->device->mSampleEffect; + + ID3D10RenderTargetView *rtViewPtr = surf->buffer_rt_view; + device->OMSetRenderTargets(1, &rtViewPtr, 0); + ID3D10EffectVectorVariable *quadDesc = effect->GetVariableByName("QuadDesc")->AsVector(); + ID3D10EffectVectorVariable *texCoords = effect->GetVariableByName("TexCoords")->AsVector(); + + float quadDescVal[] = { -1.0f, 1.0f, 2.0f, -2.0f }; + float texCoordsVal[] = { 0.0, 0.0, 1.0f, 1.0f }; + if (bounds && _cairo_operator_bounded_by_mask(op)) { + quadDescVal[0] = -1.0f + ((float)bounds->x / (float)tDesc.Width) * 2.0f; + quadDescVal[1] = 1.0f - ((float)bounds->y / (float)tDesc.Height) * 2.0f; + quadDescVal[2] = ((float)bounds->width / (float)tDesc.Width) * 2.0f; + quadDescVal[3] = -((float)bounds->height / (float)tDesc.Height) * 2.0f; + texCoordsVal[0] = (float)bounds->x / (float)tDesc.Width; + texCoordsVal[1] = (float)bounds->y / (float)tDesc.Height; + texCoordsVal[2] = (float)bounds->width / (float)tDesc.Width; + texCoordsVal[3] = (float)bounds->height / (float)tDesc.Height; + } + quadDesc->SetFloatVector(quadDescVal); + texCoords->SetFloatVector(texCoordsVal); + + _cairo_d2d_setup_for_blend(surf->device); + ID3D10EffectTechnique *technique = effect->GetTechniqueByName("SampleTexture"); + technique->GetPassByIndex(0)->Apply(0); + + ID3D10ShaderResourceView *srViewPtr = surf->buffer_sr_view; + device->PSSetShaderResources(0, 1, &srViewPtr); + + device->Draw(4, 0); + +#ifdef DEBUG + // Quiet down some info messages from D3D10 debug layer + srViewPtr = NULL; + device->PSSetShaderResources(0, 1, &srViewPtr); + rtViewPtr = NULL; + device->OMSetRenderTargets(1, &rtViewPtr, 0); +#endif + return CAIRO_INT_STATUS_SUCCESS; +} static cairo_int_status_t _cairo_d2d_paint(void *surface, @@ -2181,18 +2858,44 @@ _cairo_d2d_paint(void *surface, op = _cairo_d2d_simplify_operator(op, source); + if (op == CAIRO_OPERATOR_SOURCE) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + if (op == CAIRO_OPERATOR_CLEAR) { return _cairo_d2d_clear(d2dsurf, clip); } + if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { + const cairo_surface_pattern_t *surf_pattern = + reinterpret_cast(source); - _begin_draw_state(d2dsurf); - status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip); + status = _cairo_d2d_try_copy(d2dsurf, surf_pattern->surface, + NULL, &source->matrix, clip, op); - if (unlikely(status)) - return status; + if (status != CAIRO_INT_STATUS_UNSUPPORTED) { + return status; + } + } + RefPtr target_rt = d2dsurf->rt; +#ifndef ALWAYS_MANUAL_COMPOSITE + if (op != CAIRO_OPERATOR_OVER) { +#endif + target_rt = _cairo_d2d_get_temp_rt(d2dsurf, clip); + if (!target_rt) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } +#ifndef ALWAYS_MANUAL_COMPOSITE + } else { + _begin_draw_state(d2dsurf); + status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip); - d2dsurf->rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); + if (unlikely(status)) + return status; + } +#endif + + target_rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); RefPtr brush = _cairo_d2d_create_brush_for_pattern(d2dsurf, source); @@ -2200,23 +2903,16 @@ _cairo_d2d_paint(void *surface, if (!brush) { return CAIRO_INT_STATUS_UNSUPPORTED; } - if (op == CAIRO_OPERATOR_OVER) { - D2D1_SIZE_F size = d2dsurf->rt->GetSize(); - d2dsurf->rt->FillRectangle(D2D1::RectF((FLOAT)0, - (FLOAT)0, - (FLOAT)size.width, - (FLOAT)size.height), - brush); - } else if (op == CAIRO_OPERATOR_SOURCE) { - D2D1_SIZE_F size = d2dsurf->rt->GetSize(); - d2dsurf->rt->Clear(D2D1::ColorF(0, 0)); - d2dsurf->rt->FillRectangle(D2D1::RectF((FLOAT)0, - (FLOAT)0, - (FLOAT)size.width, - (FLOAT)size.height), - brush); - } else { - return CAIRO_INT_STATUS_UNSUPPORTED; + + D2D1_SIZE_F size = target_rt->GetSize(); + target_rt->FillRectangle(D2D1::RectF((FLOAT)0, + (FLOAT)0, + (FLOAT)size.width, + (FLOAT)size.height), + brush); + + if (target_rt.get() != d2dsurf->rt.get()) { + return _cairo_d2d_blend_temp_surface(d2dsurf, op, target_rt, clip); } return CAIRO_INT_STATUS_SUCCESS; @@ -2317,27 +3013,32 @@ _cairo_d2d_stroke(void *surface, op = _cairo_d2d_simplify_operator(op, source); - if (op != CAIRO_OPERATOR_OVER && op != CAIRO_OPERATOR_ADD) { - /** - * We don't really support ADD yet. True ADD support requires getting - * the tesselated mesh from D2D, and blending that using D3D which has - * an add operator available. - */ + if (op == CAIRO_OPERATOR_SOURCE) { return CAIRO_INT_STATUS_UNSUPPORTED; } - _begin_draw_state(d2dsurf); - - status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip); - - if (unlikely (status)) - return status; + RefPtr target_rt = d2dsurf->rt; +#ifndef ALWAYS_MANUAL_COMPOSITE + if (op != CAIRO_OPERATOR_OVER) { +#endif + target_rt = _cairo_d2d_get_temp_rt(d2dsurf, clip); + if (!target_rt) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } +#ifndef ALWAYS_MANUAL_COMPOSITE + } else { + _begin_draw_state(d2dsurf); + status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip); + if (unlikely(status)) + return status; + } +#endif if (antialias == CAIRO_ANTIALIAS_NONE) { - d2dsurf->rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); + target_rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); } else { - d2dsurf->rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); + target_rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); } RefPtr strokeStyle = _cairo_d2d_create_strokestyle_for_stroke_style(style); @@ -2351,9 +3052,9 @@ _cairo_d2d_stroke(void *surface, D2D1::Matrix3x2F inverse_mat = _cairo_d2d_invert_matrix(mat); RefPtr trans_geom; - D2DSurfFactory::Instance()->CreateTransformedGeometry(d2dpath, &inverse_mat, &trans_geom); + sD2DFactory->CreateTransformedGeometry(d2dpath, &inverse_mat, &trans_geom); - d2dsurf->rt->SetTransform(mat); + target_rt->SetTransform(mat); RefPtr brush = _cairo_d2d_create_brush_for_pattern(d2dsurf, source); @@ -2361,9 +3062,18 @@ _cairo_d2d_stroke(void *surface, return CAIRO_INT_STATUS_UNSUPPORTED; } - d2dsurf->rt->DrawGeometry(trans_geom, brush, (FLOAT)style->line_width, strokeStyle); + target_rt->DrawGeometry(trans_geom, brush, (FLOAT)style->line_width, strokeStyle); + + target_rt->SetTransform(D2D1::Matrix3x2F::Identity()); + + if (target_rt.get() != d2dsurf->rt.get()) { + D2D1_RECT_F bounds; + trans_geom->GetWidenedBounds((FLOAT)style->line_width, strokeStyle, D2D1::IdentityMatrix(), &bounds); + cairo_rectangle_int_t bound_rect; + _cairo_d2d_round_out_to_int_rect(&bound_rect, bounds.left, bounds.top, bounds.right, bounds.bottom); + return _cairo_d2d_blend_temp_surface(d2dsurf, op, target_rt, clip, &bound_rect); + } - d2dsurf->rt->SetTransform(D2D1::Matrix3x2F::Identity()); return CAIRO_INT_STATUS_SUCCESS; } @@ -2380,43 +3090,66 @@ _cairo_d2d_fill(void *surface, cairo_int_status_t status; cairo_d2d_surface_t *d2dsurf = static_cast(surface); + cairo_box_t box; + bool is_box = _cairo_path_fixed_is_box(path, &box); + + if (is_box && source->type == CAIRO_PATTERN_TYPE_SURFACE) { + const cairo_surface_pattern_t *surf_pattern = + reinterpret_cast(source); + cairo_int_status_t rv = _cairo_d2d_try_copy(d2dsurf, surf_pattern->surface, + &box, &source->matrix, clip, op); + + if (rv != CAIRO_INT_STATUS_UNSUPPORTED) { + return rv; + } + } op = _cairo_d2d_simplify_operator(op, source); - if (op != CAIRO_OPERATOR_OVER && op != CAIRO_OPERATOR_ADD && - op != CAIRO_OPERATOR_CLEAR) { - /** - * We don't really support ADD yet. True ADD support requires getting - * the tesselated mesh from D2D, and blending that using D3D which has - * an add operator available. - */ + if (op == CAIRO_OPERATOR_SOURCE) { return CAIRO_INT_STATUS_UNSUPPORTED; } - _begin_draw_state(d2dsurf); - status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip); - - if (unlikely(status)) - return status; - - - if (antialias == CAIRO_ANTIALIAS_NONE) { - d2dsurf->rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); - } else { - d2dsurf->rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); - } - - cairo_box_t box; - if (op == CAIRO_OPERATOR_CLEAR) { if (_cairo_path_fixed_is_box(path, &box)) { + _begin_draw_state(d2dsurf); + status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip); + + if (unlikely(status)) + return status; + return _cairo_d2d_clear_box (d2dsurf, clip, &box); } else { return CAIRO_INT_STATUS_UNSUPPORTED; } } - if (_cairo_path_fixed_is_box(path, &box)) { + RefPtr target_rt = d2dsurf->rt; + +#ifndef ALWAYS_MANUAL_COMPOSITE + if (op != CAIRO_OPERATOR_OVER) { +#endif + target_rt = _cairo_d2d_get_temp_rt(d2dsurf, clip); + if (!target_rt) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } +#ifndef ALWAYS_MANUAL_COMPOSITE + } else { + _begin_draw_state(d2dsurf); + status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip); + + if (unlikely(status)) + return status; + } +#endif + + if (antialias == CAIRO_ANTIALIAS_NONE) { + target_rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); + } else { + target_rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); + } + + if (is_box) { float x1 = _cairo_fixed_to_float(box.p1.x); float y1 = _cairo_fixed_to_float(box.p1.y); float x2 = _cairo_fixed_to_float(box.p2.x); @@ -2427,11 +3160,11 @@ _cairo_d2d_fill(void *surface, return CAIRO_INT_STATUS_UNSUPPORTED; } - d2dsurf->rt->FillRectangle(D2D1::RectF(x1, - y1, - x2, - y2), - brush); + target_rt->FillRectangle(D2D1::RectF(x1, + y1, + x2, + y2), + brush); } else { RefPtr d2dpath = _cairo_d2d_create_path_geometry_for_path(path, fill_rule, D2D1_FIGURE_BEGIN_FILLED); @@ -2440,8 +3173,17 @@ _cairo_d2d_fill(void *surface, if (!brush) { return CAIRO_INT_STATUS_UNSUPPORTED; } - d2dsurf->rt->FillGeometry(d2dpath, brush); + target_rt->FillGeometry(d2dpath, brush); } + + if (target_rt.get() != d2dsurf->rt.get()) { + double x1, y1, x2, y2; + _cairo_path_fixed_bounds(path, &x1, &y1, &x2, &y2); + cairo_rectangle_int_t bounds; + _cairo_d2d_round_out_to_int_rect(&bounds, x1, y1, x2, y2); + return _cairo_d2d_blend_temp_surface(d2dsurf, op, target_rt, clip, &bounds); + } + return CAIRO_INT_STATUS_SUCCESS; } @@ -2493,17 +3235,11 @@ _cairo_d2d_getextents(void *surface, /** Helper functions. */ cairo_surface_t* -cairo_d2d_surface_create_for_hwnd(HWND wnd, +cairo_d2d_surface_create_for_hwnd(cairo_device_t *cairo_device, + HWND wnd, cairo_content_t content) { - if (!D3D10Factory::Device() || !D2DSurfFactory::Instance()) { - /** - * FIXME: In the near future we can use cairo_device_t to pass in a - * device. - */ - return _cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_DEVICE)); - } - + cairo_d2d_device_t *d2d_device = reinterpret_cast(cairo_device); cairo_d2d_surface_t *newSurf = static_cast(malloc(sizeof(cairo_d2d_surface_t))); new (newSurf) cairo_d2d_surface_t(); @@ -2533,7 +3269,7 @@ cairo_d2d_surface_create_for_hwnd(HWND wnd, if (!sizePixels.height) { sizePixels.height = 1; } - ID3D10Device1 *device = D3D10Factory::Device(); + ID3D10Device1 *device = d2d_device->mD3D10Device; RefPtr dxgiDevice; RefPtr dxgiAdapter; RefPtr dxgiFactory; @@ -2595,7 +3331,7 @@ cairo_d2d_surface_create_for_hwnd(HWND wnd, dpiX, dpiY, D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE); - hr = D2DSurfFactory::Instance()->CreateDxgiSurfaceRenderTarget(newSurf->backBuf, + hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(newSurf->backBuf, props, &newSurf->rt); if (FAILED(hr)) { @@ -2609,6 +3345,9 @@ cairo_d2d_surface_create_for_hwnd(HWND wnd, _d2d_clear_surface(newSurf); + newSurf->device = d2d_device; + cairo_addref_device(cairo_device); + return reinterpret_cast(newSurf); FAIL_HWND: @@ -2618,17 +3357,12 @@ FAIL_HWND: } cairo_surface_t * -cairo_d2d_surface_create(cairo_format_t format, +cairo_d2d_surface_create(cairo_device_t *device, + cairo_format_t format, int width, int height) { - if (!D3D10Factory::Device() || !D2DSurfFactory::Instance()) { - /** - * FIXME: In the near future we can use cairo_device_t to pass in a - * device. - */ - return _cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_DEVICE)); - } + cairo_d2d_device_t *d2d_device = reinterpret_cast(device); cairo_d2d_surface_t *newSurf = static_cast(malloc(sizeof(cairo_d2d_surface_t))); new (newSurf) cairo_d2d_surface_t(); @@ -2671,7 +3405,7 @@ cairo_d2d_surface_create(cairo_format_t format, D2D1_BITMAP_PROPERTIES bitProps; D2D1_RENDER_TARGET_PROPERTIES props; - hr = D3D10Factory::Device()->CreateTexture2D(&desc, NULL, &texture); + hr = d2d_device->mD3D10Device->CreateTexture2D(&desc, NULL, &texture); if (FAILED(hr)) { goto FAIL_CREATE; @@ -2691,7 +3425,7 @@ cairo_d2d_surface_create(cairo_format_t format, if (desc.MiscFlags & D3D10_RESOURCE_MISC_GDI_COMPATIBLE) props.usage = D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE; - hr = D2DSurfFactory::Instance()->CreateDxgiSurfaceRenderTarget(dxgiSurface, + hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(dxgiSurface, props, &newSurf->rt); @@ -2720,6 +3454,9 @@ cairo_d2d_surface_create(cairo_format_t format, _d2d_clear_surface(newSurf); + newSurf->device = d2d_device; + cairo_addref_device(device); + return reinterpret_cast(newSurf); FAIL_CREATE: @@ -2728,6 +3465,108 @@ FAIL_CREATE: return _cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_MEMORY)); } +cairo_surface_t * +cairo_d2d_surface_create_for_handle(cairo_device_t *device, HANDLE handle, cairo_content_t content) +{ + if (!device) { + return _cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_DEVICE)); + } + + cairo_d2d_device_t *d2d_device = reinterpret_cast(device); + cairo_d2d_surface_t *newSurf = static_cast(malloc(sizeof(cairo_d2d_surface_t))); + new (newSurf) cairo_d2d_surface_t(); + + cairo_status_t status = CAIRO_STATUS_NO_MEMORY; + HRESULT hr; + RefPtr texture; + RefPtr dxgiSurface; + D2D1_BITMAP_PROPERTIES bitProps; + D2D1_RENDER_TARGET_PROPERTIES props; + DXGI_FORMAT format; + DXGI_SURFACE_DESC desc; + + hr = d2d_device->mD3D10Device->OpenSharedResource(handle, + __uuidof(ID3D10Resource), + (void**)&newSurf->surface); + + if (FAILED(hr)) { + goto FAIL_CREATEHANDLE; + } + + hr = newSurf->surface->QueryInterface(&dxgiSurface); + + if (FAILED(hr)) { + goto FAIL_CREATEHANDLE; + } + + dxgiSurface->GetDesc(&desc); + format = desc.Format; + + D2D1_ALPHA_MODE alpha = D2D1_ALPHA_MODE_PREMULTIPLIED; + if (format == DXGI_FORMAT_B8G8R8A8_UNORM) { + if (content == CAIRO_CONTENT_ALPHA) { + status = CAIRO_STATUS_INVALID_CONTENT; + goto FAIL_CREATEHANDLE; + } + _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, content); + if (content == CAIRO_CONTENT_COLOR) { + alpha = D2D1_ALPHA_MODE_IGNORE; + } + } else if (format == DXGI_FORMAT_A8_UNORM) { + if (content != CAIRO_CONTENT_ALPHA) { + status = CAIRO_STATUS_INVALID_CONTENT; + goto FAIL_CREATEHANDLE; + } + _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, CAIRO_CONTENT_ALPHA); + } else { + status = CAIRO_STATUS_INVALID_FORMAT; + // We don't know how to support this format! + goto FAIL_CREATEHANDLE; + } + + props = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, + D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, alpha)); + + hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(dxgiSurface, + props, + &newSurf->rt); + + if (FAILED(hr)) { + goto FAIL_CREATEHANDLE; + } + + bitProps = D2D1::BitmapProperties(D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, + alpha)); + + if (format != DXGI_FORMAT_A8_UNORM) { + /* For some reason creation of shared bitmaps for A8 UNORM surfaces + * doesn't work even though the documentation suggests it does. The + * function will return an error if we try */ + hr = newSurf->rt->CreateSharedBitmap(IID_IDXGISurface, + dxgiSurface, + &bitProps, + &newSurf->surfaceBitmap); + + if (FAILED(hr)) { + goto FAIL_CREATEHANDLE; + } + } + + newSurf->rt->CreateSolidColorBrush(D2D1::ColorF(0, 1.0), &newSurf->solidColorBrush); + + _d2d_clear_surface(newSurf); + + newSurf->device = d2d_device; + cairo_addref_device(device); + + return &newSurf->base; + +FAIL_CREATEHANDLE: + newSurf->~cairo_d2d_surface_t(); + free(newSurf); + return _cairo_surface_create_in_error(_cairo_error(status)); +} + void cairo_d2d_scroll(cairo_surface_t *surface, int x, int y, cairo_rectangle_t *clip) { if (surface->type != CAIRO_SURFACE_TYPE_D2D) { @@ -2778,8 +3617,8 @@ void cairo_d2d_scroll(cairo_surface_t *surface, int x, int y, cairo_rectangle_t } ID3D10Texture2D *texture = _cairo_d2d_get_buffer_texture(d2dsurf); - D3D10Factory::Device()->CopyResource(texture, d2dsurf->surface); - D3D10Factory::Device()->CopySubresourceRegion(d2dsurf->surface, + d2dsurf->device->mD3D10Device->CopyResource(texture, d2dsurf->surface); + d2dsurf->device->mD3D10Device->CopySubresourceRegion(d2dsurf->surface, 0, point.x, point.y, @@ -2790,19 +3629,6 @@ void cairo_d2d_scroll(cairo_surface_t *surface, int x, int y, cairo_rectangle_t } -cairo_bool_t -cairo_d2d_has_support() -{ - /** - * FIXME: We should be able to fix this in the near future when we pass in - * a cairo_device_t to our surface creation functions. - */ - if (!D3D10Factory::Device() || !D2DSurfFactory::Instance()) { - return false; - } - return true; -} - HDC cairo_d2d_get_dc(cairo_surface_t *surface, cairo_bool_t retain_contents) { diff --git a/gfx/cairo/cairo/src/cairo-dwrite-font.cpp b/gfx/cairo/cairo/src/cairo-dwrite-font.cpp index 271f5e321d4b..238539b1938a 100644 --- a/gfx/cairo/cairo/src/cairo-dwrite-font.cpp +++ b/gfx/cairo/cairo/src/cairo-dwrite-font.cpp @@ -1262,32 +1262,52 @@ _cairo_dwrite_show_glyphs_on_d2d_surface(void *surface, if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_DWRITE) return CAIRO_INT_STATUS_UNSUPPORTED; + op = _cairo_d2d_simplify_operator(op, source); - /* We can only handle operator SOURCE or OVER with the destination - * having no alpha */ - if (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_OVER) - return CAIRO_INT_STATUS_UNSUPPORTED; + /* We cannot handle operator SOURCE or CLEAR */ + if (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_CLEAR) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } - _cairo_d2d_begin_draw_state (dst); - _cairo_d2d_set_clip (dst, clip); + RefPtr target_rt = dst->rt; + cairo_rectangle_int_t fontArea; +#ifndef ALWAYS_MANUAL_COMPOSITE + if (op != CAIRO_OPERATOR_OVER) { +#endif + target_rt = _cairo_d2d_get_temp_rt(dst, clip); + + if (!target_rt) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } +#ifndef ALWAYS_MANUAL_COMPOSITE + } else { + _cairo_d2d_begin_draw_state(dst); + status = (cairo_int_status_t)_cairo_d2d_set_clip (dst, clip); + + if (unlikely(status)) + return status; + } +#endif - D2D1_TEXT_ANTIALIAS_MODE cleartype = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; + D2D1_TEXT_ANTIALIAS_MODE highest_quality = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; - if (dst->base.content != CAIRO_CONTENT_COLOR) { - cleartype = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; + // If we're rendering to a temporary surface we cannot do sub-pixel AA. + if (dst->base.content != CAIRO_CONTENT_COLOR || dst->rt.get() != target_rt.get()) { + highest_quality = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; } + switch (scaled_font->options.antialias) { case CAIRO_ANTIALIAS_DEFAULT: - dst->rt->SetTextAntialiasMode(cleartype); + target_rt->SetTextAntialiasMode(highest_quality); break; case CAIRO_ANTIALIAS_NONE: - dst->rt->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_ALIASED); + target_rt->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_ALIASED); break; case CAIRO_ANTIALIAS_GRAY: - dst->rt->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE); + target_rt->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE); break; case CAIRO_ANTIALIAS_SUBPIXEL: - dst->rt->SetTextAntialiasMode(cleartype); + target_rt->SetTextAntialiasMode(highest_quality); break; } @@ -1349,7 +1369,29 @@ _cairo_dwrite_show_glyphs_on_d2d_surface(void *surface, D2D1::Matrix3x2F mat = _cairo_d2d_matrix_from_matrix(&dwritesf->mat); if (transform) { - dst->rt->SetTransform(mat); + target_rt->SetTransform(mat); + } + + if (dst->rt.get() != target_rt.get()) { + RefPtr analysis; + DWRITE_MATRIX dwmat = _cairo_dwrite_matrix_from_matrix(&dwritesf->mat); + DWriteFactory::Instance()->CreateGlyphRunAnalysis(&run, + 1.0f, + transform ? &dwmat : 0, + DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC, + DWRITE_MEASURING_MODE_NATURAL, + 0, + 0, + &analysis); + + RECT bounds; + analysis->GetAlphaTextureBounds(scaled_font->options.antialias == CAIRO_ANTIALIAS_NONE ? + DWRITE_TEXTURE_ALIASED_1x1 : DWRITE_TEXTURE_CLEARTYPE_3x1, + &bounds); + fontArea.x = bounds.left; + fontArea.y = bounds.top; + fontArea.width = bounds.right - bounds.left; + fontArea.height = bounds.bottom - bounds.top; } RefPtr brush = _cairo_d2d_create_brush_for_pattern(dst, @@ -1374,15 +1416,19 @@ _cairo_dwrite_show_glyphs_on_d2d_surface(void *surface, brush->SetTransform(&mat_brush); } - dst->rt->DrawGlyphRun(D2D1::Point2F(0, 0), &run, brush); + target_rt->DrawGlyphRun(D2D1::Point2F(0, 0), &run, brush); if (transform) { - dst->rt->SetTransform(D2D1::Matrix3x2F::Identity()); + target_rt->SetTransform(D2D1::Matrix3x2F::Identity()); } delete [] indices; delete [] offsets; delete [] advances; + + if (target_rt.get() != dst->rt.get()) { + return _cairo_d2d_blend_temp_surface(dst, op, target_rt, clip, &fontArea); + } return CAIRO_INT_STATUS_SUCCESS; } diff --git a/gfx/cairo/cairo/src/cairo-rectangle.c b/gfx/cairo/cairo/src/cairo-rectangle.c index e887c75134aa..40a8bc95c4ba 100644 --- a/gfx/cairo/cairo/src/cairo-rectangle.c +++ b/gfx/cairo/cairo/src/cairo-rectangle.c @@ -94,6 +94,26 @@ _cairo_boxes_get_extents (const cairo_box_t *boxes, } } +/* This function will return 'true' if the containing_rectangle contains the + * contained_rectangle, and false otherwise. + */ +cairo_bool_t +_cairo_rectangle_contains (const cairo_rectangle_int_t *containing_rectangle, + const cairo_rectangle_int_t *contained_rectangle) +{ + if (containing_rectangle->x > contained_rectangle->x || + containing_rectangle->y > contained_rectangle->y) + return FALSE; + + if (containing_rectangle->x + containing_rectangle->width < + contained_rectangle->x + contained_rectangle->width || + containing_rectangle->y + containing_rectangle->height < + contained_rectangle->y + contained_rectangle->height) + return FALSE; + + return TRUE; +} + /* XXX We currently have a confusing mix of boxes and rectangles as * exemplified by this function. A #cairo_box_t is a rectangular area * represented by the coordinates of the upper left and lower right diff --git a/gfx/cairo/cairo/src/cairo-win32-refptr.h b/gfx/cairo/cairo/src/cairo-win32-refptr.h index 11fc0252eea5..6dd5ac323431 100644 --- a/gfx/cairo/cairo/src/cairo-win32-refptr.h +++ b/gfx/cairo/cairo/src/cairo-win32-refptr.h @@ -107,6 +107,11 @@ public: return mPtr; } + T* operator->() const + { + return mPtr; + } + operator bool() { return (mPtr ? true : false); diff --git a/gfx/cairo/cairo/src/cairo-win32.h b/gfx/cairo/cairo/src/cairo-win32.h index 53fed4d03ef7..e6e7feea0106 100644 --- a/gfx/cairo/cairo/src/cairo-win32.h +++ b/gfx/cairo/cairo/src/cairo-win32.h @@ -126,30 +126,88 @@ cairo_dwrite_font_face_create_for_dwrite_fontface(void *dwrite_font, void *dwrit #endif /* CAIRO_HAS_DWRITE_FONT */ #if CAIRO_HAS_D2D_SURFACE + +struct _cairo_device +{ + int type; + int refcount; +}; +typedef struct _cairo_device cairo_device_t; + +/** + * Create a D2D device + * + * \return New D2D device, NULL if creation failed. + */ +cairo_device_t * +cairo_d2d_create_device(); + +/** + * Releases a D2D device. + * + * \return References left to the device + */ +int +cairo_release_device(cairo_device_t *device); + +/** + * Addrefs a D2D device. + * + * \return References to the device + */ +int +cairo_addref_device(cairo_device_t *device); + +/** + * Flushes a D3D device. In most cases the surface backend will do this + * internally, but when using a surfaces created from a shared handle this + * should be executed manually when a different device is going to be accessing + * the same surface data. This will also block until the device is finished + * processing all work. + */ +void +cairo_d2d_finish_device(cairo_device_t *device); /** * Create a D2D surface for an HWND * + * \param device Device used to create the surface * \param wnd Handle for the window * \param content Content of the window, should be COLOR_ALPHA for transparent windows * \return New cairo surface */ cairo_public cairo_surface_t * -cairo_d2d_surface_create_for_hwnd(HWND wnd, cairo_content_t content); +cairo_d2d_surface_create_for_hwnd(cairo_device_t *device, HWND wnd, cairo_content_t content); /** * Create a D2D surface of a certain size. * + * \param device Device used to create the surface * \param format Cairo format of the surface * \param width Width of the surface * \param height Height of the surface * \return New cairo surface */ cairo_public cairo_surface_t * -cairo_d2d_surface_create(cairo_format_t format, +cairo_d2d_surface_create(cairo_device_t *device, + cairo_format_t format, int width, int height); +/** + * Create a D3D surface from a Texture SharedHandle, this is obtained from a + * CreateTexture call on a D3D9 device. This has to be an A8R8G8B8 format + * or an A8 format, the treatment of the alpha channel can be indicated using + * the content parameter. + * + * \param device Device used to create the surface + * \param handle Shared handle to the texture we want to wrap + * \param content Content of the texture, COLOR_ALPHA for ARGB + * \return New cairo surface + */ +cairo_public cairo_surface_t * +cairo_d2d_surface_create_for_handle(cairo_device_t *device, HANDLE handle, cairo_content_t content); + /** * Present the backbuffer for a surface create for an HWND. This needs * to be called when the owner of the original window surface wants to @@ -172,15 +230,6 @@ void cairo_d2d_present_backbuffer(cairo_surface_t *surface); */ void cairo_d2d_scroll(cairo_surface_t *surface, int x, int y, cairo_rectangle_t *clip); -/** - * Verify if D2D surfaces are actually supported. This will confirm the needed - * hardware is available. - * - * \return True if the support is available. If false surface creation will - * return error surfaces. - */ -cairo_bool_t cairo_d2d_has_support(); - /** * Get a DC for the current render target. When selecting the retention option this * call can be relatively slow, since it may require reading back contents from the diff --git a/gfx/cairo/cairo/src/cairoint.h b/gfx/cairo/cairo/src/cairoint.h index 28861d73ef4b..63612e63ae35 100644 --- a/gfx/cairo/cairo/src/cairoint.h +++ b/gfx/cairo/cairo/src/cairoint.h @@ -271,6 +271,10 @@ cairo_private void _cairo_box_from_rectangle (cairo_box_t *box, const cairo_rectangle_int_t *rectangle); +cairo_private cairo_bool_t +_cairo_rectangle_contains (const cairo_rectangle_int_t *containing_rectangle, + const cairo_rectangle_int_t *contained_rectangle); + cairo_private void _cairo_box_round_to_rectangle (const cairo_box_t *box, cairo_rectangle_int_t *rectangle); diff --git a/gfx/layers/Makefile.in b/gfx/layers/Makefile.in index 2f397ca30235..9ea382af17eb 100644 --- a/gfx/layers/Makefile.in +++ b/gfx/layers/Makefile.in @@ -84,7 +84,10 @@ CPPSRCS = \ ifeq ($(MOZ_WIDGET_TOOLKIT),windows) ifdef MOZ_ENABLE_D3D9_LAYER -EXPORTS += LayerManagerD3D9.h +EXPORTS += \ + LayerManagerD3D9.h \ + DeviceManagerD3D9.h \ + $(NULL) CPPSRCS += \ LayerManagerD3D9.cpp \ @@ -93,6 +96,7 @@ CPPSRCS += \ ImageLayerD3D9.cpp \ ColorLayerD3D9.cpp \ CanvasLayerD3D9.cpp \ + DeviceManagerD3D9.cpp \ $(NULL) endif endif diff --git a/gfx/layers/d3d9/CanvasLayerD3D9.cpp b/gfx/layers/d3d9/CanvasLayerD3D9.cpp index 976d3a82eb69..a3ef316b0fd0 100644 --- a/gfx/layers/d3d9/CanvasLayerD3D9.cpp +++ b/gfx/layers/d3d9/CanvasLayerD3D9.cpp @@ -70,9 +70,17 @@ CanvasLayerD3D9::Initialize(const Data& aData) mBounds.SetRect(0, 0, aData.mSize.width, aData.mSize.height); - device()->CreateTexture(mBounds.width, mBounds.height, 1, 0, - D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, - getter_AddRefs(mTexture), NULL); + if (mD3DManager->deviceManager()->HasDynamicTextures()) { + device()->CreateTexture(mBounds.width, mBounds.height, 1, D3DUSAGE_DYNAMIC, + D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, + getter_AddRefs(mTexture), NULL); + } else { + // D3DPOOL_MANAGED is fine here since we require Dynamic Textures for D3D9Ex + // devices. + device()->CreateTexture(mBounds.width, mBounds.height, 1, 0, + D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, + getter_AddRefs(mTexture), NULL); + } } void @@ -230,7 +238,7 @@ CanvasLayerD3D9::RenderLayer() opacity[0] = GetOpacity(); device()->SetPixelShaderConstantF(0, opacity, 1); - mD3DManager->SetShaderMode(LayerManagerD3D9::RGBLAYER); + mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBLAYER); if (!mGLBufferIsPremultiplied) { device()->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); diff --git a/gfx/layers/d3d9/ColorLayerD3D9.cpp b/gfx/layers/d3d9/ColorLayerD3D9.cpp index ad7fde80292c..a1f7d12db556 100644 --- a/gfx/layers/d3d9/ColorLayerD3D9.cpp +++ b/gfx/layers/d3d9/ColorLayerD3D9.cpp @@ -76,7 +76,7 @@ ColorLayerD3D9::RenderLayer() device()->SetPixelShaderConstantF(0, color, 1); - mD3DManager->SetShaderMode(LayerManagerD3D9::SOLIDCOLORLAYER); + mD3DManager->SetShaderMode(DeviceManagerD3D9::SOLIDCOLORLAYER); device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); } diff --git a/gfx/layers/d3d9/ContainerLayerD3D9.cpp b/gfx/layers/d3d9/ContainerLayerD3D9.cpp index f2f3f8fef409..8f1b2a708b45 100644 --- a/gfx/layers/d3d9/ContainerLayerD3D9.cpp +++ b/gfx/layers/d3d9/ContainerLayerD3D9.cpp @@ -254,7 +254,7 @@ ContainerLayerD3D9::RenderLayer() opacityVector[0] = opacity; device()->SetPixelShaderConstantF(0, opacityVector, 1); - mD3DManager->SetShaderMode(LayerManagerD3D9::RGBLAYER); + mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBLAYER); device()->SetTexture(0, renderTexture); device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); diff --git a/gfx/layers/d3d9/DeviceManagerD3D9.cpp b/gfx/layers/d3d9/DeviceManagerD3D9.cpp new file mode 100644 index 000000000000..051f199c853b --- /dev/null +++ b/gfx/layers/d3d9/DeviceManagerD3D9.cpp @@ -0,0 +1,562 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Corporation code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bas Schouten + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "DeviceManagerD3D9.h" +#include "LayerManagerD3D9Shaders.h" +#include "ThebesLayerD3D9.h" +#include "nsIServiceManager.h" +#include "nsIConsoleService.h" +#include "nsPrintfCString.h" + +namespace mozilla { +namespace layers { + +const LPCWSTR kClassName = L"D3D9WindowClass"; + +#define USE_D3D9EX + +typedef IDirect3D9* (WINAPI*Direct3DCreate9Func)( + UINT SDKVersion +); + +typedef HRESULT (WINAPI*Direct3DCreate9ExFunc)( + UINT SDKVersion, + IDirect3D9Ex **ppD3D +); + +struct vertex { + float x, y; +}; + +SwapChainD3D9::SwapChainD3D9(DeviceManagerD3D9 *aDeviceManager) + : mDeviceManager(aDeviceManager) + , mWnd(0) +{ + mDeviceManager->mSwapChains.AppendElement(this); +} + +SwapChainD3D9::~SwapChainD3D9() +{ + mDeviceManager->mSwapChains.RemoveElement(this); +} + +bool +SwapChainD3D9::Init(HWND hWnd) +{ + RECT r; + ::GetClientRect(hWnd, &r); + + mWnd = hWnd; + + D3DPRESENT_PARAMETERS pp; + memset(&pp, 0, sizeof(D3DPRESENT_PARAMETERS)); + + pp.BackBufferFormat = D3DFMT_UNKNOWN; + pp.SwapEffect = D3DSWAPEFFECT_COPY; + pp.Windowed = TRUE; + pp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; + pp.hDeviceWindow = mWnd; + if (r.left == r.right || r.top == r.bottom) { + pp.BackBufferHeight = 1; + pp.BackBufferWidth = 1; + } + + HRESULT hr = mDeviceManager->device()-> + CreateAdditionalSwapChain(&pp, + getter_AddRefs(mSwapChain)); + + if (FAILED(hr)) { + NS_WARNING("Failed to create swap chain for window."); + return false; + } + + return true; +} + +bool +SwapChainD3D9::PrepareForRendering() +{ + RECT r; + if (!::GetClientRect(mWnd, &r)) { + return false; + } + + if (!mDeviceManager->VerifyReadyForRendering()) { + return false; + } + + if (!mSwapChain) { + Init(mWnd); + } + + if (mSwapChain) { + nsRefPtr backBuffer; + mSwapChain->GetBackBuffer(0, + D3DBACKBUFFER_TYPE_MONO, + getter_AddRefs(backBuffer)); + + D3DSURFACE_DESC desc; + backBuffer->GetDesc(&desc); + + if (desc.Width == r.right - r.left && desc.Height == r.bottom - r.top) { + mDeviceManager->device()->SetRenderTarget(0, backBuffer); + return true; + } + + mSwapChain = nsnull; + + Init(mWnd); + + if (!mSwapChain) { + return false; + } + + mSwapChain->GetBackBuffer(0, + D3DBACKBUFFER_TYPE_MONO, + getter_AddRefs(backBuffer)); + + mDeviceManager->device()->SetRenderTarget(0, backBuffer); + + return true; + } + return false; +} + +void +SwapChainD3D9::Present(const nsIntRect &aRect) +{ + RECT r; + r.left = aRect.x; + r.top = aRect.y; + r.right = aRect.XMost(); + r.bottom = aRect.YMost(); + + mSwapChain->Present(&r, &r, 0, 0, 0); +} + +void +SwapChainD3D9::Reset() +{ + mSwapChain = nsnull; +} + +#define HAS_CAP(a, b) (((a) & (b)) == (b)) +#define LACKS_CAP(a, b) !(((a) & (b)) == (b)) + +DeviceManagerD3D9::DeviceManagerD3D9() + : mHasDynamicTextures(false) +{ +} + +bool +DeviceManagerD3D9::Init() +{ + WNDCLASSW wc; + HRESULT hr; + + if (!GetClassInfoW(GetModuleHandle(NULL), kClassName, &wc)) { + ZeroMemory(&wc, sizeof(WNDCLASSW)); + wc.hInstance = GetModuleHandle(NULL); + wc.lpfnWndProc = ::DefWindowProc; + wc.lpszClassName = kClassName; + if (!RegisterClassW(&wc)) { + NS_WARNING("Failed to register window class for DeviceManager."); + return false; + } + } + + mFocusWnd = ::CreateWindowW(kClassName, L"D3D9Window", WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, + NULL, GetModuleHandle(NULL), NULL); + + if (!mFocusWnd) { + NS_WARNING("Failed to create DeviceManagerD3D9 Window."); + return false; + } + + HMODULE d3d9 = LoadLibraryW(L"d3d9.dll"); + Direct3DCreate9Func d3d9Create = (Direct3DCreate9Func) + GetProcAddress(d3d9, "Direct3DCreate9"); + Direct3DCreate9ExFunc d3d9CreateEx = (Direct3DCreate9ExFunc) + GetProcAddress(d3d9, "Direct3DCreate9Ex"); + +#ifdef USE_D3D9EX + if (d3d9CreateEx) { + hr = d3d9CreateEx(D3D_SDK_VERSION, getter_AddRefs(mD3D9Ex)); + if (SUCCEEDED(hr)) { + mD3D9 = mD3D9Ex; + } + } +#endif + + if (!mD3D9) { + if (!d3d9Create) { + return false; + } + + mD3D9 = dont_AddRef(d3d9Create(D3D_SDK_VERSION)); + + if (!mD3D9) { + return false; + } + } + + D3DPRESENT_PARAMETERS pp; + memset(&pp, 0, sizeof(D3DPRESENT_PARAMETERS)); + + pp.BackBufferWidth = 1; + pp.BackBufferHeight = 1; + pp.BackBufferFormat = D3DFMT_A8R8G8B8; + pp.SwapEffect = D3DSWAPEFFECT_DISCARD; + pp.Windowed = TRUE; + pp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; + pp.hDeviceWindow = mFocusWnd; + + if (mD3D9Ex) { + hr = mD3D9Ex->CreateDeviceEx(D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + mFocusWnd, + D3DCREATE_FPU_PRESERVE | + D3DCREATE_MULTITHREADED | + D3DCREATE_MIXED_VERTEXPROCESSING, + &pp, + NULL, + getter_AddRefs(mDeviceEx)); + if (SUCCEEDED(hr)) { + mDevice = mDeviceEx; + } + + D3DCAPS9 caps; + if (mDeviceEx->GetDeviceCaps(&caps)) { + if (LACKS_CAP(caps.Caps2, D3DCAPS2_DYNAMICTEXTURES)) { + // XXX - Should we actually hit this we'll need a CanvasLayer that + // supports static D3DPOOL_DEFAULT textures. + NS_WARNING("D3D9Ex device not used because of lack of support for \ + dynamic textures. This is unexpected."); + mDevice = nsnull; + mDeviceEx = nsnull; + } + } + } + + if (!mDevice) { + hr = mD3D9->CreateDevice(D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + mFocusWnd, + D3DCREATE_FPU_PRESERVE | + D3DCREATE_MULTITHREADED | + D3DCREATE_MIXED_VERTEXPROCESSING, + &pp, + getter_AddRefs(mDevice)); + + if (FAILED(hr)) { + NS_WARNING("Failed to create Device for DeviceManagerD3D9."); + return false; + } + } + + if (!VerifyCaps()) { + return false; + } + + hr = mDevice->CreateVertexShader((DWORD*)LayerQuadVS, + getter_AddRefs(mLayerVS)); + + if (FAILED(hr)) { + return false; + } + + hr = mDevice->CreatePixelShader((DWORD*)RGBShaderPS, + getter_AddRefs(mRGBPS)); + + if (FAILED(hr)) { + return false; + } + + hr = mDevice->CreatePixelShader((DWORD*)YCbCrShaderPS, + getter_AddRefs(mYCbCrPS)); + + if (FAILED(hr)) { + return false; + } + + hr = mDevice->CreatePixelShader((DWORD*)SolidColorShaderPS, + getter_AddRefs(mSolidColorPS)); + + if (FAILED(hr)) { + return false; + } + + hr = mDevice->CreateVertexBuffer(sizeof(vertex) * 4, + D3DUSAGE_WRITEONLY, + 0, + D3DPOOL_DEFAULT, + getter_AddRefs(mVB), + NULL); + + if (FAILED(hr)) { + return false; + } + + vertex *vertices; + hr = mVB->Lock(0, 0, (void**)&vertices, 0); + if (FAILED(hr)) { + return false; + } + + vertices[0].x = vertices[0].y = 0; + vertices[1].x = 1; vertices[1].y = 0; + vertices[2].x = 0; vertices[2].y = 1; + vertices[3].x = 1; vertices[3].y = 1; + + mVB->Unlock(); + + hr = mDevice->SetStreamSource(0, mVB, 0, sizeof(vertex)); + if (FAILED(hr)) { + return false; + } + + D3DVERTEXELEMENT9 elements[] = { + { 0, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, + D3DDECLUSAGE_POSITION, 0 }, + D3DDECL_END() + }; + + mDevice->CreateVertexDeclaration(elements, getter_AddRefs(mVD)); + + nsCOMPtr + console(do_GetService(NS_CONSOLESERVICE_CONTRACTID)); + + D3DADAPTER_IDENTIFIER9 identifier; + mD3D9->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, &identifier); + + if (console) { + nsString msg; + msg += + NS_LITERAL_STRING("Direct3D 9 DeviceManager Initialized Succesfully.\nDriver: "); + msg += NS_ConvertUTF8toUTF16( + nsDependentCString((const char*)identifier.Driver)); + msg += NS_LITERAL_STRING("\nDescription: "); + msg += NS_ConvertUTF8toUTF16( + nsDependentCString((const char*)identifier.Description)); + msg += NS_LITERAL_STRING("\nVersion: "); + msg += NS_ConvertUTF8toUTF16( + nsPrintfCString("%d.%d.%d.%d", + HIWORD(identifier.DriverVersion.HighPart), + LOWORD(identifier.DriverVersion.HighPart), + HIWORD(identifier.DriverVersion.LowPart), + LOWORD(identifier.DriverVersion.LowPart))); + console->LogStringMessage(msg.get()); + } + + return true; +} + +void +DeviceManagerD3D9::SetupRenderState() +{ + mDevice->SetStreamSource(0, mVB, 0, sizeof(vertex)); + mDevice->SetVertexDeclaration(mVD); + mDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + mDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + mDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD); + mDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + mDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE); + mDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE); + mDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); + mDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); + mDevice->SetSamplerState(1, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); + mDevice->SetSamplerState(1, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); + mDevice->SetSamplerState(2, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); + mDevice->SetSamplerState(2, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); +} + +already_AddRefed +DeviceManagerD3D9::CreateSwapChain(HWND hWnd) +{ + nsRefPtr swapChain = new SwapChainD3D9(this); + + if (!swapChain->Init(hWnd)) { + return nsnull; + } + + return swapChain.forget(); +} + +void +DeviceManagerD3D9::SetShaderMode(ShaderMode aMode) +{ + switch (aMode) { + case RGBLAYER: + mDevice->SetVertexShader(mLayerVS); + mDevice->SetPixelShader(mRGBPS); + break; + case YCBCRLAYER: + mDevice->SetVertexShader(mLayerVS); + mDevice->SetPixelShader(mYCbCrPS); + break; + case SOLIDCOLORLAYER: + mDevice->SetVertexShader(mLayerVS); + mDevice->SetPixelShader(mSolidColorPS); + break; + } +} + +bool +DeviceManagerD3D9::VerifyReadyForRendering() +{ + HRESULT hr = mDevice->TestCooperativeLevel(); + + if (SUCCEEDED(hr)) { + if (IsD3D9Ex()) { + hr = mDeviceEx->CheckDeviceState(mFocusWnd); + if (FAILED(hr)) { + D3DPRESENT_PARAMETERS pp; + memset(&pp, 0, sizeof(D3DPRESENT_PARAMETERS)); + + pp.BackBufferWidth = 1; + pp.BackBufferHeight = 1; + pp.BackBufferFormat = D3DFMT_A8R8G8B8; + pp.SwapEffect = D3DSWAPEFFECT_DISCARD; + pp.Windowed = TRUE; + pp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; + pp.hDeviceWindow = mFocusWnd; + + hr = mDeviceEx->ResetEx(&pp, NULL); + // Handle D3DERR_DEVICEREMOVED! + if (FAILED(hr)) { + return false; + } + } + } + return true; + } + + if (hr != D3DERR_DEVICENOTRESET) { + return false; + } + + for(unsigned int i = 0; i < mThebesLayers.Length(); i++) { + mThebesLayers[i]->CleanResources(); + } + for(unsigned int i = 0; i < mSwapChains.Length(); i++) { + mSwapChains[i]->Reset(); + } + + D3DPRESENT_PARAMETERS pp; + memset(&pp, 0, sizeof(D3DPRESENT_PARAMETERS)); + + pp.BackBufferWidth = 1; + pp.BackBufferHeight = 1; + pp.BackBufferFormat = D3DFMT_A8R8G8B8; + pp.SwapEffect = D3DSWAPEFFECT_DISCARD; + pp.Windowed = TRUE; + pp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; + pp.hDeviceWindow = mFocusWnd; + + hr = mDevice->Reset(&pp); + + if (FAILED(hr)) { + return false; + } + + return true; +} + +bool +DeviceManagerD3D9::VerifyCaps() +{ + D3DCAPS9 caps; + HRESULT hr = mDevice->GetDeviceCaps(&caps); + + if (FAILED(hr)) { + return false; + } + + if (LACKS_CAP(caps.DevCaps, D3DDEVCAPS_TEXTUREVIDEOMEMORY)) { + return false; + } + + if (LACKS_CAP(caps.PrimitiveMiscCaps, D3DPMISCCAPS_CULLNONE)) { + return false; + } + + if (LACKS_CAP(caps.SrcBlendCaps, D3DPBLENDCAPS_ONE) || + LACKS_CAP(caps.SrcBlendCaps, D3DBLEND_SRCALPHA) || + LACKS_CAP(caps.DestBlendCaps, D3DPBLENDCAPS_INVSRCALPHA)) { + return false; + } + + if (LACKS_CAP(caps.RasterCaps, D3DPRASTERCAPS_SCISSORTEST)) { + return false; + } + + if (LACKS_CAP(caps.TextureCaps, D3DPTEXTURECAPS_ALPHA) || + HAS_CAP(caps.TextureCaps, D3DPTEXTURECAPS_SQUAREONLY) || + (HAS_CAP(caps.TextureCaps, D3DPTEXTURECAPS_POW2) && + LACKS_CAP(caps.TextureCaps, D3DPTEXTURECAPS_NONPOW2CONDITIONAL))) { + return false; + } + + if (LACKS_CAP(caps.TextureFilterCaps, D3DPTFILTERCAPS_MAGFLINEAR) || + LACKS_CAP(caps.TextureFilterCaps, D3DPTFILTERCAPS_MINFLINEAR)) { + return false; + } + + if (LACKS_CAP(caps.TextureAddressCaps, D3DPTADDRESSCAPS_CLAMP)) { + return false; + } + + if (caps.MaxTextureHeight < 4096 || + caps.MaxTextureWidth < 4096) { + return false; + } + + if ((caps.PixelShaderVersion & 0xffff) < 0x200 || + (caps.VertexShaderVersion & 0xffff) < 0x200) { + return false; + } + + if (HAS_CAP(caps.Caps2, D3DCAPS2_DYNAMICTEXTURES)) { + mHasDynamicTextures = true; + } + + return true; +} + +} /* namespace layers */ +} /* namespace mozilla */ diff --git a/gfx/layers/d3d9/DeviceManagerD3D9.h b/gfx/layers/d3d9/DeviceManagerD3D9.h new file mode 100644 index 000000000000..b3b921142447 --- /dev/null +++ b/gfx/layers/d3d9/DeviceManagerD3D9.h @@ -0,0 +1,207 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Corporation code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bas Schouten + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef GFX_DEVICEMANAGERD3D9_H +#define GFX_DEVICEMANAGERD3D9_H + +#include "gfxTypes.h" +#include "nsRect.h" +#include "nsAutoPtr.h" +#include "d3d9.h" +#include "nsTArray.h" + +namespace mozilla { +namespace layers { + +class DeviceManagerD3D9; +class ThebesLayerD3D9; + +/** + * SwapChain class, this class manages the swap chain belonging to a + * LayerManagerD3D9. + */ +class THEBES_API SwapChainD3D9 +{ + NS_INLINE_DECL_REFCOUNTING(SwapChainD3D9) +public: + ~SwapChainD3D9(); + + /** + * This function will prepare the device this swap chain belongs to for + * rendering to this swap chain. Only after calling this function can the + * swap chain be drawn to, and only until this function is called on another + * swap chain belonging to this device will the device draw to it. Passed in + * is the size of the swap chain. If the window size differs from the size + * during the last call to this function the swap chain will resize. Note that + * in no case does this function guarantee the backbuffer to still have its + * old content. + */ + bool PrepareForRendering(); + + /** + * This function will present the selected rectangle of the swap chain to + * its associated window. + */ + void Present(const nsIntRect &aRect); + +private: + friend class DeviceManagerD3D9; + + SwapChainD3D9(DeviceManagerD3D9 *aDeviceManager); + + bool Init(HWND hWnd); + + /** + * This causes us to release our swap chain, clearing out our resource usage + * so the master device may reset. + */ + void Reset(); + + nsRefPtr mSwapChain; + nsRefPtr mDeviceManager; + HWND mWnd; +}; + +/** + * Device manager, this class is used by the layer managers to share the D3D9 + * device and create swap chains for the individual windows the layer managers + * belong to. + */ +class THEBES_API DeviceManagerD3D9 +{ +public: + DeviceManagerD3D9(); + + // We want the nsrefcnt return value. So we cannot use the inline refcnt macro + NS_IMPL_ADDREF(DeviceManagerD3D9) + NS_IMPL_RELEASE(DeviceManagerD3D9) + + bool Init(); + + /** + * Sets up the render state for the device for layer rendering. + */ + void SetupRenderState(); + + /** + * Create a swap chain setup to work with the specified window. + */ + already_AddRefed CreateSwapChain(HWND hWnd); + + IDirect3DDevice9 *device() { return mDevice; } + + bool IsD3D9Ex() { return mDeviceEx; } + + bool HasDynamicTextures() { return mHasDynamicTextures; } + + enum ShaderMode { + RGBLAYER, + YCBCRLAYER, + SOLIDCOLORLAYER + }; + + void SetShaderMode(ShaderMode aMode); + + /** + * We keep a list of all thebes layers since we need their D3DPOOL_DEFAULT + * surfaces to be released when we want to reset the device. + */ + nsTArray mThebesLayers; +private: + friend class SwapChainD3D9; + + /** + * This function verifies the device is ready for rendering, internally this + * will test the cooperative level of the device and reset the device if + * needed. If this returns false subsequent rendering calls may return errors. + */ + bool VerifyReadyForRendering(); + + /* Array used to store all swap chains for device resets */ + nsTArray mSwapChains; + + /* The D3D device we use */ + nsRefPtr mDevice; + + /* The D3D9Ex device - only valid on Vista+ with WDDM */ + nsRefPtr mDeviceEx; + + /* An instance of the D3D9 object */ + nsRefPtr mD3D9; + + /* An instance of the D3D9Ex object - only valid on Vista+ with WDDM */ + nsRefPtr mD3D9Ex; + + /* Vertex shader used for layer quads */ + nsRefPtr mLayerVS; + + /* Pixel shader used for RGB textures */ + nsRefPtr mRGBPS; + + /* Pixel shader used for RGB textures */ + nsRefPtr mYCbCrPS; + + /* Pixel shader used for solid colors */ + nsRefPtr mSolidColorPS; + + /* Vertex buffer containing our basic vertex structure */ + nsRefPtr mVB; + + /* Our vertex declaration */ + nsRefPtr mVD; + + /* Our focus window - this is really a dummy window we can associate our + * device with. + */ + HWND mFocusWnd; + + /* If this device supports dynamic textures */ + bool mHasDynamicTextures; + + nsAutoRefCnt mRefCnt; + NS_DECL_OWNINGTHREAD + + /** + * Verifies all required device capabilities are present. + */ + bool VerifyCaps(); +}; + +} /* namespace layers */ +} /* namespace mozilla */ + +#endif /* GFX_DEVICEMANAGERD3D9_H */ diff --git a/gfx/layers/d3d9/ImageLayerD3D9.cpp b/gfx/layers/d3d9/ImageLayerD3D9.cpp index d4fba66d40ed..eedbd1d1847a 100644 --- a/gfx/layers/d3d9/ImageLayerD3D9.cpp +++ b/gfx/layers/d3d9/ImageLayerD3D9.cpp @@ -185,7 +185,7 @@ ImageLayerD3D9::RenderLayer() opacity[0] = GetOpacity(); device()->SetPixelShaderConstantF(0, opacity, 1); - mD3DManager->SetShaderMode(LayerManagerD3D9::YCBCRLAYER); + mD3DManager->SetShaderMode(DeviceManagerD3D9::YCBCRLAYER); device()->SetTexture(0, yuvImage->mYTexture); device()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); @@ -227,7 +227,7 @@ ImageLayerD3D9::RenderLayer() opacity[0] = GetOpacity(); device()->SetPixelShaderConstantF(0, opacity, 1); - mD3DManager->SetShaderMode(LayerManagerD3D9::RGBLAYER); + mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBLAYER); device()->SetTexture(0, cairoImage->mTexture); device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); @@ -244,6 +244,7 @@ PlanarYCbCrImageD3D9::PlanarYCbCrImageD3D9(mozilla::layers::LayerManagerD3D9* aM void PlanarYCbCrImageD3D9::SetData(const PlanarYCbCrImage::Data &aData) { + // XXX - For D3D9Ex we really should just copy to systemmem surfaces here. // For now, we copy the data int width_shift = 0; int height_shift = 0; @@ -309,76 +310,116 @@ PlanarYCbCrImageD3D9::AllocateTextures() { - D3DLOCKED_RECT lockrect; + D3DLOCKED_RECT lockrectY; + D3DLOCKED_RECT lockrectCb; + D3DLOCKED_RECT lockrectCr; PRUint8* src; PRUint8* dest; - //XXX: ensure correct usage flags - mManager->device()->CreateTexture(mData.mYSize.width, mData.mYSize.height, - 1, 0, D3DFMT_L8, D3DPOOL_MANAGED, - getter_AddRefs(mYTexture), NULL); + nsRefPtr tmpSurfaceY; + nsRefPtr tmpSurfaceCb; + nsRefPtr tmpSurfaceCr; - /* lock the entire texture */ - mYTexture->LockRect(0, &lockrect, NULL, 0); + if (mManager->deviceManager()->IsD3D9Ex()) { + // D3D9Ex does not support the managed pool, could use dynamic textures + // here. But since an Image is immutable static textures are probably a + // better idea. + mManager->device()->CreateTexture(mData.mYSize.width, mData.mYSize.height, + 1, 0, D3DFMT_L8, D3DPOOL_DEFAULT, + getter_AddRefs(mYTexture), NULL); + mManager->device()->CreateTexture(mData.mCbCrSize.width, mData.mCbCrSize.height, + 1, 0, D3DFMT_L8, D3DPOOL_DEFAULT, + getter_AddRefs(mCbTexture), NULL); + mManager->device()->CreateTexture(mData.mCbCrSize.width, mData.mCbCrSize.height, + 1, 0, D3DFMT_L8, D3DPOOL_DEFAULT, + getter_AddRefs(mCrTexture), NULL); + mManager->device()->CreateOffscreenPlainSurface(mData.mYSize.width, + mData.mYSize.height, + D3DFMT_L8, + D3DPOOL_SYSTEMMEM, + getter_AddRefs(tmpSurfaceY), + NULL); + mManager->device()->CreateOffscreenPlainSurface(mData.mCbCrSize.width, + mData.mCbCrSize.height, + D3DFMT_L8, + D3DPOOL_SYSTEMMEM, + getter_AddRefs(tmpSurfaceCb), + NULL); + mManager->device()->CreateOffscreenPlainSurface(mData.mCbCrSize.width, + mData.mCbCrSize.height, + D3DFMT_L8, + D3DPOOL_SYSTEMMEM, + getter_AddRefs(tmpSurfaceCr), + NULL); + tmpSurfaceY->LockRect(&lockrectY, NULL, 0); + tmpSurfaceCb->LockRect(&lockrectCb, NULL, 0); + tmpSurfaceCr->LockRect(&lockrectCr, NULL, 0); + } else { + mManager->device()->CreateTexture(mData.mYSize.width, mData.mYSize.height, + 1, 0, D3DFMT_L8, D3DPOOL_MANAGED, + getter_AddRefs(mYTexture), NULL); + mManager->device()->CreateTexture(mData.mCbCrSize.width, mData.mCbCrSize.height, + 1, 0, D3DFMT_L8, D3DPOOL_MANAGED, + getter_AddRefs(mCbTexture), NULL); + mManager->device()->CreateTexture(mData.mCbCrSize.width, mData.mCbCrSize.height, + 1, 0, D3DFMT_L8, D3DPOOL_MANAGED, + getter_AddRefs(mCrTexture), NULL); + + /* lock the entire texture */ + mYTexture->LockRect(0, &lockrectY, NULL, 0); + mCbTexture->LockRect(0, &lockrectCb, NULL, 0); + mCrTexture->LockRect(0, &lockrectCr, NULL, 0); + } src = mData.mYChannel; //FIX cast - dest = (PRUint8*)lockrect.pBits; + dest = (PRUint8*)lockrectY.pBits; // copy over data for (int h=0; hUnlockRect(0); - - //XXX: ensure correct usage flags - mManager->device()->CreateTexture(mData.mCbCrSize.width, mData.mCbCrSize.height, - 1, 0, D3DFMT_L8, D3DPOOL_MANAGED, - getter_AddRefs(mCbTexture), NULL); - - - /* lock the entire texture */ - mCbTexture->LockRect(0, &lockrect, NULL, 0); - src = mData.mCbChannel; //FIX cast - dest = (PRUint8*)lockrect.pBits; + dest = (PRUint8*)lockrectCb.pBits; // copy over data for (int h=0; hUnlockRect(0); - - - //XXX: ensure correct usage flags - mManager->device()->CreateTexture(mData.mCbCrSize.width, mData.mCbCrSize.height, - 1, 0, D3DFMT_L8, D3DPOOL_MANAGED, - getter_AddRefs(mCrTexture), NULL); - - - /* lock the entire texture */ - mCrTexture->LockRect(0, &lockrect, NULL, 0); - src = mData.mCrChannel; //FIX cast - dest = (PRUint8*)lockrect.pBits; + dest = (PRUint8*)lockrectCr.pBits; // copy over data for (int h=0; hUnlockRect(0); - + if (mManager->deviceManager()->IsD3D9Ex()) { + tmpSurfaceY->UnlockRect(); + tmpSurfaceCb->UnlockRect(); + tmpSurfaceCr->UnlockRect(); + nsRefPtr dstSurface; + mYTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); + mManager->device()->UpdateSurface(tmpSurfaceY, NULL, dstSurface, NULL); + mCbTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); + mManager->device()->UpdateSurface(tmpSurfaceCb, NULL, dstSurface, NULL); + mCrTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); + mManager->device()->UpdateSurface(tmpSurfaceCr, NULL, dstSurface, NULL); + } else { + mYTexture->UnlockRect(0); + mCbTexture->UnlockRect(0); + mCrTexture->UnlockRect(0); + } } void @@ -426,31 +467,49 @@ CairoImageD3D9::SetData(const CairoImage::Data &aData) context->SetSource(aData.mSurface); context->Paint(); - //XXX: make sure we're using the correct usage flags - mManager->device()->CreateTexture(aData.mSize.width, aData.mSize.height, - 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, - getter_AddRefs(mTexture), NULL); + if (mManager->deviceManager()->IsD3D9Ex()) { + // D3D9Ex doesn't support managed textures. We could use dynamic textures + // here but since Images are immutable that probably isn't such a great + // idea. + mManager->device()->CreateTexture(aData.mSize.width, aData.mSize.height, + 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, + getter_AddRefs(mTexture), NULL); + nsRefPtr surface; + mManager->device()->CreateOffscreenPlainSurface(aData.mSize.width, + aData.mSize.height, + D3DFMT_A8R8G8B8, + D3DPOOL_SYSTEMMEM, + getter_AddRefs(surface), + NULL); + D3DLOCKED_RECT lockedRect; + surface->LockRect(&lockedRect, NULL, 0); + for (int y = 0; y < aData.mSize.height; y++) { + memcpy((char*)lockedRect.pBits + lockedRect.Pitch * y, + imageSurface->Data() + imageSurface->Stride() * y, + aData.mSize.width * 4); + } + surface->UnlockRect(); + nsRefPtr dstSurface; + mTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); + mManager->device()->UpdateSurface(surface, NULL, dstSurface, NULL); + } else { + mManager->device()->CreateTexture(aData.mSize.width, aData.mSize.height, + 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, + getter_AddRefs(mTexture), NULL); + D3DLOCKED_RECT lockrect; + /* lock the entire texture */ + mTexture->LockRect(0, &lockrect, NULL, 0); - D3DLOCKED_RECT lockrect; - /* lock the entire texture */ - mTexture->LockRect(0, &lockrect, NULL, 0); + // copy over data. If we don't need to do any swaping we can + // use memcpy + for (int y = 0; y < aData.mSize.height; y++) { + memcpy((char*)lockrect.pBits + lockrect.Pitch * y, + imageSurface->Data() + imageSurface->Stride() * y, + aData.mSize.width * 4); + } - PRUint8* src = imageSurface->Data(); - //FIX cast - PRUint8* dest = (PRUint8*)lockrect.pBits; - - // copy over data. If we don't need to do any swaping we can - // use memcpy - for (int i=0; iUnlockRect(0); } - - mTexture->UnlockRect(0); } already_AddRefed diff --git a/gfx/layers/d3d9/LayerManagerD3D9.cpp b/gfx/layers/d3d9/LayerManagerD3D9.cpp index b29278e6ce14..13a5d56bca63 100644 --- a/gfx/layers/d3d9/LayerManagerD3D9.cpp +++ b/gfx/layers/d3d9/LayerManagerD3D9.cpp @@ -43,25 +43,10 @@ #include "ColorLayerD3D9.h" #include "CanvasLayerD3D9.h" -#include "LayerManagerD3D9Shaders.h" - -#include "nsIServiceManager.h" -#include "nsIConsoleService.h" -#include "nsPrintfCString.h" - namespace mozilla { namespace layers { -struct vertex { - float x, y; -}; - -IDirect3D9 *LayerManagerD3D9::mD3D9 = NULL; - -typedef IDirect3D9* (WINAPI*Direct3DCreate9Func)( - UINT SDKVersion -); - +DeviceManagerD3D9 *LayerManagerD3D9::mDeviceManager = nsnull; LayerManagerD3D9::LayerManagerD3D9(nsIWidget *aWidget) { @@ -72,145 +57,39 @@ LayerManagerD3D9::LayerManagerD3D9(nsIWidget *aWidget) LayerManagerD3D9::~LayerManagerD3D9() { -} + /* Important to release this first since it also holds a reference to the + * device manager + */ + mSwapChain = nsnull; -#define HAS_CAP(a, b) (((a) & (b)) == (b)) -#define LACKS_CAP(a, b) !(((a) & (b)) == (b)) + if (mDeviceManager) { + if (!mDeviceManager->Release()) { + mDeviceManager = nsnull; + } + } +} PRBool LayerManagerD3D9::Initialize() { - if (!mD3D9) { - Direct3DCreate9Func d3d9create = (Direct3DCreate9Func) - GetProcAddress(LoadLibraryW(L"d3d9.dll"), "Direct3DCreate9"); - if (!d3d9create) { - return PR_FALSE; - } + if (!mDeviceManager) { + mDeviceManager = new DeviceManagerD3D9; - mD3D9 = d3d9create(D3D_SDK_VERSION); - if (!mD3D9) { + if (!mDeviceManager->Init()) { + mDeviceManager = nsnull; return PR_FALSE; } } - D3DPRESENT_PARAMETERS pp; - memset(&pp, 0, sizeof(D3DPRESENT_PARAMETERS)); + mDeviceManager->AddRef(); - pp.BackBufferFormat = D3DFMT_A8R8G8B8; - pp.SwapEffect = D3DSWAPEFFECT_COPY; - pp.Windowed = TRUE; - pp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; - pp.hDeviceWindow = (HWND)mWidget->GetNativeData(NS_NATIVE_WINDOW); + mSwapChain = mDeviceManager-> + CreateSwapChain((HWND)mWidget->GetNativeData(NS_NATIVE_WINDOW)); - HRESULT hr = mD3D9->CreateDevice(D3DADAPTER_DEFAULT, - D3DDEVTYPE_HAL, - NULL, - D3DCREATE_FPU_PRESERVE | - D3DCREATE_MULTITHREADED | - D3DCREATE_MIXED_VERTEXPROCESSING, - &pp, - getter_AddRefs(mDevice)); - - if (FAILED(hr)) { + if (!mSwapChain) { return PR_FALSE; } - if (!VerifyCaps()) { - return PR_FALSE; - } - - hr = mDevice->CreateVertexShader((DWORD*)LayerQuadVS, - getter_AddRefs(mLayerVS)); - - if (FAILED(hr)) { - return PR_FALSE; - } - - hr = mDevice->CreatePixelShader((DWORD*)RGBShaderPS, - getter_AddRefs(mRGBPS)); - - if (FAILED(hr)) { - return PR_FALSE; - } - - hr = mDevice->CreatePixelShader((DWORD*)YCbCrShaderPS, - getter_AddRefs(mYCbCrPS)); - - if (FAILED(hr)) { - return PR_FALSE; - } - - hr = mDevice->CreatePixelShader((DWORD*)SolidColorShaderPS, - getter_AddRefs(mSolidColorPS)); - - if (FAILED(hr)) { - return PR_FALSE; - } - - hr = mDevice->CreateVertexBuffer(sizeof(vertex) * 4, - 0, - 0, - D3DPOOL_MANAGED, - getter_AddRefs(mVB), - NULL); - - if (FAILED(hr)) { - return PR_FALSE; - } - - vertex *vertices; - hr = mVB->Lock(0, 0, (void**)&vertices, 0); - if (FAILED(hr)) { - return PR_FALSE; - } - - vertices[0].x = vertices[0].y = 0; - vertices[1].x = 1; vertices[1].y = 0; - vertices[2].x = 0; vertices[2].y = 1; - vertices[3].x = 1; vertices[3].y = 1; - - mVB->Unlock(); - - hr = mDevice->SetStreamSource(0, mVB, 0, sizeof(vertex)); - if (FAILED(hr)) { - return PR_FALSE; - } - - D3DVERTEXELEMENT9 elements[] = { - { 0, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, - D3DDECLUSAGE_POSITION, 0 }, - D3DDECL_END() - }; - - mDevice->CreateVertexDeclaration(elements, getter_AddRefs(mVD)); - - SetupRenderState(); - - nsCOMPtr - console(do_GetService(NS_CONSOLESERVICE_CONTRACTID)); - - D3DADAPTER_IDENTIFIER9 identifier; - mD3D9->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, &identifier); - - if (console) { - nsString msg; - msg += - NS_LITERAL_STRING("Direct3D 9 LayerManager Initialized Succesfully.\nDriver: "); - msg += NS_ConvertUTF8toUTF16( - nsDependentCString((const char*)identifier.Driver)); - msg += NS_LITERAL_STRING("\nDescription: "); - msg += NS_ConvertUTF8toUTF16( - nsDependentCString((const char*)identifier.Description)); - msg += NS_LITERAL_STRING("\nVersion: "); - msg += NS_ConvertUTF8toUTF16( - nsPrintfCString("%d.%d.%d.%d", - HIWORD(identifier.DriverVersion.HighPart), - LOWORD(identifier.DriverVersion.HighPart), - HIWORD(identifier.DriverVersion.LowPart), - LOWORD(identifier.DriverVersion.LowPart))); - console->LogStringMessage(msg.get()); - } - return PR_TRUE; } @@ -298,38 +177,21 @@ LayerManagerD3D9::CreateImageContainer() return container.forget(); } -void -LayerManagerD3D9::SetShaderMode(ShaderMode aMode) -{ - switch (aMode) { - case RGBLAYER: - mDevice->SetVertexShader(mLayerVS); - mDevice->SetPixelShader(mRGBPS); - break; - case YCBCRLAYER: - mDevice->SetVertexShader(mLayerVS); - mDevice->SetPixelShader(mYCbCrPS); - break; - case SOLIDCOLORLAYER: - mDevice->SetVertexShader(mLayerVS); - mDevice->SetPixelShader(mSolidColorPS); - break; - } -} - void LayerManagerD3D9::Render() { - if (!SetupBackBuffer()) { + if (!mSwapChain->PrepareForRendering()) { return; } + deviceManager()->SetupRenderState(); + SetupPipeline(); nsIntRect rect; mWidget->GetClientBounds(rect); - mDevice->Clear(0, NULL, D3DCLEAR_TARGET, 0xffffffff, 0, 0); + device()->Clear(0, NULL, D3DCLEAR_TARGET, 0x00000000, 0, 0); - mDevice->BeginScene(); + device()->BeginScene(); if (mRootLayer) { const nsIntRect *clipRect = mRootLayer->GetLayer()->GetClipRect(); @@ -344,24 +206,18 @@ LayerManagerD3D9::Render() r.right = rect.width; r.bottom = rect.height; } - mDevice->SetScissorRect(&r); + device()->SetScissorRect(&r); mRootLayer->RenderLayer(); } - mDevice->EndScene(); + device()->EndScene(); if (!mTarget) { const nsIntRect *r; for (nsIntRegionRectIterator iter(mClippingRegion); (r = iter.Next()) != nsnull;) { - RECT rect; - rect.left = r->x; - rect.top = r->y; - rect.right = r->XMost(); - rect.bottom = r->YMost(); - - mDevice->Present(&rect, &rect, NULL, NULL); + mSwapChain->Present(*r); } } else { PaintToTarget(); @@ -387,86 +243,29 @@ LayerManagerD3D9::SetupPipeline() viewMatrix[3][1] = 1.0f; viewMatrix[3][3] = 1.0f; - HRESULT hr = mDevice->SetVertexShaderConstantF(8, &viewMatrix[0][0], 4); + HRESULT hr = device()->SetVertexShaderConstantF(8, &viewMatrix[0][0], 4); if (FAILED(hr)) { NS_WARNING("Failed to set projection shader constant!"); } } -PRBool -LayerManagerD3D9::SetupBackBuffer() -{ - nsRefPtr backBuffer; - mDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, - getter_AddRefs(backBuffer)); - - D3DSURFACE_DESC desc; - nsIntRect rect; - mWidget->GetClientBounds(rect); - backBuffer->GetDesc(&desc); - - HRESULT hr = mDevice->TestCooperativeLevel(); - - /* The device is lost or something else is wrong, failure */ - if (FAILED(hr) && hr != D3DERR_DEVICENOTRESET) { - return PR_FALSE; - } - - /* - * If the backbuffer is the right size, and the device is not lost, we can - * safely render without doing anything. - */ - if ((desc.Width == rect.width && desc.Height == rect.height) && - SUCCEEDED(hr)) { - return PR_TRUE; - } - - /* - * Our device is lost or our backbuffer needs resizing, start by clearing - * out all D3DPOOL_DEFAULT surfaces. - */ - for(unsigned int i = 0; i < mThebesLayers.Length(); i++) { - mThebesLayers[i]->CleanResources(); - } - - backBuffer = NULL; - - D3DPRESENT_PARAMETERS pp; - memset(&pp, 0, sizeof(D3DPRESENT_PARAMETERS)); - - pp.BackBufferFormat = D3DFMT_A8R8G8B8; - pp.SwapEffect = D3DSWAPEFFECT_COPY; - pp.Windowed = TRUE; - pp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; - pp.hDeviceWindow = (HWND)mWidget->GetNativeData(NS_NATIVE_WINDOW); - - hr = mDevice->Reset(&pp); - if (FAILED(hr)) { - return PR_FALSE; - } - - SetupRenderState(); - - return PR_TRUE; -} - void LayerManagerD3D9::PaintToTarget() { nsRefPtr backBuff; nsRefPtr destSurf; - mDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, + device()->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, getter_AddRefs(backBuff)); D3DSURFACE_DESC desc; backBuff->GetDesc(&desc); - mDevice->CreateOffscreenPlainSurface(desc.Width, desc.Height, + device()->CreateOffscreenPlainSurface(desc.Width, desc.Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, getter_AddRefs(destSurf), NULL); - mDevice->GetRenderTargetData(backBuff, destSurf); + device()->GetRenderTargetData(backBuff, destSurf); D3DLOCKED_RECT rect; destSurf->LockRect(&rect, NULL, D3DLOCK_READONLY); @@ -483,81 +282,6 @@ LayerManagerD3D9::PaintToTarget() destSurf->UnlockRect(); } -void -LayerManagerD3D9::SetupRenderState() -{ - mDevice->SetStreamSource(0, mVB, 0, sizeof(vertex)); - mDevice->SetVertexDeclaration(mVD); - mDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); - mDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); - mDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD); - mDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); - mDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE); - mDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE); - mDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); - mDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); - mDevice->SetSamplerState(1, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); - mDevice->SetSamplerState(1, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); - mDevice->SetSamplerState(2, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); - mDevice->SetSamplerState(2, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); -} - -PRBool -LayerManagerD3D9::VerifyCaps() -{ - D3DCAPS9 caps; - HRESULT hr = mDevice->GetDeviceCaps(&caps); - - if (FAILED(hr)) { - return PR_FALSE; - } - - if (LACKS_CAP(caps.DevCaps, D3DDEVCAPS_TEXTUREVIDEOMEMORY)) { - return PR_FALSE; - } - - if (LACKS_CAP(caps.PrimitiveMiscCaps, D3DPMISCCAPS_CULLNONE)) { - return PR_FALSE; - } - - if (LACKS_CAP(caps.SrcBlendCaps, D3DPBLENDCAPS_ONE) || - LACKS_CAP(caps.SrcBlendCaps, D3DBLEND_SRCALPHA) || - LACKS_CAP(caps.DestBlendCaps, D3DPBLENDCAPS_INVSRCALPHA)) { - return PR_FALSE; - } - - if (LACKS_CAP(caps.RasterCaps, D3DPRASTERCAPS_SCISSORTEST)) { - return PR_FALSE; - } - - if (LACKS_CAP(caps.TextureCaps, D3DPTEXTURECAPS_ALPHA) || - HAS_CAP(caps.TextureCaps, D3DPTEXTURECAPS_SQUAREONLY) || - (HAS_CAP(caps.TextureCaps, D3DPTEXTURECAPS_POW2) && - LACKS_CAP(caps.TextureCaps, D3DPTEXTURECAPS_NONPOW2CONDITIONAL))) { - return PR_FALSE; - } - - if (LACKS_CAP(caps.TextureFilterCaps, D3DPTFILTERCAPS_MAGFLINEAR) || - LACKS_CAP(caps.TextureFilterCaps, D3DPTFILTERCAPS_MINFLINEAR)) { - return PR_FALSE; - } - - if (LACKS_CAP(caps.TextureAddressCaps, D3DPTADDRESSCAPS_CLAMP)) { - return PR_FALSE; - } - - if (caps.MaxTextureHeight < 4096 || - caps.MaxTextureWidth < 4096) { - return PR_FALSE; - } - - if ((caps.PixelShaderVersion & 0xffff) < 0x200 || - (caps.VertexShaderVersion & 0xffff) < 0x200) { - return PR_FALSE; - } - return PR_TRUE; -} - LayerD3D9::LayerD3D9(LayerManagerD3D9 *aManager) : mD3DManager(aManager) { diff --git a/gfx/layers/d3d9/LayerManagerD3D9.h b/gfx/layers/d3d9/LayerManagerD3D9.h index b17c9a4a125b..56f7bcaad45c 100644 --- a/gfx/layers/d3d9/LayerManagerD3D9.h +++ b/gfx/layers/d3d9/LayerManagerD3D9.h @@ -46,6 +46,8 @@ #include "gfxContext.h" #include "nsIWidget.h" +#include "DeviceManagerD3D9.h" + namespace mozilla { namespace layers { @@ -122,49 +124,27 @@ public: */ void SetClippingEnabled(PRBool aEnabled); - IDirect3DDevice9 *device() const { return mDevice; } + void SetShaderMode(DeviceManagerD3D9::ShaderMode aMode) + { mDeviceManager->SetShaderMode(aMode); } - enum ShaderMode { - RGBLAYER, - YCBCRLAYER, - SOLIDCOLORLAYER - }; - - void SetShaderMode(ShaderMode aMode); - - nsTArray mThebesLayers; + IDirect3DDevice9 *device() const { return mDeviceManager->device(); } + DeviceManagerD3D9 *deviceManager() const { return mDeviceManager; } private: - /* Direct3D9 instance */ - static IDirect3D9 *mD3D9; + /* Device manager instance */ + static DeviceManagerD3D9 *mDeviceManager; + + /* Swap chain associated with this layer manager */ + nsRefPtr mSwapChain; /* Widget associated with this layer manager */ nsIWidget *mWidget; + /* * Context target, NULL when drawing directly to our swap chain. */ nsRefPtr mTarget; - nsRefPtr mDevice; - - /* Vertex shader used for layer quads */ - nsRefPtr mLayerVS; - - /* Pixel shader used for RGB textures */ - nsRefPtr mRGBPS; - - /* Pixel shader used for RGB textures */ - nsRefPtr mYCbCrPS; - - /* Pixel shader used for solid colors */ - nsRefPtr mSolidColorPS; - - /* Vertex buffer containing our basic vertex structure */ - nsRefPtr mVB; - - /* Our vertex declaration */ - nsRefPtr mVD; - /* Current root layer. */ LayerD3D9 *mRootLayer; @@ -175,32 +155,21 @@ private: * Region we're clipping our current drawing to. */ nsIntRegion mClippingRegion; + /* * Render the current layer tree to the active target. */ void Render(); + /* * Setup the pipeline. */ void SetupPipeline(); - /* - * Setup the backbuffer. - * - * \return PR_TRUE if setup was succesful - */ - PRBool SetupBackBuffer(); - /* - * Setup the render state for the surface. - */ - void SetupRenderState(); + /* * Copies the content of our backbuffer to the set transaction target. */ void PaintToTarget(); - /* - * Verifies all required device capabilities are present. - */ - PRBool VerifyCaps(); }; diff --git a/gfx/layers/d3d9/ThebesLayerD3D9.cpp b/gfx/layers/d3d9/ThebesLayerD3D9.cpp index ac659e431284..786b2fb5aad0 100644 --- a/gfx/layers/d3d9/ThebesLayerD3D9.cpp +++ b/gfx/layers/d3d9/ThebesLayerD3D9.cpp @@ -39,6 +39,9 @@ #include "gfxPlatform.h" #include "gfxWindowsPlatform.h" +#ifdef CAIRO_HAS_D2D_SURFACE +#include "gfxD2DSurface.h" +#endif namespace mozilla { namespace layers { @@ -69,12 +72,12 @@ ThebesLayerD3D9::ThebesLayerD3D9(LayerManagerD3D9 *aManager) , LayerD3D9(aManager) { mImplData = static_cast(this); - aManager->mThebesLayers.AppendElement(this); + aManager->deviceManager()->mThebesLayers.AppendElement(this); } ThebesLayerD3D9::~ThebesLayerD3D9() { - mD3DManager->mThebesLayers.RemoveElement(this); + mD3DManager->deviceManager()->mThebesLayers.RemoveElement(this); } /** @@ -101,7 +104,8 @@ ThebesLayerD3D9::SetVisibleRegion(const nsIntRegion &aRegion) return; } - D3DFORMAT fmt = UseOpaqueSurface(this) ? D3DFMT_X8R8G8B8 : D3DFMT_A8R8G8B8; + D3DFORMAT fmt = (UseOpaqueSurface(this) && !mD2DSurface) ? + D3DFMT_X8R8G8B8 : D3DFMT_A8R8G8B8; D3DSURFACE_DESC desc; mTexture->GetLevelDesc(0, &desc); @@ -116,10 +120,8 @@ ThebesLayerD3D9::SetVisibleRegion(const nsIntRegion &aRegion) nsIntRect oldBounds = oldVisibleRegion.GetBounds(); nsIntRect newBounds = mVisibleRegion.GetBounds(); - - device()->CreateTexture(newBounds.width, newBounds.height, 1, - D3DUSAGE_RENDERTARGET, fmt, - D3DPOOL_DEFAULT, getter_AddRefs(mTexture), NULL); + + CreateNewTexture(gfxIntSize(newBounds.width, newBounds.height)); // Old visible region will become the region that is covered by both the // old and the new visible region. @@ -191,13 +193,14 @@ ThebesLayerD3D9::RenderLayer() if (mVisibleRegion.IsEmpty()) { return; } - HRESULT hr; nsIntRect visibleRect = mVisibleRegion.GetBounds(); // We differentiate between these formats since D3D9 will only allow us to // call GetDC on an opaque surface. - D3DFORMAT fmt = UseOpaqueSurface(this) ? D3DFMT_X8R8G8B8 : D3DFMT_A8R8G8B8; + D3DFORMAT fmt = (UseOpaqueSurface(this) && !mD2DSurface) ? + D3DFMT_X8R8G8B8 : D3DFMT_A8R8G8B8; + if (mTexture) { D3DSURFACE_DESC desc; mTexture->GetLevelDesc(0, &desc); @@ -211,103 +214,16 @@ ThebesLayerD3D9::RenderLayer() } if (!mTexture) { - device()->CreateTexture(visibleRect.width, visibleRect.height, 1, - D3DUSAGE_RENDERTARGET, fmt, - D3DPOOL_DEFAULT, getter_AddRefs(mTexture), NULL); + CreateNewTexture(gfxIntSize(visibleRect.width, visibleRect.height)); mValidRegion.SetEmpty(); } if (!mValidRegion.IsEqual(mVisibleRegion)) { nsIntRegion region; region.Sub(mVisibleRegion, mValidRegion); - nsIntRect bounds = region.GetBounds(); - gfxASurface::gfxImageFormat imageFormat = gfxASurface::ImageFormatARGB32; - nsRefPtr destinationSurface; - nsRefPtr context; + DrawRegion(region); - nsRefPtr tmpTexture; - device()->CreateTexture(bounds.width, bounds.height, 1, - 0, fmt, - D3DPOOL_SYSTEMMEM, getter_AddRefs(tmpTexture), NULL); - - nsRefPtr surf; - HDC dc; - if (UseOpaqueSurface(this)) { - hr = tmpTexture->GetSurfaceLevel(0, getter_AddRefs(surf)); - - if (FAILED(hr)) { - // Uh-oh, bail. - NS_WARNING("Failed to get texture surface level."); - return; - } - - hr = surf->GetDC(&dc); - - if (FAILED(hr)) { - NS_WARNING("Failed to get device context for texture surface."); - return; - } - - destinationSurface = new gfxWindowsSurface(dc); - } else { - // XXX - We may consider retaining a SYSTEMMEM texture texture the size - // of our DEFAULT texture and then use UpdateTexture and add dirty rects - // to update in a single call. - destinationSurface = - gfxPlatform::GetPlatform()-> - CreateOffscreenSurface(gfxIntSize(bounds.width, - bounds.height), - imageFormat); - } - - context = new gfxContext(destinationSurface); - context->Translate(gfxPoint(-bounds.x, -bounds.y)); - LayerManagerD3D9::CallbackInfo cbInfo = mD3DManager->GetCallbackInfo(); - cbInfo.Callback(this, context, region, nsIntRegion(), cbInfo.CallbackData); - - if (UseOpaqueSurface(this)) { - surf->ReleaseDC(dc); - } else { - D3DLOCKED_RECT r; - tmpTexture->LockRect(0, &r, NULL, 0); - - nsRefPtr imgSurface = - new gfxImageSurface((unsigned char *)r.pBits, - gfxIntSize(bounds.width, - bounds.height), - r.Pitch, - imageFormat); - - context = new gfxContext(imgSurface); - context->SetSource(destinationSurface); - context->SetOperator(gfxContext::OPERATOR_SOURCE); - context->Paint(); - - imgSurface = NULL; - - tmpTexture->UnlockRect(0); - } - - nsRefPtr srcSurface; - nsRefPtr dstSurface; - - mTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); - tmpTexture->GetSurfaceLevel(0, getter_AddRefs(srcSurface)); - - nsIntRegionRectIterator iter(region); - const nsIntRect *iterRect; - while ((iterRect = iter.Next())) { - RECT rect; - rect.left = iterRect->x - bounds.x; - rect.top = iterRect->y - bounds.y; - rect.right = rect.left + iterRect->width; - rect.bottom = rect.top + iterRect->height; - POINT point; - point.x = iterRect->x - visibleRect.x; - point.y = iterRect->y - visibleRect.y; - device()->UpdateSurface(srcSurface, &rect, dstSurface, &point); - } mValidRegion = mVisibleRegion; } @@ -338,7 +254,7 @@ ThebesLayerD3D9::RenderLayer() opacity[0] = GetOpacity(); device()->SetPixelShaderConstantF(0, opacity, 1); - mD3DManager->SetShaderMode(LayerManagerD3D9::RGBLAYER); + mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBLAYER); device()->SetTexture(0, mTexture); device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); @@ -362,5 +278,168 @@ ThebesLayerD3D9::IsEmpty() return !mTexture; } +void +ThebesLayerD3D9::DrawRegion(const nsIntRegion &aRegion) +{ + HRESULT hr; + nsIntRect visibleRect = mVisibleRegion.GetBounds(); + nsRefPtr context; + +#ifdef CAIRO_HAS_D2D_SURFACE + if (mD2DSurface) { + context = new gfxContext(mD2DSurface); + nsIntRegionRectIterator iter(aRegion); + context->Translate(gfxPoint(-visibleRect.x, -visibleRect.y)); + context->NewPath(); + const nsIntRect *iterRect; + while ((iterRect = iter.Next())) { + context->Rectangle(gfxRect(iterRect->x, iterRect->y, iterRect->width, iterRect->height)); + } + context->Clip(); + if (mD2DSurface->GetContentType() != gfxASurface::CONTENT_COLOR) { + context->SetOperator(gfxContext::OPERATOR_CLEAR); + context->Paint(); + context->SetOperator(gfxContext::OPERATOR_OVER); + } + LayerManagerD3D9::CallbackInfo cbInfo = mD3DManager->GetCallbackInfo(); + cbInfo.Callback(this, context, aRegion, nsIntRegion(), cbInfo.CallbackData); + mD2DSurface->Flush(); + + // XXX - This call is quite expensive, we may want to consider doing our + // drawing in a seperate 'validation' iteration. And then flushing once for + // all the D2D surfaces we might have drawn, before doing our D3D9 rendering + // loop. + cairo_d2d_finish_device(gfxWindowsPlatform::GetPlatform()->GetD2DDevice()); + return; + } +#endif + + D3DFORMAT fmt = UseOpaqueSurface(this) ? D3DFMT_X8R8G8B8 : D3DFMT_A8R8G8B8; + nsIntRect bounds = aRegion.GetBounds(); + + gfxASurface::gfxImageFormat imageFormat = gfxASurface::ImageFormatARGB32; + nsRefPtr destinationSurface; + + nsRefPtr tmpTexture; + device()->CreateTexture(bounds.width, bounds.height, 1, + 0, fmt, + D3DPOOL_SYSTEMMEM, getter_AddRefs(tmpTexture), NULL); + + nsRefPtr surf; + HDC dc; + if (UseOpaqueSurface(this)) { + hr = tmpTexture->GetSurfaceLevel(0, getter_AddRefs(surf)); + + if (FAILED(hr)) { + // Uh-oh, bail. + NS_WARNING("Failed to get texture surface level."); + return; + } + + hr = surf->GetDC(&dc); + + if (FAILED(hr)) { + NS_WARNING("Failed to get device context for texture surface."); + return; + } + + destinationSurface = new gfxWindowsSurface(dc); + } else { + // XXX - We may consider retaining a SYSTEMMEM texture texture the size + // of our DEFAULT texture and then use UpdateTexture and add dirty rects + // to update in a single call. + destinationSurface = + gfxPlatform::GetPlatform()-> + CreateOffscreenSurface(gfxIntSize(bounds.width, + bounds.height), + imageFormat); + } + + context = new gfxContext(destinationSurface); + context->Translate(gfxPoint(-bounds.x, -bounds.y)); + LayerManagerD3D9::CallbackInfo cbInfo = mD3DManager->GetCallbackInfo(); + cbInfo.Callback(this, context, aRegion, nsIntRegion(), cbInfo.CallbackData); + + if (UseOpaqueSurface(this)) { + surf->ReleaseDC(dc); + } else { + D3DLOCKED_RECT r; + tmpTexture->LockRect(0, &r, NULL, 0); + + nsRefPtr imgSurface = + new gfxImageSurface((unsigned char *)r.pBits, + gfxIntSize(bounds.width, + bounds.height), + r.Pitch, + imageFormat); + + context = new gfxContext(imgSurface); + context->SetSource(destinationSurface); + context->SetOperator(gfxContext::OPERATOR_SOURCE); + context->Paint(); + + imgSurface = NULL; + + tmpTexture->UnlockRect(0); + } + + nsRefPtr srcSurface; + nsRefPtr dstSurface; + + mTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); + tmpTexture->GetSurfaceLevel(0, getter_AddRefs(srcSurface)); + + nsIntRegionRectIterator iter(aRegion); + const nsIntRect *iterRect; + while ((iterRect = iter.Next())) { + RECT rect; + rect.left = iterRect->x - bounds.x; + rect.top = iterRect->y - bounds.y; + rect.right = rect.left + iterRect->width; + rect.bottom = rect.top + iterRect->height; + POINT point; + point.x = iterRect->x - visibleRect.x; + point.y = iterRect->y - visibleRect.y; + device()->UpdateSurface(srcSurface, &rect, dstSurface, &point); + } +} + +void +ThebesLayerD3D9::CreateNewTexture(const gfxIntSize &aSize) +{ + if (aSize.width == 0 | aSize.height == 0) { + // Nothing to do. + return; + } + + mTexture = nsnull; +#ifdef CAIRO_HAS_D2D_SURFACE + if (gfxWindowsPlatform::GetPlatform()->GetRenderMode() == + gfxWindowsPlatform::RENDER_DIRECT2D) { + if (mD3DManager->deviceManager()->IsD3D9Ex()) { + // We should have D3D9Ex where we have D2D. + HANDLE sharedHandle = 0; + device()->CreateTexture(aSize.width, aSize.height, 1, + D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, + D3DPOOL_DEFAULT, getter_AddRefs(mTexture), &sharedHandle); + + mD2DSurface = new gfxD2DSurface(sharedHandle, UseOpaqueSurface(this) ? + gfxASurface::CONTENT_COLOR : gfxASurface::CONTENT_COLOR_ALPHA); + + // If there's an error, go on and do what we always do. + if (mD2DSurface->CairoStatus()) { + mD2DSurface = nsnull; + mTexture = nsnull; + } + } + } +#endif + if (!mTexture) { + device()->CreateTexture(aSize.width, aSize.height, 1, + D3DUSAGE_RENDERTARGET, UseOpaqueSurface(this) ? D3DFMT_X8R8G8B8 : D3DFMT_A8R8G8B8, + D3DPOOL_DEFAULT, getter_AddRefs(mTexture), NULL); + } +} + } /* namespace layers */ } /* namespace mozilla */ diff --git a/gfx/layers/d3d9/ThebesLayerD3D9.h b/gfx/layers/d3d9/ThebesLayerD3D9.h index 13bb29f3da17..d51a9a86d4c5 100644 --- a/gfx/layers/d3d9/ThebesLayerD3D9.h +++ b/gfx/layers/d3d9/ThebesLayerD3D9.h @@ -42,7 +42,6 @@ #include "LayerManagerD3D9.h" #include "gfxImageSurface.h" - namespace mozilla { namespace layers { @@ -70,6 +69,15 @@ private: * D3D9 texture */ nsRefPtr mTexture; + + /* This contains the D2D surface if we have one */ + nsRefPtr mD2DSurface; + + /* Have a region of our layer drawn */ + void DrawRegion(const nsIntRegion &aRegion); + + /* Create a new texture */ + void CreateNewTexture(const gfxIntSize &aSize); }; } /* layers */ diff --git a/gfx/thebes/gfxD2DSurface.cpp b/gfx/thebes/gfxD2DSurface.cpp index dc0a92fec3be..dfa1ef1884f5 100644 --- a/gfx/thebes/gfxD2DSurface.cpp +++ b/gfx/thebes/gfxD2DSurface.cpp @@ -36,10 +36,22 @@ #include "gfxD2DSurface.h" #include "cairo.h" #include "cairo-win32.h" +#include "gfxWindowsPlatform.h" gfxD2DSurface::gfxD2DSurface(HWND aWnd, gfxContentType aContent) { - Init(cairo_d2d_surface_create_for_hwnd(aWnd, (cairo_content_t)aContent)); + Init(cairo_d2d_surface_create_for_hwnd( + gfxWindowsPlatform::GetPlatform()->GetD2DDevice(), + aWnd, + (cairo_content_t)aContent)); +} + +gfxD2DSurface::gfxD2DSurface(HANDLE handle, gfxContentType aContent) +{ + Init(cairo_d2d_surface_create_for_handle( + gfxWindowsPlatform::GetPlatform()->GetD2DDevice(), + handle, + (cairo_content_t)aContent)); } gfxD2DSurface::gfxD2DSurface(cairo_surface_t *csurf) @@ -50,7 +62,10 @@ gfxD2DSurface::gfxD2DSurface(cairo_surface_t *csurf) gfxD2DSurface::gfxD2DSurface(const gfxIntSize& size, gfxImageFormat imageFormat) { - Init(cairo_d2d_surface_create((cairo_format_t)imageFormat, size.width, size.height)); + Init(cairo_d2d_surface_create( + gfxWindowsPlatform::GetPlatform()->GetD2DDevice(), + (cairo_format_t)imageFormat, + size.width, size.height)); } gfxD2DSurface::~gfxD2DSurface() diff --git a/gfx/thebes/gfxD2DSurface.h b/gfx/thebes/gfxD2DSurface.h index 6d3748cc705f..1a3d641fdc57 100644 --- a/gfx/thebes/gfxD2DSurface.h +++ b/gfx/thebes/gfxD2DSurface.h @@ -51,6 +51,7 @@ public: gfxD2DSurface(const gfxIntSize& size, gfxImageFormat imageFormat = ImageFormatRGB24); + gfxD2DSurface(HANDLE handle, gfxContentType aContent); gfxD2DSurface(cairo_surface_t *csurf); diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp index daca44bd8fed..c58318c5e81a 100644 --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -2235,17 +2235,19 @@ gfxFontGroup::InitTextRun(gfxContext *aContext, PRUint32 matchedLength = range.Length(); gfxFont *matchedFont = (range.font ? range.font.get() : nsnull); + // create the glyph run for this range + aTextRun->AddGlyphRun(matchedFont ? matchedFont : mainFont, + runStart, (matchedLength > 0)); if (matchedFont) { - // create the glyph run for this range - aTextRun->AddGlyphRun(matchedFont, runStart, (matchedLength > 0)); - // do glyph layout and record the resulting positioned glyphs - matchedFont->InitTextRun(aContext, aTextRun, aString, - runStart, matchedLength, aRunScript); - } else { - // create the glyph run before calling SetMissing Glyph - aTextRun->AddGlyphRun(mainFont, runStart, matchedLength); - + if (!matchedFont->InitTextRun(aContext, aTextRun, aString, + runStart, matchedLength, + aRunScript)) { + // glyph layout failed! treat as missing glyphs + matchedFont = nsnull; + } + } + if (!matchedFont) { for (PRUint32 index = runStart; index < runStart + matchedLength; index++) { // Record the char code so we can draw a box with the Unicode value if (NS_IS_HIGH_SURROGATE(aString[index]) && @@ -3627,6 +3629,8 @@ PRUint32 gfxTextRun::FindFirstGlyphRunContaining(PRUint32 aOffset) { NS_ASSERTION(aOffset <= mCharacterCount, "Bad offset looking for glyphrun"); + NS_ASSERTION(mCharacterCount == 0 || mGlyphRuns.Length() > 0, + "non-empty text but no glyph runs present!"); if (aOffset == mCharacterCount) return mGlyphRuns.Length(); PRUint32 start = 0; diff --git a/gfx/thebes/gfxUniscribeShaper.cpp b/gfx/thebes/gfxUniscribeShaper.cpp index 63dda2c7d6d2..a9785160580a 100644 --- a/gfx/thebes/gfxUniscribeShaper.cpp +++ b/gfx/thebes/gfxUniscribeShaper.cpp @@ -394,48 +394,15 @@ private: PRPackedBool mFontSelected; }; -#define MAX_ITEM_LENGTH 32768 - -static PRUint32 FindNextItemStart(int aOffset, int aLimit, - nsTArray &aLogAttr, - const PRUnichar *aString) -{ - if (aOffset + MAX_ITEM_LENGTH >= aLimit) { - // The item starting at aOffset can't be longer than the max length, - // so starting the next item at aLimit won't cause ScriptShape() to fail. - return aLimit; - } - - // Try to start the next item before or after a space, since spaces - // don't kern or ligate. - PRUint32 off; - int boundary = -1; - for (off = MAX_ITEM_LENGTH; off > 1; --off) { - if (aLogAttr[off].fCharStop) { - if (off > boundary) { - boundary = off; - } - if (aString[aOffset+off] == ' ' || aString[aOffset+off - 1] == ' ') - return aOffset+off; - } - } - - // Try to start the next item at the last cluster boundary in the range. - if (boundary > 0) { - return aOffset+boundary; - } - - // No nice cluster boundaries inside MAX_ITEM_LENGTH characters, break - // on the size limit. It won't be visually plesaing, but at least it - // won't cause ScriptShape() to fail. - return aOffset + MAX_ITEM_LENGTH; -} +#define MAX_ITEM_LENGTH 16384 class Uniscribe { public: - Uniscribe(const PRUnichar *aString, PRUint32 aLength, PRBool aIsRTL) : - mString(aString), mLength(aLength), mIsRTL(aIsRTL) + Uniscribe(const PRUnichar *aString, + PRUint32 aLength, + gfxTextRun *aTextRun): + mString(aString), mLength(aLength), mTextRun(aTextRun) { } ~Uniscribe() { @@ -446,44 +413,79 @@ public: memset(&mState, 0, sizeof(SCRIPT_STATE)); // Lock the direction. Don't allow the itemizer to change directions // based on character type. - mState.uBidiLevel = mIsRTL; + mState.uBidiLevel = mTextRun->IsRightToLeft() ? 1 : 0; mState.fOverrideDirection = PR_TRUE; } private: +// We try to avoid calling Uniscribe with text runs that may generate +// more than this many glyphs, because of the possibility of arithmetic +// overflow of 16-bit variables. If long runs need to be split because +// of this, we'll look for whitespace to break on so that shaping needed +// (e.g. for complex scripts) should be unaffected. +#define MAX_UNISCRIBE_GLYPHS 32767 + // Append mItems[aIndex] to aDest, adding extra items to aDest to ensure // that no item is too long for ScriptShape() to handle. See bug 366643. nsresult CopyItemSplitOversize(int aIndex, nsTArray &aDest) { aDest.AppendElement(mItems[aIndex]); - const int itemLength = mItems[aIndex+1].iCharPos - mItems[aIndex].iCharPos; - if (ESTIMATE_MAX_GLYPHS(itemLength) > 65535) { - // This items length would cause ScriptShape() to fail. We need to - // add extra items here so that no item's length could cause the fail. - - // Get cluster boundaries, so we can break cleanly if possible. - nsTArray logAttr; - if (!logAttr.SetLength(itemLength)) - return NS_ERROR_FAILURE; - HRESULT rv = ScriptBreak(mString+mItems[aIndex].iCharPos, itemLength, - &mItems[aIndex].a, logAttr.Elements()); - if (FAILED(rv)) - return NS_ERROR_FAILURE; + const int itemLength = + mItems[aIndex+1].iCharPos - mItems[aIndex].iCharPos; + if (ESTIMATE_MAX_GLYPHS(itemLength) > MAX_UNISCRIBE_GLYPHS) { + // This item's length would cause ScriptShape() to fail. + // We need to add extra items here so that no item's length + // could cause the fail. + // We break on whitespace or cluster boundaries if possible. const int nextItemStart = mItems[aIndex+1].iCharPos; int start = FindNextItemStart(mItems[aIndex].iCharPos, - nextItemStart, logAttr, mString); + nextItemStart); while (start < nextItemStart) { SCRIPT_ITEM item = mItems[aIndex]; item.iCharPos = start; aDest.AppendElement(item); - start = FindNextItemStart(start, nextItemStart, logAttr, mString); + start = FindNextItemStart(start, nextItemStart); } } return NS_OK; } + PRUint32 FindNextItemStart(int aOffset, int aLimit) { + if (aOffset + MAX_ITEM_LENGTH >= aLimit) { + // The item starting at aOffset can't be longer than max length, + // so starting the next item at aLimit won't cause ScriptShape() + // to fail. + return aLimit; + } + // Try to start the next item before or after a space, since spaces + // don't kern or ligate. + PRInt32 off; + int boundary = -1; + for (off = MAX_ITEM_LENGTH; off > 1; --off) { + if (mTextRun->IsClusterStart(off)) { + if (off > boundary) { + boundary = off; + } + if (mString[aOffset+off] == ' ' || + mString[aOffset+off - 1] == ' ') { + return aOffset+off; + } + } + } + + // Try to start the next item at last cluster boundary in the range. + if (boundary > 0) { + return aOffset+boundary; + } + + // No nice cluster boundaries inside MAX_ITEM_LENGTH characters, break + // on the size limit. It won't be visually pleasing, but at least it + // won't cause ScriptShape() to fail. + return aOffset + MAX_ITEM_LENGTH; + } + public: int Itemize() { @@ -537,7 +539,7 @@ public: private: const PRUnichar *mString; const PRUint32 mLength; - const PRBool mIsRTL; + gfxTextRun *mTextRun; SCRIPT_CONTROL mControl; SCRIPT_STATE mState; @@ -556,15 +558,13 @@ gfxUniscribeShaper::InitTextRun(gfxContext *aContext, { DCFromContext aDC(aContext); - const PRBool isRTL = aTextRun->IsRightToLeft(); - PRBool result = PR_TRUE; HRESULT rv; gfxGDIFont *font = static_cast(mFont); AutoSelectFont fs(aDC, font->GetHFONT()); - Uniscribe us(aString + aRunStart, aRunLength, isRTL); + Uniscribe us(aString + aRunStart, aRunLength, aTextRun); /* itemize the string */ int numItems = us.Itemize(); @@ -633,10 +633,9 @@ gfxUniscribeShaper::InitTextRun(gfxContext *aContext, } if (FAILED(rv)) { - aTextRun->ResetGlyphRuns(); // Uniscribe doesn't like this font for some reason. - // Returning FALSE will make the gfxGDIFont discard this - // shaper and replace it with a "dumb" GDI one. + // Returning FALSE will make the gfxGDIFont retry with the + // "dumb" GDI one, unless useUniscribeOnly was set. result = PR_FALSE; break; } diff --git a/gfx/thebes/gfxWindowsPlatform.cpp b/gfx/thebes/gfxWindowsPlatform.cpp index c3b1b6249189..ae50a3969917 100644 --- a/gfx/thebes/gfxWindowsPlatform.cpp +++ b/gfx/thebes/gfxWindowsPlatform.cpp @@ -170,6 +170,7 @@ gfxWindowsPlatform::gfxWindowsPlatform() #ifdef CAIRO_HAS_D2D_SURFACE NS_RegisterMemoryReporter(new D2DCacheReporter()); + mD2DDevice = NULL; #endif #ifdef CAIRO_HAS_DWRITE_FONT nsresult rv; @@ -213,7 +214,8 @@ gfxWindowsPlatform::gfxWindowsPlatform() #ifndef CAIRO_HAS_D2D_SURFACE return; #else - if (!cairo_d2d_has_support()) { + mD2DDevice = cairo_d2d_create_device(); + if (!mD2DDevice) { return; } #ifdef CAIRO_HAS_DWRITE_FONT @@ -235,6 +237,11 @@ gfxWindowsPlatform::~gfxWindowsPlatform() { // not calling FT_Done_FreeType because cairo may still hold references to // these FT_Faces. See bug 458169. +#ifdef CAIRO_HAS_D2D_SURFACE + if (mD2DDevice) { + cairo_release_device(mD2DDevice); + } +#endif } gfxPlatformFontList* diff --git a/gfx/thebes/gfxWindowsPlatform.h b/gfx/thebes/gfxWindowsPlatform.h index 10d1756afb73..9b8d6230de39 100644 --- a/gfx/thebes/gfxWindowsPlatform.h +++ b/gfx/thebes/gfxWindowsPlatform.h @@ -214,6 +214,9 @@ public: #ifdef CAIRO_HAS_DWRITE_FONT IDWriteFactory *GetDWriteFactory() { return mDWriteFactory; } #endif +#ifdef CAIRO_HAS_D2D_SURFACE + cairo_device_t *GetD2DDevice() { return mD2DDevice; } +#endif #ifdef MOZ_FT2_FONTS FT_Library GetFTLibrary(); @@ -233,6 +236,9 @@ private: #ifdef CAIRO_HAS_DWRITE_FONT nsRefPtr mDWriteFactory; #endif +#ifdef CAIRO_HAS_D2D_SURFACE + cairo_device_t *mD2DDevice; +#endif virtual qcms_profile* GetPlatformCMSOutputProfile(); diff --git a/intl/locale/src/nsLocaleService.cpp b/intl/locale/src/nsLocaleService.cpp index a2a10acd88a1..1e584904b88c 100644 --- a/intl/locale/src/nsLocaleService.cpp +++ b/intl/locale/src/nsLocaleService.cpp @@ -60,6 +60,9 @@ # include # include # include "nsIPosixLocale.h" +#if (MOZ_PLATFORM_MAEMO >= 6) +# include "nsIGConfService.h" +#endif #endif // @@ -174,24 +177,43 @@ nsLocaleService::nsLocaleService(void) if ( resultLocale == NULL ) { return; } + + // Get system configuration + const char* lang = getenv("LANG"); +#if (MOZ_PLATFORM_MAEMO >= 6) + nsCAutoString gconfLocaleString; + nsresult rv; + nsCOMPtr gconf = + do_GetService(NS_GCONFSERVICE_CONTRACTID, &rv); + if (NS_SUCCEEDED(rv)) { + rv = gconf->GetString(NS_LITERAL_CSTRING("/meegotouch/i18n/language"), + gconfLocaleString); + if (NS_SUCCEEDED(rv) && !gconfLocaleString.IsEmpty()) { + lang = gconfLocaleString.get(); + // For setlocale() doing the right thing we need to export + // this as LANG to the environment + setenv("LANG", lang, 1); + } + } +#endif for( i = 0; i < LocaleListLength; i++ ) { nsresult result; + // setlocale( , "") evaluates LC_* and LANG char* lc_temp = setlocale(posix_locale_category[i], ""); CopyASCIItoUTF16(LocaleList[i], category); - category_platform = category; + category_platform = category; category_platform.AppendLiteral("##PLATFORM"); if (lc_temp != nsnull) { result = posixConverter->GetXPLocale(lc_temp, xpLocale); CopyASCIItoUTF16(lc_temp, platformLocale); } else { - char* lang = getenv("LANG"); if ( lang == nsnull ) { platformLocale.AssignLiteral("en_US"); result = posixConverter->GetXPLocale("en-US", xpLocale); } else { CopyASCIItoUTF16(lang, platformLocale); - result = posixConverter->GetXPLocale(lang, xpLocale); + result = posixConverter->GetXPLocale(lang, xpLocale); } } if (NS_FAILED(result)) { diff --git a/ipc/chromium/src/chrome/common/child_process_host.cc b/ipc/chromium/src/chrome/common/child_process_host.cc index 6c3b2eb9cf0b..d52074bdfc7d 100644 --- a/ipc/chromium/src/chrome/common/child_process_host.cc +++ b/ipc/chromium/src/chrome/common/child_process_host.cc @@ -11,6 +11,7 @@ #include "base/singleton.h" #include "base/waitable_event.h" #ifdef CHROMIUM_MOZILLA_BUILD +#include "mozilla/ipc/ProcessChild.h" #include "mozilla/ipc/BrowserProcessSubThread.h" typedef mozilla::ipc::BrowserProcessSubThread ChromeThread; #else @@ -121,6 +122,8 @@ bool ChildProcessHost::Send(IPC::Message* msg) { void ChildProcessHost::Notify(NotificationType type) { #ifdef CHROMIUM_MOZILLA_BUILD MessageLoop* loop = ChromeThread::GetMessageLoop(ChromeThread::IO); + if (!loop) + loop = mozilla::ipc::ProcessChild::message_loop(); if (!loop) loop = MessageLoop::current(); loop->PostTask( diff --git a/ipc/glue/GeckoChildProcessHost.cpp b/ipc/glue/GeckoChildProcessHost.cpp index 91e1c8454135..363064660f31 100644 --- a/ipc/glue/GeckoChildProcessHost.cpp +++ b/ipc/glue/GeckoChildProcessHost.cpp @@ -253,14 +253,6 @@ GeckoChildProcessHost::PerformAsyncLaunch(std::vector aExtraOpts) newEnvVars["LD_LIBRARY_PATH"] = path.get(); #elif OS_MACOSX newEnvVars["DYLD_LIBRARY_PATH"] = path.get(); -#endif -#ifdef MOZ_OMNIJAR - // Make sure the child process can find the omnijar - // See ScopedXPCOMStartup::Initialize in nsAppRunner.cpp - nsCAutoString omnijarPath; - if (mozilla::OmnijarPath()) - mozilla::OmnijarPath()->GetNativePath(omnijarPath); - newEnvVars["OMNIJAR_PATH"] = omnijarPath.get(); #endif } else { @@ -296,6 +288,17 @@ GeckoChildProcessHost::PerformAsyncLaunch(std::vector aExtraOpts) childArgv.insert(childArgv.end(), aExtraOpts.begin(), aExtraOpts.end()); +#ifdef MOZ_OMNIJAR + // Make sure the child process can find the omnijar + // See XRE_InitCommandLine in nsAppRunner.cpp + nsCAutoString omnijarPath; + if (mozilla::OmnijarPath()) { + mozilla::OmnijarPath()->GetNativePath(omnijarPath); + childArgv.push_back("-omnijar"); + childArgv.push_back(omnijarPath.get()); + } +#endif + childArgv.push_back(pidstring); childArgv.push_back(childProcessType); @@ -346,6 +349,18 @@ GeckoChildProcessHost::PerformAsyncLaunch(std::vector aExtraOpts) } cmdLine.AppendLooseValue(std::wstring(mGroupId.get())); + +#ifdef MOZ_OMNIJAR + // Make sure the child process can find the omnijar + // See XRE_InitCommandLine in nsAppRunner.cpp + nsAutoString omnijarPath; + if (mozilla::OmnijarPath()) { + mozilla::OmnijarPath()->GetPath(omnijarPath); + cmdLine.AppendLooseValue(UTF8ToWide("-omnijar")); + cmdLine.AppendLooseValue(omnijarPath.get()); + } +#endif + cmdLine.AppendLooseValue(UTF8ToWide(pidstring)); cmdLine.AppendLooseValue(UTF8ToWide(childProcessType)); #if defined(MOZ_CRASHREPORTER) diff --git a/ipc/testshell/XPCShellEnvironment.cpp b/ipc/testshell/XPCShellEnvironment.cpp index 0d4ccab5fd69..c5f139631d80 100644 --- a/ipc/testshell/XPCShellEnvironment.cpp +++ b/ipc/testshell/XPCShellEnvironment.cpp @@ -1215,6 +1215,9 @@ bool XPCShellEnvironment::EvaluateString(const nsString& aString, nsString* aResult) { + XPCShellEnvironment* env = Environment(mCx); + XPCShellEnvironment::AutoContextPusher pusher(env); + JSAutoRequest ar(mCx); JS_ClearPendingException(mCx); diff --git a/js/jsd/jsd_xpc.cpp b/js/jsd/jsd_xpc.cpp index 64fc3e5f96db..4f0450bc0606 100644 --- a/js/jsd/jsd_xpc.cpp +++ b/js/jsd/jsd_xpc.cpp @@ -1231,8 +1231,10 @@ jsdScript::GetParameterNames(PRUint32* count, PRUnichar*** paramNames) JSAutoRequest ar(cx); - uintN nargs = JS_GetFunctionArgumentCount(cx, fun); - if (!fun || !JS_FunctionHasLocalNames(cx, fun) || nargs == 0) { + uintN nargs; + if (!fun || + !JS_FunctionHasLocalNames(cx, fun) || + (nargs = JS_GetFunctionArgumentCount(cx, fun)) == 0) { *count = 0; *paramNames = nsnull; return NS_OK; diff --git a/js/src/Makefile.in b/js/src/Makefile.in index b47463ea6953..3c68f1426e7f 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -292,23 +292,12 @@ endif # ENABLE_TRACEJIT ifdef ENABLE_METHODJIT ############################################### -# BEGIN include sources for the Nitro assembler +# BEGIN include sources for the method JIT # -VPATH += $(srcdir)/assembler \ - $(srcdir)/assembler/wtf \ - $(srcdir)/assembler/jit \ - $(srcdir)/assembler/assembler \ - $(srcdir)/methodjit +VPATH += $(srcdir)/methodjit -CPPSRCS += Assertions.cpp \ - ExecutableAllocatorPosix.cpp \ - ExecutableAllocatorWin.cpp \ - ExecutableAllocator.cpp \ - ARMAssembler.cpp \ - MacroAssemblerARM.cpp \ - MethodJIT.cpp \ +CPPSRCS += MethodJIT.cpp \ BytecodeAnalyzer.cpp \ - Logging.cpp \ StubCalls.cpp \ Compiler.cpp \ FrameState.cpp \ @@ -344,6 +333,71 @@ ifeq (arm, $(TARGET_CPU)) #CPPSRCS += only_on_arm.cpp endif # +# END enclude sources for the method JIT +############################################# + +endif + +ifeq ($(TARGET_CPU), powerpc) + +VPATH += $(srcdir)/assembler \ + $(srcdir)/assembler/wtf \ + $(srcdir)/yarr/pcre \ + $(NULL) + +CPPSRCS += pcre_compile.cpp \ + pcre_exec.cpp \ + pcre_tables.cpp \ + pcre_xclass.cpp \ + pcre_ucp_searchfuncs.cpp \ + $(NULL) +else + +############################################### +# BEGIN include sources for the Nitro assembler +# +VPATH += $(srcdir)/assembler \ + $(srcdir)/assembler/wtf \ + $(srcdir)/assembler/jit \ + $(srcdir)/assembler/assembler \ + $(srcdir)/methodjit \ + $(srcdir)/yarr \ + $(srcdir)/yarr/yarr \ + $(srcdir)/yarr/pcre \ + $(srcdir)/yarr/wtf \ + $(NONE) + +CPPSRCS += Assertions.cpp \ + ExecutableAllocatorPosix.cpp \ + ExecutableAllocatorWin.cpp \ + ExecutableAllocator.cpp \ + ARMAssembler.cpp \ + Logging.cpp \ + MacroAssemblerARM.cpp \ + MacroAssemblerX86Common.cpp \ + RegexCompiler.cpp \ + RegexJIT.cpp \ + pcre_compile.cpp \ + pcre_exec.cpp \ + pcre_tables.cpp \ + pcre_xclass.cpp \ + pcre_ucp_searchfuncs.cpp \ + $(NONE) + +ifeq (86, $(findstring 86,$(TARGET_CPU))) +ifeq (x86_64, $(TARGET_CPU)) +ifeq ($(OS_ARCH),WINNT) +ASFILES += TrampolineMasmX64.asm +endif +#CPPSRCS += only_on_x86_64.cpp +else +#CPPSRCS += only_on_x86.cpp +endif +endif +ifeq (arm, $(TARGET_CPU)) +#CPPSRCS += only_on_arm.cpp +endif +# # END enclude sources for the Nitro assembler ############################################# @@ -825,7 +879,7 @@ endif ## backwards to that point. Ideally, merge with the rest of the ## Nitro assembler stuff, far above. ifdef ENABLE_METHODJIT -INCLUDES += -I$(srcdir)/assembler +INCLUDES += -I$(srcdir)/assembler -I$(srcdir)/yarr # # Needed to "configure" it correctly. Unfortunately these # flags wind up being applied to all code in js/src, not just diff --git a/js/src/assembler/assembler/ARMAssembler.cpp b/js/src/assembler/assembler/ARMAssembler.cpp index 1bea01db34d2..2d550f8416bb 100644 --- a/js/src/assembler/assembler/ARMAssembler.cpp +++ b/js/src/assembler/assembler/ARMAssembler.cpp @@ -24,8 +24,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include // MOCO - +#include "assembler/wtf/Platform.h" + #if ENABLE_ASSEMBLER && WTF_CPU_ARM_TRADITIONAL #include "ARMAssembler.h" diff --git a/js/src/assembler/assembler/ARMAssembler.h b/js/src/assembler/assembler/ARMAssembler.h index d2b4443dd3e5..d0e808e8dee4 100644 --- a/js/src/assembler/assembler/ARMAssembler.h +++ b/js/src/assembler/assembler/ARMAssembler.h @@ -27,7 +27,7 @@ #ifndef ARMAssembler_h #define ARMAssembler_h -#include +#include "assembler/wtf/Platform.h" // Some debug code uses s(n)printf for instruction logging. #include @@ -35,7 +35,7 @@ #if ENABLE_ASSEMBLER && WTF_CPU_ARM_TRADITIONAL #include "AssemblerBufferWithConstantPool.h" -#include +#include "assembler/wtf/Assertions.h" #include "methodjit/Logging.h" #define IPFX " %s" diff --git a/js/src/assembler/assembler/ARMv7Assembler.h b/js/src/assembler/assembler/ARMv7Assembler.h index 19c0bc468118..2de39fd1a90d 100644 --- a/js/src/assembler/assembler/ARMv7Assembler.h +++ b/js/src/assembler/assembler/ARMv7Assembler.h @@ -27,13 +27,13 @@ #ifndef ARMAssembler_h #define ARMAssembler_h -#include +#include "assembler/wtf/Platform.h" #if ENABLE(ASSEMBLER) && CPU(ARM_THUMB2) #include "AssemblerBuffer.h" -#include -#include +#include "assembler/wtf/Assertions.h" +#include "assembler/wtf/Vector.h" #include namespace JSC { diff --git a/js/src/assembler/assembler/AbstractMacroAssembler.h b/js/src/assembler/assembler/AbstractMacroAssembler.h index bacfc94de9ca..5ad6b1134d22 100644 --- a/js/src/assembler/assembler/AbstractMacroAssembler.h +++ b/js/src/assembler/assembler/AbstractMacroAssembler.h @@ -26,9 +26,9 @@ #ifndef AbstractMacroAssembler_h #define AbstractMacroAssembler_h -#include -#include -#include +#include "assembler/wtf/Platform.h" +#include "assembler/assembler/MacroAssemblerCodeRef.h" +#include "assembler/assembler/CodeLocation.h" #include "jsstdint.h" #if ENABLE_ASSEMBLER @@ -410,12 +410,12 @@ public: void append(JumpList& other) { - m_jumps.append(other.m_jumps.begin(), other.m_jumps.size()); + m_jumps.append(other.m_jumps.begin(), other.m_jumps.length()); } bool empty() { - return !m_jumps.size(); + return !m_jumps.length(); } const JumpVector& jumps() { return m_jumps; } diff --git a/js/src/assembler/assembler/AssemblerBuffer.h b/js/src/assembler/assembler/AssemblerBuffer.h index 6836dcacf0e2..d195cc52c78c 100644 --- a/js/src/assembler/assembler/AssemblerBuffer.h +++ b/js/src/assembler/assembler/AssemblerBuffer.h @@ -26,13 +26,13 @@ #ifndef AssemblerBuffer_h #define AssemblerBuffer_h -#include +#include "assembler/wtf/Platform.h" #if ENABLE_ASSEMBLER #include -#include -#include +#include "assembler/jit/ExecutableAllocator.h" +#include "assembler/wtf/Assertions.h" #include "jsstdint.h" namespace JSC { diff --git a/js/src/assembler/assembler/AssemblerBufferWithConstantPool.h b/js/src/assembler/assembler/AssemblerBufferWithConstantPool.h index ce77b18d8e33..ae301c663423 100644 --- a/js/src/assembler/assembler/AssemblerBufferWithConstantPool.h +++ b/js/src/assembler/assembler/AssemblerBufferWithConstantPool.h @@ -27,12 +27,12 @@ #ifndef AssemblerBufferWithConstantPool_h #define AssemblerBufferWithConstantPool_h -#include +#include "assembler/wtf/Platform.h" #if ENABLE_ASSEMBLER #include "AssemblerBuffer.h" -#include +#include "assembler/wtf/SegmentedVector.h" #define ASSEMBLER_HAS_CONSTANT_POOL 1 diff --git a/js/src/assembler/assembler/CodeLocation.h b/js/src/assembler/assembler/CodeLocation.h index 905e586cb559..ffe8269fed88 100644 --- a/js/src/assembler/assembler/CodeLocation.h +++ b/js/src/assembler/assembler/CodeLocation.h @@ -26,8 +26,8 @@ #ifndef CodeLocation_h #define CodeLocation_h -#include -#include +#include "assembler/wtf/Platform.h" +#include "assembler/assembler/MacroAssemblerCodeRef.h" #if ENABLE_ASSEMBLER diff --git a/js/src/assembler/assembler/LinkBuffer.h b/js/src/assembler/assembler/LinkBuffer.h index abdca755c089..0c9cf19b2bc8 100644 --- a/js/src/assembler/assembler/LinkBuffer.h +++ b/js/src/assembler/assembler/LinkBuffer.h @@ -26,7 +26,7 @@ #ifndef LinkBuffer_h #define LinkBuffer_h -#include +#include "assembler/wtf/Platform.h" #if ENABLE_ASSEMBLER diff --git a/js/src/assembler/assembler/MacroAssembler.h b/js/src/assembler/assembler/MacroAssembler.h index 155865568078..da4057168725 100644 --- a/js/src/assembler/assembler/MacroAssembler.h +++ b/js/src/assembler/assembler/MacroAssembler.h @@ -26,7 +26,7 @@ #ifndef MacroAssembler_h #define MacroAssembler_h -#include +#include "assembler/wtf/Platform.h" #if ENABLE_ASSEMBLER diff --git a/js/src/assembler/assembler/MacroAssemblerARM.cpp b/js/src/assembler/assembler/MacroAssemblerARM.cpp index 104749f22f09..a62fb9d72bcc 100644 --- a/js/src/assembler/assembler/MacroAssemblerARM.cpp +++ b/js/src/assembler/assembler/MacroAssemblerARM.cpp @@ -24,7 +24,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include +#include "assembler/wtf/Platform.h" #if ENABLE_ASSEMBLER && WTF_CPU_ARM_TRADITIONAL @@ -36,9 +36,18 @@ #include #include #include + +// lame check for kernel version +// see bug 586550 +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) +#include +#else #include #endif +#endif + namespace JSC { static bool isVFPPresent() diff --git a/js/src/assembler/assembler/MacroAssemblerARM.h b/js/src/assembler/assembler/MacroAssemblerARM.h index d9cf79bdf8f0..d49448adcb58 100644 --- a/js/src/assembler/assembler/MacroAssemblerARM.h +++ b/js/src/assembler/assembler/MacroAssemblerARM.h @@ -28,7 +28,7 @@ #ifndef MacroAssemblerARM_h #define MacroAssemblerARM_h -#include +#include "assembler/wtf/Platform.h" #if ENABLE_ASSEMBLER && WTF_CPU_ARM_TRADITIONAL @@ -972,7 +972,6 @@ public: // Truncates 'src' to an integer, and places the resulting 'dest'. // If the result is not representable as a 32 bit value, branch. // May also branch for some values that are representable in 32 bits - // (specifically, in this case, INT_MIN and INT_MAX). Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest) { m_assembler.ftosizd_r(ARMRegisters::SD0, src); diff --git a/js/src/assembler/assembler/MacroAssemblerARMv7.h b/js/src/assembler/assembler/MacroAssemblerARMv7.h index 6daff3e0396b..634e4a3d8048 100644 --- a/js/src/assembler/assembler/MacroAssemblerARMv7.h +++ b/js/src/assembler/assembler/MacroAssemblerARMv7.h @@ -27,7 +27,7 @@ #ifndef MacroAssemblerARMv7_h #define MacroAssemblerARMv7_h -#include +#include "assembler/wtf/Platform.h" #if ENABLE(ASSEMBLER) diff --git a/js/src/assembler/assembler/MacroAssemblerCodeRef.h b/js/src/assembler/assembler/MacroAssemblerCodeRef.h index a6940a56cddb..013c42640c49 100644 --- a/js/src/assembler/assembler/MacroAssemblerCodeRef.h +++ b/js/src/assembler/assembler/MacroAssemblerCodeRef.h @@ -26,8 +26,8 @@ #ifndef MacroAssemblerCodeRef_h #define MacroAssemblerCodeRef_h -#include -#include +#include "assembler/wtf/Platform.h" +#include "assembler/jit/ExecutableAllocator.h" #if ENABLE_ASSEMBLER diff --git a/js/src/assembler/assembler/MacroAssemblerX86.h b/js/src/assembler/assembler/MacroAssemblerX86.h index 5213213c115c..f7997dcaa996 100644 --- a/js/src/assembler/assembler/MacroAssemblerX86.h +++ b/js/src/assembler/assembler/MacroAssemblerX86.h @@ -26,7 +26,7 @@ #ifndef MacroAssemblerX86_h #define MacroAssemblerX86_h -#include +#include "assembler/wtf/Platform.h" #if ENABLE_ASSEMBLER && WTF_CPU_X86 diff --git a/js/src/assembler/assembler/MacroAssemblerX86Common.cpp b/js/src/assembler/assembler/MacroAssemblerX86Common.cpp new file mode 100644 index 000000000000..3c69c7b82f6f --- /dev/null +++ b/js/src/assembler/assembler/MacroAssemblerX86Common.cpp @@ -0,0 +1,11 @@ +#include "assembler/wtf/Platform.h" + +#if WTF_CPU_X86 && !WTF_PLATFORM_MAC + +#include "MacroAssemblerX86Common.h" + +using namespace JSC; + +MacroAssemblerX86Common::SSE2CheckState MacroAssemblerX86Common::s_sse2CheckState = NotCheckedSSE2; + +#endif diff --git a/js/src/assembler/assembler/MacroAssemblerX86Common.h b/js/src/assembler/assembler/MacroAssemblerX86Common.h index 42d8b3fd2fe2..f9574ff7550b 100644 --- a/js/src/assembler/assembler/MacroAssemblerX86Common.h +++ b/js/src/assembler/assembler/MacroAssemblerX86Common.h @@ -26,7 +26,7 @@ #ifndef MacroAssemblerX86Common_h #define MacroAssemblerX86Common_h -#include +#include "assembler/wtf/Platform.h" #if ENABLE_ASSEMBLER diff --git a/js/src/assembler/assembler/MacroAssemblerX86_64.h b/js/src/assembler/assembler/MacroAssemblerX86_64.h index c48c515cb729..6d3c8e2dc450 100644 --- a/js/src/assembler/assembler/MacroAssemblerX86_64.h +++ b/js/src/assembler/assembler/MacroAssemblerX86_64.h @@ -26,7 +26,7 @@ #ifndef MacroAssemblerX86_64_h #define MacroAssemblerX86_64_h -#include +#include "assembler/wtf/Platform.h" #if ENABLE_ASSEMBLER && WTF_CPU_X86_64 diff --git a/js/src/assembler/assembler/RepatchBuffer.h b/js/src/assembler/assembler/RepatchBuffer.h index c0170acbb56c..d1e8bfc4e196 100644 --- a/js/src/assembler/assembler/RepatchBuffer.h +++ b/js/src/assembler/assembler/RepatchBuffer.h @@ -26,7 +26,7 @@ #ifndef RepatchBuffer_h #define RepatchBuffer_h -#include +#include "assembler/wtf/Platform.h" #if ENABLE_ASSEMBLER diff --git a/js/src/assembler/assembler/X86Assembler.h b/js/src/assembler/assembler/X86Assembler.h index da53f27c71a0..e56ec5858fb6 100644 --- a/js/src/assembler/assembler/X86Assembler.h +++ b/js/src/assembler/assembler/X86Assembler.h @@ -26,13 +26,13 @@ #ifndef X86Assembler_h #define X86Assembler_h -#include +#include "assembler/wtf/Platform.h" #if ENABLE_ASSEMBLER && (WTF_CPU_X86 || WTF_CPU_X86_64) #include "AssemblerBuffer.h" #include "jsstdint.h" -#include +#include "assembler/wtf/Assertions.h" #include "jsvector.h" #include "methodjit/Logging.h" diff --git a/js/src/assembler/wtf/Platform.h b/js/src/assembler/wtf/Platform.h index 6c79105d89e5..29afdbda5664 100644 --- a/js/src/assembler/wtf/Platform.h +++ b/js/src/assembler/wtf/Platform.h @@ -894,31 +894,17 @@ on MinGW. See https://bugs.webkit.org/show_bug.cgi?id=29268 */ #if !defined(ENABLE_YARR_JIT) /* YARR supports x86 & x86-64, and has been tested on Mac and Windows. */ -#if (WTF_CPU_X86 && WTF_PLATFORM_MAC) \ - || (WTF_CPU_X86_64 && WTF_PLATFORM_MAC) \ - || (WTF_CPU_ARM_THUMB2 && WTF_PLATFORM_IPHONE) \ - || (WTF_CPU_X86 && WTF_PLATFORM_WIN) -#define ENABLE_YARR 1 +#if WTF_CPU_X86 \ + || WTF_CPU_X86_64 \ + || WTF_CPU_ARM_THUMB2 \ + || WTF_CPU_X86 #define ENABLE_YARR_JIT 1 -#endif - -#if WTF_PLATFORM_QT -#if (WTF_CPU_X86 && WTF_PLATFORM_WIN_OS && WTF_COMPILER_MINGW && GCC_VERSION >= 40100) \ - || (WTF_CPU_X86 && WTF_PLATFORM_WIN_OS && WTF_COMPILER_MSVC) \ - || (WTF_CPU_X86 && WTF_PLATFORM_LINUX && GCC_VERSION >= 40100) \ - || (WTF_CPU_ARM_TRADITIONAL && WTF_PLATFORM_LINUX) -#define ENABLE_YARR 1 -#define ENABLE_YARR_JIT 1 -#endif +#else +#define ENABLE_YARR_JIT 0 #endif #endif /* !defined(ENABLE_YARR_JIT) */ -/* Sanity Check */ -#if ENABLE_YARR_JIT && !ENABLE_YARR -#error "YARR_JIT requires YARR" -#endif - #if ENABLE_JIT || ENABLE_YARR_JIT #define ENABLE_ASSEMBLER 1 #endif diff --git a/js/src/config/system-headers b/js/src/config/system-headers index b9b29b8811ac..de25c54cfe80 100644 --- a/js/src/config/system-headers +++ b/js/src/config/system-headers @@ -1036,4 +1036,7 @@ event.h #ifdef MOZ_ENABLE_LIBPROXY proxy.h #endif +#if MOZ_PLATFORM_MAEMO==6 +contentaction/contentaction.h +#endif diff --git a/js/src/js.msg b/js/src/js.msg index 77110431217d..d8a4cb57e7ec 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -125,7 +125,7 @@ MSG_DEF(JSMSG_BYTECODE_TOO_BIG, 42, 2, JSEXN_INTERNALERR, "bytecode {0} t MSG_DEF(JSMSG_UNKNOWN_FORMAT, 43, 1, JSEXN_INTERNALERR, "unknown bytecode format {0}") MSG_DEF(JSMSG_TOO_MANY_CON_ARGS, 44, 0, JSEXN_SYNTAXERR, "too many constructor arguments") MSG_DEF(JSMSG_TOO_MANY_FUN_ARGS, 45, 0, JSEXN_SYNTAXERR, "too many function arguments") -MSG_DEF(JSMSG_BAD_QUANTIFIER, 46, 1, JSEXN_SYNTAXERR, "invalid quantifier {0}") +MSG_DEF(JSMSG_BAD_QUANTIFIER, 46, 0, JSEXN_SYNTAXERR, "invalid quantifier") MSG_DEF(JSMSG_MIN_TOO_BIG, 47, 1, JSEXN_SYNTAXERR, "overlarge minimum {0}") MSG_DEF(JSMSG_MAX_TOO_BIG, 48, 1, JSEXN_SYNTAXERR, "overlarge maximum {0}") MSG_DEF(JSMSG_OUT_OF_ORDER, 49, 1, JSEXN_SYNTAXERR, "maximum {0} less than minimum") diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index ce8d299f841b..df8bb1c2525a 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -94,6 +94,13 @@ #include "jsobjinlines.h" #include "jsscopeinlines.h" #include "jscntxtinlines.h" +#include "jsregexpinlines.h" +#include "assembler/wtf/Platform.h" + +#if ENABLE_YARR_JIT +#include "assembler/jit/ExecutableAllocator.h" +#include "methodjit/Logging.h" +#endif #if JS_HAS_XML_SUPPORT #include "jsxml.h" @@ -557,6 +564,10 @@ JSRuntime::JSRuntime() bool JSRuntime::init(uint32 maxbytes) { +#ifdef JS_METHODJIT_SPEW + JMCheckLogging(); +#endif + #ifdef DEBUG functionMeterFilename = getenv("JS_FUNCTION_STATFILE"); if (functionMeterFilename) { @@ -576,6 +587,12 @@ JSRuntime::init(uint32 maxbytes) if (!js_InitGC(this, maxbytes) || !js_InitAtomState(this)) return false; +#if ENABLE_YARR_JIT + regExpAllocator = new JSC::ExecutableAllocator(); + if (!regExpAllocator) + return false; +#endif + deflatedStringCache = new js::DeflatedStringCache(); if (!deflatedStringCache || !deflatedStringCache->init()) return false; @@ -640,6 +657,9 @@ JSRuntime::~JSRuntime() * calling js_FinishAtomState, which finalizes strings. */ delete deflatedStringCache; +#if ENABLE_YARR_JIT + delete regExpAllocator; +#endif js_FinishGC(this); #ifdef JS_THREADSAFE if (gcLock) @@ -5412,14 +5432,11 @@ JS_SetErrorReporter(JSContext *cx, JSErrorReporter er) JS_PUBLIC_API(JSObject *) JS_NewRegExpObject(JSContext *cx, char *bytes, size_t length, uintN flags) { - jschar *chars; - JSObject *obj; - CHECK_REQUEST(cx); - chars = js_InflateString(cx, bytes, &length); + jschar *chars = js_InflateString(cx, bytes, &length); if (!chars) return NULL; - obj = js_NewRegExpObject(cx, NULL, chars, length, flags); + JSObject *obj = RegExp::createObject(cx, chars, length, flags); cx->free(chars); return obj; } @@ -5428,22 +5445,17 @@ JS_PUBLIC_API(JSObject *) JS_NewUCRegExpObject(JSContext *cx, jschar *chars, size_t length, uintN flags) { CHECK_REQUEST(cx); - return js_NewRegExpObject(cx, NULL, chars, length, flags); + return RegExp::createObject(cx, chars, length, flags); } JS_PUBLIC_API(void) JS_SetRegExpInput(JSContext *cx, JSString *input, JSBool multiline) { - JSRegExpStatics *res; - CHECK_REQUEST(cx); assertSameCompartment(cx, input); /* No locking required, cx is thread-private and input must be live. */ - res = &cx->regExpStatics; - res->clearRoots(); - res->input = input; - res->multiline = multiline; + cx->regExpStatics.reset(input, !!multiline); } JS_PUBLIC_API(void) @@ -5457,7 +5469,7 @@ JS_PUBLIC_API(void) JS_ClearRegExpRoots(JSContext *cx) { /* No locking required, cx is thread-private and input must be live. */ - cx->regExpStatics.clearRoots(); + cx->regExpStatics.clear(); } /* TODO: compile, execute, get/set other statics... */ diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 3a0f127086d1..6d38e29040dc 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -765,8 +765,9 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize) JS_InitArenaPool(&cx->tempPool, "temp", TEMP_POOL_CHUNK_SIZE, sizeof(jsdouble), &cx->scriptStackQuota); + JS_InitArenaPool(&cx->regExpPool, "regExp", TEMP_POOL_CHUNK_SIZE, sizeof(int), + &cx->scriptStackQuota); - js_InitRegExpStatics(cx); JS_ASSERT(cx->resolveFlags == 0); #ifdef JS_THREADSAFE @@ -1127,7 +1128,7 @@ FreeContext(JSContext *cx) #endif /* Free the stuff hanging off of cx. */ - js_FreeRegExpStatics(cx); + cx->regExpStatics.clear(); VOUCH_DOES_NOT_REQUIRE_STACK(); JS_FinishArenaPool(&cx->tempPool); @@ -2190,7 +2191,7 @@ FreeOldArenas(JSRuntime *rt, JSArenaPool *pool) void JSContext::purge() { - FreeOldArenas(runtime, ®expPool); + FreeOldArenas(runtime, ®ExpPool); /* FIXME: bug 586161 */ compartment->purge(this); } diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 059518bb8a6d..a998449e39e6 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -1698,6 +1698,8 @@ struct JSRuntime { JSWrapObjectCallback wrapObjectCallback; + JSC::ExecutableAllocator *regExpAllocator; + JSRuntime(); ~JSRuntime(); @@ -1793,23 +1795,117 @@ namespace js { class AutoGCRooter; } -struct JSRegExpStatics { - JSContext *cx; - JSString *input; /* input string to match (perl $_, GC root) */ - JSBool multiline; /* whether input contains newlines (perl $*) */ - JSSubString lastMatch; /* last string matched (perl $&) */ - JSSubString lastParen; /* last paren matched (perl $+) */ - JSSubString leftContext; /* input to left of last match (perl $`) */ - JSSubString rightContext; /* input to right of last match (perl $') */ - js::Vector parens; /* last set of parens matched (perl $1, $2) */ +namespace js { - JSRegExpStatics(JSContext *cx) : cx(cx), parens(cx) {} +class RegExp; - bool copy(const JSRegExpStatics& other); - void clearRoots(); - void clear(); +class RegExpStatics +{ + js::Vector matchPairs; + JSContext *cx; + JSString *input; + uintN flags; + + bool createDependent(size_t start, size_t end, Value *out) const; + + size_t pairCount() const { + JS_ASSERT(matchPairs.length() % 2 == 0); + return matchPairs.length() / 2; + } + /* + * Check whether the index at |checkValidIndex| is valid (>= 0). + * If so, construct a string for it and place it in |*out|. + * If not, place undefined in |*out|. + */ + bool makeMatch(size_t checkValidIndex, size_t pairNum, Value *out) const; + static const uintN allFlags = JSREG_FOLD | JSREG_GLOB | JSREG_STICKY | JSREG_MULTILINE; + friend class RegExp; + + public: + explicit RegExpStatics(JSContext *cx) : matchPairs(cx), cx(cx) { clear(); } + void clone(const RegExpStatics &other); + + /* Mutators. */ + + void setMultiline(bool enabled) { + if (enabled) + flags = flags | JSREG_MULTILINE; + else + flags = flags & ~JSREG_MULTILINE; + } + + void clear() { + input = 0; + flags = 0; + matchPairs.clear(); + } + + void checkInvariants() { + if (pairCount() > 0) { + JS_ASSERT(input); + JS_ASSERT(get(0, 0) <= get(0, 1)); + JS_ASSERT(get(0, 1) <= int(input->length())); + } + } + + void reset(JSString *newInput, bool newMultiline) { + clear(); + input = newInput; + setMultiline(newMultiline); + checkInvariants(); + } + + void setInput(JSString *newInput) { + input = newInput; + } + + /* Accessors. */ + + JSString *getInput() const { return input; } + uintN getFlags() const { return flags; } + bool multiline() const { return flags & JSREG_MULTILINE; } + bool matched() const { JS_ASSERT(pairCount() > 0); return get(0, 1) - get(0, 0) > 0; } + size_t getParenCount() const { JS_ASSERT(pairCount() > 0); return pairCount() - 1; } + + void mark(JSTracer *trc) const { + if (input) + JS_CALL_STRING_TRACER(trc, input, "res->input"); + } + + size_t getParenLength(size_t parenNum) const { + if (pairCount() <= parenNum + 1) + return 0; + return get(parenNum + 1, 1) - get(parenNum + 1, 0); + } + + int get(size_t pairNum, bool which) const { + JS_ASSERT(pairNum < pairCount()); + return matchPairs[2 * pairNum + which]; + } + + /* Value creators. */ + + bool createInput(Value *out) const; + bool createLastMatch(Value *out) const { return makeMatch(0, 0, out); } + bool createLastParen(Value *out) const; + bool createLeftContext(Value *out) const; + bool createRightContext(Value *out) const; + + bool createParen(size_t parenNum, Value *out) const { + return makeMatch((parenNum + 1) * 2, parenNum + 1, out); + } + + /* Substring creators. */ + + void getParen(size_t num, JSSubString *out) const; + void getLastMatch(JSSubString *out) const; + void getLastParen(JSSubString *out) const; + void getLeftContext(JSSubString *out) const; + void getRightContext(JSSubString *out) const; }; +} + struct JSContext { explicit JSContext(JSRuntime *rt); @@ -1883,6 +1979,9 @@ struct JSContext /* Temporary arena pool used while compiling and decompiling. */ JSArenaPool tempPool; + /* Temporary arena pool used while evaluate regular expressions. */ + JSArenaPool regExpPool; + /* Top-level object and pointer to top stack frame's scope chain. */ JSObject *globalObject; @@ -1890,7 +1989,7 @@ struct JSContext JSWeakRoots weakRoots; /* Regular expression class statics. */ - JSRegExpStatics regExpStatics; + js::RegExpStatics regExpStatics; /* State for object and array toSource conversion. */ JSSharpObjectMap sharpObjectMap; @@ -2015,9 +2114,6 @@ struct JSContext /* Security callbacks that override any defined on the runtime. */ JSSecurityCallbacks *securityCallbacks; - /* Pinned regexp pool used for regular expressions. */ - JSArenaPool regexpPool; - /* Stored here to avoid passing it around as a parameter. */ uintN resolveFlags; @@ -2257,6 +2353,12 @@ private: JS_FRIEND_API(void) checkMallocGCPressure(void *p); }; +static inline void +js_TraceRegExpStatics(JSTracer *trc, JSContext *acx) +{ + acx->regExpStatics.mark(trc); +} + JS_ALWAYS_INLINE JSObject * JSStackFrame::varobj(js::StackSegment *seg) const { @@ -2763,6 +2865,30 @@ class AutoKeepAtoms { ~AutoKeepAtoms() { JS_UNKEEP_ATOMS(rt); } }; +class AutoArenaAllocator { + JSArenaPool *pool; + void *mark; + public: + explicit AutoArenaAllocator(JSArenaPool *pool) : pool(pool) { mark = JS_ARENA_MARK(pool); } + ~AutoArenaAllocator() { JS_ARENA_RELEASE(pool, mark); } + + template + T *alloc(size_t elems) { + void *ptr; + JS_ARENA_ALLOCATE(ptr, pool, elems * sizeof(T)); + return static_cast(ptr); + } +}; + +class AutoReleasePtr { + JSContext *cx; + void *ptr; + AutoReleasePtr operator=(const AutoReleasePtr &other); + public: + explicit AutoReleasePtr(JSContext *cx, void *ptr) : cx(cx), ptr(ptr) {} + ~AutoReleasePtr() { cx->free(ptr); } +}; + } /* namespace js */ class JSAutoResolveFlags diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 8818743c9f32..0b0eacba2d28 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -287,9 +287,8 @@ args_delProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp) } static JS_REQUIRES_STACK JSObject * -WrapEscapingClosure(JSContext *cx, JSStackFrame *fp, JSObject *funobj, JSFunction *fun) +WrapEscapingClosure(JSContext *cx, JSStackFrame *fp, JSFunction *fun) { - JS_ASSERT(GET_FUNCTION_PRIVATE(cx, funobj) == fun); JS_ASSERT(fun->optimizedClosure()); JS_ASSERT(!fun->u.i.wrapper); @@ -304,11 +303,7 @@ WrapEscapingClosure(JSContext *cx, JSStackFrame *fp, JSObject *funobj, JSFunctio if (!scopeChain) return NULL; - /* - * We must wrap funobj with a JSFunction. - */ - JS_ASSERT (funobj); - JSObject *wfunobj = NewFunction(cx, funobj, scopeChain); + JSObject *wfunobj = NewFunction(cx, scopeChain); if (!wfunobj) return NULL; AutoObjectRooter tvr(cx, wfunobj); @@ -730,7 +725,7 @@ CheckForEscapingClosure(JSContext *cx, JSObject *obj, Value *vp) JSStackFrame *fp = (JSStackFrame *) obj->getPrivate(); if (fp) { - JSObject *wrapper = WrapEscapingClosure(cx, fp, funobj, fun); + JSObject *wrapper = WrapEscapingClosure(cx, fp, fun); if (!wrapper) return false; vp->setObject(*wrapper); @@ -1294,7 +1289,7 @@ JSStackFrame::getValidCalleeObject(JSContext *cx, Value *vp) * alas, it seems foo.caller is still used on the Web. */ if (fun->needsWrapper()) { - JSObject *wrapper = WrapEscapingClosure(cx, this, fun, fun); + JSObject *wrapper = WrapEscapingClosure(cx, this, fun); if (!wrapper) return false; vp->setObject(*wrapper); @@ -2188,7 +2183,7 @@ Function(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval) TokenKind tt; if (!JS_IsConstructing(cx)) { - obj = NewFunction(cx, NULL, NULL); + obj = NewFunction(cx, NULL); if (!obj) return JS_FALSE; rval->setObject(*obj); @@ -2433,7 +2428,7 @@ js_NewFunction(JSContext *cx, JSObject *funobj, Native native, uintN nargs, JS_ASSERT(funobj->isFunction()); funobj->setParent(parent); } else { - funobj = NewFunction(cx, NULL, parent); + funobj = NewFunction(cx, parent); if (!funobj) return NULL; } @@ -2565,7 +2560,7 @@ js_NewDebuggableFlatClosure(JSContext *cx, JSFunction *fun) JS_ASSERT(!cx->fp->fun->optimizedClosure()); JS_ASSERT(FUN_FLAT_CLOSURE(fun)); - return WrapEscapingClosure(cx, cx->fp, FUN_OBJECT(fun), fun); + return WrapEscapingClosure(cx, cx->fp, fun); } JSFunction * diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 664c31aea9d6..1ee9854e79bb 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -729,9 +729,9 @@ out: } static JS_ALWAYS_INLINE JSObject * -NewFunction(JSContext *cx, JSObject *proto, JSObject *parent) +NewFunction(JSContext *cx, JSObject *parent) { - return detail::NewObject(cx, &js_FunctionClass, proto, parent); + return detail::NewObject(cx, &js_FunctionClass, NULL, parent); } template @@ -745,10 +745,9 @@ template static JS_ALWAYS_INLINE JSObject * NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent) { - if (clasp == &js_FunctionClass) - return NewFunction(cx, NULL, parent); - - return NewNonFunction(cx, clasp, proto, parent); + return (clasp == &js_FunctionClass) + ? detail::NewObject(cx, clasp, proto, parent) + : detail::NewObject(cx, clasp, proto, parent); } } /* namespace js */ diff --git a/js/src/jsparse.cpp b/js/src/jsparse.cpp index d8d7ba00949d..6eabd88f9650 100644 --- a/js/src/jsparse.cpp +++ b/js/src/jsparse.cpp @@ -90,8 +90,12 @@ #include "jsatominlines.h" #include "jsobjinlines.h" +#include "jsregexpinlines.h" -#include "jsatominlines.h" +// Grr, windows.h or something under it #defines CONST... +#ifdef CONST +#undef CONST +#endif using namespace js; @@ -8448,16 +8452,13 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot) case TOK_REGEXP: { - JSObject *obj; - pn = NullaryNode::create(tc); if (!pn) return NULL; - obj = js_NewRegExpObject(context, &tokenStream, - tokenStream.getTokenbuf().begin(), - tokenStream.getTokenbuf().length(), - tokenStream.currentToken().t_reflags); + JSObject *obj = RegExp::createObject(context, tokenStream.getTokenbuf().begin(), + tokenStream.getTokenbuf().length(), + tokenStream.currentToken().t_reflags); if (!obj) return NULL; if (!tc->compileAndGo()) { diff --git a/js/src/jsprvtd.h b/js/src/jsprvtd.h index 0815c7c3c24b..7338ad816f26 100644 --- a/js/src/jsprvtd.h +++ b/js/src/jsprvtd.h @@ -81,6 +81,7 @@ extern "C++" { namespace js { struct Parser; struct Compiler; +class RegExp; } } @@ -112,7 +113,6 @@ typedef struct JSAtomMap JSAtomMap; typedef struct JSAtomState JSAtomState; typedef struct JSCodeSpec JSCodeSpec; typedef struct JSPrinter JSPrinter; -typedef struct JSRegExp JSRegExp; typedef struct JSRegExpStatics JSRegExpStatics; typedef struct JSScope JSScope; typedef struct JSScopeOps JSScopeOps; @@ -137,6 +137,9 @@ extern "C++" { namespace js { +class RegExp; +class RegExpStatics; +class AutoStringRooter; class ExecuteArgsGuard; class InvokeFrameGuard; class InvokeArgsGuard; diff --git a/js/src/jsregexp.cpp b/js/src/jsregexp.cpp index d5c06e420924..8dc423dca45f 100644 --- a/js/src/jsregexp.cpp +++ b/js/src/jsregexp.cpp @@ -1,5 +1,5 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set sw=4 ts=8 et tw=78: + * vim: set sw=4 ts=8 et tw=99: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -43,5058 +43,347 @@ */ #include #include -#include #include "jstypes.h" #include "jsstdint.h" -#include "jsarena.h" /* Added by JSIFY */ -#include "jsutil.h" /* Added by JSIFY */ +#include "jsutil.h" #include "jsapi.h" -#include "jsarray.h" -#include "jsatom.h" -#include "jsbuiltins.h" #include "jscntxt.h" -#include "jsversion.h" -#include "jsfun.h" #include "jsgc.h" -#include "jsinterp.h" -#include "jslock.h" #include "jsnum.h" #include "jsobj.h" -#include "jsopcode.h" #include "jsregexp.h" -#include "jsscan.h" -#include "jsscope.h" -#include "jsstaticcheck.h" #include "jsstr.h" #include "jsvector.h" +#include "jsobjinlines.h" +#include "jsregexpinlines.h" + #ifdef JS_TRACER #include "jstracer.h" using namespace avmplus; using namespace nanojit; #endif -#include "jsobjinlines.h" - using namespace js; -typedef enum REOp { -#define REOP_DEF(opcode, name) opcode, -#include "jsreops.tbl" -#undef REOP_DEF - REOP_LIMIT /* META: no operator >= to this */ -} REOp; -#define REOP_IS_SIMPLE(op) ((op) <= REOP_NCLASS) - -#ifdef REGEXP_DEBUG -const char *reop_names[] = { -#define REOP_DEF(opcode, name) name, -#include "jsreops.tbl" -#undef REOP_DEF - NULL -}; -#endif - -#ifdef __GNUC__ -static int -re_debug(const char *fmt, ...) __attribute__ ((format(printf, 1, 2))); -#endif - -#ifdef REGEXP_DEBUG -static int -re_debug(const char *fmt, ...) +class RegExpMatchBuilder { - va_list ap; - int retval; + JSContext *cx; + JSObject *array; - va_start(ap, fmt); - retval = vprintf(fmt, ap); - va_end(ap); - return retval; -} - -static void -re_debug_chars(const jschar *chrs, size_t length) -{ - int i = 0; - - printf(" \""); - while (*chrs && i++ < length) { - putchar((char)*chrs++); - } - printf("\""); -} -#else /* !REGEXP_DEBUG */ -/* This should be optimized to a no-op by our tier-1 compilers. */ -static int -re_debug(const char *fmt, ...) -{ - return 0; -} - -static void -re_debug_chars(const jschar *chrs, size_t length) -{ -} -#endif /* !REGEXP_DEBUG */ - -struct RENode { - REOp op; /* r.e. op bytecode */ - RENode *next; /* next in concatenation order */ - void *kid; /* first operand */ - union { - void *kid2; /* second operand */ - jsint num; /* could be a number */ - size_t parenIndex; /* or a parenthesis index */ - struct { /* or a quantifier range */ - uintN min; - uintN max; - JSPackedBool greedy; - } range; - struct { /* or a character class */ - size_t startIndex; - size_t kidlen; /* length of string at kid, in jschars */ - size_t index; /* index into class list */ - uint16 bmsize; /* bitmap size, based on max char code */ - JSPackedBool sense; - } ucclass; - struct { /* or a literal sequence */ - jschar chr; /* of one character */ - size_t length; /* or many (via the kid) */ - } flat; - struct { - RENode *kid2; /* second operand from ALT */ - jschar ch1; /* match char for ALTPREREQ */ - jschar ch2; /* ditto, or class index for ALTPREREQ2 */ - } altprereq; - } u; -}; - -#define RE_IS_LETTER(c) (((c >= 'A') && (c <= 'Z')) || \ - ((c >= 'a') && (c <= 'z')) ) -#define RE_IS_LINE_TERM(c) ((c == '\n') || (c == '\r') || \ - (c == LINE_SEPARATOR) || (c == PARA_SEPARATOR)) - -#define CLASS_CACHE_SIZE 4 - -typedef struct CompilerState { - JSContext *context; - TokenStream *tokenStream; /* For reporting errors */ - const jschar *cpbegin; - const jschar *cpend; - const jschar *cp; - size_t parenCount; - size_t classCount; /* number of [] encountered */ - size_t treeDepth; /* maximum depth of parse tree */ - size_t progLength; /* estimated bytecode length */ - RENode *result; - size_t classBitmapsMem; /* memory to hold all class bitmaps */ - struct { - const jschar *start; /* small cache of class strings */ - size_t length; /* since they're often the same */ - size_t index; - } classCache[CLASS_CACHE_SIZE]; - uint16 flags; -} CompilerState; - -typedef struct EmitStateStackEntry { - jsbytecode *altHead; /* start of REOP_ALT* opcode */ - jsbytecode *nextAltFixup; /* fixup pointer to next-alt offset */ - jsbytecode *nextTermFixup; /* fixup ptr. to REOP_JUMP offset */ - jsbytecode *endTermFixup; /* fixup ptr. to REOPT_ALTPREREQ* offset */ - RENode *continueNode; /* original REOP_ALT* node being stacked */ - jsbytecode continueOp; /* REOP_JUMP or REOP_ENDALT continuation */ - JSPackedBool jumpToJumpFlag; /* true if we've patched jump-to-jump to - avoid 16-bit unsigned offset overflow */ -} EmitStateStackEntry; - -/* - * Immediate operand sizes and getter/setters. Unlike the ones in jsopcode.h, - * the getters and setters take the pc of the offset, not of the opcode before - * the offset. - */ -#define ARG_LEN 2 -#define GET_ARG(pc) ((uint16)(((pc)[0] << 8) | (pc)[1])) -#define SET_ARG(pc, arg) ((pc)[0] = (jsbytecode) ((arg) >> 8), \ - (pc)[1] = (jsbytecode) (arg)) - -#define OFFSET_LEN ARG_LEN -#define OFFSET_MAX (JS_BIT(ARG_LEN * 8) - 1) -#define GET_OFFSET(pc) GET_ARG(pc) - -/* - * Maximum supported tree depth is maximum size of EmitStateStackEntry stack. - * For sanity, we limit it to 2^24 bytes. - */ -#define TREE_DEPTH_MAX (JS_BIT(24) / sizeof(EmitStateStackEntry)) - -/* - * The maximum memory that can be allocated for class bitmaps. - * For sanity, we limit it to 2^24 bytes. - */ -#define CLASS_BITMAPS_MEM_LIMIT JS_BIT(24) - -/* - * Functions to get size and write/read bytecode that represent small indexes - * compactly. - * Each byte in the code represent 7-bit chunk of the index. 8th bit when set - * indicates that the following byte brings more bits to the index. Otherwise - * this is the last byte in the index bytecode representing highest index bits. - */ -static size_t -GetCompactIndexWidth(size_t index) -{ - size_t width; - - for (width = 1; (index >>= 7) != 0; ++width) { } - return width; -} - -static JS_ALWAYS_INLINE jsbytecode * -WriteCompactIndex(jsbytecode *pc, size_t index) -{ - size_t next; - - while ((next = index >> 7) != 0) { - *pc++ = (jsbytecode)(index | 0x80); - index = next; - } - *pc++ = (jsbytecode)index; - return pc; -} - -static JS_ALWAYS_INLINE jsbytecode * -ReadCompactIndex(jsbytecode *pc, size_t *result) -{ - size_t nextByte; - - nextByte = *pc++; - if ((nextByte & 0x80) == 0) { - /* - * Short-circuit the most common case when compact index <= 127. - */ - *result = nextByte; - } else { - size_t shift = 7; - *result = 0x7F & nextByte; - do { - nextByte = *pc++; - *result |= (nextByte & 0x7F) << shift; - shift += 7; - } while ((nextByte & 0x80) != 0); - } - return pc; -} - -typedef struct RECapture { - ptrdiff_t index; /* start of contents, -1 for empty */ - size_t length; /* length of capture */ -} RECapture; - -typedef struct REMatchState { - const jschar *cp; - RECapture parens[1]; /* first of 're->parenCount' captures, - allocated at end of this struct */ -} REMatchState; - -struct REBackTrackData; - -typedef struct REProgState { - jsbytecode *continue_pc; /* current continuation data */ - jsbytecode continue_op; - ptrdiff_t index; /* progress in text */ - size_t parenSoFar; /* highest indexed paren started */ - union { - struct { - uintN min; /* current quantifier limits */ - uintN max; - } quantifier; - struct { - size_t top; /* backtrack stack state */ - size_t sz; - } assertion; - } u; -} REProgState; - -typedef struct REBackTrackData { - size_t sz; /* size of previous stack entry */ - jsbytecode *backtrack_pc; /* where to backtrack to */ - jsbytecode backtrack_op; - const jschar *cp; /* index in text of match at backtrack */ - size_t parenIndex; /* start index of saved paren contents */ - size_t parenCount; /* # of saved paren contents */ - size_t saveStateStackTop; /* number of parent states */ - /* saved parent states follow */ - /* saved paren contents follow */ -} REBackTrackData; - -#define INITIAL_STATESTACK 100 -#define INITIAL_BACKTRACK 8000 - -typedef struct REGlobalData { - JSContext *cx; - JSRegExp *regexp; /* the RE in execution */ - JSBool ok; /* runtime error (out_of_memory only?) */ - size_t start; /* offset to start at */ - ptrdiff_t skipped; /* chars skipped anchoring this r.e. */ - const jschar *cpbegin; /* text base address */ - const jschar *cpend; /* text limit address */ - - REProgState *stateStack; /* stack of state of current parents */ - size_t stateStackTop; - size_t stateStackLimit; - - REBackTrackData *backTrackStack;/* stack of matched-so-far positions */ - REBackTrackData *backTrackSP; - size_t backTrackStackSize; - size_t cursz; /* size of current stack entry */ - size_t backTrackCount; /* how many times we've backtracked */ - size_t backTrackLimit; /* upper limit on backtrack states */ -} REGlobalData; - -void -JSRegExpStatics::clearRoots() -{ - input = NULL; - cx->runtime->gcPoke = JS_TRUE; -} - -bool -JSRegExpStatics::copy(const JSRegExpStatics& other) -{ - clearRoots(); - input = other.input; - multiline = other.multiline; - lastMatch = other.lastMatch; - lastParen = other.lastParen; - leftContext = other.leftContext; - rightContext = other.rightContext; - if (!parens.resize(other.parens.length())) - return false; - memcpy(parens.begin(), other.parens.begin(), sizeof(JSSubString) * parens.length()); - return true; -} - -void -JSRegExpStatics::clear() -{ - clearRoots(); - multiline = false; - lastMatch = lastParen = leftContext = rightContext = js_EmptySubString; - parens.clear(); -} - -/* - * 1. If IgnoreCase is false, return ch. - * 2. Let u be ch converted to upper case as if by calling - * String.prototype.toUpperCase on the one-character string ch. - * 3. If u does not consist of a single character, return ch. - * 4. Let cu be u's character. - * 5. If ch's code point value is greater than or equal to decimal 128 and cu's - * code point value is less than decimal 128, then return ch. - * 6. Return cu. - */ -static JS_ALWAYS_INLINE uintN -upcase(uintN ch) -{ - uintN cu; - - JS_ASSERT((uintN) (jschar) ch == ch); - if (ch < 128) { - if (ch - (uintN) 'a' <= (uintN) ('z' - 'a')) - ch -= (uintN) ('a' - 'A'); - return ch; - } - - cu = JS_TOUPPER(ch); - return (cu < 128) ? ch : cu; -} - -/* - * Return the 'canonical' inverse upcase of |ch|. That is the character - * |lch| such that |upcase(lch) == ch| and (|lch| is the lower-case form - * of |ch| or is |ch|). - */ -static inline jschar inverse_upcase(jschar ch) -{ - jschar lch = JS_TOLOWER(ch); - return (upcase(lch) == ch) ? lch : ch; -} - -/* Construct and initialize an RENode, returning NULL for out-of-memory */ -static RENode * -NewRENode(CompilerState *state, REOp op) -{ - JSContext *cx; - RENode *ren; - - cx = state->context; - JS_ARENA_ALLOCATE_CAST(ren, RENode *, &cx->tempPool, sizeof *ren); - if (!ren) { - js_ReportOutOfScriptQuota(cx); - return NULL; - } - ren->op = op; - ren->next = NULL; - ren->kid = NULL; - return ren; -} - -/* - * Validates and converts hex ascii value. - */ -static JSBool -isASCIIHexDigit(jschar c, uintN *digit) -{ - uintN cv = c; - - if (cv < '0') - return JS_FALSE; - if (cv <= '9') { - *digit = cv - '0'; - return JS_TRUE; - } - cv |= 0x20; - if (cv >= 'a' && cv <= 'f') { - *digit = cv - 'a' + 10; - return JS_TRUE; - } - return JS_FALSE; -} - - -typedef struct { - REOp op; - const jschar *errPos; - size_t parenIndex; -} REOpData; - -static JSBool -ReportRegExpErrorHelper(CompilerState *state, uintN flags, uintN errorNumber, - const jschar *arg) -{ - if (state->tokenStream) { - return ReportCompileErrorNumber(state->context, state->tokenStream, - NULL, JSREPORT_UC | flags, errorNumber, arg); - } - return JS_ReportErrorFlagsAndNumberUC(state->context, flags, - js_GetErrorMessage, NULL, - errorNumber, arg); -} - -static JSBool -ReportRegExpError(CompilerState *state, uintN flags, uintN errorNumber) -{ - return ReportRegExpErrorHelper(state, flags, errorNumber, NULL); -} - -/* - * Process the op against the two top operands, reducing them to a single - * operand in the penultimate slot. Update progLength and treeDepth. - */ -static JSBool -ProcessOp(CompilerState *state, REOpData *opData, RENode **operandStack, - intN operandSP) -{ - RENode *result; - - switch (opData->op) { - case REOP_ALT: - result = NewRENode(state, REOP_ALT); - if (!result) - return JS_FALSE; - result->kid = operandStack[operandSP - 2]; - result->u.kid2 = operandStack[operandSP - 1]; - operandStack[operandSP - 2] = result; - - if (state->treeDepth == TREE_DEPTH_MAX) { - ReportRegExpError(state, JSREPORT_ERROR, JSMSG_REGEXP_TOO_COMPLEX); - return JS_FALSE; - } - ++state->treeDepth; - - /* - * Look at both alternates to see if there's a FLAT or a CLASS at - * the start of each. If so, use a prerequisite match. - */ - if (((RENode *) result->kid)->op == REOP_FLAT && - ((RENode *) result->u.kid2)->op == REOP_FLAT && - (state->flags & JSREG_FOLD) == 0) { - result->op = REOP_ALTPREREQ; - result->u.altprereq.ch1 = ((RENode *) result->kid)->u.flat.chr; - result->u.altprereq.ch2 = ((RENode *) result->u.kid2)->u.flat.chr; - /* ALTPREREQ, , uch1, uch2, , ..., - JUMP, ... ENDALT */ - state->progLength += 13; - } - else - if (((RENode *) result->kid)->op == REOP_CLASS && - ((RENode *) result->kid)->u.ucclass.index < 256 && - ((RENode *) result->u.kid2)->op == REOP_FLAT && - (state->flags & JSREG_FOLD) == 0) { - result->op = REOP_ALTPREREQ2; - result->u.altprereq.ch1 = ((RENode *) result->u.kid2)->u.flat.chr; - result->u.altprereq.ch2 = jschar(((RENode *) result->kid)->u.ucclass.index); - /* ALTPREREQ2, , uch1, uch2, , ..., - JUMP, ... ENDALT */ - state->progLength += 13; - } - else - if (((RENode *) result->kid)->op == REOP_FLAT && - ((RENode *) result->u.kid2)->op == REOP_CLASS && - ((RENode *) result->u.kid2)->u.ucclass.index < 256 && - (state->flags & JSREG_FOLD) == 0) { - result->op = REOP_ALTPREREQ2; - result->u.altprereq.ch1 = ((RENode *) result->kid)->u.flat.chr; - result->u.altprereq.ch2 = - jschar(((RENode *) result->u.kid2)->u.ucclass.index); - /* ALTPREREQ2, , uch1, uch2, , ..., - JUMP, ... ENDALT */ - state->progLength += 13; - } - else { - /* ALT, , ..., JUMP, ... ENDALT */ - state->progLength += 7; - } - break; - - case REOP_CONCAT: - result = operandStack[operandSP - 2]; - while (result->next) - result = result->next; - result->next = operandStack[operandSP - 1]; - break; - - case REOP_ASSERT: - case REOP_ASSERT_NOT: - case REOP_LPARENNON: - case REOP_LPAREN: - /* These should have been processed by a close paren. */ - ReportRegExpErrorHelper(state, JSREPORT_ERROR, JSMSG_MISSING_PAREN, - opData->errPos); - return JS_FALSE; - - default:; - } - return JS_TRUE; -} - -/* - * Parser forward declarations. - */ -static JSBool ParseTerm(CompilerState *state); -static JSBool ParseQuantifier(CompilerState *state); -static intN ParseMinMaxQuantifier(CompilerState *state, JSBool ignoreValues); - -/* - * Top-down regular expression grammar, based closely on Perl4. - * - * regexp: altern A regular expression is one or more - * altern '|' regexp alternatives separated by vertical bar. - */ -#define INITIAL_STACK_SIZE 128 - -static JSBool -ParseRegExp(CompilerState *state) -{ - size_t parenIndex; - RENode *operand; - REOpData *operatorStack; - RENode **operandStack; - REOp op; - intN i; - JSBool result = JS_FALSE; - - intN operatorSP = 0, operatorStackSize = INITIAL_STACK_SIZE; - intN operandSP = 0, operandStackSize = INITIAL_STACK_SIZE; - - /* Watch out for empty regexp */ - if (state->cp == state->cpend) { - state->result = NewRENode(state, REOP_EMPTY); - return (state->result != NULL); - } - - operatorStack = (REOpData *) - state->context->malloc(sizeof(REOpData) * operatorStackSize); - if (!operatorStack) - return JS_FALSE; - - operandStack = (RENode **) - state->context->malloc(sizeof(RENode *) * operandStackSize); - if (!operandStack) - goto out; - - for (;;) { - parenIndex = state->parenCount; - if (state->cp == state->cpend) { - /* - * If we are at the end of the regexp and we're short one or more - * operands, the regexp must have the form /x|/ or some such, with - * left parentheses making us short more than one operand. - */ - if (operatorSP >= operandSP) { - operand = NewRENode(state, REOP_EMPTY); - if (!operand) - goto out; - goto pushOperand; - } - } else { - switch (*state->cp) { - case '(': - ++state->cp; - if (state->cp + 1 < state->cpend && - *state->cp == '?' && - (state->cp[1] == '=' || - state->cp[1] == '!' || - state->cp[1] == ':')) { - switch (state->cp[1]) { - case '=': - op = REOP_ASSERT; - /* ASSERT, , ... ASSERTTEST */ - state->progLength += 4; - break; - case '!': - op = REOP_ASSERT_NOT; - /* ASSERTNOT, , ... ASSERTNOTTEST */ - state->progLength += 4; - break; - default: - op = REOP_LPARENNON; - break; - } - state->cp += 2; - } else { - op = REOP_LPAREN; - /* LPAREN, , ... RPAREN, */ - state->progLength - += 2 * (1 + GetCompactIndexWidth(parenIndex)); - state->parenCount++; - if (state->parenCount == 65535) { - ReportRegExpError(state, JSREPORT_ERROR, - JSMSG_TOO_MANY_PARENS); - goto out; - } - } - goto pushOperator; - - case ')': - /* - * If there's no stacked open parenthesis, throw syntax error. - */ - for (i = operatorSP - 1; ; i--) { - if (i < 0) { - ReportRegExpError(state, JSREPORT_ERROR, - JSMSG_UNMATCHED_RIGHT_PAREN); - goto out; - } - if (operatorStack[i].op == REOP_ASSERT || - operatorStack[i].op == REOP_ASSERT_NOT || - operatorStack[i].op == REOP_LPARENNON || - operatorStack[i].op == REOP_LPAREN) { - break; - } - } - /* FALL THROUGH */ - - case '|': - /* Expected an operand before these, so make an empty one */ - operand = NewRENode(state, REOP_EMPTY); - if (!operand) - goto out; - goto pushOperand; - - default: - if (!ParseTerm(state)) - goto out; - operand = state->result; -pushOperand: - if (operandSP == operandStackSize) { - RENode **tmp; - operandStackSize += operandStackSize; - tmp = (RENode **) - state->context->realloc(operandStack, - sizeof(RENode *) * operandStackSize); - if (!tmp) - goto out; - operandStack = tmp; - } - operandStack[operandSP++] = operand; - break; - } - } - - /* At the end; process remaining operators. */ -restartOperator: - if (state->cp == state->cpend) { - while (operatorSP) { - --operatorSP; - if (!ProcessOp(state, &operatorStack[operatorSP], - operandStack, operandSP)) - goto out; - --operandSP; - } - JS_ASSERT(operandSP == 1); - state->result = operandStack[0]; - result = JS_TRUE; - goto out; - } - - switch (*state->cp) { - case '|': - /* Process any stacked 'concat' operators */ - ++state->cp; - while (operatorSP && - operatorStack[operatorSP - 1].op == REOP_CONCAT) { - --operatorSP; - if (!ProcessOp(state, &operatorStack[operatorSP], - operandStack, operandSP)) { - goto out; - } - --operandSP; - } - op = REOP_ALT; - goto pushOperator; - - case ')': - /* - * If there's no stacked open parenthesis, throw syntax error. - */ - for (i = operatorSP - 1; ; i--) { - if (i < 0) { - ReportRegExpError(state, JSREPORT_ERROR, - JSMSG_UNMATCHED_RIGHT_PAREN); - goto out; - } - if (operatorStack[i].op == REOP_ASSERT || - operatorStack[i].op == REOP_ASSERT_NOT || - operatorStack[i].op == REOP_LPARENNON || - operatorStack[i].op == REOP_LPAREN) { - break; - } - } - ++state->cp; - - /* Process everything on the stack until the open parenthesis. */ - for (;;) { - JS_ASSERT(operatorSP); - --operatorSP; - switch (operatorStack[operatorSP].op) { - case REOP_ASSERT: - case REOP_ASSERT_NOT: - case REOP_LPAREN: - operand = NewRENode(state, operatorStack[operatorSP].op); - if (!operand) - goto out; - operand->u.parenIndex = - operatorStack[operatorSP].parenIndex; - JS_ASSERT(operandSP); - operand->kid = operandStack[operandSP - 1]; - operandStack[operandSP - 1] = operand; - if (state->treeDepth == TREE_DEPTH_MAX) { - ReportRegExpError(state, JSREPORT_ERROR, - JSMSG_REGEXP_TOO_COMPLEX); - goto out; - } - ++state->treeDepth; - /* FALL THROUGH */ - - case REOP_LPARENNON: - state->result = operandStack[operandSP - 1]; - if (!ParseQuantifier(state)) - goto out; - operandStack[operandSP - 1] = state->result; - goto restartOperator; - default: - if (!ProcessOp(state, &operatorStack[operatorSP], - operandStack, operandSP)) - goto out; - --operandSP; - break; - } - } - break; - - case '{': - { - const jschar *errp = state->cp; - - if (ParseMinMaxQuantifier(state, JS_TRUE) < 0) { - /* - * This didn't even scan correctly as a quantifier, so we should - * treat it as flat. - */ - op = REOP_CONCAT; - goto pushOperator; - } - - state->cp = errp; - /* FALL THROUGH */ - } - - case '+': - case '*': - case '?': - ReportRegExpErrorHelper(state, JSREPORT_ERROR, JSMSG_BAD_QUANTIFIER, - state->cp); - result = JS_FALSE; - goto out; - - default: - /* Anything else is the start of the next term. */ - op = REOP_CONCAT; -pushOperator: - if (operatorSP == operatorStackSize) { - REOpData *tmp; - operatorStackSize += operatorStackSize; - tmp = (REOpData *) - state->context->realloc(operatorStack, - sizeof(REOpData) * operatorStackSize); - if (!tmp) - goto out; - operatorStack = tmp; - } - operatorStack[operatorSP].op = op; - operatorStack[operatorSP].errPos = state->cp; - operatorStack[operatorSP++].parenIndex = parenIndex; - break; - } - } -out: - if (operatorStack) - state->context->free(operatorStack); - if (operandStack) - state->context->free(operandStack); - return result; -} - -/* - * Hack two bits in CompilerState.flags, for use within FindParenCount to flag - * its being on the stack, and to propagate errors to its callers. - */ -#define JSREG_FIND_PAREN_COUNT 0x8000 -#define JSREG_FIND_PAREN_ERROR 0x4000 - -/* - * Magic return value from FindParenCount and GetDecimalValue, to indicate - * overflow beyond GetDecimalValue's max parameter, or a computed maximum if - * its findMax parameter is non-null. - */ -#define OVERFLOW_VALUE ((uintN)-1) - -static uintN -FindParenCount(CompilerState *state) -{ - CompilerState temp; - int i; - - if (state->flags & JSREG_FIND_PAREN_COUNT) - return OVERFLOW_VALUE; - - /* - * Copy state into temp, flag it so we never report an invalid backref, - * and reset its members to parse the entire regexp. This is obviously - * suboptimal, but GetDecimalValue calls us only if a backref appears to - * refer to a forward parenthetical, which is rare. - */ - temp = *state; - temp.flags |= JSREG_FIND_PAREN_COUNT; - temp.cp = temp.cpbegin; - temp.parenCount = 0; - temp.classCount = 0; - temp.progLength = 0; - temp.treeDepth = 0; - temp.classBitmapsMem = 0; - for (i = 0; i < CLASS_CACHE_SIZE; i++) - temp.classCache[i].start = NULL; - - if (!ParseRegExp(&temp)) { - state->flags |= JSREG_FIND_PAREN_ERROR; - return OVERFLOW_VALUE; - } - return temp.parenCount; -} - -/* - * Extract and return a decimal value at state->cp. The initial character c - * has already been read. Return OVERFLOW_VALUE if the result exceeds max. - * Callers who pass a non-null findMax should test JSREG_FIND_PAREN_ERROR in - * state->flags to discover whether an error occurred under findMax. - */ -static uintN -GetDecimalValue(jschar c, uintN max, uintN (*findMax)(CompilerState *state), - CompilerState *state) -{ - uintN value = JS7_UNDEC(c); - JSBool overflow = (value > max && (!findMax || value > findMax(state))); - - /* The following restriction allows simpler overflow checks. */ - JS_ASSERT(max <= ((uintN)-1 - 9) / 10); - while (state->cp < state->cpend) { - c = *state->cp; - if (!JS7_ISDEC(c)) - break; - value = 10 * value + JS7_UNDEC(c); - if (!overflow && value > max && (!findMax || value > findMax(state))) - overflow = JS_TRUE; - ++state->cp; - } - return overflow ? OVERFLOW_VALUE : value; -} - -/* - * Calculate the total size of the bitmap required for a class expression. - */ -static JSBool -CalculateBitmapSize(CompilerState *state, RENode *target, const jschar *src, - const jschar *end) -{ - uintN max = 0; - JSBool inRange = JS_FALSE; - jschar c, rangeStart = 0; - uintN n, digit, nDigits, i; - - target->u.ucclass.bmsize = 0; - target->u.ucclass.sense = JS_TRUE; - - if (src == end) - return JS_TRUE; - - if (*src == '^') { - ++src; - target->u.ucclass.sense = JS_FALSE; - } - - while (src != end) { - JSBool canStartRange = JS_TRUE; - jschar localMax = 0; - - switch (*src) { - case '\\': - ++src; - c = *src++; - switch (c) { - case 'b': - localMax = 0x8; - break; - case 'f': - localMax = 0xC; - break; - case 'n': - localMax = 0xA; - break; - case 'r': - localMax = 0xD; - break; - case 't': - localMax = 0x9; - break; - case 'v': - localMax = 0xB; - break; - case 'c': - if (src < end && RE_IS_LETTER(*src)) { - localMax = (uintN) (*src++) & 0x1F; - } else { - --src; - localMax = '\\'; - } - break; - case 'x': - nDigits = 2; - goto lexHex; - case 'u': - nDigits = 4; -lexHex: - n = 0; - for (i = 0; (i < nDigits) && (src < end); i++) { - c = *src++; - if (!isASCIIHexDigit(c, &digit)) { - /* - * Back off to accepting the original - *'\' as a literal. - */ - src -= i + 1; - n = '\\'; - break; - } - n = (n << 4) | digit; - } - localMax = jschar(n); - break; - case 'd': - canStartRange = JS_FALSE; - if (inRange) { - JS_ReportErrorNumber(state->context, - js_GetErrorMessage, NULL, - JSMSG_BAD_CLASS_RANGE); - return JS_FALSE; - } - localMax = '9'; - break; - case 'D': - case 's': - case 'S': - case 'w': - case 'W': - canStartRange = JS_FALSE; - if (inRange) { - JS_ReportErrorNumber(state->context, - js_GetErrorMessage, NULL, - JSMSG_BAD_CLASS_RANGE); - return JS_FALSE; - } - max = 65535; - - /* - * If this is the start of a range, ensure that it's less than - * the end. - */ - localMax = 0; - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - /* - * This is a non-ECMA extension - decimal escapes (in this - * case, octal!) are supposed to be an error inside class - * ranges, but supported here for backwards compatibility. - * - */ - n = JS7_UNDEC(c); - c = *src; - if ('0' <= c && c <= '7') { - src++; - n = 8 * n + JS7_UNDEC(c); - c = *src; - if ('0' <= c && c <= '7') { - src++; - i = 8 * n + JS7_UNDEC(c); - if (i <= 0377) - n = i; - else - src--; - } - } - localMax = jschar(n); - break; - - default: - localMax = c; - break; - } - break; - default: - localMax = *src++; - break; - } - - if (inRange) { - /* Throw a SyntaxError here, per ECMA-262, 15.10.2.15. */ - if (rangeStart > localMax) { - JS_ReportErrorNumber(state->context, - js_GetErrorMessage, NULL, - JSMSG_BAD_CLASS_RANGE); - return JS_FALSE; - } - inRange = JS_FALSE; - } else { - if (canStartRange && src < end - 1) { - if (*src == '-') { - ++src; - inRange = JS_TRUE; - rangeStart = (jschar)localMax; - continue; - } - } - if (state->flags & JSREG_FOLD) - rangeStart = localMax; /* one run of the uc/dc loop below */ - } - - if (state->flags & JSREG_FOLD) { - jschar maxch = localMax; - - for (i = rangeStart; i <= localMax; i++) { - jschar uch, dch; - - uch = jschar(upcase(i)); - dch = inverse_upcase(jschar(i)); - maxch = JS_MAX(maxch, uch); - maxch = JS_MAX(maxch, dch); - } - localMax = maxch; - } - - if (localMax > max) - max = uintN(localMax); - } - target->u.ucclass.bmsize = uint16(max); - return JS_TRUE; -} - -/* - * item: assertion An item is either an assertion or - * quantatom a quantified atom. - * - * assertion: '^' Assertions match beginning of string - * (or line if the class static property - * RegExp.multiline is true). - * '$' End of string (or line if the class - * static property RegExp.multiline is - * true). - * '\b' Word boundary (between \w and \W). - * '\B' Word non-boundary. - * - * quantatom: atom An unquantified atom. - * quantatom '{' n ',' m '}' - * Atom must occur between n and m times. - * quantatom '{' n ',' '}' Atom must occur at least n times. - * quantatom '{' n '}' Atom must occur exactly n times. - * quantatom '*' Zero or more times (same as {0,}). - * quantatom '+' One or more times (same as {1,}). - * quantatom '?' Zero or one time (same as {0,1}). - * - * any of which can be optionally followed by '?' for ungreedy - * - * atom: '(' regexp ')' A parenthesized regexp (what matched - * can be addressed using a backreference, - * see '\' n below). - * '.' Matches any char except '\n'. - * '[' classlist ']' A character class. - * '[' '^' classlist ']' A negated character class. - * '\f' Form Feed. - * '\n' Newline (Line Feed). - * '\r' Carriage Return. - * '\t' Horizontal Tab. - * '\v' Vertical Tab. - * '\d' A digit (same as [0-9]). - * '\D' A non-digit. - * '\w' A word character, [0-9a-z_A-Z]. - * '\W' A non-word character. - * '\s' A whitespace character, [ \b\f\n\r\t\v]. - * '\S' A non-whitespace character. - * '\' n A backreference to the nth (n decimal - * and positive) parenthesized expression. - * '\' octal An octal escape sequence (octal must be - * two or three digits long, unless it is - * 0 for the null character). - * '\x' hex A hex escape (hex must be two digits). - * '\u' unicode A unicode escape (must be four digits). - * '\c' ctrl A control character, ctrl is a letter. - * '\' literalatomchar Any character except one of the above - * that follow '\' in an atom. - * otheratomchar Any character not first among the other - * atom right-hand sides. - */ -static JSBool -ParseTerm(CompilerState *state) -{ - jschar c = *state->cp++; - uintN nDigits; - uintN num, tmp, n, i; - const jschar *termStart; - - switch (c) { - /* assertions and atoms */ - case '^': - state->result = NewRENode(state, REOP_BOL); - if (!state->result) - return JS_FALSE; - state->progLength++; - return JS_TRUE; - case '$': - state->result = NewRENode(state, REOP_EOL); - if (!state->result) - return JS_FALSE; - state->progLength++; - return JS_TRUE; - case '\\': - if (state->cp >= state->cpend) { - /* a trailing '\' is an error */ - ReportRegExpError(state, JSREPORT_ERROR, JSMSG_TRAILING_SLASH); - return JS_FALSE; - } - c = *state->cp++; - switch (c) { - /* assertion escapes */ - case 'b' : - state->result = NewRENode(state, REOP_WBDRY); - if (!state->result) - return JS_FALSE; - state->progLength++; - return JS_TRUE; - case 'B': - state->result = NewRENode(state, REOP_WNONBDRY); - if (!state->result) - return JS_FALSE; - state->progLength++; - return JS_TRUE; - /* Decimal escape */ - case '0': - /* Give a strict warning. See also the note below. */ - if (!ReportRegExpError(state, JSREPORT_WARNING | JSREPORT_STRICT, - JSMSG_INVALID_BACKREF)) { - return JS_FALSE; - } - doOctal: - num = 0; - while (state->cp < state->cpend) { - c = *state->cp; - if (c < '0' || '7' < c) - break; - state->cp++; - tmp = 8 * num + (uintN)JS7_UNDEC(c); - if (tmp > 0377) - break; - num = tmp; - } - c = (jschar)num; - doFlat: - state->result = NewRENode(state, REOP_FLAT); - if (!state->result) - return JS_FALSE; - state->result->u.flat.chr = c; - state->result->u.flat.length = 1; - state->progLength += 3; - break; - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - termStart = state->cp - 1; - num = GetDecimalValue(c, state->parenCount, FindParenCount, state); - if (state->flags & JSREG_FIND_PAREN_ERROR) - return JS_FALSE; - if (num == OVERFLOW_VALUE) { - /* Give a strict mode warning. */ - if (!ReportRegExpError(state, - JSREPORT_WARNING | JSREPORT_STRICT, - (c >= '8') - ? JSMSG_INVALID_BACKREF - : JSMSG_BAD_BACKREF)) { - return JS_FALSE; - } - - /* - * Note: ECMA 262, 15.10.2.9 says that we should throw a syntax - * error here. However, for compatibility with IE, we treat the - * whole backref as flat if the first character in it is not a - * valid octal character, and as an octal escape otherwise. - */ - state->cp = termStart; - if (c >= '8') { - /* Treat this as flat. termStart - 1 is the \. */ - c = '\\'; - goto asFlat; - } - - /* Treat this as an octal escape. */ - goto doOctal; - } - - /* - * When FindParenCount calls the regex parser recursively (to find - * the number of backrefs) num can be arbitrary and the maximum - * supported number of backrefs does not bound it. - */ - JS_ASSERT_IF(!(state->flags & JSREG_FIND_PAREN_COUNT), - 1 <= num && num <= 0x10000); - state->result = NewRENode(state, REOP_BACKREF); - if (!state->result) - return JS_FALSE; - state->result->u.parenIndex = num - 1; - state->progLength - += 1 + GetCompactIndexWidth(state->result->u.parenIndex); - break; - /* Control escape */ - case 'f': - c = 0xC; - goto doFlat; - case 'n': - c = 0xA; - goto doFlat; - case 'r': - c = 0xD; - goto doFlat; - case 't': - c = 0x9; - goto doFlat; - case 'v': - c = 0xB; - goto doFlat; - /* Control letter */ - case 'c': - if (state->cp < state->cpend && RE_IS_LETTER(*state->cp)) { - c = (jschar) (*state->cp++ & 0x1F); - } else { - /* back off to accepting the original '\' as a literal */ - --state->cp; - c = '\\'; - } - goto doFlat; - /* HexEscapeSequence */ - case 'x': - nDigits = 2; - goto lexHex; - /* UnicodeEscapeSequence */ - case 'u': - nDigits = 4; -lexHex: - n = 0; - for (i = 0; i < nDigits && state->cp < state->cpend; i++) { - uintN digit; - c = *state->cp++; - if (!isASCIIHexDigit(c, &digit)) { - /* - * Back off to accepting the original 'u' or 'x' as a - * literal. - */ - state->cp -= i + 2; - n = *state->cp++; - break; - } - n = (n << 4) | digit; - } - c = (jschar) n; - goto doFlat; - /* Character class escapes */ - case 'd': - state->result = NewRENode(state, REOP_DIGIT); -doSimple: - if (!state->result) - return JS_FALSE; - state->progLength++; - break; - case 'D': - state->result = NewRENode(state, REOP_NONDIGIT); - goto doSimple; - case 's': - state->result = NewRENode(state, REOP_SPACE); - goto doSimple; - case 'S': - state->result = NewRENode(state, REOP_NONSPACE); - goto doSimple; - case 'w': - state->result = NewRENode(state, REOP_ALNUM); - goto doSimple; - case 'W': - state->result = NewRENode(state, REOP_NONALNUM); - goto doSimple; - /* IdentityEscape */ - default: - state->result = NewRENode(state, REOP_FLAT); - if (!state->result) - return JS_FALSE; - state->result->u.flat.chr = c; - state->result->u.flat.length = 1; - state->result->kid = (void *) (state->cp - 1); - state->progLength += 3; - break; - } - break; - case '[': - state->result = NewRENode(state, REOP_CLASS); - if (!state->result) - return JS_FALSE; - termStart = state->cp; - state->result->u.ucclass.startIndex = termStart - state->cpbegin; - for (;;) { - if (state->cp == state->cpend) { - ReportRegExpErrorHelper(state, JSREPORT_ERROR, - JSMSG_UNTERM_CLASS, termStart); - - return JS_FALSE; - } - if (*state->cp == '\\') { - state->cp++; - if (state->cp != state->cpend) - state->cp++; - continue; - } - if (*state->cp == ']') { - state->result->u.ucclass.kidlen = state->cp - termStart; - break; - } - state->cp++; - } - for (i = 0; i < CLASS_CACHE_SIZE; i++) { - if (!state->classCache[i].start) { - state->classCache[i].start = termStart; - state->classCache[i].length = state->result->u.ucclass.kidlen; - state->classCache[i].index = state->classCount; - break; - } - if (state->classCache[i].length == - state->result->u.ucclass.kidlen) { - for (n = 0; ; n++) { - if (n == state->classCache[i].length) { - state->result->u.ucclass.index - = state->classCache[i].index; - goto claim; - } - if (state->classCache[i].start[n] != termStart[n]) - break; - } - } - } - state->result->u.ucclass.index = state->classCount++; - - claim: - /* - * Call CalculateBitmapSize now as we want any errors it finds - * to be reported during the parse phase, not at execution. - */ - if (!CalculateBitmapSize(state, state->result, termStart, state->cp++)) - return JS_FALSE; - /* - * Update classBitmapsMem with number of bytes to hold bmsize bits, - * which is (bitsCount + 7) / 8 or (highest_bit + 1 + 7) / 8 - * or highest_bit / 8 + 1 where highest_bit is u.ucclass.bmsize. - */ - n = (state->result->u.ucclass.bmsize >> 3) + 1; - if (n > CLASS_BITMAPS_MEM_LIMIT - state->classBitmapsMem) { - ReportRegExpError(state, JSREPORT_ERROR, JSMSG_REGEXP_TOO_COMPLEX); - return JS_FALSE; - } - state->classBitmapsMem += n; - /* CLASS, */ - state->progLength - += 1 + GetCompactIndexWidth(state->result->u.ucclass.index); - break; - - case '.': - state->result = NewRENode(state, REOP_DOT); - goto doSimple; - - case '{': - { - const jschar *errp = state->cp--; - intN err; - - err = ParseMinMaxQuantifier(state, JS_TRUE); - state->cp = errp; - - if (err < 0) - goto asFlat; - - /* FALL THROUGH */ - } - case '*': - case '+': - case '?': - ReportRegExpErrorHelper(state, JSREPORT_ERROR, - JSMSG_BAD_QUANTIFIER, state->cp - 1); - return JS_FALSE; - default: -asFlat: - state->result = NewRENode(state, REOP_FLAT); - if (!state->result) - return JS_FALSE; - state->result->u.flat.chr = c; - state->result->u.flat.length = 1; - state->result->kid = (void *) (state->cp - 1); - state->progLength += 3; - break; - } - return ParseQuantifier(state); -} - -static JSBool -ParseQuantifier(CompilerState *state) -{ - RENode *term; - term = state->result; - if (state->cp < state->cpend) { - switch (*state->cp) { - case '+': - state->result = NewRENode(state, REOP_QUANT); - if (!state->result) - return JS_FALSE; - state->result->u.range.min = 1; - state->result->u.range.max = (uintN)-1; - /* , ... */ - state->progLength += 4; - goto quantifier; - case '*': - state->result = NewRENode(state, REOP_QUANT); - if (!state->result) - return JS_FALSE; - state->result->u.range.min = 0; - state->result->u.range.max = (uintN)-1; - /* , ... */ - state->progLength += 4; - goto quantifier; - case '?': - state->result = NewRENode(state, REOP_QUANT); - if (!state->result) - return JS_FALSE; - state->result->u.range.min = 0; - state->result->u.range.max = 1; - /* , ... */ - state->progLength += 4; - goto quantifier; - case '{': /* balance '}' */ - { - intN err; - const jschar *errp = state->cp; - - err = ParseMinMaxQuantifier(state, JS_FALSE); - if (err == 0) - goto quantifier; - if (err == -1) - return JS_TRUE; - - ReportRegExpErrorHelper(state, JSREPORT_ERROR, err, errp); - return JS_FALSE; - } - default:; - } - } - return JS_TRUE; - -quantifier: - if (state->treeDepth == TREE_DEPTH_MAX) { - ReportRegExpError(state, JSREPORT_ERROR, JSMSG_REGEXP_TOO_COMPLEX); - return JS_FALSE; - } - - ++state->treeDepth; - ++state->cp; - state->result->kid = term; - if (state->cp < state->cpend && *state->cp == '?') { - ++state->cp; - state->result->u.range.greedy = JS_FALSE; - } else { - state->result->u.range.greedy = JS_TRUE; - } - return JS_TRUE; -} - -static intN -ParseMinMaxQuantifier(CompilerState *state, JSBool ignoreValues) -{ - uintN min, max; - jschar c; - const jschar *errp = state->cp++; - - c = *state->cp; - if (JS7_ISDEC(c)) { - ++state->cp; - min = GetDecimalValue(c, 0xFFFF, NULL, state); - c = *state->cp; - - if (!ignoreValues && min == OVERFLOW_VALUE) - return JSMSG_MIN_TOO_BIG; - - if (c == ',') { - c = *++state->cp; - if (JS7_ISDEC(c)) { - ++state->cp; - max = GetDecimalValue(c, 0xFFFF, NULL, state); - c = *state->cp; - if (!ignoreValues && max == OVERFLOW_VALUE) - return JSMSG_MAX_TOO_BIG; - if (!ignoreValues && min > max) - return JSMSG_OUT_OF_ORDER; - } else { - max = (uintN)-1; - } - } else { - max = min; - } - if (c == '}') { - state->result = NewRENode(state, REOP_QUANT); - if (!state->result) - return JSMSG_OUT_OF_MEMORY; - state->result->u.range.min = min; - state->result->u.range.max = max; - /* - * QUANT, , , ... - * where is written as compact(max+1) to make - * (uintN)-1 sentinel to occupy 1 byte, not width_of(max)+1. - */ - state->progLength += (1 + GetCompactIndexWidth(min) - + GetCompactIndexWidth(max + 1) - +3); - return 0; - } - } - - state->cp = errp; - return -1; -} - -static JSBool -SetForwardJumpOffset(jsbytecode *jump, jsbytecode *target) -{ - ptrdiff_t offset = target - jump; - - /* Check that target really points forward. */ - JS_ASSERT(offset >= 2); - if ((size_t)offset > OFFSET_MAX) - return JS_FALSE; - - jump[0] = JUMP_OFFSET_HI(offset); - jump[1] = JUMP_OFFSET_LO(offset); - return JS_TRUE; -} - -/* Copy the charset data from a character class node to the charset list - * in the regexp object. */ -static JS_ALWAYS_INLINE RECharSet * -InitNodeCharSet(JSRegExp *re, RENode *node) -{ - RECharSet *charSet = &re->classList[node->u.ucclass.index]; - charSet->converted = JS_FALSE; - charSet->length = node->u.ucclass.bmsize; - charSet->u.src.startIndex = node->u.ucclass.startIndex; - charSet->u.src.length = node->u.ucclass.kidlen; - charSet->sense = node->u.ucclass.sense; - return charSet; -} - -/* - * Generate bytecode for the tree rooted at t using an explicit stack instead - * of recursion. - */ -static jsbytecode * -EmitREBytecode(CompilerState *state, JSRegExp *re, size_t treeDepth, - jsbytecode *pc, RENode *t) -{ - EmitStateStackEntry *emitStateSP, *emitStateStack; - REOp op; - - if (treeDepth == 0) { - emitStateStack = NULL; - } else { - emitStateStack = - (EmitStateStackEntry *) - state->context->malloc(sizeof(EmitStateStackEntry) * treeDepth); - if (!emitStateStack) - return NULL; - } - emitStateSP = emitStateStack; - op = t->op; - JS_ASSERT(op < REOP_LIMIT); - - for (;;) { - *pc++ = op; - switch (op) { - case REOP_EMPTY: - --pc; - break; - - case REOP_ALTPREREQ2: - case REOP_ALTPREREQ: - JS_ASSERT(emitStateSP); - emitStateSP->altHead = pc - 1; - emitStateSP->endTermFixup = pc; - pc += OFFSET_LEN; - SET_ARG(pc, t->u.altprereq.ch1); - pc += ARG_LEN; - SET_ARG(pc, t->u.altprereq.ch2); - pc += ARG_LEN; - - emitStateSP->nextAltFixup = pc; /* offset to next alternate */ - pc += OFFSET_LEN; - - emitStateSP->continueNode = t; - emitStateSP->continueOp = REOP_JUMP; - emitStateSP->jumpToJumpFlag = JS_FALSE; - ++emitStateSP; - JS_ASSERT((size_t)(emitStateSP - emitStateStack) <= treeDepth); - t = (RENode *) t->kid; - op = t->op; - JS_ASSERT(op < REOP_LIMIT); - continue; - - case REOP_JUMP: - emitStateSP->nextTermFixup = pc; /* offset to following term */ - pc += OFFSET_LEN; - if (!SetForwardJumpOffset(emitStateSP->nextAltFixup, pc)) - goto jump_too_big; - emitStateSP->continueOp = REOP_ENDALT; - ++emitStateSP; - JS_ASSERT((size_t)(emitStateSP - emitStateStack) <= treeDepth); - t = (RENode *) t->u.kid2; - op = t->op; - JS_ASSERT(op < REOP_LIMIT); - continue; - - case REOP_ENDALT: - /* - * If we already patched emitStateSP->nextTermFixup to jump to - * a nearer jump, to avoid 16-bit immediate offset overflow, we - * are done here. - */ - if (emitStateSP->jumpToJumpFlag) - break; - - /* - * Fix up the REOP_JUMP offset to go to the op after REOP_ENDALT. - * REOP_ENDALT is executed only on successful match of the last - * alternate in a group. - */ - if (!SetForwardJumpOffset(emitStateSP->nextTermFixup, pc)) - goto jump_too_big; - if (t->op != REOP_ALT) { - if (!SetForwardJumpOffset(emitStateSP->endTermFixup, pc)) - goto jump_too_big; - } - - /* - * If the program is bigger than the REOP_JUMP offset range, then - * we must check for alternates before this one that are part of - * the same group, and fix up their jump offsets to target jumps - * close enough to fit in a 16-bit unsigned offset immediate. - */ - if ((size_t)(pc - re->program) > OFFSET_MAX && - emitStateSP > emitStateStack) { - EmitStateStackEntry *esp, *esp2; - jsbytecode *alt, *jump; - ptrdiff_t span, header; - - esp2 = emitStateSP; - alt = esp2->altHead; - for (esp = esp2 - 1; esp >= emitStateStack; --esp) { - if (esp->continueOp == REOP_ENDALT && - !esp->jumpToJumpFlag && - esp->nextTermFixup + OFFSET_LEN == alt && - (size_t)(pc - ((esp->continueNode->op != REOP_ALT) - ? esp->endTermFixup - : esp->nextTermFixup)) > OFFSET_MAX) { - alt = esp->altHead; - jump = esp->nextTermFixup; - - /* - * The span must be 1 less than the distance from - * jump offset to jump offset, so we actually jump - * to a REOP_JUMP bytecode, not to its offset! - */ - for (;;) { - JS_ASSERT(jump < esp2->nextTermFixup); - span = esp2->nextTermFixup - jump - 1; - if ((size_t)span <= OFFSET_MAX) - break; - do { - if (--esp2 == esp) - goto jump_too_big; - } while (esp2->continueOp != REOP_ENDALT); - } - - jump[0] = JUMP_OFFSET_HI(span); - jump[1] = JUMP_OFFSET_LO(span); - - if (esp->continueNode->op != REOP_ALT) { - /* - * We must patch the offset at esp->endTermFixup - * as well, for the REOP_ALTPREREQ{,2} opcodes. - * If we're unlucky and endTermFixup is more than - * OFFSET_MAX bytes from its target, we cheat by - * jumping 6 bytes to the jump whose offset is at - * esp->nextTermFixup, which has the same target. - */ - jump = esp->endTermFixup; - header = esp->nextTermFixup - jump; - span += header; - if ((size_t)span > OFFSET_MAX) - span = header; - - jump[0] = JUMP_OFFSET_HI(span); - jump[1] = JUMP_OFFSET_LO(span); - } - - esp->jumpToJumpFlag = JS_TRUE; - } - } - } - break; - - case REOP_ALT: - JS_ASSERT(emitStateSP); - emitStateSP->altHead = pc - 1; - emitStateSP->nextAltFixup = pc; /* offset to next alternate */ - pc += OFFSET_LEN; - emitStateSP->continueNode = t; - emitStateSP->continueOp = REOP_JUMP; - emitStateSP->jumpToJumpFlag = JS_FALSE; - ++emitStateSP; - JS_ASSERT((size_t)(emitStateSP - emitStateStack) <= treeDepth); - t = (RENode *) t->kid; - op = t->op; - JS_ASSERT(op < REOP_LIMIT); - continue; - - case REOP_FLAT: - /* - * Coalesce FLATs if possible and if it would not increase bytecode - * beyond preallocated limit. The latter happens only when bytecode - * size for coalesced string with offset p and length 2 exceeds 6 - * bytes preallocated for 2 single char nodes, i.e. when - * 1 + GetCompactIndexWidth(p) + GetCompactIndexWidth(2) > 6 or - * GetCompactIndexWidth(p) > 4. - * Since when GetCompactIndexWidth(p) <= 4 coalescing of 3 or more - * nodes strictly decreases bytecode size, the check has to be - * done only for the first coalescing. - */ - if (t->kid && - GetCompactIndexWidth((jschar *)t->kid - state->cpbegin) <= 4) - { - while (t->next && - t->next->op == REOP_FLAT && - (jschar*)t->kid + t->u.flat.length == - (jschar*)t->next->kid) { - t->u.flat.length += t->next->u.flat.length; - t->next = t->next->next; - } - } - if (t->kid && t->u.flat.length > 1) { - pc[-1] = (state->flags & JSREG_FOLD) ? REOP_FLATi : REOP_FLAT; - pc = WriteCompactIndex(pc, (jschar *)t->kid - state->cpbegin); - pc = WriteCompactIndex(pc, t->u.flat.length); - } else if (t->u.flat.chr < 256) { - pc[-1] = (state->flags & JSREG_FOLD) ? REOP_FLAT1i : REOP_FLAT1; - *pc++ = (jsbytecode) t->u.flat.chr; - } else { - pc[-1] = (state->flags & JSREG_FOLD) - ? REOP_UCFLAT1i - : REOP_UCFLAT1; - SET_ARG(pc, t->u.flat.chr); - pc += ARG_LEN; - } - break; - - case REOP_LPAREN: - JS_ASSERT(emitStateSP); - pc = WriteCompactIndex(pc, t->u.parenIndex); - emitStateSP->continueNode = t; - emitStateSP->continueOp = REOP_RPAREN; - ++emitStateSP; - JS_ASSERT((size_t)(emitStateSP - emitStateStack) <= treeDepth); - t = (RENode *) t->kid; - op = t->op; - continue; - - case REOP_RPAREN: - pc = WriteCompactIndex(pc, t->u.parenIndex); - break; - - case REOP_BACKREF: - pc = WriteCompactIndex(pc, t->u.parenIndex); - break; - - case REOP_ASSERT: - JS_ASSERT(emitStateSP); - emitStateSP->nextTermFixup = pc; - pc += OFFSET_LEN; - emitStateSP->continueNode = t; - emitStateSP->continueOp = REOP_ASSERTTEST; - ++emitStateSP; - JS_ASSERT((size_t)(emitStateSP - emitStateStack) <= treeDepth); - t = (RENode *) t->kid; - op = t->op; - continue; - - case REOP_ASSERTTEST: - case REOP_ASSERTNOTTEST: - if (!SetForwardJumpOffset(emitStateSP->nextTermFixup, pc)) - goto jump_too_big; - break; - - case REOP_ASSERT_NOT: - JS_ASSERT(emitStateSP); - emitStateSP->nextTermFixup = pc; - pc += OFFSET_LEN; - emitStateSP->continueNode = t; - emitStateSP->continueOp = REOP_ASSERTNOTTEST; - ++emitStateSP; - JS_ASSERT((size_t)(emitStateSP - emitStateStack) <= treeDepth); - t = (RENode *) t->kid; - op = t->op; - continue; - - case REOP_QUANT: - JS_ASSERT(emitStateSP); - if (t->u.range.min == 0 && t->u.range.max == (uintN)-1) { - pc[-1] = (t->u.range.greedy) ? REOP_STAR : REOP_MINIMALSTAR; - } else if (t->u.range.min == 0 && t->u.range.max == 1) { - pc[-1] = (t->u.range.greedy) ? REOP_OPT : REOP_MINIMALOPT; - } else if (t->u.range.min == 1 && t->u.range.max == (uintN) -1) { - pc[-1] = (t->u.range.greedy) ? REOP_PLUS : REOP_MINIMALPLUS; - } else { - if (!t->u.range.greedy) - pc[-1] = REOP_MINIMALQUANT; - pc = WriteCompactIndex(pc, t->u.range.min); - /* - * Write max + 1 to avoid using size_t(max) + 1 bytes - * for (uintN)-1 sentinel. - */ - pc = WriteCompactIndex(pc, t->u.range.max + 1); - } - emitStateSP->nextTermFixup = pc; - pc += OFFSET_LEN; - emitStateSP->continueNode = t; - emitStateSP->continueOp = REOP_ENDCHILD; - ++emitStateSP; - JS_ASSERT((size_t)(emitStateSP - emitStateStack) <= treeDepth); - t = (RENode *) t->kid; - op = t->op; - continue; - - case REOP_ENDCHILD: - if (!SetForwardJumpOffset(emitStateSP->nextTermFixup, pc)) - goto jump_too_big; - break; - - case REOP_CLASS: - if (!t->u.ucclass.sense) - pc[-1] = REOP_NCLASS; - pc = WriteCompactIndex(pc, t->u.ucclass.index); - InitNodeCharSet(re, t); - break; - - default: - break; - } - - t = t->next; - if (t) { - op = t->op; - } else { - if (emitStateSP == emitStateStack) - break; - --emitStateSP; - t = emitStateSP->continueNode; - op = (REOp) emitStateSP->continueOp; - } - } - - cleanup: - if (emitStateStack) - state->context->free(emitStateStack); - return pc; - - jump_too_big: - ReportRegExpError(state, JSREPORT_ERROR, JSMSG_REGEXP_TOO_COMPLEX); - pc = NULL; - goto cleanup; -} - -static JSBool -CompileRegExpToAST(JSContext* cx, TokenStream* ts, - JSString* str, uintN flags, CompilerState& state) -{ - uintN i; - size_t len; - - len = str->length(); - - state.context = cx; - state.tokenStream = ts; - state.cp = str->undepend(cx); - if (!state.cp) - return JS_FALSE; - state.cpbegin = state.cp; - state.cpend = state.cp + len; - state.flags = uint16(flags); - state.parenCount = 0; - state.classCount = 0; - state.progLength = 0; - state.treeDepth = 0; - state.classBitmapsMem = 0; - for (i = 0; i < CLASS_CACHE_SIZE; i++) - state.classCache[i].start = NULL; - - if (len != 0 && (flags & JSREG_FLAT)) { - state.result = NewRENode(&state, REOP_FLAT); - if (!state.result) - return JS_FALSE; - state.result->u.flat.chr = *state.cpbegin; - state.result->u.flat.length = len; - state.result->kid = (void *) state.cpbegin; - /* Flat bytecode: REOP_FLAT compact(string_offset) compact(len). */ - state.progLength += 1 + GetCompactIndexWidth(0) - + GetCompactIndexWidth(len); - return JS_TRUE; - } - - return ParseRegExp(&state); -} - -#ifdef JS_TRACER -typedef js::Vector LInsList; - -namespace js { - -struct REFragment : public nanojit::Fragment -{ - REFragment(const void* _ip verbose_only(, uint32_t profFragID)) - : nanojit::Fragment(ip verbose_only(, profFragID)) - {} -}; - -} /* namespace js */ - -/* Return the cached fragment for the given regexp, or create one. */ -static Fragment* -LookupNativeRegExp(JSContext* cx, uint16 re_flags, - const jschar* re_chars, size_t re_length) -{ - TraceMonitor *tm = &JS_TRACE_MONITOR(cx); - VMAllocator &alloc = *tm->dataAlloc; - REHashMap &table = *tm->reFragments; - - REHashKey k(re_length, re_flags, re_chars); - REFragment *frag = table.get(k); - - if (!frag) { - verbose_only( - uint32_t profFragID = (LogController.lcbits & LC_FragProfile) - ? (++(tm->lastFragID)) : 0; - ) - frag = new (alloc) REFragment(0 verbose_only(, profFragID)); - /* - * Copy the re_chars portion of the hash key into the Allocator, so - * its lifecycle is disconnected from the lifecycle of the - * underlying regexp. - */ - k.re_chars = (const jschar*) new (alloc) jschar[re_length]; - memcpy((void*) k.re_chars, re_chars, re_length * sizeof(jschar)); - table.put(k, frag); - } - return frag; -} - -static JSBool -ProcessCharSet(JSContext *cx, JSRegExp *re, RECharSet *charSet); - -/* Utilities for the RegExpNativeCompiler */ - -namespace { - /* - * An efficient way to simultaneously statically guard that the sizeof(bool) is a - * small power of 2 and take its log2. - */ - template struct StaticLog2 {}; - template <> struct StaticLog2<1> { static const int result = 0; }; - template <> struct StaticLog2<2> { static const int result = 1; }; - template <> struct StaticLog2<4> { static const int result = 2; }; - template <> struct StaticLog2<8> { static const int result = 3; }; -} - -/* - * This table allows efficient testing for the ASCII portion of \s during a - * trace. ECMA-262 15.10.2.12 defines the following characters below 128 to be - * whitespace: 0x9 (0), 0xA (10), 0xB (11), 0xC (12), 0xD (13), 0x20 (32). The - * index must be <= 32. - */ -static const bool js_ws[] = { -/* 0 1 2 3 4 5 5 7 8 9 */ -/* 0 */ false, false, false, false, false, false, false, false, false, true, -/* 1 */ true, true, true, true, false, false, false, false, false, false, -/* 2 */ false, false, false, false, false, false, false, false, false, false, -/* 3 */ false, false, true -}; - -/* Sets of characters are described in terms of individuals and classes. */ -class CharSet { public: - CharSet() : charEnd(charBuf), classes(0) {} - - static const uintN sBufSize = 8; - - bool full() { return charEnd == charBuf + sBufSize; } - - /* Add a single char to the set. */ - bool addChar(jschar c) - { - if (full()) - return false; - *charEnd++ = c; - return true; + RegExpMatchBuilder(JSContext *cx, JSObject *array) : cx(cx), array(array) {} + bool append(int index, JSString *str) { + JS_ASSERT(str); + return append(INT_TO_JSID(index), StringValue(str)); } - enum Class { - LineTerms = 1 << 0, /* Line Terminators (E262 7.3) */ - OtherSpace = 1 << 1, /* \s (E262 15.10.2.12) - LineTerms */ - Digit = 1 << 2, /* \d (E262 15.10.2.12) */ - OtherAlnum = 1 << 3, /* \w (E262 15,10.2.12) - Digit */ - Other = 1 << 4, /* all other characters */ - All = LineTerms | OtherSpace | Digit | OtherAlnum | Other, - - Space = LineTerms | OtherSpace, - AlNum = Digit | OtherAlnum, - Dot = All & ~LineTerms - }; - - /* Add a set of chars to the set. */ - void addClass(Class c) { classes |= c; } - - /* Return whether two sets of chars are disjoint. */ - bool disjoint(const CharSet &) const; - - private: - static bool disjoint(const jschar *beg, const jschar *end, uintN classes); - - mutable jschar charBuf[sBufSize]; - jschar *charEnd; - uintN classes; -}; - -/* Appease the type checker. */ -static inline CharSet::Class -operator|(CharSet::Class c1, CharSet::Class c2) { - return (CharSet::Class)(((int)c1) | ((int)c2)); -} -static inline CharSet::Class -operator~(CharSet::Class c) { - return (CharSet::Class)(~(int)c); -} - -/* - * Return whether the characters in the range [beg, end) fall within any of the - * classes with a bit set in 'classes'. - */ -bool -CharSet::disjoint(const jschar *beg, const jschar *end, uintN classes) -{ - for (const jschar *p = beg; p != end; ++p) { - if (JS7_ISDEC(*p)) { - if (classes & Digit) - return false; - } else if (JS_ISWORD(*p)) { - if (classes & OtherAlnum) - return false; - } else if (RE_IS_LINE_TERM(*p)) { - if (classes & LineTerms) - return false; - } else if (JS_ISSPACE(*p)) { - if (classes & OtherSpace) - return false; - } else { - if (classes & Other) - return false; - } - } - return true; -} - -/* - * Predicate version of the STL's set_intersection. Assumes both ranges are - * sorted and thus runs in linear time. - * - * FIXME: This is a reusable algorithm, perhaps it should be put somewhere. - */ -template -bool -set_disjoint(InputIterator1 p1, InputIterator1 end1, - InputIterator2 p2, InputIterator2 end2) -{ - if (p1 == end1 || p2 == end2) - return true; - while (*p1 != *p2) { - if (*p1 < *p2) { - ++p1; - if (p1 == end1) - return true; - } else if (*p2 < *p1) { - ++p2; - if (p2 == end2) - return true; - } - } - return false; -} - -static JSBool -CharCmp(void *arg, const void *a, const void *b, int *result) -{ - jschar ca = *(jschar *)a, cb = *(jschar *)b; - *result = ca - cb; - return JS_TRUE; -} - -bool -CharSet::disjoint(const CharSet &other) const -{ - /* Check overlap between classes. */ - if (classes & other.classes) - return false; - - /* - * Check char-class overlap. Compare this->charBuf with other.classes and - * vice versa with a loop. - */ - if (!disjoint(this->charBuf, this->charEnd, other.classes) || - !disjoint(other.charBuf, other.charEnd, this->classes)) - return false; - - /* Check char-char overlap. */ - jschar tmp[CharSet::sBufSize]; - js_MergeSort(charBuf, charEnd - charBuf, sizeof(jschar), - CharCmp, 0, tmp, JS_SORTING_GENERIC); - js_MergeSort(other.charBuf, other.charEnd - other.charBuf, sizeof(jschar), - CharCmp, 0, tmp, JS_SORTING_GENERIC); - return set_disjoint(charBuf, charEnd, other.charBuf, other.charEnd); -} - -/* - * Return true if the given subexpression may match the empty string. The - * conservative answer is |true|. If |next| is true, then the subexpression is - * considered to be |node| followed by the rest of |node->next|. Otherwise, the - * subexpression is considered to be |node| by itself. - */ -static bool -mayMatchEmpty(RENode *node, bool next = true) -{ - if (!node) - return true; - switch (node->op) { - case REOP_EMPTY: return true; - case REOP_FLAT: return false; - case REOP_CLASS: return false; - case REOP_ALNUM: return false; - case REOP_ALT: return (mayMatchEmpty((RENode *)node->kid) || - mayMatchEmpty((RENode *)node->u.kid2)) && - (!next || mayMatchEmpty(node->next)); - case REOP_QUANT: return (node->u.range.min == 0 || - mayMatchEmpty((RENode *)node->kid)) && - (!next || mayMatchEmpty(node->next)); - default: return true; - } -} - -/* - * Enumerate the set of characters that may be consumed next by the given - * subexpression in isolation. Return whether the enumeration was successful. - */ -static bool -enumerateNextChars(JSContext *cx, RENode *node, CharSet &set) -{ - JS_CHECK_RECURSION(cx, return JS_FALSE); - - if (!node) - return true; - - switch (node->op) { - /* Record as bitflags. */ - case REOP_DOT: set.addClass(CharSet::Dot); return true; - case REOP_DIGIT: set.addClass(CharSet::Digit); return true; - case REOP_NONDIGIT: set.addClass(~CharSet::Digit); return true; - case REOP_ALNUM: set.addClass(CharSet::AlNum); return true; - case REOP_NONALNUM: set.addClass(~CharSet::AlNum); return true; - case REOP_SPACE: set.addClass(CharSet::Space); return true; - case REOP_NONSPACE: set.addClass(~CharSet::Space); return true; - - /* Record as individual characters. */ - case REOP_FLAT: - return set.addChar(node->u.flat.chr); - - /* Control structures. */ - case REOP_EMPTY: - return true; - case REOP_ALT: - case REOP_ALTPREREQ: - return enumerateNextChars(cx, (RENode *)node->kid, set) && - enumerateNextChars(cx, (RENode *)node->u.kid2, set) && - (!mayMatchEmpty(node, false) || - enumerateNextChars(cx, (RENode *)node->next, set)); - case REOP_QUANT: - return enumerateNextChars(cx, (RENode *)node->kid, set) && - (!mayMatchEmpty(node, false) || - enumerateNextChars(cx, (RENode *)node->next, set)); - - /* Arbitrary character classes and oddities. */ - default: - return false; - } -} - -class RegExpNativeCompiler { - private: - VMAllocator& tempAlloc; - JSContext* cx; - JSRegExp* re; - CompilerState* cs; /* RegExp to compile */ - Fragment* fragment; - LirWriter* lir; -#ifdef DEBUG - ValidateWriter* validate_writer; -#endif -#ifdef NJ_VERBOSE - LirWriter* verbose_filter; -#endif - LirBufWriter* lirBufWriter; /* for skip */ - - LIns* state; - LIns* start; - LIns* cpend; - - LirBuffer* const lirbuf; - - bool outOfMemory() { - return tempAlloc.outOfMemory() || JS_TRACE_MONITOR(cx).dataAlloc->outOfMemory(); + bool append(jsid id, Value val) { + return !!js_DefineProperty(cx, array, id, &val, js::PropertyStub, js::PropertyStub, + JSPROP_ENUMERATE); } - JSBool isCaseInsensitive() const { return (cs->flags & JSREG_FOLD) != 0; } - - void targetCurrentPoint(LIns *ins) - { - ins->setTarget(lir->ins0(LIR_label)); + bool appendIndex(int index) { + return append(ATOM_TO_JSID(cx->runtime->atomState.indexAtom), Int32Value(index)); } - void targetCurrentPoint(LInsList &fails) - { - LIns *fail = lir->ins0(LIR_label); - for (size_t i = 0; i < fails.length(); ++i) { - fails[i]->setTarget(fail); - } - fails.clear(); - } - - /* - * These functions return the new position after their match operation, - * or NULL if there was an error. - */ - LIns* compileEmpty(RENode* node, LIns* pos, LInsList& fails) - { - return pos; - } - -#if defined(AVMPLUS_ARM) || defined(AVMPLUS_SPARC) -/* We can't do this on ARM or SPARC, since it relies on doing a 32-bit load from - * a pointer which is only 2-byte aligned. - */ -#undef USE_DOUBLE_CHAR_MATCH -#else -#define USE_DOUBLE_CHAR_MATCH -#endif - - LIns* compileFlatSingleChar(jschar ch, LIns* pos, LInsList& fails) - { - LIns* to_fail = lir->insBranch(LIR_jf, lir->ins2(LIR_ltp, pos, cpend), 0); - if (!fails.append(to_fail)) - return NULL; - LIns* text_ch = lir->insLoad(LIR_ldus2ui, pos, 0, ACCSET_OTHER, LOAD_CONST); - - // Extra characters that need to be compared against when doing folding. - struct extra { - jschar ch; - LIns *match; - }; - extra extras[5]; - int nextras = 0; - - if (cs->flags & JSREG_FOLD) { - ch = JS_TOUPPER(ch); - jschar lch = inverse_upcase(ch); - - if (ch != lch) { - if (L'A' <= ch && ch <= L'Z') { - // Fast conversion of text character to lower case by OR-ing with 32. - text_ch = lir->ins2(LIR_ori, text_ch, lir->insImmI(32)); - // These ASCII letters have 2 lower-case forms. We put the ASCII one in - // |extras| so it is tested first, because we expect that to be the common - // case. Note that the code points of the non-ASCII forms both have the - // 32 bit set, so it is OK to compare against the OR-32-converted text char. - ch = lch; - if (ch == L'i') { - extras[nextras++].ch = ch; - ch = 0x131; - } else if (ch == L's') { - extras[nextras++].ch = ch; - ch = 0x17f; - } - goto gen; - } else if (0x01c4 <= ch && ch <= 0x1e60) { - // The following group of conditionals handles characters that have 1 or 2 - // lower-case forms in addition to JS_TOLOWER(ch). - if (ch <= 0x1f1) { // DZ,LJ,NJ - if (ch == 0x01c4) { - extras[nextras++].ch = 0x01c5; - } else if (ch == 0x01c7) { - extras[nextras++].ch = 0x01c8; - } else if (ch == 0x01ca) { - extras[nextras++].ch = 0x01cb; - } else if (ch == 0x01f1) { - extras[nextras++].ch = 0x01f2; - } - } else if (ch < 0x0392) { // no extra lower-case forms in this range - } else if (ch <= 0x03a6) { // Greek - if (ch == 0x0392) { - extras[nextras++].ch = 0x03d0; - } else if (ch == 0x0395) { - extras[nextras++].ch = 0x03f5; - } else if (ch == 0x0398) { - extras[nextras++].ch = 0x03d1; - } else if (ch == 0x0399) { - extras[nextras++].ch = 0x0345; - extras[nextras++].ch = 0x1fbe; - } else if (ch == 0x039a) { - extras[nextras++].ch = 0x03f0; - } else if (ch == 0x039c) { - extras[nextras++].ch = 0xb5; - } else if (ch == 0x03a0) { - extras[nextras++].ch = 0x03d6; - } else if (ch == 0x03a1) { - extras[nextras++].ch = 0x03f1; - } else if (ch == 0x03a3) { - extras[nextras++].ch = 0x03c2; - } else if (ch == 0x03a6) { - extras[nextras++].ch = 0x03d5; - } - } else if (ch == 0x1e60) { // S with dot above - extras[nextras++].ch = 0x1e9b; - } - } - - extras[nextras++].ch = lch; - } - } - - gen: - for (int i = 0; i < nextras; ++i) { - LIns *test = lir->ins2(LIR_eqi, text_ch, lir->insImmI(extras[i].ch)); - LIns *branch = lir->insBranch(LIR_jt, test, 0); - extras[i].match = branch; - } - - if (!fails.append(lir->insBranch(LIR_jf, lir->ins2(LIR_eqi, text_ch, lir->insImmI(ch)), 0))) - return NULL; - - for (int i = 0; i < nextras; ++i) - targetCurrentPoint(extras[i].match); - return lir->ins2(LIR_addp, pos, lir->insImmWord(2)); - } - - JS_INLINE bool hasCases(jschar ch) - { - return JS_TOLOWER(ch) != JS_TOUPPER(ch); - } - - LIns* compileFlatDoubleChar(jschar ch1, jschar ch2, LIns* pos, LInsList& fails) - { -#ifdef IS_BIG_ENDIAN - uint32 word = (ch1 << 16) | ch2; -#else - uint32 word = (ch2 << 16) | ch1; -#endif - /* - * Fast case-insensitive test for ASCII letters: convert text - * char to lower case by bit-or-ing in 32 and compare. - */ - JSBool useFastCI = JS_FALSE; - union { jschar c[2]; uint32 i; } mask; - if (cs->flags & JSREG_FOLD) { - jschar uch1 = JS_TOUPPER(ch1); - jschar uch2 = JS_TOUPPER(ch2); - JSBool mask1 = (L'A' <= uch1 && uch1 <= L'Z' && uch1 != L'I' && uch1 != L'S'); - JSBool mask2 = (L'A' <= uch2 && uch2 <= L'Z' && uch2 != L'I' && uch2 != L'S'); - if ((!mask1 && hasCases(ch1)) || (!mask2 && hasCases(ch2))) { - pos = compileFlatSingleChar(ch1, pos, fails); - if (!pos) return NULL; - return compileFlatSingleChar(ch2, pos, fails); - } - - mask.c[0] = mask1 ? 0x0020 : 0x0; - mask.c[1] = mask2 ? 0x0020 : 0x0; - - if (mask.i) { - word |= mask.i; - useFastCI = JS_TRUE; - } - } - - LIns* to_fail = lir->insBranch(LIR_jf, - lir->ins2(LIR_ltp, - pos, - lir->ins2(LIR_addp, - cpend, - lir->insImmWord(-2))), - 0); - if (!fails.append(to_fail)) - return NULL; - LIns* text_word = lir->insLoad(LIR_ldi, pos, 0, ACCSET_OTHER); - LIns* comp_word = useFastCI ? - lir->ins2(LIR_ori, text_word, lir->insImmI(mask.i)) : - text_word; - if (!fails.append(lir->insBranch(LIR_jf, lir->ins2(LIR_eqi, comp_word, lir->insImmI(word)), 0))) - return NULL; - - return lir->ins2(LIR_addp, pos, lir->insImmWord(4)); - } - - LIns* compileFlat(RENode *&node, LIns* pos, LInsList& fails) - { -#ifdef USE_DOUBLE_CHAR_MATCH - if (node->u.flat.length == 1) { - if (node->next && node->next->op == REOP_FLAT && - node->next->u.flat.length == 1) { - pos = compileFlatDoubleChar(node->u.flat.chr, - node->next->u.flat.chr, - pos, fails); - node = node->next; - } else { - pos = compileFlatSingleChar(node->u.flat.chr, pos, fails); - } - return pos; - } else { - size_t i; - for (i = 0; i < node->u.flat.length - 1; i += 2) { - if (outOfMemory()) - return 0; - pos = compileFlatDoubleChar(((jschar*) node->kid)[i], - ((jschar*) node->kid)[i+1], - pos, fails); - if (!pos) - return 0; - } - JS_ASSERT(pos != 0); - if (i == node->u.flat.length - 1) - pos = compileFlatSingleChar(((jschar*) node->kid)[i], pos, fails); - return pos; - } -#else - if (node->u.flat.length == 1) { - return compileFlatSingleChar(node->u.flat.chr, pos, fails); - } else { - for (size_t i = 0; i < node->u.flat.length; i++) { - if (outOfMemory()) - return 0; - pos = compileFlatSingleChar(((jschar*) node->kid)[i], pos, fails); - if (!pos) - return 0; - } - return pos; - } -#endif - } - - LIns* compileClass(RENode* node, LIns* pos, LInsList& fails) - { - if (!node->u.ucclass.sense) - return JS_FALSE; - /* - * If we share generated native code, we need to make a copy - * of the bitmap because the original regexp's copy is destroyed when - * that regexp is. - */ - RECharSet *charSet = &re->classList[node->u.ucclass.index]; - size_t bitmapLen = (charSet->length >> 3) + 1; - /* Arbitrary size limit on bitmap. */ - if (bitmapLen > 1024) - return NULL; - Allocator &alloc = *JS_TRACE_MONITOR(cx).dataAlloc; - /* The following line allocates charSet.u.bits if successful. */ - if (!charSet->converted && !ProcessCharSet(cx, re, charSet)) - return NULL; - void* bitmapData = alloc.alloc(bitmapLen); - if (outOfMemory()) - return NULL; - memcpy(bitmapData, charSet->u.bits, bitmapLen); - - LIns* to_fail = lir->insBranch(LIR_jf, lir->ins2(LIR_ltp, pos, cpend), 0); - if (!fails.append(to_fail)) - return NULL; - LIns* text_ch = lir->insLoad(LIR_ldus2ui, pos, 0, ACCSET_OTHER, LOAD_CONST); - if (!fails.append(lir->insBranch(LIR_jf, - lir->ins2(LIR_lei, text_ch, lir->insImmI(charSet->length)), - 0))) { - return NULL; - } - LIns* byteIndex = lir->insI2P(lir->ins2(LIR_rshi, text_ch, lir->insImmI(3))); - LIns* bitmap = lir->insImmP(bitmapData); - LIns* byte = lir->insLoad(LIR_lduc2ui, lir->ins2(LIR_addp, bitmap, byteIndex), (int) 0, - ACCSET_OTHER, LOAD_CONST); - LIns* bitMask = lir->ins2(LIR_lshi, lir->insImmI(1), - lir->ins2(LIR_andi, text_ch, lir->insImmI(0x7))); - LIns* test = lir->ins2(LIR_eqi, lir->ins2(LIR_andi, byte, bitMask), lir->insImmI(0)); - - LIns* to_next = lir->insBranch(LIR_jt, test, 0); - if (!fails.append(to_next)) - return NULL; - return lir->ins2(LIR_addp, pos, lir->insImmWord(2)); - } - - /* Factor out common code to index js_alnum. */ - LIns *compileTableRead(LIns *chr, const bool *tbl) - { - if (sizeof(bool) != 1) { - LIns *sizeLog2 = lir->insImmI(StaticLog2::result); - chr = lir->ins2(LIR_lshi, chr, sizeLog2); - } - LIns *addr = lir->ins2(LIR_addp, lir->insImmP(tbl), lir->insUI2P(chr)); - return lir->insLoad(LIR_lduc2ui, addr, 0, ACCSET_OTHER, LOAD_CONST); - } - - /* Compile a builtin character class. */ - LIns *compileBuiltinClass(RENode *node, LIns *pos, LInsList &fails) - { - /* All the builtins checked below consume one character. */ - if (!fails.append(lir->insBranch(LIR_jf, lir->ins2(LIR_ltp, pos, cpend), 0))) - return NULL; - LIns *chr = lir->insLoad(LIR_ldus2ui, pos, 0, ACCSET_OTHER, LOAD_CONST); - - switch (node->op) { - case REOP_DOT: - { - /* Accept any character except those in ECMA-262 15.10.2.8. */ - LIns *eq1 = lir->ins2(LIR_eqi, chr, lir->insImmI('\n')); - if (!fails.append(lir->insBranch(LIR_jt, eq1, NULL))) - return NULL; - LIns *eq2 = lir->ins2(LIR_eqi, chr, lir->insImmI('\r')); - if (!fails.append(lir->insBranch(LIR_jt, eq2, NULL))) - return NULL; - LIns *eq3 = lir->ins2(LIR_eqi, chr, lir->insImmI(LINE_SEPARATOR)); - if (!fails.append(lir->insBranch(LIR_jt, eq3, NULL))) - return NULL; - LIns *eq4 = lir->ins2(LIR_eqi, chr, lir->insImmI(PARA_SEPARATOR)); - if (!fails.append(lir->insBranch(LIR_jt, eq4, NULL))) - return NULL; - break; - } - case REOP_DIGIT: - { - LIns *ge = lir->ins2(LIR_gei, chr, lir->insImmI('0')); - if (!fails.append(lir->insBranch(LIR_jf, ge, NULL))) - return NULL; - LIns *le = lir->ins2(LIR_lei, chr, lir->insImmI('9')); - if (!fails.append(lir->insBranch(LIR_jf, le, NULL))) - return NULL; - break; - } - case REOP_NONDIGIT: - { - /* Use 'and' to give a predictable branch for success path. */ - LIns *ge = lir->ins2(LIR_gei, chr, lir->insImmI('0')); - LIns *le = lir->ins2(LIR_lei, chr, lir->insImmI('9')); - LIns *both = lir->ins2(LIR_andi, ge, le); - if (!fails.append(lir->insBranch(LIR_jf, lir->insEqI_0(both), NULL))) - return NULL; - break; - } - case REOP_ALNUM: - { - /* - * Compile the condition: - * ((uint)*cp) < 128 && js_alnum[(uint)*cp] - */ - LIns *rangeCnd = lir->ins2(LIR_ltui, chr, lir->insImmI(128)); - if (!fails.append(lir->insBranch(LIR_jf, rangeCnd, NULL))) - return NULL; - LIns *tableVal = compileTableRead(chr, js_alnum); - if (!fails.append(lir->insBranch(LIR_jt, lir->insEqI_0(tableVal), NULL))) - return NULL; - break; - } - case REOP_NONALNUM: - { - /* - * Compile the condition: - * ((uint)*cp) >= 128 || !js_alnum[(uint)*cp] - */ - LIns *rangeCnd = lir->ins2(LIR_geui, chr, lir->insImmI(128)); - LIns *rangeBr = lir->insBranch(LIR_jt, rangeCnd, NULL); - LIns *tableVal = compileTableRead(chr, js_alnum); - if (!fails.append(lir->insBranch(LIR_jf, lir->insEqI_0(tableVal), NULL))) - return NULL; - LIns *success = lir->ins0(LIR_label); - rangeBr->setTarget(success); - break; - } - case REOP_SPACE: - case REOP_NONSPACE: - { - /* - * ECMA-262 7.2, 7.3, and 15.10.2.12 define a bunch of Unicode code - * points for whitespace. We optimize here for the common case of - * ASCII characters using a table lookup for the lower block that - * can actually contain spaces. For the rest, use a (more or less) - * binary search to minimize tests. - * - * [0000,0020]: 9, A, B, C, D, 20 - * (0020,00A0): none - * [00A0,2000): A0, 1680, 180E - * [2000,200A]: all - * (200A, max): 2028, 2029, 202F, 205F, 3000 - */ - /* Below 0x20? */ - LIns *tableRangeCnd = lir->ins2(LIR_leui, chr, lir->insImmI(0x20)); - LIns *tableRangeBr = lir->insBranch(LIR_jt, tableRangeCnd, NULL); - /* Fall through means *chr > 0x20. */ - - /* Handle (0x20,0xA0). */ - LIns *asciiCnd = lir->ins2(LIR_ltui, chr, lir->insImmI(0xA0)); - LIns *asciiMissBr = lir->insBranch(LIR_jt, asciiCnd, NULL); - /* Fall through means *chr >= 0xA0. */ - - /* Partition around [0x2000,0x200A]. */ - LIns *belowCnd = lir->ins2(LIR_ltui, chr, lir->insImmI(0x2000)); - LIns *belowBr = lir->insBranch(LIR_jt, belowCnd, NULL); - LIns *aboveCnd = lir->ins2(LIR_gtui, chr, lir->insImmI(0x200A)); - LIns *aboveBr = lir->insBranch(LIR_jt, aboveCnd, NULL); - LIns *intervalMatchBr = lir->insBranch(LIR_j, NULL, NULL); - - /* Handle [0xA0,0x2000). */ - LIns *belowLbl = lir->ins0(LIR_label); - belowBr->setTarget(belowLbl); - LIns *eq1Cnd = lir->ins2(LIR_eqi, chr, lir->insImmI(0xA0)); - LIns *eq1Br = lir->insBranch(LIR_jt, eq1Cnd, NULL); - LIns *eq2Cnd = lir->ins2(LIR_eqi, chr, lir->insImmI(0x1680)); - LIns *eq2Br = lir->insBranch(LIR_jt, eq2Cnd, NULL); - LIns *eq3Cnd = lir->ins2(LIR_eqi, chr, lir->insImmI(0x180E)); - LIns *eq3Br = lir->insBranch(LIR_jt, eq3Cnd, NULL); - LIns *belowMissBr = lir->insBranch(LIR_j, NULL, NULL); - - /* Handle (0x200A, max). */ - LIns *aboveLbl = lir->ins0(LIR_label); - aboveBr->setTarget(aboveLbl); - LIns *eq4Cnd = lir->ins2(LIR_eqi, chr, lir->insImmI(0x2028)); - LIns *eq4Br = lir->insBranch(LIR_jt, eq4Cnd, NULL); - LIns *eq5Cnd = lir->ins2(LIR_eqi, chr, lir->insImmI(0x2029)); - LIns *eq5Br = lir->insBranch(LIR_jt, eq5Cnd, NULL); - LIns *eq6Cnd = lir->ins2(LIR_eqi, chr, lir->insImmI(0x202F)); - LIns *eq6Br = lir->insBranch(LIR_jt, eq6Cnd, NULL); - LIns *eq7Cnd = lir->ins2(LIR_eqi, chr, lir->insImmI(0x205F)); - LIns *eq7Br = lir->insBranch(LIR_jt, eq7Cnd, NULL); - LIns *eq8Cnd = lir->ins2(LIR_eqi, chr, lir->insImmI(0x3000)); - LIns *eq8Br = lir->insBranch(LIR_jt, eq8Cnd, NULL); - LIns *aboveMissBr = lir->insBranch(LIR_j, NULL, NULL); - - /* Handle [0,0x20]. */ - LIns *tableLbl = lir->ins0(LIR_label); - tableRangeBr->setTarget(tableLbl); - LIns *tableVal = compileTableRead(chr, js_ws); - LIns *tableCnd = lir->insEqI_0(tableVal); - LIns *tableMatchBr = lir->insBranch(LIR_jf, tableCnd, NULL); - - /* Collect misses. */ - LIns *missLbl = lir->ins0(LIR_label); - asciiMissBr->setTarget(missLbl); - belowMissBr->setTarget(missLbl); - aboveMissBr->setTarget(missLbl); - LIns *missBr = lir->insBranch(LIR_j, NULL, NULL); - if (node->op == REOP_SPACE) { - if (!fails.append(missBr)) - return NULL; - } - - /* Collect matches. */ - LIns *matchLbl = lir->ins0(LIR_label); - intervalMatchBr->setTarget(matchLbl); - tableMatchBr->setTarget(matchLbl); - eq1Br->setTarget(matchLbl); eq2Br->setTarget(matchLbl); - eq3Br->setTarget(matchLbl); eq4Br->setTarget(matchLbl); - eq5Br->setTarget(matchLbl); eq6Br->setTarget(matchLbl); - eq7Br->setTarget(matchLbl); eq8Br->setTarget(matchLbl); - if (node->op == REOP_NONSPACE) { - LIns *matchBr = lir->insBranch(LIR_j, NULL, NULL); - if (!fails.append(matchBr)) - return NULL; - } - /* Fall through means match == success. */ - - /* Collect successes to fall through. */ - LIns *success = lir->ins0(LIR_label); - if (node->op == REOP_NONSPACE) - missBr->setTarget(success); - break; - } - default: - return NULL; - } - - return lir->ins2(LIR_addp, pos, lir->insImmWord(2)); - } - - LIns *compileAlt(RENode *node, LIns *pos, bool atEnd, LInsList &fails) - { - RENode *leftRe = (RENode *)node->kid, *rightRe = (RENode *)node->u.kid2; - - /* - * If the RE continues after the alternative, we need to ensure that no - * backtracking is required. Recursive calls to compileNode will fail - * on capturing parens, so the only thing we have to check here is that, - * if the left subexpression matches, we can keep going without later - * deciding we need to try the right subexpression. - */ - if (!atEnd) { - /* - * If there is no character overlap between left and right, then - * there is only one possible path through the alternative. - */ - CharSet leftSet, rightSet; - if (!enumerateNextChars(cx, leftRe, leftSet) || - !enumerateNextChars(cx, rightRe, rightSet) || - !leftSet.disjoint(rightSet)) - return NULL; - - /* - * If there is an empty path through either subexpression, the above - * check is incomplete; we need to include |node->next| as well. - */ - bool epsLeft = mayMatchEmpty(leftRe), - epsRight = mayMatchEmpty(rightRe); - if (epsRight && epsLeft) { - return NULL; - } else if (epsLeft || epsRight) { - CharSet nextSet; - if (!enumerateNextChars(cx, node->next, nextSet) || - (epsLeft && !nextSet.disjoint(rightSet)) || - (epsRight && !nextSet.disjoint(leftSet))) { - return NULL; - } - } - } - - /* Try left branch. */ - LInsList kidFails(cx); - LIns *branchEnd = compileNode(leftRe, pos, atEnd, kidFails); - if (!branchEnd) - return NULL; - - /* - * Since there are no phis, simulate by writing to and reading from - * memory (REGlobalData::stateStack, since it is unused). - */ - lir->insStore(branchEnd, state, - offsetof(REGlobalData, stateStack), ACCSET_OTHER); - LIns *leftSuccess = lir->insBranch(LIR_j, NULL, NULL); - - /* Try right branch. */ - targetCurrentPoint(kidFails); - if (!(branchEnd = compileNode(rightRe, pos, atEnd, fails))) - return NULL; - lir->insStore(branchEnd, state, - offsetof(REGlobalData, stateStack), ACCSET_OTHER); - - /* Land success on the left branch. */ - targetCurrentPoint(leftSuccess); - return addName(fragment->lirbuf, - lir->insLoad(LIR_ldp, state, offsetof(REGlobalData, stateStack), ACCSET_OTHER), - "pos"); - } - - LIns *compileOpt(RENode *node, LIns *pos, bool atEnd, LInsList &fails) - { - /* - * Since there are no phis, simulate by writing to and reading from - * memory (REGlobalData::stateStack, since it is unused). - */ - lir->insStore(pos, state, offsetof(REGlobalData, stateStack), ACCSET_OTHER); - - /* Try ? body. */ - LInsList kidFails(cx); - if (!(pos = compileNode(node, pos, atEnd, kidFails))) - return NULL; - lir->insStore(pos, state, offsetof(REGlobalData, stateStack), ACCSET_OTHER); - - /* Join success and failure and get new position. */ - targetCurrentPoint(kidFails); - pos = addName(fragment->lirbuf, - lir->insLoad(LIR_ldp, state, offsetof(REGlobalData, stateStack), ACCSET_OTHER), - "pos"); - - return pos; - } - - LIns *compileQuant(RENode *node, LIns *pos, bool atEnd, LInsList &fails) - { - /* Only support greedy *, +, ?. */ - if (!node->u.range.greedy || - node->u.range.min > 1 || - (node->u.range.max > 1 && node->u.range.max < (uintN)-1)) { - return NULL; - } - - RENode *bodyRe = (RENode *)node->kid; - - /* - * If the RE continues after the alternative, we need to ensure that no - * backtracking is required. Recursive calls to compileNode will fail - * on capturing parens, so the only thing we have to check here is that, - * if the quantifier body matches, we can continue matching the body - * without later deciding we need to undo the body matches. - */ - if (!atEnd) { - /* - * If there is no character overlap between the body and - * |node->next|, then all possible body matches are used. - */ - CharSet bodySet, nextSet; - if (!enumerateNextChars(cx, bodyRe, bodySet) || - !enumerateNextChars(cx, node->next, nextSet) || - !bodySet.disjoint(nextSet)) { - return NULL; - } - } - - /* Fork off ? and {1,1}. */ - if (node->u.range.max == 1) { - if (node->u.range.min == 1) - return compileNode(bodyRe, pos, atEnd, fails); - else - return compileOpt(bodyRe, pos, atEnd, fails); - } - - /* For +, compile a copy of the body where failure is real failure. */ - if (node->u.range.min == 1) { - if (!(pos = compileNode(bodyRe, pos, atEnd, fails))) - return NULL; - } - - /* - * Since there are no phis, simulate by writing to and reading from - * memory (REGlobalData::stateStack, since it is unused). - */ - lir->insStore(pos, state, offsetof(REGlobalData, stateStack), ACCSET_OTHER); - - /* Begin iteration: load loop variables. */ - LIns *loopTop = lir->ins0(LIR_label); - LIns *iterBegin = addName(fragment->lirbuf, - lir->insLoad(LIR_ldp, state, - offsetof(REGlobalData, stateStack), ACCSET_OTHER), - "pos"); - - /* Match quantifier body. */ - LInsList kidFails(cx); - LIns *iterEnd = compileNode(bodyRe, iterBegin, atEnd, kidFails); - if (!iterEnd) - return NULL; - - /* - * If there is an epsilon path through the body then, when it is taken, - * we need to abort the loop or else we will loop forever. - */ - if (mayMatchEmpty(bodyRe)) { - LIns *eqCnd = lir->ins2(LIR_eqp, iterBegin, iterEnd); - if (!kidFails.append(lir->insBranch(LIR_jt, eqCnd, NULL))) - return NULL; - } - - /* End iteration: store loop variables, increment, jump */ - lir->insStore(iterEnd, state, offsetof(REGlobalData, stateStack), ACCSET_OTHER); - lir->insBranch(LIR_j, NULL, loopTop); - - /* - * Using '+' as branch, the intended control flow is: - * - * ... - * A -> | - * |<---. - * B -> | | - * +--. | - * C -> | | | - * +--. | - * D -> | | | - * +--|-' - * X -> | | - * |<-' - * E -> | - * ... - * - * We are currently at point X. Since the regalloc makes a single, - * linear, backwards sweep over the IR (going from E to A), point X - * must tell the regalloc what LIR insns are live at the end of D. - * Thus, we need to report *all* insns defined *before* the end of D - * that may be used *after* D. This means insns defined in A, B, C, or - * D and used in B, C, D, or E. Since insns in B, C, and D are - * conditionally executed, and we (currently) don't have real phi - * nodes, we need only consider insns defined in A and used in E. - */ - lir->ins1(LIR_livep, state); - lir->ins1(LIR_livep, cpend); - lir->ins1(LIR_livep, start); - - /* After the loop: reload 'pos' from memory and continue. */ - targetCurrentPoint(kidFails); - return iterBegin; - } - - /* - * Compile the regular expression rooted at 'node'. Return 0 on failed - * compilation. Otherwise, generate code that falls through on success (the - * returned LIns* is the current 'pos') and jumps to the end on failure (by - * adding the guard LIns to 'fails'). - */ - LIns *compileNode(RENode *node, LIns *pos, bool atEnd, LInsList &fails) - { - for (; pos && node; node = node->next) { - if (outOfMemory()) - return NULL; - - bool childNextIsEnd = atEnd && !node->next; - - switch (node->op) { - case REOP_EMPTY: - pos = compileEmpty(node, pos, fails); - break; - case REOP_FLAT: - pos = compileFlat(node, pos, fails); - break; - case REOP_ALT: - case REOP_ALTPREREQ: - pos = compileAlt(node, pos, childNextIsEnd, fails); - break; - case REOP_QUANT: - pos = compileQuant(node, pos, childNextIsEnd, fails); - break; - case REOP_CLASS: - pos = compileClass(node, pos, fails); - break; - case REOP_DOT: - case REOP_DIGIT: - case REOP_NONDIGIT: - case REOP_ALNUM: - case REOP_NONALNUM: - case REOP_SPACE: - case REOP_NONSPACE: - pos = compileBuiltinClass(node, pos, fails); - break; - default: - return NULL; - } - } - return pos; - } - - /* - * This function kicks off recursive compileNode compilation, finishes the - * success path, and lets the failed-match path fall through. - */ - bool compileRootNode(RENode *root, LIns *pos, LIns *anchorFail) - { - /* Compile the regular expression body. */ - LInsList fails(cx); - pos = compileNode(root, pos, true, fails); - if (!pos) - return false; - - /* Fall-through from compileNode means success. */ - lir->insStore(pos, state, offsetof(REGlobalData, stateStack), ACCSET_OTHER); - lir->ins0(LIR_regfence); - lir->ins1(LIR_reti, lir->insImmI(1)); - - /* Stick return here so we don't have to jump over it every time. */ - if (anchorFail) { - targetCurrentPoint(anchorFail); - lir->ins0(LIR_regfence); - lir->ins1(LIR_reti, lir->insImmI(0)); - } - - /* Target failed matches. */ - targetCurrentPoint(fails); - return true; - } - - /* Compile a regular expressions that can only match on the first char. */ - bool compileSticky(RENode *root, LIns *start) - { - if (!compileRootNode(root, start, NULL)) - return false; - - /* Failed to match on first character, so fail whole match. */ - lir->ins0(LIR_regfence); - lir->ins1(LIR_reti, lir->insImmI(0)); - return !outOfMemory(); - } - - /* Compile normal regular expressions that can match starting at any char. */ - bool compileAnchoring(RENode *root, LIns *start) - { - /* Guard outer anchoring loop. Use <= to allow empty regexp match. */ - LIns *anchorFail = lir->insBranch(LIR_jf, lir->ins2(LIR_lep, start, cpend), 0); - - if (!compileRootNode(root, start, anchorFail)) - return false; - - /* Outer loop increment. */ - lir->insStore(lir->ins2(LIR_addp, start, lir->insImmWord(2)), state, - offsetof(REGlobalData, skipped), ACCSET_OTHER); - - return !outOfMemory(); - } - - inline LIns* - addName(LirBuffer* lirbuf, LIns* ins, const char* name) - { -#ifdef NJ_VERBOSE - debug_only_stmt(lirbuf->printer->lirNameMap->addName(ins, name);) -#endif - return ins; - } - - /* - * Insert the side exit and guard record for a compiled regexp. Most - * of the fields are not used. The important part is the regexp source - * and flags, which we use as the fragment lookup key. - */ - GuardRecord* insertGuard(LIns* loopLabel, const jschar* re_chars, size_t re_length) - { - if (loopLabel) { - lir->insBranch(LIR_j, NULL, loopLabel); - LirBuffer* lirbuf = fragment->lirbuf; - lir->ins1(LIR_livep, lirbuf->state); - lir->ins1(LIR_livep, lirbuf->param1); - } - - Allocator &alloc = *JS_TRACE_MONITOR(cx).dataAlloc; - - /* Must only create a VMSideExit; see StackFilter::getTops. */ - size_t len = (sizeof(GuardRecord) + - sizeof(VMSideExit) + - (re_length-1) * sizeof(jschar)); - GuardRecord* guard = (GuardRecord *) alloc.alloc(len); - VMSideExit* exit = (VMSideExit*)(guard+1); - guard->exit = exit; - guard->exit->target = fragment; - fragment->lastIns = lir->insGuard(LIR_x, NULL, guard); - // guard->profCount is calloc'd to zero - verbose_only( - guard->profGuardID = fragment->guardNumberer++; - guard->nextInFrag = fragment->guardsForFrag; - fragment->guardsForFrag = guard; - ) - return guard; - } - - public: - RegExpNativeCompiler(JSContext* cx, JSRegExp* re, CompilerState* cs, Fragment* fragment) - : tempAlloc(*JS_TRACE_MONITOR(cx).reTempAlloc), cx(cx), - re(re), cs(cs), fragment(fragment), lir(NULL), lirBufWriter(NULL), - lirbuf(new (tempAlloc) LirBuffer(tempAlloc)) - { - fragment->lirbuf = lirbuf; -#ifdef DEBUG - lirbuf->printer = new (tempAlloc) LInsPrinter(tempAlloc, TM_NUM_USED_ACCS); -#endif - } - - ~RegExpNativeCompiler() { - /* Purge the tempAlloc used during recording. */ - tempAlloc.reset(); - } - - JSBool compile() - { - GuardRecord* guard = NULL; - const jschar* re_chars; - size_t re_length; - TraceMonitor* tm = &JS_TRACE_MONITOR(cx); - Assembler *assm = tm->assembler; - LIns* loopLabel = NULL; - - if (outOfMemory() || OverfullJITCache(tm)) - return JS_FALSE; - - re->source->getCharsAndLength(re_chars, re_length); - /* - * If the regexp is too long nanojit will assert when we - * try to insert the guard record. - */ - if (re_length > 1024) { - re->flags |= JSREG_NOCOMPILE; - return JS_FALSE; - } - - /* At this point we have an empty fragment. */ - LirBuffer* lirbuf = fragment->lirbuf; - if (outOfMemory()) - goto fail; - /* FIXME Use bug 463260 smart pointer when available. */ - lir = lirBufWriter = new LirBufWriter(lirbuf, nanojit::AvmCore::config); - - /* FIXME Use bug 463260 smart pointer when available. */ -#ifdef NJ_VERBOSE - debug_only_stmt( - if (LogController.lcbits & LC_TMRegexp) { - lir = verbose_filter = new VerboseWriter(tempAlloc, lir, lirbuf->printer, - &LogController); - } - ) -#endif -#ifdef DEBUG - lir = validate_writer = new ValidateWriter(lir, lirbuf->printer, "regexp writer pipeline"); -#endif - - /* - * Although we could just load REGlobalData::cpend from 'state', by - * passing it as a parameter, we avoid loading it every iteration. - */ - lir->ins0(LIR_start); - - for (int i = 0; i < NumSavedRegs; ++i) - lir->insParam(i, 1); -#ifdef DEBUG - for (int i = 0; i < NumSavedRegs; ++i) - addName(lirbuf, lirbuf->savedRegs[i], regNames[Assembler::savedRegs[i]]); -#endif - - lirbuf->state = state = addName(lirbuf, lir->insParam(0, 0), "state"); - lirbuf->param1 = cpend = addName(lirbuf, lir->insParam(1, 0), "cpend"); - -#ifdef DEBUG - // Do this before any REGlobalData loads/stores occur. 'extras' can - // be stack-allocated. - { - void* extras[] = { /* dummy */NULL, /* dummy */NULL }; - validate_writer->setCheckAccSetExtras(extras); - } -#endif - - loopLabel = lir->ins0(LIR_label); - // If profiling, record where the loop label is, so that the - // assembler can insert a frag-entry-counter increment at that - // point - verbose_only( if (LogController.lcbits & LC_FragProfile) { - NanoAssert(!fragment->loopLabel); - fragment->loopLabel = loopLabel; - }) - - start = addName(lirbuf, - lir->insLoad(LIR_ldp, state, offsetof(REGlobalData, skipped), ACCSET_OTHER), - "start"); - - if (cs->flags & JSREG_STICKY) { - if (!compileSticky(cs->result, start)) - goto fail; - } else { - if (!compileAnchoring(cs->result, start)) - goto fail; - } - - guard = insertGuard(loopLabel, re_chars, re_length); - - if (outOfMemory()) - goto fail; - - /* - * Deep in the nanojit compiler, the StackFilter is trying to throw - * away stores above the VM interpreter/native stacks. We have no such - * stacks, so rely on the fact that lirbuf->sp and lirbuf->rp are null - * to ensure our stores are ignored. - */ - JS_ASSERT(!lirbuf->sp && !lirbuf->rp); - - assm->compile(fragment, tempAlloc, /*optimize*/true verbose_only(, lirbuf->printer)); - if (assm->error() != nanojit::None) - goto fail; - - delete lirBufWriter; -#ifdef DEBUG - delete validate_writer; -#endif -#ifdef NJ_VERBOSE - debug_only_stmt( if (LogController.lcbits & LC_TMRegexp) - delete verbose_filter; ) -#endif - return JS_TRUE; - fail: - if (outOfMemory() || OverfullJITCache(tm)) { - delete lirBufWriter; - // recover profiling data from expiring Fragments - verbose_only( - REHashMap::Iter iter(*(tm->reFragments)); - while (iter.next()) { - nanojit::Fragment* frag = iter.value(); - FragProfiling_FragFinalizer(frag, tm); - } - ) - FlushJITCache(cx); - } else { - if (!guard) insertGuard(loopLabel, re_chars, re_length); - re->flags |= JSREG_NOCOMPILE; - delete lirBufWriter; - } -#ifdef DEBUG - delete validate_writer; -#endif -#ifdef NJ_VERBOSE - debug_only_stmt( if (LogController.lcbits & LC_TMRegexp) - delete verbose_filter; ) -#endif - return JS_FALSE; + /* Sets the input attribute of the match array. */ + bool appendInput(JSString *str) { + JS_ASSERT(str); + return append(ATOM_TO_JSID(cx->runtime->atomState.inputAtom), StringValue(str)); } }; /* - * Compile a regexp to native code in the given fragment. + * Lock obj and replace its regexp internals with |newRegExp|. + * Decref the replaced regexp internals. */ -static inline JSBool -CompileRegExpToNative(JSContext* cx, JSRegExp* re, Fragment* fragment) -{ - JSBool rv = JS_FALSE; - void* mark; - CompilerState state; - RegExpNativeCompiler rc(cx, re, &state, fragment); - - JS_ASSERT(!fragment->code()); - mark = JS_ARENA_MARK(&cx->tempPool); - if (!CompileRegExpToAST(cx, NULL, re->source, re->flags, state)) { - goto out; - } - rv = rc.compile(); - out: - JS_ARENA_RELEASE(&cx->tempPool, mark); - return rv; -} - -/* Function type for a compiled native regexp. */ -typedef void *(FASTCALL *NativeRegExp)(REGlobalData*, const jschar *); - -/* - * Return a compiled native regexp if one already exists or can be created - * now, or NULL otherwise. - */ -static NativeRegExp -GetNativeRegExp(JSContext* cx, JSRegExp* re) -{ - const jschar *re_chars; - size_t re_length; - re->source->getCharsAndLength(re_chars, re_length); - Fragment *fragment = LookupNativeRegExp(cx, re->flags, re_chars, re_length); - JS_ASSERT(fragment); - if (!fragment->code() && fragment->recordAttempts == 0) { - fragment->recordAttempts++; - if (!CompileRegExpToNative(cx, re, fragment)) - return NULL; - } - union { NIns *code; NativeRegExp func; } u; - u.code = fragment->code(); - return u.func; -} -#endif - -JSRegExp * -js_NewRegExp(JSContext *cx, TokenStream *ts, - JSString *str, uintN flags, JSBool flat) -{ - JSRegExp *re; - void *mark; - CompilerState state; - size_t resize; - jsbytecode *endPC; - uintN i; - - re = NULL; - mark = JS_ARENA_MARK(&cx->tempPool); - - /* - * Parsing the string as flat is now expressed internally using - * a flag, so that we keep this information in the JSRegExp, but - * we keep the 'flat' parameter for now for compatibility. - */ - if (flat) flags |= JSREG_FLAT; - if (!CompileRegExpToAST(cx, ts, str, flags, state)) - goto out; - - resize = offsetof(JSRegExp, program) + state.progLength + 1; - re = (JSRegExp *) cx->malloc(resize); - if (!re) - goto out; - - re->nrefs = 1; - JS_ASSERT(state.classBitmapsMem <= CLASS_BITMAPS_MEM_LIMIT); - re->classCount = state.classCount; - if (re->classCount) { - re->classList = (RECharSet *) - cx->malloc(re->classCount * sizeof(RECharSet)); - if (!re->classList) { - js_DestroyRegExp(cx, re); - re = NULL; - goto out; - } - for (i = 0; i < re->classCount; i++) - re->classList[i].converted = JS_FALSE; - } else { - re->classList = NULL; - } - - /* Compile the bytecode version. */ - endPC = EmitREBytecode(&state, re, state.treeDepth, re->program, state.result); - if (!endPC) { - js_DestroyRegExp(cx, re); - re = NULL; - goto out; - } - *endPC++ = REOP_END; - /* - * Check whether size was overestimated and shrink using realloc. - * This is safe since no pointers to newly parsed regexp or its parts - * besides re exist here. - */ - if ((size_t)(endPC - re->program) != state.progLength + 1) { - JSRegExp *tmp; - JS_ASSERT((size_t)(endPC - re->program) < state.progLength + 1); - resize = offsetof(JSRegExp, program) + (endPC - re->program); - tmp = (JSRegExp *) cx->realloc(re, resize); - if (tmp) - re = tmp; - } - - re->flags = uint16(flags); - re->parenCount = state.parenCount; - re->source = str; - -out: - JS_ARENA_RELEASE(&cx->tempPool, mark); - return re; -} - -JSRegExp * -js_NewRegExpOpt(JSContext *cx, JSString *str, JSString *opt, JSBool flat) -{ - uintN flags; - const jschar *s; - size_t i, n; - char charBuf[2]; - - flags = 0; - if (opt) { - opt->getCharsAndLength(s, n); - for (i = 0; i < n; i++) { -#define HANDLE_FLAG(name) \ - JS_BEGIN_MACRO \ - if (flags & (name)) \ - goto bad_flag; \ - flags |= (name); \ - JS_END_MACRO - switch (s[i]) { - case 'g': - HANDLE_FLAG(JSREG_GLOB); - break; - case 'i': - HANDLE_FLAG(JSREG_FOLD); - break; - case 'm': - HANDLE_FLAG(JSREG_MULTILINE); - break; - case 'y': - HANDLE_FLAG(JSREG_STICKY); - break; - default: - bad_flag: - charBuf[0] = (char)s[i]; - charBuf[1] = '\0'; - JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, - js_GetErrorMessage, NULL, - JSMSG_BAD_REGEXP_FLAG, charBuf); - return NULL; - } -#undef HANDLE_FLAG - } - } - return js_NewRegExp(cx, NULL, str, flags, flat); -} - -/* - * Save the current state of the match - the position in the input - * text as well as the position in the bytecode. The state of any - * parent expressions is also saved (preceding state). - * Contents of parenCount parentheses from parenIndex are also saved. - */ -static REBackTrackData * -PushBackTrackState(REGlobalData *gData, REOp op, - jsbytecode *target, REMatchState *x, const jschar *cp, - size_t parenIndex, size_t parenCount) -{ - size_t i; - REBackTrackData *result = - (REBackTrackData *) ((char *)gData->backTrackSP + gData->cursz); - - size_t sz = sizeof(REBackTrackData) + - gData->stateStackTop * sizeof(REProgState) + - parenCount * sizeof(RECapture); - - ptrdiff_t btsize = gData->backTrackStackSize; - ptrdiff_t btincr = ((char *)result + sz) - - ((char *)gData->backTrackStack + btsize); - - re_debug("\tBT_Push: %lu,%lu", - (unsigned long) parenIndex, (unsigned long) parenCount); - - if (btincr > 0) { - ptrdiff_t offset = (char *)result - (char *)gData->backTrackStack; - - btincr = JS_ROUNDUP(btincr, btsize); - JS_ARENA_GROW_CAST(gData->backTrackStack, REBackTrackData *, - &gData->cx->regexpPool, btsize, btincr); - if (!gData->backTrackStack) { - js_ReportOutOfScriptQuota(gData->cx); - gData->ok = JS_FALSE; - return NULL; - } - gData->backTrackStackSize = btsize + btincr; - result = (REBackTrackData *) ((char *)gData->backTrackStack + offset); - } - gData->backTrackSP = result; - result->sz = gData->cursz; - gData->cursz = sz; - - result->backtrack_op = op; - result->backtrack_pc = target; - result->cp = cp; - result->parenCount = parenCount; - result->parenIndex = parenIndex; - - result->saveStateStackTop = gData->stateStackTop; - JS_ASSERT(gData->stateStackTop); - memcpy(result + 1, gData->stateStack, - sizeof(REProgState) * result->saveStateStackTop); - - if (parenCount != 0) { - memcpy((char *)(result + 1) + - sizeof(REProgState) * result->saveStateStackTop, - &x->parens[parenIndex], - sizeof(RECapture) * parenCount); - for (i = 0; i != parenCount; i++) - x->parens[parenIndex + i].index = -1; - } - - return result; -} - - -/* - * Consecutive literal characters. - */ -#if 0 -static REMatchState * -FlatNMatcher(REGlobalData *gData, REMatchState *x, jschar *matchChars, - size_t length) -{ - size_t i; - if (length > gData->cpend - x->cp) - return NULL; - for (i = 0; i != length; i++) { - if (matchChars[i] != x->cp[i]) - return NULL; - } - x->cp += length; - return x; -} -#endif - -static JS_ALWAYS_INLINE REMatchState * -FlatNIMatcher(REGlobalData *gData, REMatchState *x, jschar *matchChars, - size_t length) -{ - size_t i; - JS_ASSERT(gData->cpend >= x->cp); - if (length > (size_t)(gData->cpend - x->cp)) - return NULL; - for (i = 0; i != length; i++) { - if (upcase(matchChars[i]) != upcase(x->cp[i])) - return NULL; - } - x->cp += length; - return x; -} - -/* - * 1. Evaluate DecimalEscape to obtain an EscapeValue E. - * 2. If E is not a character then go to step 6. - * 3. Let ch be E's character. - * 4. Let A be a one-element RECharSet containing the character ch. - * 5. Call CharacterSetMatcher(A, false) and return its Matcher result. - * 6. E must be an integer. Let n be that integer. - * 7. If n=0 or n>NCapturingParens then throw a SyntaxError exception. - * 8. Return an internal Matcher closure that takes two arguments, a State x - * and a Continuation c, and performs the following: - * 1. Let cap be x's captures internal array. - * 2. Let s be cap[n]. - * 3. If s is undefined, then call c(x) and return its result. - * 4. Let e be x's endIndex. - * 5. Let len be s's length. - * 6. Let f be e+len. - * 7. If f>InputLength, return failure. - * 8. If there exists an integer i between 0 (inclusive) and len (exclusive) - * such that Canonicalize(s[i]) is not the same character as - * Canonicalize(Input [e+i]), then return failure. - * 9. Let y be the State (f, cap). - * 10. Call c(y) and return its result. - */ -static REMatchState * -BackrefMatcher(REGlobalData *gData, REMatchState *x, size_t parenIndex) -{ - size_t len, i; - const jschar *parenContent; - RECapture *cap = &x->parens[parenIndex]; - - if (cap->index == -1) - return x; - - len = cap->length; - if (x->cp + len > gData->cpend) - return NULL; - - parenContent = &gData->cpbegin[cap->index]; - if (gData->regexp->flags & JSREG_FOLD) { - for (i = 0; i < len; i++) { - if (upcase(parenContent[i]) != upcase(x->cp[i])) - return NULL; - } - } else { - for (i = 0; i < len; i++) { - if (parenContent[i] != x->cp[i]) - return NULL; - } - } - x->cp += len; - return x; -} - - -/* Add a single character to the RECharSet */ static void -AddCharacterToCharSet(RECharSet *cs, jschar c) +SwapObjectRegExp(JSContext *cx, JSObject *obj, RegExp &newRegExp) { - uintN byteIndex = (uintN)(c >> 3); - JS_ASSERT(c <= cs->length); - cs->u.bits[byteIndex] |= 1 << (c & 0x7); -} - - -/* Add a character range, c1 to c2 (inclusive) to the RECharSet */ -static void -AddCharacterRangeToCharSet(RECharSet *cs, uintN c1, uintN c2) -{ - uintN i; - - uintN byteIndex1 = c1 >> 3; - uintN byteIndex2 = c2 >> 3; - - JS_ASSERT(c2 <= cs->length && c1 <= c2); - - c1 &= 0x7; - c2 &= 0x7; - - if (byteIndex1 == byteIndex2) { - cs->u.bits[byteIndex1] |= ((uint8)0xFF >> (7 - (c2 - c1))) << c1; - } else { - cs->u.bits[byteIndex1] |= 0xFF << c1; - for (i = byteIndex1 + 1; i < byteIndex2; i++) - cs->u.bits[i] = 0xFF; - cs->u.bits[byteIndex2] |= (uint8)0xFF >> (7 - c2); + RegExp *oldRegExp; + { + AutoObjectLocker lock(cx, obj); + oldRegExp = RegExp::extractFrom(obj); + obj->setPrivate(&newRegExp); + obj->zeroRegExpLastIndex(); } -} - -struct CharacterRange { - jschar start; - jschar end; -}; - -/* - * The following characters are taken from the ECMA-262 standard, section 7.2 - * and 7.3, and the Unicode 3 standard, Table 6-1. - */ -static const CharacterRange WhiteSpaceRanges[] = { - /* TAB, LF, VT, FF, CR */ - { 0x0009, 0x000D }, - /* SPACE */ - { 0x0020, 0x0020 }, - /* NO-BREAK SPACE */ - { 0x00A0, 0x00A0 }, - /* - * EN QUAD, EM QUAD, EN SPACE, EM SPACE, THREE-PER-EM SPACE, FOUR-PER-EM - * SPACE, SIX-PER-EM SPACE, FIGURE SPACE, PUNCTUATION SPACE, THIN SPACE, - * HAIR SPACE, ZERO WIDTH SPACE - */ - { 0x2000, 0x200B }, - /* LS, PS */ - { 0x2028, 0x2029 }, - /* NARROW NO-BREAK SPACE */ - { 0x202F, 0x202F }, - /* IDEOGRAPHIC SPACE */ - { 0x3000, 0x3000 } -}; - -/* ECMA-262 standard, section 15.10.2.6. */ -static const CharacterRange WordRanges[] = { - { jschar('0'), jschar('9') }, - { jschar('A'), jschar('Z') }, - { jschar('_'), jschar('_') }, - { jschar('a'), jschar('z') } -}; - -static void -AddCharacterRanges(RECharSet *charSet, - const CharacterRange *range, - const CharacterRange *end) -{ - for (; range < end; ++range) - AddCharacterRangeToCharSet(charSet, range->start, range->end); -} - -static void -AddInvertedCharacterRanges(RECharSet *charSet, - const CharacterRange *range, - const CharacterRange *end) -{ - uint16 previous = 0; - for (; range < end; ++range) { - AddCharacterRangeToCharSet(charSet, previous, range->start - 1); - previous = range->end + 1; - } - AddCharacterRangeToCharSet(charSet, previous, charSet->length); -} - -/* Compile the source of the class into a RECharSet */ -static JSBool -ProcessCharSet(JSContext *cx, JSRegExp *re, RECharSet *charSet) -{ - const jschar *src, *end; - JSBool inRange = JS_FALSE; - jschar rangeStart = 0; - uintN byteLength, n; - jschar c, thisCh; - intN nDigits, i; - - JS_ASSERT(!charSet->converted); - /* - * Assert that startIndex and length points to chars inside [] inside - * source string. - */ - JS_ASSERT(1 <= charSet->u.src.startIndex); - JS_ASSERT(charSet->u.src.startIndex < re->source->length()); - JS_ASSERT(charSet->u.src.length <= re->source->length() - - 1 - charSet->u.src.startIndex); - - charSet->converted = JS_TRUE; - src = re->source->chars() + charSet->u.src.startIndex; - end = src + charSet->u.src.length; - JS_ASSERT(src[-1] == '['); - JS_ASSERT(end[0] == ']'); - - byteLength = (charSet->length >> 3) + 1; - charSet->u.bits = (uint8 *)cx->malloc(byteLength); - if (!charSet->u.bits) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - memset(charSet->u.bits, 0, byteLength); - - if (src == end) - return JS_TRUE; - - if (*src == '^') { - JS_ASSERT(charSet->sense == JS_FALSE); - ++src; - } else { - JS_ASSERT(charSet->sense == JS_TRUE); - } - - while (src != end) { - switch (*src) { - case '\\': - ++src; - c = *src++; - switch (c) { - case 'b': - thisCh = 0x8; - break; - case 'f': - thisCh = 0xC; - break; - case 'n': - thisCh = 0xA; - break; - case 'r': - thisCh = 0xD; - break; - case 't': - thisCh = 0x9; - break; - case 'v': - thisCh = 0xB; - break; - case 'c': - if (src < end && JS_ISWORD(*src)) { - thisCh = (jschar)(*src++ & 0x1F); - } else { - --src; - thisCh = '\\'; - } - break; - case 'x': - nDigits = 2; - goto lexHex; - case 'u': - nDigits = 4; - lexHex: - n = 0; - for (i = 0; (i < nDigits) && (src < end); i++) { - uintN digit; - c = *src++; - if (!isASCIIHexDigit(c, &digit)) { - /* - * Back off to accepting the original '\' - * as a literal - */ - src -= i + 1; - n = '\\'; - break; - } - n = (n << 4) | digit; - } - thisCh = (jschar)n; - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - /* - * This is a non-ECMA extension - decimal escapes (in this - * case, octal!) are supposed to be an error inside class - * ranges, but supported here for backwards compatibility. - */ - n = JS7_UNDEC(c); - c = *src; - if ('0' <= c && c <= '7') { - src++; - n = 8 * n + JS7_UNDEC(c); - c = *src; - if ('0' <= c && c <= '7') { - src++; - i = 8 * n + JS7_UNDEC(c); - if (i <= 0377) - n = i; - else - src--; - } - } - thisCh = (jschar)n; - break; - - case 'd': - AddCharacterRangeToCharSet(charSet, '0', '9'); - continue; /* don't need range processing */ - case 'D': - AddCharacterRangeToCharSet(charSet, 0, '0' - 1); - AddCharacterRangeToCharSet(charSet, - (jschar)('9' + 1), - (jschar)charSet->length); - continue; - case 's': - AddCharacterRanges(charSet, WhiteSpaceRanges, - WhiteSpaceRanges + JS_ARRAY_LENGTH(WhiteSpaceRanges)); - continue; - case 'S': - AddInvertedCharacterRanges(charSet, WhiteSpaceRanges, - WhiteSpaceRanges + JS_ARRAY_LENGTH(WhiteSpaceRanges)); - continue; - case 'w': - AddCharacterRanges(charSet, WordRanges, - WordRanges + JS_ARRAY_LENGTH(WordRanges)); - continue; - case 'W': - AddInvertedCharacterRanges(charSet, WordRanges, - WordRanges + JS_ARRAY_LENGTH(WordRanges)); - continue; - default: - thisCh = c; - break; - - } - break; - - default: - thisCh = *src++; - break; - - } - if (inRange) { - if (re->flags & JSREG_FOLD) { - int i; - - JS_ASSERT(rangeStart <= thisCh); - for (i = rangeStart; i <= thisCh; i++) { - jschar uch, dch; - - AddCharacterToCharSet(charSet, jschar(i)); - uch = jschar(upcase(i)); - dch = inverse_upcase(jschar(i)); - if (i != uch) - AddCharacterToCharSet(charSet, uch); - if (i != dch) - AddCharacterToCharSet(charSet, dch); - } - } else { - AddCharacterRangeToCharSet(charSet, rangeStart, thisCh); - } - inRange = JS_FALSE; - } else { - if (re->flags & JSREG_FOLD) { - AddCharacterToCharSet(charSet, jschar(upcase(thisCh))); - AddCharacterToCharSet(charSet, inverse_upcase(thisCh)); - } else { - AddCharacterToCharSet(charSet, thisCh); - } - if (src < end - 1) { - if (*src == '-') { - ++src; - inRange = JS_TRUE; - rangeStart = thisCh; - } - } - } - } - return JS_TRUE; -} - -static inline JSBool -MatcherProcessCharSet(REGlobalData *gData, RECharSet *charSet) { - JSBool rv = ProcessCharSet(gData->cx, gData->regexp, charSet); - if (!rv) gData->ok = JS_FALSE; - return rv; + if (oldRegExp) + oldRegExp->decref(cx); } void -js_DestroyRegExp(JSContext *cx, JSRegExp *re) +js_SaveAndClearRegExpStatics(JSContext *cx, js::RegExpStatics *statics, js::AutoStringRooter *tvr) { - if (JS_ATOMIC_DECREMENT(&re->nrefs) == 0) { - if (re->classList) { - uintN i; - for (i = 0; i < re->classCount; i++) { - if (re->classList[i].converted) - cx->free(re->classList[i].u.bits); - re->classList[i].u.bits = NULL; - } - cx->free(re->classList); - } - cx->free(re); - } + JS_ASSERT(statics); + statics->clone(cx->regExpStatics); + if (statics->getInput()) + tvr->setString(statics->getInput()); + cx->regExpStatics.clear(); } -static JSBool -ReallocStateStack(REGlobalData *gData) +void +js_RestoreRegExpStatics(JSContext *cx, js::RegExpStatics *statics) { - size_t limit = gData->stateStackLimit; - size_t sz = sizeof(REProgState) * limit; - - JS_ARENA_GROW_CAST(gData->stateStack, REProgState *, - &gData->cx->regexpPool, sz, sz); - if (!gData->stateStack) { - js_ReportOutOfScriptQuota(gData->cx); - gData->ok = JS_FALSE; - return JS_FALSE; - } - gData->stateStackLimit = limit + limit; - return JS_TRUE; + JS_ASSERT(statics); + cx->regExpStatics.clone(*statics); } -#define PUSH_STATE_STACK(data) \ - JS_BEGIN_MACRO \ - ++(data)->stateStackTop; \ - if ((data)->stateStackTop == (data)->stateStackLimit && \ - !ReallocStateStack((data))) { \ - return NULL; \ - } \ - JS_END_MACRO - -/* - * Apply the current op against the given input to see if it's going to match - * or fail. Return false if we don't get a match, true if we do. If updatecp is - * true, then update the current state's cp. Always update startpc to the next - * op. - */ -static JS_ALWAYS_INLINE REMatchState * -SimpleMatch(REGlobalData *gData, REMatchState *x, REOp op, - jsbytecode **startpc, JSBool updatecp) +JSObject * JS_FASTCALL +js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *proto) { - REMatchState *result = NULL; - jschar matchCh; - size_t parenIndex; - size_t offset, length, index; - jsbytecode *pc = *startpc; /* pc has already been incremented past op */ - jschar *source; - const jschar *startcp = x->cp; - jschar ch; - RECharSet *charSet; - -#ifdef REGEXP_DEBUG - const char *opname = reop_names[op]; - re_debug("\n%06d: %*s%s", pc - gData->regexp->program, - gData->stateStackTop * 2, "", opname); -#endif - switch (op) { - case REOP_EMPTY: - result = x; - break; - case REOP_BOL: - if (x->cp != gData->cpbegin) { - if (!gData->cx->regExpStatics.multiline && - !(gData->regexp->flags & JSREG_MULTILINE)) { - break; - } - if (!RE_IS_LINE_TERM(x->cp[-1])) - break; - } - result = x; - break; - case REOP_EOL: - if (x->cp != gData->cpend) { - if (!gData->cx->regExpStatics.multiline && - !(gData->regexp->flags & JSREG_MULTILINE)) { - break; - } - if (!RE_IS_LINE_TERM(*x->cp)) - break; - } - result = x; - break; - case REOP_WBDRY: - if ((x->cp == gData->cpbegin || !JS_ISWORD(x->cp[-1])) ^ - !(x->cp != gData->cpend && JS_ISWORD(*x->cp))) { - result = x; - } - break; - case REOP_WNONBDRY: - if ((x->cp == gData->cpbegin || !JS_ISWORD(x->cp[-1])) ^ - (x->cp != gData->cpend && JS_ISWORD(*x->cp))) { - result = x; - } - break; - case REOP_DOT: - if (x->cp != gData->cpend && !RE_IS_LINE_TERM(*x->cp)) { - result = x; - result->cp++; - } - break; - case REOP_DIGIT: - if (x->cp != gData->cpend && JS7_ISDEC(*x->cp)) { - result = x; - result->cp++; - } - break; - case REOP_NONDIGIT: - if (x->cp != gData->cpend && !JS7_ISDEC(*x->cp)) { - result = x; - result->cp++; - } - break; - case REOP_ALNUM: - if (x->cp != gData->cpend && JS_ISWORD(*x->cp)) { - result = x; - result->cp++; - } - break; - case REOP_NONALNUM: - if (x->cp != gData->cpend && !JS_ISWORD(*x->cp)) { - result = x; - result->cp++; - } - break; - case REOP_SPACE: - if (x->cp != gData->cpend && JS_ISSPACE(*x->cp)) { - result = x; - result->cp++; - } - break; - case REOP_NONSPACE: - if (x->cp != gData->cpend && !JS_ISSPACE(*x->cp)) { - result = x; - result->cp++; - } - break; - case REOP_BACKREF: - pc = ReadCompactIndex(pc, &parenIndex); - JS_ASSERT(parenIndex < gData->regexp->parenCount); - result = BackrefMatcher(gData, x, parenIndex); - break; - case REOP_FLAT: - pc = ReadCompactIndex(pc, &offset); - JS_ASSERT(offset < gData->regexp->source->length()); - pc = ReadCompactIndex(pc, &length); - JS_ASSERT(1 <= length); - JS_ASSERT(length <= gData->regexp->source->length() - offset); - if (length <= (size_t)(gData->cpend - x->cp)) { - source = gData->regexp->source->chars() + offset; - re_debug_chars(source, length); - for (index = 0; index != length; index++) { - if (source[index] != x->cp[index]) - return NULL; - } - x->cp += length; - result = x; - } - break; - case REOP_FLAT1: - matchCh = *pc++; - re_debug(" '%c' == '%c'", (char)matchCh, (char)*x->cp); - if (x->cp != gData->cpend && *x->cp == matchCh) { - result = x; - result->cp++; - } - break; - case REOP_FLATi: - pc = ReadCompactIndex(pc, &offset); - JS_ASSERT(offset < gData->regexp->source->length()); - pc = ReadCompactIndex(pc, &length); - JS_ASSERT(1 <= length); - JS_ASSERT(length <= gData->regexp->source->length() - offset); - source = gData->regexp->source->chars(); - result = FlatNIMatcher(gData, x, source + offset, length); - break; - case REOP_FLAT1i: - matchCh = *pc++; - if (x->cp != gData->cpend && upcase(*x->cp) == upcase(matchCh)) { - result = x; - result->cp++; - } - break; - case REOP_UCFLAT1: - matchCh = GET_ARG(pc); - re_debug(" '%c' == '%c'", (char)matchCh, (char)*x->cp); - pc += ARG_LEN; - if (x->cp != gData->cpend && *x->cp == matchCh) { - result = x; - result->cp++; - } - break; - case REOP_UCFLAT1i: - matchCh = GET_ARG(pc); - pc += ARG_LEN; - if (x->cp != gData->cpend && upcase(*x->cp) == upcase(matchCh)) { - result = x; - result->cp++; - } - break; - case REOP_CLASS: - pc = ReadCompactIndex(pc, &index); - JS_ASSERT(index < gData->regexp->classCount); - if (x->cp != gData->cpend) { - charSet = &gData->regexp->classList[index]; - JS_ASSERT(charSet->converted); - ch = *x->cp; - index = ch >> 3; - if (ch <= charSet->length && - (charSet->u.bits[index] & (1 << (ch & 0x7)))) { - result = x; - result->cp++; - } - } - break; - case REOP_NCLASS: - pc = ReadCompactIndex(pc, &index); - JS_ASSERT(index < gData->regexp->classCount); - if (x->cp != gData->cpend) { - charSet = &gData->regexp->classList[index]; - JS_ASSERT(charSet->converted); - ch = *x->cp; - index = ch >> 3; - if (ch > charSet->length || - !(charSet->u.bits[index] & (1 << (ch & 0x7)))) { - result = x; - result->cp++; - } - } - break; - - default: - JS_ASSERT(JS_FALSE); - } - if (result) { - if (!updatecp) - x->cp = startcp; - *startpc = pc; - re_debug(" * "); - return result; - } - x->cp = startcp; - return NULL; -} - -static JS_ALWAYS_INLINE REMatchState * -ExecuteREBytecode(REGlobalData *gData, REMatchState *x) -{ - REMatchState *result = NULL; - REBackTrackData *backTrackData; - jsbytecode *nextpc, *testpc; - REOp nextop; - RECapture *cap; - REProgState *curState; - const jschar *startcp; - size_t parenIndex, k; - size_t parenSoFar = 0; - - jschar matchCh1, matchCh2; - RECharSet *charSet; - - JSBool anchor; - jsbytecode *pc = gData->regexp->program; - REOp op = (REOp) *pc++; - - /* - * If the first node is a simple match, step the index into the string - * until that match is made, or fail if it can't be found at all. - */ - if (REOP_IS_SIMPLE(op) && !(gData->regexp->flags & JSREG_STICKY)) { - anchor = JS_FALSE; - while (x->cp <= gData->cpend) { - nextpc = pc; /* reset back to start each time */ - result = SimpleMatch(gData, x, op, &nextpc, JS_TRUE); - if (result) { - anchor = JS_TRUE; - x = result; - pc = nextpc; /* accept skip to next opcode */ - op = (REOp) *pc++; - JS_ASSERT(op < REOP_LIMIT); - break; - } - gData->skipped++; - x->cp++; - } - if (!anchor) - goto bad; - } - - for (;;) { -#ifdef REGEXP_DEBUG - const char *opname = reop_names[op]; - re_debug("\n%06d: %*s%s", pc - gData->regexp->program, - gData->stateStackTop * 2, "", opname); -#endif - if (REOP_IS_SIMPLE(op)) { - result = SimpleMatch(gData, x, op, &pc, JS_TRUE); + JS_ASSERT(obj->getClass() == &js_RegExpClass); + JS_ASSERT(proto); + JS_ASSERT(proto->getClass() == &js_RegExpClass); + JSObject *clone = NewNativeClassInstance(cx, &js_RegExpClass, proto, proto->getParent()); + if (!clone) + return NULL; + RegExp *re = RegExp::extractFrom(obj); + { + uint32 origFlags = re->getFlags(); + uint32 staticsFlags = cx->regExpStatics.getFlags(); + if ((origFlags & staticsFlags) != staticsFlags) { + /* + * This regex is lacking flags from the statics, so we must recompile with the new + * flags instead of increffing. + */ + re = RegExp::create(cx, re->getSource(), origFlags | staticsFlags); } else { - curState = &gData->stateStack[gData->stateStackTop]; - switch (op) { - case REOP_END: - goto good; - case REOP_ALTPREREQ2: - nextpc = pc + GET_OFFSET(pc); /* start of next op */ - pc += ARG_LEN; - matchCh2 = GET_ARG(pc); - pc += ARG_LEN; - k = GET_ARG(pc); - pc += ARG_LEN; - - if (x->cp != gData->cpend) { - if (*x->cp == matchCh2) - goto doAlt; - - charSet = &gData->regexp->classList[k]; - if (!charSet->converted && !MatcherProcessCharSet(gData, charSet)) - goto bad; - matchCh1 = *x->cp; - k = matchCh1 >> 3; - if ((matchCh1 > charSet->length || - !(charSet->u.bits[k] & (1 << (matchCh1 & 0x7)))) ^ - charSet->sense) { - goto doAlt; - } - } - result = NULL; - break; - - case REOP_ALTPREREQ: - nextpc = pc + GET_OFFSET(pc); /* start of next op */ - pc += ARG_LEN; - matchCh1 = GET_ARG(pc); - pc += ARG_LEN; - matchCh2 = GET_ARG(pc); - pc += ARG_LEN; - if (x->cp == gData->cpend || - (*x->cp != matchCh1 && *x->cp != matchCh2)) { - result = NULL; - break; - } - /* else false thru... */ - - case REOP_ALT: - doAlt: - nextpc = pc + GET_OFFSET(pc); /* start of next alternate */ - pc += ARG_LEN; /* start of this alternate */ - curState->parenSoFar = parenSoFar; - PUSH_STATE_STACK(gData); - op = (REOp) *pc++; - startcp = x->cp; - if (REOP_IS_SIMPLE(op)) { - if (!SimpleMatch(gData, x, op, &pc, JS_TRUE)) { - op = (REOp) *nextpc++; - pc = nextpc; - continue; - } - result = x; - op = (REOp) *pc++; - } - nextop = (REOp) *nextpc++; - if (!PushBackTrackState(gData, nextop, nextpc, x, startcp, 0, 0)) - goto bad; - continue; - - /* - * Occurs at (successful) end of REOP_ALT, - */ - case REOP_JUMP: - /* - * If we have not gotten a result here, it is because of an - * empty match. Do the same thing REOP_EMPTY would do. - */ - if (!result) - result = x; - - --gData->stateStackTop; - pc += GET_OFFSET(pc); - op = (REOp) *pc++; - continue; - - /* - * Occurs at last (successful) end of REOP_ALT, - */ - case REOP_ENDALT: - /* - * If we have not gotten a result here, it is because of an - * empty match. Do the same thing REOP_EMPTY would do. - */ - if (!result) - result = x; - - --gData->stateStackTop; - op = (REOp) *pc++; - continue; - - case REOP_LPAREN: - pc = ReadCompactIndex(pc, &parenIndex); - re_debug("[ %lu ]", (unsigned long) parenIndex); - JS_ASSERT(parenIndex < gData->regexp->parenCount); - if (parenIndex + 1 > parenSoFar) - parenSoFar = parenIndex + 1; - x->parens[parenIndex].index = x->cp - gData->cpbegin; - x->parens[parenIndex].length = 0; - op = (REOp) *pc++; - continue; - - case REOP_RPAREN: - { - ptrdiff_t delta; - - pc = ReadCompactIndex(pc, &parenIndex); - JS_ASSERT(parenIndex < gData->regexp->parenCount); - cap = &x->parens[parenIndex]; - delta = x->cp - (gData->cpbegin + cap->index); - cap->length = (delta < 0) ? 0 : (size_t) delta; - op = (REOp) *pc++; - - if (!result) - result = x; - continue; - } - case REOP_ASSERT: - nextpc = pc + GET_OFFSET(pc); /* start of term after ASSERT */ - pc += ARG_LEN; /* start of ASSERT child */ - op = (REOp) *pc++; - testpc = pc; - if (REOP_IS_SIMPLE(op) && - !SimpleMatch(gData, x, op, &testpc, JS_FALSE)) { - result = NULL; - break; - } - curState->u.assertion.top = - (char *)gData->backTrackSP - (char *)gData->backTrackStack; - curState->u.assertion.sz = gData->cursz; - curState->index = x->cp - gData->cpbegin; - curState->parenSoFar = parenSoFar; - PUSH_STATE_STACK(gData); - if (!PushBackTrackState(gData, REOP_ASSERTTEST, - nextpc, x, x->cp, 0, 0)) { - goto bad; - } - continue; - - case REOP_ASSERT_NOT: - nextpc = pc + GET_OFFSET(pc); - pc += ARG_LEN; - op = (REOp) *pc++; - testpc = pc; - if (REOP_IS_SIMPLE(op) /* Note - fail to fail! */ && - SimpleMatch(gData, x, op, &testpc, JS_FALSE) && - *testpc == REOP_ASSERTNOTTEST) { - result = NULL; - break; - } - curState->u.assertion.top - = (char *)gData->backTrackSP - - (char *)gData->backTrackStack; - curState->u.assertion.sz = gData->cursz; - curState->index = x->cp - gData->cpbegin; - curState->parenSoFar = parenSoFar; - PUSH_STATE_STACK(gData); - if (!PushBackTrackState(gData, REOP_ASSERTNOTTEST, - nextpc, x, x->cp, 0, 0)) { - goto bad; - } - continue; - - case REOP_ASSERTTEST: - --gData->stateStackTop; - --curState; - x->cp = gData->cpbegin + curState->index; - gData->backTrackSP = - (REBackTrackData *) ((char *)gData->backTrackStack + - curState->u.assertion.top); - gData->cursz = curState->u.assertion.sz; - if (result) - result = x; - break; - - case REOP_ASSERTNOTTEST: - --gData->stateStackTop; - --curState; - x->cp = gData->cpbegin + curState->index; - gData->backTrackSP = - (REBackTrackData *) ((char *)gData->backTrackStack + - curState->u.assertion.top); - gData->cursz = curState->u.assertion.sz; - result = (!result) ? x : NULL; - break; - case REOP_STAR: - curState->u.quantifier.min = 0; - curState->u.quantifier.max = (uintN)-1; - goto quantcommon; - case REOP_PLUS: - curState->u.quantifier.min = 1; - curState->u.quantifier.max = (uintN)-1; - goto quantcommon; - case REOP_OPT: - curState->u.quantifier.min = 0; - curState->u.quantifier.max = 1; - goto quantcommon; - case REOP_QUANT: - pc = ReadCompactIndex(pc, &k); - curState->u.quantifier.min = k; - pc = ReadCompactIndex(pc, &k); - /* max is k - 1 to use one byte for (uintN)-1 sentinel. */ - curState->u.quantifier.max = k - 1; - JS_ASSERT(curState->u.quantifier.min - <= curState->u.quantifier.max); - quantcommon: - if (curState->u.quantifier.max == 0) { - pc = pc + GET_OFFSET(pc); - op = (REOp) *pc++; - result = x; - continue; - } - /* Step over */ - nextpc = pc + ARG_LEN; - op = (REOp) *nextpc++; - startcp = x->cp; - if (REOP_IS_SIMPLE(op)) { - if (!SimpleMatch(gData, x, op, &nextpc, JS_TRUE)) { - if (curState->u.quantifier.min == 0) - result = x; - else - result = NULL; - pc = pc + GET_OFFSET(pc); - break; - } - op = (REOp) *nextpc++; - result = x; - } - curState->index = startcp - gData->cpbegin; - curState->continue_op = REOP_REPEAT; - curState->continue_pc = pc; - curState->parenSoFar = parenSoFar; - PUSH_STATE_STACK(gData); - if (curState->u.quantifier.min == 0 && - !PushBackTrackState(gData, REOP_REPEAT, pc, x, startcp, - 0, 0)) { - goto bad; - } - pc = nextpc; - continue; - - case REOP_ENDCHILD: /* marks the end of a quantifier child */ - pc = curState[-1].continue_pc; - op = (REOp) curState[-1].continue_op; - - if (!result) - result = x; - continue; - - case REOP_REPEAT: - --curState; - do { - --gData->stateStackTop; - if (!result) { - /* Failed, see if we have enough children. */ - if (curState->u.quantifier.min == 0) - goto repeatDone; - goto break_switch; - } - if (curState->u.quantifier.min == 0 && - x->cp == gData->cpbegin + curState->index) { - /* matched an empty string, that'll get us nowhere */ - result = NULL; - goto break_switch; - } - if (curState->u.quantifier.min != 0) - curState->u.quantifier.min--; - if (curState->u.quantifier.max != (uintN) -1) - curState->u.quantifier.max--; - if (curState->u.quantifier.max == 0) - goto repeatDone; - nextpc = pc + ARG_LEN; - nextop = (REOp) *nextpc; - startcp = x->cp; - if (REOP_IS_SIMPLE(nextop)) { - nextpc++; - if (!SimpleMatch(gData, x, nextop, &nextpc, JS_TRUE)) { - if (curState->u.quantifier.min == 0) - goto repeatDone; - result = NULL; - goto break_switch; - } - result = x; - } - curState->index = startcp - gData->cpbegin; - PUSH_STATE_STACK(gData); - if (curState->u.quantifier.min == 0 && - !PushBackTrackState(gData, REOP_REPEAT, - pc, x, startcp, - curState->parenSoFar, - parenSoFar - - curState->parenSoFar)) { - goto bad; - } - } while (*nextpc == REOP_ENDCHILD); - pc = nextpc; - op = (REOp) *pc++; - parenSoFar = curState->parenSoFar; - continue; - - repeatDone: - result = x; - pc += GET_OFFSET(pc); - goto break_switch; - - case REOP_MINIMALSTAR: - curState->u.quantifier.min = 0; - curState->u.quantifier.max = (uintN)-1; - goto minimalquantcommon; - case REOP_MINIMALPLUS: - curState->u.quantifier.min = 1; - curState->u.quantifier.max = (uintN)-1; - goto minimalquantcommon; - case REOP_MINIMALOPT: - curState->u.quantifier.min = 0; - curState->u.quantifier.max = 1; - goto minimalquantcommon; - case REOP_MINIMALQUANT: - pc = ReadCompactIndex(pc, &k); - curState->u.quantifier.min = k; - pc = ReadCompactIndex(pc, &k); - /* See REOP_QUANT comments about k - 1. */ - curState->u.quantifier.max = k - 1; - JS_ASSERT(curState->u.quantifier.min - <= curState->u.quantifier.max); - minimalquantcommon: - curState->index = x->cp - gData->cpbegin; - curState->parenSoFar = parenSoFar; - PUSH_STATE_STACK(gData); - if (curState->u.quantifier.min != 0) { - curState->continue_op = REOP_MINIMALREPEAT; - curState->continue_pc = pc; - /* step over */ - pc += OFFSET_LEN; - op = (REOp) *pc++; - } else { - if (!PushBackTrackState(gData, REOP_MINIMALREPEAT, - pc, x, x->cp, 0, 0)) { - goto bad; - } - --gData->stateStackTop; - pc = pc + GET_OFFSET(pc); - op = (REOp) *pc++; - } - continue; - - case REOP_MINIMALREPEAT: - --gData->stateStackTop; - --curState; - - re_debug("{%d,%d}", curState->u.quantifier.min, - curState->u.quantifier.max); -#define PREPARE_REPEAT() \ - JS_BEGIN_MACRO \ - curState->index = x->cp - gData->cpbegin; \ - curState->continue_op = REOP_MINIMALREPEAT; \ - curState->continue_pc = pc; \ - pc += ARG_LEN; \ - for (k = curState->parenSoFar; k < parenSoFar; k++) \ - x->parens[k].index = -1; \ - PUSH_STATE_STACK(gData); \ - op = (REOp) *pc++; \ - JS_ASSERT(op < REOP_LIMIT); \ - JS_END_MACRO - - if (!result) { - re_debug(" - "); - /* - * Non-greedy failure - try to consume another child. - */ - if (curState->u.quantifier.max == (uintN) -1 || - curState->u.quantifier.max > 0) { - PREPARE_REPEAT(); - continue; - } - /* Don't need to adjust pc since we're going to pop. */ - break; - } - if (curState->u.quantifier.min == 0 && - x->cp == gData->cpbegin + curState->index) { - /* Matched an empty string, that'll get us nowhere. */ - result = NULL; - break; - } - if (curState->u.quantifier.min != 0) - curState->u.quantifier.min--; - if (curState->u.quantifier.max != (uintN) -1) - curState->u.quantifier.max--; - if (curState->u.quantifier.min != 0) { - PREPARE_REPEAT(); - continue; - } - curState->index = x->cp - gData->cpbegin; - curState->parenSoFar = parenSoFar; - PUSH_STATE_STACK(gData); - if (!PushBackTrackState(gData, REOP_MINIMALREPEAT, - pc, x, x->cp, - curState->parenSoFar, - parenSoFar - curState->parenSoFar)) { - goto bad; - } - --gData->stateStackTop; - pc = pc + GET_OFFSET(pc); - op = (REOp) *pc++; - JS_ASSERT(op < REOP_LIMIT); - continue; - default: - JS_ASSERT(JS_FALSE); - result = NULL; - } - break_switch:; + re->incref(cx); } - - /* - * If the match failed and there's a backtrack option, take it. - * Otherwise this is a complete and utter failure. - */ - if (!result) { - if (gData->cursz == 0) - return NULL; - if (!JS_CHECK_OPERATION_LIMIT(gData->cx)) { - gData->ok = JS_FALSE; - return NULL; - } - - /* Potentially detect explosive regex here. */ - gData->backTrackCount++; - if (gData->backTrackLimit && - gData->backTrackCount >= gData->backTrackLimit) { - JS_ReportErrorNumber(gData->cx, js_GetErrorMessage, NULL, - JSMSG_REGEXP_TOO_COMPLEX); - gData->ok = JS_FALSE; - return NULL; - } - - backTrackData = gData->backTrackSP; - gData->cursz = backTrackData->sz; - gData->backTrackSP = - (REBackTrackData *) ((char *)backTrackData - backTrackData->sz); - x->cp = backTrackData->cp; - pc = backTrackData->backtrack_pc; - op = (REOp) backTrackData->backtrack_op; - JS_ASSERT(op < REOP_LIMIT); - gData->stateStackTop = backTrackData->saveStateStackTop; - JS_ASSERT(gData->stateStackTop); - - memcpy(gData->stateStack, backTrackData + 1, - sizeof(REProgState) * backTrackData->saveStateStackTop); - curState = &gData->stateStack[gData->stateStackTop - 1]; - - if (backTrackData->parenCount) { - memcpy(&x->parens[backTrackData->parenIndex], - (char *)(backTrackData + 1) + - sizeof(REProgState) * backTrackData->saveStateStackTop, - sizeof(RECapture) * backTrackData->parenCount); - parenSoFar = backTrackData->parenIndex + backTrackData->parenCount; - } else { - for (k = curState->parenSoFar; k < parenSoFar; k++) - x->parens[k].index = -1; - parenSoFar = curState->parenSoFar; - } - - re_debug("\tBT_Pop: %ld,%ld", - (unsigned long) backTrackData->parenIndex, - (unsigned long) backTrackData->parenCount); - continue; - } - x = result; - - /* - * Continue with the expression. - */ - op = (REOp)*pc++; - JS_ASSERT(op < REOP_LIMIT); } - -bad: - re_debug("\n"); - return NULL; - -good: - re_debug("\n"); - return x; + clone->setPrivate(re); + clone->zeroRegExpLastIndex(); + return clone; } -static REMatchState * -MatchRegExp(REGlobalData *gData, REMatchState *x) -{ - const jschar *cpOrig = x->cp; - #ifdef JS_TRACER - NativeRegExp native; - - /* Run with native regexp if possible. */ - if (REGEX_JIT_ENABLED(gData->cx) && - !(gData->regexp->flags & JSREG_NOCOMPILE) && - (native = GetNativeRegExp(gData->cx, gData->regexp))) { - - /* - * For efficient native execution, store offset as a direct pointer into - * the buffer and convert back after execution finishes. - */ - gData->skipped = (ptrdiff_t)cpOrig; - -#ifdef JS_JIT_SPEW - debug_only_stmt({ - VOUCH_DOES_NOT_REQUIRE_STACK(); - JSStackFrame *caller = (JS_ON_TRACE(gData->cx)) - ? NULL - : js_GetScriptedCaller(gData->cx, NULL); - debug_only_printf(LC_TMRegexp, - "entering REGEXP trace at %s:%u@%u, code: %p\n", - caller ? caller->script->filename : "", - caller ? js_FramePCToLineNumber(gData->cx, caller) : 0, - caller ? FramePCOffset(gData->cx, caller) : 0, - JS_FUNC_TO_DATA_PTR(void *, native)); - }) +JS_DEFINE_CALLINFO_3(extern, OBJECT, js_CloneRegExpObject, CONTEXT, OBJECT, OBJECT, 0, + ACCSET_STORE_ANY) #endif - void *result; -#if defined(JS_NO_FASTCALL) && defined(NANOJIT_IA32) - /* - * Although a NativeRegExp takes one argument and SIMULATE_FASTCALL is - * passing two, the second goes into 'edx' and can safely be ignored. - */ - SIMULATE_FASTCALL(result, gData, gData->cpend, native); -#else - result = native(gData, gData->cpend); -#endif - debug_only_print0(LC_TMRegexp, "leaving REGEXP trace\n"); - if (!result) - return NULL; - - /* Restore REGlobalData::skipped and fill REMatchState. */ - x->cp = (const jschar *)gData->stateStack; - gData->skipped = (const jschar *)gData->skipped - cpOrig; - return x; - } -#endif - - /* - * Have to include the position beyond the last character - * in order to detect end-of-input/line condition. - */ - for (const jschar *p = cpOrig; p <= gData->cpend; p++) { - gData->skipped = p - cpOrig; - x->cp = p; - for (uintN j = 0; j < gData->regexp->parenCount; j++) - x->parens[j].index = -1; - REMatchState *result = ExecuteREBytecode(gData, x); - if (!gData->ok || result || (gData->regexp->flags & JSREG_STICKY)) - return result; - gData->backTrackSP = gData->backTrackStack; - gData->cursz = 0; - gData->stateStackTop = 0; - p = cpOrig + gData->skipped; - } - return NULL; -} - -#define MIN_BACKTRACK_LIMIT 400000 - -static REMatchState * -InitMatch(JSContext *cx, REGlobalData *gData, JSRegExp *re, size_t length) -{ - REMatchState *result; - uintN i; - - gData->backTrackStackSize = INITIAL_BACKTRACK; - JS_ARENA_ALLOCATE_CAST(gData->backTrackStack, REBackTrackData *, - &cx->regexpPool, - INITIAL_BACKTRACK); - if (!gData->backTrackStack) - goto bad; - - gData->backTrackSP = gData->backTrackStack; - gData->cursz = 0; - gData->backTrackCount = 0; - gData->backTrackLimit = 0; - if (JS_GetOptions(cx) & JSOPTION_RELIMIT) { - gData->backTrackLimit = length * length * length; /* O(n^3) */ - if (gData->backTrackLimit < MIN_BACKTRACK_LIMIT) - gData->backTrackLimit = MIN_BACKTRACK_LIMIT; - } - - gData->stateStackLimit = INITIAL_STATESTACK; - JS_ARENA_ALLOCATE_CAST(gData->stateStack, REProgState *, - &cx->regexpPool, - sizeof(REProgState) * INITIAL_STATESTACK); - if (!gData->stateStack) - goto bad; - - gData->stateStackTop = 0; - gData->cx = cx; - gData->regexp = re; - gData->ok = JS_TRUE; - - JS_ARENA_ALLOCATE_CAST(result, REMatchState *, - &cx->regexpPool, - offsetof(REMatchState, parens) - + re->parenCount * sizeof(RECapture)); - if (!result) - goto bad; - - for (i = 0; i < re->classCount; i++) { - if (!re->classList[i].converted && - !MatcherProcessCharSet(gData, &re->classList[i])) { - return NULL; - } - } - - return result; - -bad: - js_ReportOutOfScriptQuota(cx); - gData->ok = JS_FALSE; - return NULL; -} - JSBool -js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, - JSBool test, Value *rval) +js_ObjectIsRegExp(JSObject *obj) { - REGlobalData gData; - REMatchState *x, *result; + return obj->isRegExp(); +} - const jschar *cp, *ep; - size_t i, length, start; - JSBool ok; - JSRegExpStatics *res; - ptrdiff_t matchlen; - uintN num; - JSString *parstr, *matchstr; - JSObject *obj; - - RECapture *parsub = NULL; - void *mark; - int64 *timestamp; +/* + * js::RegExp + */ +bool +RegExp::execute(JSContext *cx, JSString *input, size_t *lastIndex, bool test, Value *rval) +{ +#if !ENABLE_YARR_JIT + JS_ASSERT(compiled); +#endif + const size_t pairCount = parenCount + 1; + const size_t bufCount = pairCount * 3; /* Should be x2, but PCRE has... needs. */ + const size_t matchItemCount = pairCount * 2; + AutoArenaAllocator aaa(&cx->regExpPool); + int *buf = aaa.alloc(bufCount); + if (!buf) + return false; /* - * It's safe to load from cp because JSStrings have a zero at the end, - * and we never let cp get beyond cpend. + * The JIT regexp procedure doesn't always initialize matchPair values. + * Maybe we can make this faster by ensuring it does? */ - start = *indexp; - str->getCharsAndLength(cp, length); - if (start > length) - start = length; - gData.cpbegin = cp; - gData.cpend = cp + length; - cp += start; - gData.start = start; - gData.skipped = 0; - - if (!cx->regexpPool.first.next) { - /* - * The first arena in the regexpPool must have a timestamp at its base. - */ - JS_ARENA_ALLOCATE_CAST(timestamp, int64 *, - &cx->regexpPool, sizeof *timestamp); - if (!timestamp) - return JS_FALSE; - *timestamp = JS_Now(); + for (int *it = buf; it != buf + matchItemCount; ++it) + *it = -1; + const jschar *chars = input->chars(); + size_t len = input->length(); + size_t inputOffset = 0; + if (sticky()) { + /* Sticky matches at the last index for the regexp object. */ + chars += *lastIndex; + len -= *lastIndex; + inputOffset = *lastIndex; } - mark = JS_ARENA_MARK(&cx->regexpPool); - - x = InitMatch(cx, &gData, re, length); - - if (!x) { - ok = JS_FALSE; - goto out; +#if ENABLE_YARR_JIT + bool found = JSC::Yarr::executeRegex(cx, compiled, chars, *lastIndex - inputOffset, len, buf, + bufCount) != -1; +#else + bool found; + if (jsRegExpExecute(cx, compiled, chars, len, *lastIndex - inputOffset, buf, bufCount) < 0) + return false; /* FIXME: error code reporting for PPC. */ + found = buf[0] > 0; +#endif + if (!found) { + *rval = NullValue(); + return true; } - x->cp = cp; - - /* - * Call the recursive matcher to do the real work. Return null on mismatch - * whether testing or not. On match, return an extended Array object. - */ - result = MatchRegExp(&gData, x); - ok = gData.ok; - if (!ok) - goto out; - if (!result) { - rval->setNull(); - goto out; + RegExpStatics &statics = cx->regExpStatics; + statics.input = input; + statics.matchPairs.clear(); + if (!statics.matchPairs.reserve(matchItemCount)) + return false; + for (size_t idx = 0; idx < matchItemCount; idx += 2) { + JS_ASSERT(buf[idx + 1] >= buf[idx]); + if (!statics.matchPairs.append(buf[idx] + inputOffset)) + return false; + if (!statics.matchPairs.append(buf[idx + 1] + inputOffset)) + return false; } - cp = result->cp; - i = cp - gData.cpbegin; - *indexp = i; - matchlen = i - (start + gData.skipped); - JS_ASSERT(matchlen >= 0); - ep = cp; - cp -= matchlen; - + *lastIndex = statics.matchPairs[1]; if (test) { - /* - * Testing for a match and updating cx->regExpStatics: don't allocate - * an array object, do return true. - */ - rval->setBoolean(true); - - /* Avoid warning. (gcc doesn't detect that obj is needed iff !test); */ - obj = NULL; - } else { - /* - * The array returned on match has element 0 bound to the matched - * string, elements 1 through state.parenCount bound to the paren - * matches, an index property telling the length of the left context, - * and an input property referring to the input string. - */ - obj = js_NewSlowArrayObject(cx); - if (!obj) { - ok = JS_FALSE; - goto out; - } - rval->setObject(*obj); - -#define DEFVAL(valinit, id) { \ - Value tmp = valinit; \ - ok = js_DefineProperty(cx, obj, id, &tmp, \ - PropertyStub, PropertyStub, \ - JSPROP_ENUMERATE); \ - if (!ok) \ - goto out; \ -} - - matchstr = js_NewDependentString(cx, str, cp - str->chars(), - matchlen); - if (!matchstr) { - ok = JS_FALSE; - goto out; - } - - DEFVAL(StringValue(matchstr), INT_TO_JSID(0)); + *rval = BooleanValue(true); + return true; } - res = &cx->regExpStatics; - res->input = str; - if (!res->parens.resize(re->parenCount)) { - ok = JS_FALSE; - goto out; - } - if (re->parenCount == 0) { - res->lastParen = js_EmptySubString; - } else { - for (num = 0; num < re->parenCount; num++) { - JSSubString *sub = &res->parens[num]; - parsub = &result->parens[num]; - if (parsub->index == -1) { - sub->chars = NULL; - sub->length = 0; - } else { - sub->chars = gData.cpbegin + parsub->index; - sub->length = parsub->length; - } - if (test) - continue; - if (parsub->index == -1) { - Value tmp = UndefinedValue(); - ok = js_DefineProperty(cx, obj, INT_TO_JSID(num + 1), - &tmp, NULL, NULL, JSPROP_ENUMERATE); - } else { - parstr = js_NewDependentString(cx, str, - gData.cpbegin + parsub->index - - str->chars(), - parsub->length); - if (!parstr) { - ok = JS_FALSE; - goto out; - } - Value tmp = StringValue(parstr); - ok = js_DefineProperty(cx, obj, INT_TO_JSID(num + 1), - &tmp, NULL, NULL, JSPROP_ENUMERATE); - } - if (!ok) - goto out; - } - if (parsub->index == -1) { - res->lastParen = js_EmptySubString; - } else { - res->lastParen.chars = gData.cpbegin + parsub->index; - res->lastParen.length = parsub->length; - } - } - - if (!test) { - /* - * Define the index and input properties last for better for/in loop - * order (so they come after the elements). - */ - DEFVAL(Int32Value(start + gData.skipped), - ATOM_TO_JSID(cx->runtime->atomState.indexAtom)); - DEFVAL(StringValue(str), - ATOM_TO_JSID(cx->runtime->atomState.inputAtom)); - } - -#undef DEFVAL - - res->lastMatch.chars = cp; - res->lastMatch.length = matchlen; - /* - * For JS1.3 and ECMAv2, emulate Perl5 exactly: - * - * js1.3 "hi", "hi there" "hihitherehi therebye" + * Create the return array for a match. Returned array contents: + * 0: matched string + * 1..parenCount: paren matches */ - res->leftContext.chars = str->chars(); - res->leftContext.length = start + gData.skipped; - res->rightContext.chars = ep; - res->rightContext.length = gData.cpend - ep; - -out: - JS_ARENA_RELEASE(&cx->regexpPool, mark); - return ok; + JSObject *array = js_NewSlowArrayObject(cx); + if (!array) + return false; + *rval = ObjectValue(*array); + RegExpMatchBuilder builder(cx, array); + for (size_t idx = 0; idx < matchItemCount; idx += 2) { + int start = statics.matchPairs[idx]; + int end = statics.matchPairs[idx + 1]; + JSString *captured; + if (start >= 0) { + JS_ASSERT(start <= end); + JS_ASSERT((unsigned) end <= input->length()); + captured = js_NewDependentString(cx, input, start, end - start); + if (!(captured && builder.append(idx / 2, captured))) + return false; + } else { + /* Missing parenthesized match. */ + JS_ASSERT(idx != 0); /* Since we had a match, first pair must be present. */ + JS_ASSERT(start == end && end == -1); + if (!builder.append(INT_TO_JSID(idx / 2), UndefinedValue())) + return false; + } + } + return builder.appendIndex(statics.matchPairs[0]) && builder.appendInput(input); } -/************************************************************************/ +void +RegExp::handleYarrError(JSContext *cx, int error) +{ + /* Hack: duplicated from yarr/yarr/RegexParser.h */ + enum ErrorCode { + NoError, + PatternTooLarge, + QuantifierOutOfOrder, + QuantifierWithoutAtom, + MissingParentheses, + ParenthesesUnmatched, + ParenthesesTypeInvalid, /* "(?" with bad next char or end of pattern. */ + CharacterClassUnmatched, + CharacterClassOutOfOrder, + QuantifierTooLarge, + EscapeUnterminated + }; + switch (error) { + case NoError: + JS_NOT_REACHED("Precondition violation: an error must have occurred."); + return; +#define COMPILE_EMSG(__code, __msg) \ + case __code: \ + JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, __msg); \ + return + COMPILE_EMSG(PatternTooLarge, JSMSG_REGEXP_TOO_COMPLEX); + COMPILE_EMSG(QuantifierOutOfOrder, JSMSG_BAD_QUANTIFIER); + COMPILE_EMSG(QuantifierWithoutAtom, JSMSG_BAD_QUANTIFIER); + COMPILE_EMSG(MissingParentheses, JSMSG_MISSING_PAREN); + COMPILE_EMSG(ParenthesesUnmatched, JSMSG_UNMATCHED_RIGHT_PAREN); + COMPILE_EMSG(ParenthesesTypeInvalid, JSMSG_BAD_QUANTIFIER); + COMPILE_EMSG(CharacterClassUnmatched, JSMSG_BAD_CLASS_RANGE); + COMPILE_EMSG(CharacterClassOutOfOrder, JSMSG_BAD_CLASS_RANGE); + COMPILE_EMSG(EscapeUnterminated, JSMSG_TRAILING_SLASH); + COMPILE_EMSG(QuantifierTooLarge, JSMSG_BAD_QUANTIFIER); +#undef COMPILE_EMSG + default: + JS_NOT_REACHED("Precondition violation: unknown Yarr error code."); + } +} +void +RegExp::handlePCREError(JSContext *cx, int error) +{ +#define REPORT(__msg) \ + JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, __msg); \ + return + switch (error) { + case 0: JS_NOT_REACHED("Precondition violation: an error must have occurred."); + case 1: REPORT(JSMSG_TRAILING_SLASH); + case 2: REPORT(JSMSG_TRAILING_SLASH); + case 3: REPORT(JSMSG_REGEXP_TOO_COMPLEX); + case 4: REPORT(JSMSG_BAD_QUANTIFIER); + case 5: REPORT(JSMSG_BAD_QUANTIFIER); + case 6: REPORT(JSMSG_BAD_CLASS_RANGE); + case 7: REPORT(JSMSG_REGEXP_TOO_COMPLEX); + case 8: REPORT(JSMSG_BAD_CLASS_RANGE); + case 9: REPORT(JSMSG_BAD_QUANTIFIER); + case 10: REPORT(JSMSG_UNMATCHED_RIGHT_PAREN); + case 11: REPORT(JSMSG_REGEXP_TOO_COMPLEX); + case 12: REPORT(JSMSG_UNMATCHED_RIGHT_PAREN); + case 13: REPORT(JSMSG_REGEXP_TOO_COMPLEX); + case 14: REPORT(JSMSG_MISSING_PAREN); + case 15: REPORT(JSMSG_BAD_BACKREF); + case 16: REPORT(JSMSG_REGEXP_TOO_COMPLEX); + case 17: REPORT(JSMSG_REGEXP_TOO_COMPLEX); + default: + JS_NOT_REACHED("Precondition violation: unknown PCRE error code."); + } +#undef REPORT +} + +bool +RegExp::parseFlags(JSContext *cx, JSString *flagStr, uint32 &flagsOut) +{ + const jschar *s; + size_t n; + flagStr->getCharsAndLength(s, n); + flagsOut = 0; + for (size_t i = 0; i < n; i++) { +#define HANDLE_FLAG(__name) \ + JS_BEGIN_MACRO \ + if (flagsOut & (__name)) \ + goto bad_flag; \ + flagsOut |= (__name); \ + JS_END_MACRO + switch (s[i]) { + case 'i': HANDLE_FLAG(JSREG_FOLD); break; + case 'g': HANDLE_FLAG(JSREG_GLOB); break; + case 'm': HANDLE_FLAG(JSREG_MULTILINE); break; + case 'y': HANDLE_FLAG(JSREG_STICKY); break; + default: + bad_flag: + { + char charBuf[2]; + charBuf[0] = char(s[i]); + charBuf[1] = '\0'; + JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, + JSMSG_BAD_REGEXP_FLAG, charBuf); + return false; + } + } +#undef HANDLE_FLAG + } + return true; +} + +RegExp * +RegExp::createFlagged(JSContext *cx, JSString *str, JSString *opt) +{ + if (!opt) + return create(cx, str, 0); + uint32 flags = 0; + if (!parseFlags(cx, opt, flags)) + return false; + return create(cx, str, flags); +} + +/* + * RegExp instance properties. + */ #define DEFINE_GETTER(name, code) \ static JSBool \ name(JSContext *cx, JSObject *obj, jsid id, Value *vp) \ @@ -5104,20 +393,19 @@ out: if (!obj) \ return true; \ } \ - JS_LOCK_OBJ(cx, obj); \ - JSRegExp *re = (JSRegExp *) obj->getPrivate(); \ + AutoObjectLocker(cx, obj); \ + RegExp *re = RegExp::extractFrom(obj); \ code; \ - JS_UNLOCK_OBJ(cx, obj); \ return true; \ } /* lastIndex is stored in the object, re = re silences the compiler warning. */ DEFINE_GETTER(lastIndex_getter, re = re; *vp = obj->getRegExpLastIndex()) -DEFINE_GETTER(source_getter, vp->setString(re->source)) -DEFINE_GETTER(global_getter, vp->setBoolean((re->flags & JSREG_GLOB) != 0)) -DEFINE_GETTER(ignoreCase_getter, vp->setBoolean((re->flags & JSREG_FOLD) != 0)) -DEFINE_GETTER(multiline_getter, vp->setBoolean((re->flags & JSREG_MULTILINE) != 0)) -DEFINE_GETTER(sticky_getter, vp->setBoolean((re->flags & JSREG_STICKY) != 0)) +DEFINE_GETTER(source_getter, *vp = StringValue(re->getSource())) +DEFINE_GETTER(global_getter, *vp = BooleanValue(re->global())) +DEFINE_GETTER(ignoreCase_getter, *vp = BooleanValue(re->ignoreCase())) +DEFINE_GETTER(multiline_getter, *vp = BooleanValue(re->multiline())) +DEFINE_GETTER(sticky_getter, *vp = BooleanValue(re->sticky())) static JSBool lastIndex_setter(JSContext *cx, JSObject *obj, jsid id, Value *vp) @@ -5144,26 +432,7 @@ static const struct LazyProp { }; static JSBool -regexp_enumerate(JSContext *cx, JSObject *obj) -{ - JS_ASSERT(obj->isRegExp()); - - jsval v; - if (!JS_LookupPropertyById(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.lastIndexAtom), &v)) - return false; - - for (size_t i = 0; i < JS_ARRAY_LENGTH(lazyRegExpProps); i++) { - const LazyProp &lazy = lazyRegExpProps[i]; - jsid id = ATOM_TO_JSID(OFFSET_TO_ATOM(cx->runtime, lazy.atomOffset)); - if (!JS_LookupPropertyById(cx, obj, id, &v)) - return false; - } - - return true; -} - -static JSBool -regexp_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp) +regexp_resolve(JSContext *cx, JSObject *obj, jsid id, uint32 flags, JSObject **objp) { JS_ASSERT(obj->isRegExp()); @@ -5173,11 +442,12 @@ regexp_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **ob if (id == ATOM_TO_JSID(cx->runtime->atomState.lastIndexAtom)) { if (!js_DefineNativeProperty(cx, obj, id, UndefinedValue(), lastIndex_getter, lastIndex_setter, - JSPROP_PERMANENT | JSPROP_SHARED, 0, 0, NULL)) { - return JS_FALSE; + JSPROP_PERMANENT | JSPROP_SHARED, + 0, 0, NULL)) { + return false; } *objp = obj; - return JS_TRUE; + return true; } for (size_t i = 0; i < JS_ARRAY_LENGTH(lazyRegExpProps); i++) { @@ -5188,17 +458,19 @@ regexp_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **ob lazy.getter, NULL, JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0, NULL)) { - return JS_FALSE; + return false; } *objp = obj; - return JS_TRUE; + return true; } } - return JS_TRUE; + return true; } /* + * RegExp static properties. + * * RegExp class static properties and their Perl counterparts: * * RegExp.input $_ @@ -5209,125 +481,53 @@ regexp_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **ob * RegExp.rightContext $' */ -void -js_InitRegExpStatics(JSContext *cx) -{ - /* - * To avoid multiple allocations in InitMatch(), the arena size parameter - * should be at least as big as: - * INITIAL_BACKTRACK - * + (sizeof(REProgState) * INITIAL_STATESTACK) - * + (offsetof(REMatchState, parens) + avgParanSize * sizeof(RECapture)) - */ - JS_InitArenaPool(&cx->regexpPool, "regexp", - 12 * 1024 - 40, /* FIXME: bug 421435 */ - sizeof(void *), &cx->scriptStackQuota); - - JS_ClearRegExpStatics(cx); -} - -JS_FRIEND_API(void) -js_SaveAndClearRegExpStatics(JSContext *cx, JSRegExpStatics *statics, - AutoStringRooter *tvr) -{ - statics->copy(cx->regExpStatics); - if (statics->input) - tvr->setString(statics->input); - JS_ClearRegExpStatics(cx); -} - -JS_FRIEND_API(void) -js_RestoreRegExpStatics(JSContext *cx, JSRegExpStatics *statics) -{ - /* Clear/free any new JSRegExpStatics data before clobbering. */ - cx->regExpStatics.copy(*statics); -} - -void -js_TraceRegExpStatics(JSTracer *trc, JSContext *acx) -{ - JSRegExpStatics *res = &acx->regExpStatics; - - if (res->input) - JS_CALL_STRING_TRACER(trc, res->input, "res->input"); -} - -void -js_FreeRegExpStatics(JSContext *cx) -{ - JS_ClearRegExpStatics(cx); - JS_FinishArenaPool(&cx->regexpPool); -} - -#define DEFINE_STATIC_GETTER(name, code) \ - static JSBool \ - name(JSContext *cx, JSObject *obj, jsid id, jsval *vp) \ - { \ - JSRegExpStatics *res = &cx->regExpStatics; \ - code; \ +#define DEFINE_STATIC_GETTER(name, code) \ + static JSBool \ + name(JSContext *cx, JSObject *obj, jsid id, jsval *vp) \ + { \ + RegExpStatics &statics = cx->regExpStatics; \ + code; \ } -static bool -MakeString(JSContext *cx, JSSubString *sub, jsval *vp) -{ - JSString *str = js_NewStringCopyN(cx, sub->chars, sub->length); - if (!str) - return false; - *vp = STRING_TO_JSVAL(str); - return true; -} +DEFINE_STATIC_GETTER(static_input_getter, return statics.createInput(Valueify(vp))) +DEFINE_STATIC_GETTER(static_multiline_getter, *vp = BOOLEAN_TO_JSVAL(statics.multiline()); + return true) +DEFINE_STATIC_GETTER(static_lastMatch_getter, return statics.createLastMatch(Valueify(vp))) +DEFINE_STATIC_GETTER(static_lastParen_getter, return statics.createLastParen(Valueify(vp))) +DEFINE_STATIC_GETTER(static_leftContext_getter, return statics.createLeftContext(Valueify(vp))) +DEFINE_STATIC_GETTER(static_rightContext_getter, return statics.createRightContext(Valueify(vp))) -DEFINE_STATIC_GETTER(static_input_getter, - *vp = res->input - ? STRING_TO_JSVAL(res->input) - : JS_GetEmptyStringValue(cx); - return true) -DEFINE_STATIC_GETTER(static_multiline_getter, *vp = BOOLEAN_TO_JSVAL(res->multiline); return true) -DEFINE_STATIC_GETTER(static_lastMatch_getter, return MakeString(cx, &res->lastMatch, vp)) -DEFINE_STATIC_GETTER(static_lastParen_getter, return MakeString(cx, &res->lastParen, vp)) -DEFINE_STATIC_GETTER(static_leftContext_getter, return MakeString(cx, &res->leftContext, vp)) -DEFINE_STATIC_GETTER(static_rightContext_getter, return MakeString(cx, &res->rightContext, vp)) +DEFINE_STATIC_GETTER(static_paren1_getter, return statics.createParen(0, Valueify(vp))) +DEFINE_STATIC_GETTER(static_paren2_getter, return statics.createParen(1, Valueify(vp))) +DEFINE_STATIC_GETTER(static_paren3_getter, return statics.createParen(2, Valueify(vp))) +DEFINE_STATIC_GETTER(static_paren4_getter, return statics.createParen(3, Valueify(vp))) +DEFINE_STATIC_GETTER(static_paren5_getter, return statics.createParen(4, Valueify(vp))) +DEFINE_STATIC_GETTER(static_paren6_getter, return statics.createParen(5, Valueify(vp))) +DEFINE_STATIC_GETTER(static_paren7_getter, return statics.createParen(6, Valueify(vp))) +DEFINE_STATIC_GETTER(static_paren8_getter, return statics.createParen(7, Valueify(vp))) +DEFINE_STATIC_GETTER(static_paren9_getter, return statics.createParen(8, Valueify(vp))) -static bool -Paren(JSContext *cx, JSRegExpStatics *res, size_t n, jsval *vp) -{ - return MakeString(cx, n < res->parens.length() ? &res->parens[n] : &js_EmptySubString, vp); -} - -DEFINE_STATIC_GETTER(static_paren1_getter, return Paren(cx, res, 0, vp)) -DEFINE_STATIC_GETTER(static_paren2_getter, return Paren(cx, res, 1, vp)) -DEFINE_STATIC_GETTER(static_paren3_getter, return Paren(cx, res, 2, vp)) -DEFINE_STATIC_GETTER(static_paren4_getter, return Paren(cx, res, 3, vp)) -DEFINE_STATIC_GETTER(static_paren5_getter, return Paren(cx, res, 4, vp)) -DEFINE_STATIC_GETTER(static_paren6_getter, return Paren(cx, res, 5, vp)) -DEFINE_STATIC_GETTER(static_paren7_getter, return Paren(cx, res, 6, vp)) -DEFINE_STATIC_GETTER(static_paren8_getter, return Paren(cx, res, 7, vp)) -DEFINE_STATIC_GETTER(static_paren9_getter, return Paren(cx, res, 8, vp)) - -#define DEFINE_STATIC_SETTER(name, code) \ - static JSBool \ - name(JSContext *cx, JSObject *obj, jsid id, jsval *vp) \ - { \ - JSRegExpStatics *res = &cx->regExpStatics; \ - code; \ - return true; \ +#define DEFINE_STATIC_SETTER(name, code) \ + static JSBool \ + name(JSContext *cx, JSObject *obj, jsid id, jsval *vp) \ + { \ + RegExpStatics &statics = cx->regExpStatics; \ + code; \ + return true; \ } DEFINE_STATIC_SETTER(static_input_setter, if (!JSVAL_IS_STRING(*vp) && !JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp)) return false; - res->input = JSVAL_TO_STRING(*vp)) + statics.setInput(JSVAL_TO_STRING(*vp))) DEFINE_STATIC_SETTER(static_multiline_setter, if (!JSVAL_IS_BOOLEAN(*vp) && !JS_ConvertValue(cx, *vp, JSTYPE_BOOLEAN, vp)) return false; - res->multiline = JSVAL_TO_BOOLEAN(*vp)) + statics.setMultiline(!!JSVAL_TO_BOOLEAN(*vp))) const uint8 REGEXP_STATIC_PROP_ATTRS = JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_ENUMERATE; const uint8 RO_REGEXP_STATIC_PROP_ATTRS = REGEXP_STATIC_PROP_ATTRS | JSPROP_READONLY; -#define G Jsvalify(regexp_static_getProperty) -#define S Jsvalify(regexp_static_setProperty) - static JSPropertySpec regexp_static_props[] = { {"input", 0, REGEXP_STATIC_PROP_ATTRS, static_input_getter, static_input_setter}, {"multiline", 0, REGEXP_STATIC_PROP_ATTRS, static_multiline_getter, @@ -5351,21 +551,20 @@ static JSPropertySpec regexp_static_props[] = { static void regexp_finalize(JSContext *cx, JSObject *obj) { - JSRegExp *re = (JSRegExp *) obj->getPrivate(); + RegExp *re = RegExp::extractFrom(obj); if (!re) return; - js_DestroyRegExp(cx, re); + re->decref(cx); } /* Forward static prototype. */ static JSBool -regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, Value *argv, - JSBool test, Value *rval); +regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, Value *argv, JSBool test, Value *rval); static JSBool regexp_call(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval) { - return regexp_exec_sub(cx, &argv[-2].toObject(), argc, argv, JS_FALSE, rval); + return regexp_exec_sub(cx, argv[-2].toObjectOrNull(), argc, argv, JS_FALSE, rval); } #if JS_HAS_XDR @@ -5375,36 +574,33 @@ regexp_call(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval) JSBool js_XDRRegExpObject(JSXDRState *xdr, JSObject **objp) { - JSRegExp *re; - JSString *source; - uint32 flagsword; - JSObject *obj; + JSString *source = 0; + uint32 flagsword = 0; if (xdr->mode == JSXDR_ENCODE) { - re = (JSRegExp *) (*objp)->getPrivate(); + JS_ASSERT(objp); + RegExp *re = RegExp::extractFrom(*objp); if (!re) - return JS_FALSE; - source = re->source; - flagsword = (uint32)re->flags; - } - if (!JS_XDRString(xdr, &source) || - !JS_XDRUint32(xdr, &flagsword)) { - return JS_FALSE; + return false; + source = re->getSource(); + flagsword = re->getFlags(); } + if (!JS_XDRString(xdr, &source) || !JS_XDRUint32(xdr, &flagsword)) + return false; if (xdr->mode == JSXDR_DECODE) { - obj = NewBuiltinClassInstance(xdr->cx, &js_RegExpClass); + JSObject *obj = NewBuiltinClassInstance(xdr->cx, &js_RegExpClass); if (!obj) - return JS_FALSE; + return false; obj->clearParent(); obj->clearProto(); - re = js_NewRegExp(xdr->cx, NULL, source, (uint8)flagsword, JS_FALSE); + RegExp *re = RegExp::create(xdr->cx, source, flagsword); if (!re) - return JS_FALSE; + return false; obj->setPrivate(re); obj->zeroRegExpLastIndex(); *objp = obj; } - return JS_TRUE; + return true; } #else /* !JS_HAS_XDR */ @@ -5416,12 +612,31 @@ js_XDRRegExpObject(JSXDRState *xdr, JSObject **objp) static void regexp_trace(JSTracer *trc, JSObject *obj) { - JSRegExp *re = (JSRegExp *) obj->getPrivate(); - if (re && re->source) - JS_CALL_STRING_TRACER(trc, re->source, "source"); + RegExp *re = RegExp::extractFrom(obj); + if (re && re->getSource()) + JS_CALL_STRING_TRACER(trc, re->getSource(), "source"); } -Class js_RegExpClass = { +static JSBool +regexp_enumerate(JSContext *cx, JSObject *obj) +{ + JS_ASSERT(obj->isRegExp()); + + jsval v; + if (!JS_LookupPropertyById(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.lastIndexAtom), &v)) + return false; + + for (size_t i = 0; i < JS_ARRAY_LENGTH(lazyRegExpProps); i++) { + const LazyProp &lazy = lazyRegExpProps[i]; + jsid id = ATOM_TO_JSID(OFFSET_TO_ATOM(cx->runtime, lazy.atomOffset)); + if (!JS_LookupPropertyById(cx, obj, id, &v)) + return false; + } + + return true; +} + +js::Class js_RegExpClass = { js_RegExp_str, JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(JSObject::REGEXP_FIXED_RESERVED_SLOTS) | @@ -5434,230 +649,216 @@ Class js_RegExpClass = { reinterpret_cast(regexp_resolve), ConvertStub, regexp_finalize, - NULL, /* reserved0 */ + NULL, /* reserved0 */ NULL, /* checkAccess */ regexp_call, - NULL, /* construct */ + NULL, /* construct */ js_XDRRegExpObject, NULL, /* hasInstance */ JS_CLASS_TRACE(regexp_trace) }; -static const jschar empty_regexp_ucstr[] = {'(', '?', ':', ')', 0}; +/* + * RegExp instance methods. + */ JSBool js_regexp_toString(JSContext *cx, JSObject *obj, Value *vp) { - JSRegExp *re; - const jschar *source; - jschar *chars; - size_t length, nflags; - uintN flags; - JSString *str; - + static const jschar empty_regexp_ucstr[] = {'(', '?', ':', ')', 0}; if (!InstanceOf(cx, obj, &js_RegExpClass, vp + 2)) - return JS_FALSE; + return false; JS_LOCK_OBJ(cx, obj); - re = (JSRegExp *) obj->getPrivate(); + RegExp *re = RegExp::extractFrom(obj); if (!re) { JS_UNLOCK_OBJ(cx, obj); - vp->setString(cx->runtime->emptyString); - return JS_TRUE; + *vp = StringValue(cx->runtime->emptyString); + return true; } - re->source->getCharsAndLength(source, length); + const jschar *source; + size_t length; + re->getSource()->getCharsAndLength(source, length); if (length == 0) { source = empty_regexp_ucstr; length = JS_ARRAY_LENGTH(empty_regexp_ucstr) - 1; } length += 2; - nflags = 0; - for (flags = re->flags; flags != 0; flags &= flags - 1) - nflags++; - chars = (jschar*) cx->malloc((length + nflags + 1) * sizeof(jschar)); + uint32 nflags = re->flagCount(); + jschar *chars = (jschar*) cx->malloc((length + nflags + 1) * sizeof(jschar)); if (!chars) { JS_UNLOCK_OBJ(cx, obj); - return JS_FALSE; + return false; } chars[0] = '/'; js_strncpy(&chars[1], source, length - 2); - chars[length-1] = '/'; + chars[length - 1] = '/'; if (nflags) { - if (re->flags & JSREG_GLOB) + if (re->global()) chars[length++] = 'g'; - if (re->flags & JSREG_FOLD) + if (re->ignoreCase()) chars[length++] = 'i'; - if (re->flags & JSREG_MULTILINE) + if (re->multiline()) chars[length++] = 'm'; - if (re->flags & JSREG_STICKY) + if (re->sticky()) chars[length++] = 'y'; } JS_UNLOCK_OBJ(cx, obj); chars[length] = 0; - str = js_NewString(cx, chars, length); + JSString *str = js_NewString(cx, chars, length); if (!str) { cx->free(chars); - return JS_FALSE; + return false; } - vp->setString(str); - return JS_TRUE; + *vp = StringValue(str); + return true; } static JSBool regexp_toString(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = ComputeThisFromVp(cx, vp); + JSObject *obj = JS_THIS_OBJECT(cx, Jsvalify(vp)); return obj && js_regexp_toString(cx, obj, vp); } -static JSBool -regexp_compile_sub(JSContext *cx, JSObject *obj, uintN argc, Value *argv, - Value *rval) +/* + * Return: + * - The original if no escaping need be performed. + * - A new string if escaping need be performed. + * - NULL on error. + */ +static JSString * +EscapeNakedForwardSlashes(JSContext *cx, JSString *unescaped) { - JSString *opt, *str; - JSRegExp *oldre, *re; - JSObject *obj2; - size_t length, nbytes; - const jschar *cp, *start, *end; - jschar *nstart, *ncp, *tmp; - - if (!InstanceOf(cx, obj, &js_RegExpClass, argv)) - return JS_FALSE; - opt = NULL; - if (argc == 0) { - str = cx->runtime->emptyString; - } else { - if (argv[0].isObjectOrNull()) { - /* - * If we get passed in a RegExp object we construct a new - * RegExp that is a duplicate of it by re-compiling the - * original source code. ECMA requires that it be an error - * here if the flags are specified. (We must use the flags - * from the original RegExp also). - */ - obj2 = argv[0].toObjectOrNull(); - if (obj2 && obj2->getClass() == &js_RegExpClass) { - if (argc >= 2 && !argv[1].isUndefined()) { /* 'flags' passed */ - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_NEWREGEXP_FLAGGED); - return JS_FALSE; - } - JS_LOCK_OBJ(cx, obj2); - re = (JSRegExp *) obj2->getPrivate(); - if (!re) { - JS_UNLOCK_OBJ(cx, obj2); - return JS_FALSE; - } - re = js_NewRegExp(cx, NULL, re->source, re->flags, JS_FALSE); - JS_UNLOCK_OBJ(cx, obj2); - goto created; - } - } - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - argv[0].setString(str); - if (argc > 1) { - if (argv[1].isUndefined()) { - opt = NULL; - } else { - opt = js_ValueToString(cx, argv[1]); - if (!opt) - return JS_FALSE; - argv[1].setString(opt); + const jschar *oldChars; + size_t oldLen; + unescaped->getCharsAndLength(oldChars, oldLen); + js::Vector newChars(cx); + for (const jschar *it = oldChars; it < oldChars + oldLen; ++it) { + if (*it == '/' && (it == oldChars || it[-1] != '\\')) { + if (!newChars.length()) { + if (!newChars.reserve(oldLen + 1)) + return NULL; + newChars.append(oldChars, size_t(it - oldChars)); } + newChars.append('\\'); } - /* Escape any naked slashes in the regexp source. */ - str->getCharsAndLength(start, length); - end = start + length; - nstart = ncp = NULL; - for (cp = start; cp < end; cp++) { - if (*cp == '/' && (cp == start || cp[-1] != '\\')) { - nbytes = (++length + 1) * sizeof(jschar); - if (!nstart) { - nstart = (jschar *) cx->malloc(nbytes); - if (!nstart) - return JS_FALSE; - ncp = nstart + (cp - start); - js_strncpy(nstart, start, cp - start); - } else { - tmp = (jschar *) cx->realloc(nstart, nbytes); - if (!tmp) { - cx->free(nstart); - return JS_FALSE; - } - ncp = tmp + (ncp - nstart); - nstart = tmp; - } - *ncp++ = '\\'; - } - if (nstart) - *ncp++ = *cp; - } - - if (nstart) { - /* Don't forget to store the backstop after the new string. */ - JS_ASSERT((size_t)(ncp - nstart) == length); - *ncp = 0; - str = js_NewString(cx, nstart, length); - if (!str) { - cx->free(nstart); - return JS_FALSE; - } - argv[0].setString(str); - } + if (newChars.length()) + newChars.append(*it); } - re = js_NewRegExpOpt(cx, str, opt, JS_FALSE); -created: + if (newChars.length()) { + size_t len = newChars.length(); + jschar *chars = newChars.extractRawBuffer(); + if (!chars) + return NULL; + JSString *escaped = js_NewString(cx, chars, len); + if (!escaped) + cx->free(chars); + return escaped; + } + return unescaped; +} + +static inline JSBool +regexp_compile_sub_tail(JSContext *cx, JSObject *obj, Value *rval, JSString *str, uint32 flags = 0) +{ + flags |= cx->regExpStatics.getFlags(); + RegExp *re = RegExp::create(cx, str, flags); if (!re) - return JS_FALSE; - JS_LOCK_OBJ(cx, obj); - oldre = (JSRegExp *) obj->getPrivate(); - obj->setPrivate(re); - obj->zeroRegExpLastIndex(); - JS_UNLOCK_OBJ(cx, obj); - if (oldre) - js_DestroyRegExp(cx, oldre); - rval->setObject(*obj); - return JS_TRUE; + return false; + SwapObjectRegExp(cx, obj, *re); + *rval = ObjectValue(*obj); + return true; +} + +static JSBool +regexp_compile_sub(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval) +{ + if (!InstanceOf(cx, obj, &js_RegExpClass, argv)) + return false; + if (argc == 0) + return regexp_compile_sub_tail(cx, obj, rval, cx->runtime->emptyString); + + Value sourceValue = argv[0]; + if (sourceValue.isObject() && sourceValue.toObject().getClass() == &js_RegExpClass) { + /* + * If we get passed in a RegExp object we construct a new + * RegExp that is a duplicate of it by re-compiling the + * original source code. ECMA requires that it be an error + * here if the flags are specified. (We must use the flags + * from the original RegExp also). + */ + JSObject &sourceObj = sourceValue.toObject(); + if (argc >= 2 && !argv[1].isUndefined()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEWREGEXP_FLAGGED); + return false; + } + RegExp *clone; + { + AutoObjectLocker lock(cx, &sourceObj); + RegExp *re = RegExp::extractFrom(&sourceObj); + if (!re) + return false; + clone = RegExp::clone(cx, *re); + } + if (!clone) + return false; + SwapObjectRegExp(cx, obj, *clone); + *rval = ObjectValue(*obj); + return true; + } + + /* Coerce to string and compile. */ + JSString *sourceStr = js_ValueToString(cx, sourceValue); + if (!sourceStr) + return false; + argv[0] = StringValue(sourceStr); + uint32 flags = 0; + if (argc > 1 && !argv[1].isUndefined()) { + JSString *flagStr = js_ValueToString(cx, argv[1]); + if (!flagStr) + return false; + argv[1] = StringValue(flagStr); + if (!RegExp::parseFlags(cx, flagStr, flags)) + return false; + } + + JSString *escapedSourceStr = EscapeNakedForwardSlashes(cx, sourceStr); + if (!escapedSourceStr) + return false; + argv[0] = StringValue(escapedSourceStr); + return regexp_compile_sub_tail(cx, obj, rval, escapedSourceStr, flags); } static JSBool regexp_compile(JSContext *cx, uintN argc, Value *vp) { - JSObject *obj = ComputeThisFromVp(cx, vp); + JSObject *obj = JS_THIS_OBJECT(cx, Jsvalify(vp)); return obj && regexp_compile_sub(cx, obj, argc, vp + 2, vp); } static JSBool -regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, Value *argv, - JSBool test, Value *rval) +regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, Value *argv, JSBool test, Value *rval) { - JSBool ok, sticky; - JSRegExp *re; - jsdouble lastIndex; - JSString *str; - size_t i; - - ok = InstanceOf(cx, obj, &js_RegExpClass, argv); + bool ok = InstanceOf(cx, obj, &js_RegExpClass, argv); if (!ok) return JS_FALSE; JS_LOCK_OBJ(cx, obj); - re = (JSRegExp *) obj->getPrivate(); + RegExp *re = RegExp::extractFrom(obj); if (!re) { JS_UNLOCK_OBJ(cx, obj); return JS_TRUE; } /* NB: we must reach out: after this paragraph, in order to drop re. */ - HOLD_REGEXP(cx, re); - sticky = (re->flags & JSREG_STICKY) != 0; - if (re->flags & (JSREG_GLOB | JSREG_STICKY)) { - const Value &v = obj->getRegExpLastIndex(); + re->incref(cx); + jsdouble lastIndex; + if (re->global() || re->sticky()) { + const Value v = obj->getRegExpLastIndex(); if (v.isInt32()) { lastIndex = v.toInt32(); } else { @@ -5673,68 +874,64 @@ regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, Value *argv, JS_UNLOCK_OBJ(cx, obj); /* Now that obj is unlocked, it's safe to (potentially) grab the GC lock. */ - if (argc == 0) { - str = cx->regExpStatics.input; - if (!str) { - const char *bytes = js_GetStringBytes(cx, re->source); - - if (bytes) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_NO_INPUT, - bytes, - (re->flags & JSREG_GLOB) ? "g" : "", - (re->flags & JSREG_FOLD) ? "i" : "", - (re->flags & JSREG_MULTILINE) ? "m" : "", - (re->flags & JSREG_STICKY) ? "y" : ""); - } - ok = JS_FALSE; - goto out; - } - } else { + JSString *str; + if (argc) { str = js_ValueToString(cx, argv[0]); if (!str) { ok = JS_FALSE; goto out; } - argv[0].setString(str); + argv[0] = StringValue(str); + } else { + /* Need to grab input from statics. */ + str = cx->regExpStatics.getInput(); + if (!str) { + const char *sourceBytes = js_GetStringBytes(cx, re->getSource()); + if (sourceBytes) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_INPUT, sourceBytes, + re->global() ? "g" : "", + re->ignoreCase() ? "i" : "", + re->multiline() ? "m" : "", + re->sticky() ? "y" : ""); + } + ok = false; + goto out; + } } if (lastIndex < 0 || str->length() < lastIndex) { obj->zeroRegExpLastIndex(); - rval->setNull(); + *rval = NullValue(); } else { - i = (size_t) lastIndex; - ok = js_ExecuteRegExp(cx, re, str, &i, test, rval); - if (ok && - ((re->flags & JSREG_GLOB) || (!rval->isNull() && sticky))) { + size_t lastIndexInt = (size_t) lastIndex; + ok = re->execute(cx, str, &lastIndexInt, !!test, rval); + if (ok && (re->global() || (!rval->isNull() && re->sticky()))) { if (rval->isNull()) obj->zeroRegExpLastIndex(); else - obj->setRegExpLastIndex(i); + obj->setRegExpLastIndex(lastIndexInt); } } -out: - DROP_REGEXP(cx, re); + out: + re->decref(cx); return ok; } static JSBool regexp_exec(JSContext *cx, uintN argc, Value *vp) { - return regexp_exec_sub(cx, ComputeThisFromVp(cx, vp), - argc, vp + 2, JS_FALSE, vp); + return regexp_exec_sub(cx, JS_THIS_OBJECT(cx, Jsvalify(vp)), argc, vp + 2, JS_FALSE, vp); } static JSBool regexp_test(JSContext *cx, uintN argc, Value *vp) { - if (!regexp_exec_sub(cx, ComputeThisFromVp(cx, vp), - argc, vp + 2, JS_TRUE, vp)) - return JS_FALSE; + if (!regexp_exec_sub(cx, JS_THIS_OBJECT(cx, Jsvalify(vp)), argc, vp + 2, JS_TRUE, vp)) + return false; if (!vp->isTrue()) vp->setBoolean(false); - return JS_TRUE; + return true; } static JSFunctionSpec regexp_methods[] = { @@ -5749,7 +946,7 @@ static JSFunctionSpec regexp_methods[] = { }; static JSBool -RegExp(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval) +regexp_construct(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval) { if (!JS_IsConstructing(cx)) { /* @@ -5757,22 +954,22 @@ RegExp(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval) * (regexp_compile_sub detects the regexp + flags case and throws a * TypeError.) See 10.15.3.1. */ - if ((argc < 2 || argv[1].isUndefined()) && argv[0].isObject() && + if ((argc < 2 || argv[1].isUndefined()) && !argv[0].isPrimitive() && argv[0].toObject().getClass() == &js_RegExpClass) { *rval = argv[0]; - return JS_TRUE; + return true; } /* Otherwise, replace obj with a new RegExp object. */ obj = NewBuiltinClassInstance(cx, &js_RegExpClass); if (!obj) - return JS_FALSE; + return false; /* * regexp_compile_sub does not use rval to root its temporaries so we * can use it to root obj. */ - rval->setObject(*obj); + *rval = ObjectValue(*obj); } return regexp_compile_sub(cx, obj, argc, argv, rval); } @@ -5780,7 +977,7 @@ RegExp(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval) JSObject * js_InitRegExpClass(JSContext *cx, JSObject *obj) { - JSObject *proto = js_InitClass(cx, obj, NULL, &js_RegExpClass, RegExp, 1, + JSObject *proto = js_InitClass(cx, obj, NULL, &js_RegExpClass, regexp_construct, 1, NULL, regexp_methods, regexp_static_props, NULL); if (!proto) return NULL; @@ -5803,72 +1000,3 @@ js_InitRegExpClass(JSContext *cx, JSObject *obj) return proto; } - -JSObject * -js_NewRegExpObject(JSContext *cx, TokenStream *ts, - const jschar *chars, size_t length, uintN flags) -{ - JSString *str; - JSObject *obj; - JSRegExp *re; - - str = js_NewStringCopyN(cx, chars, length); - if (!str) - return NULL; - AutoStringRooter tvr(cx, str); - re = js_NewRegExp(cx, ts, str, flags, JS_FALSE); - if (!re) - return NULL; - obj = NewBuiltinClassInstance(cx, &js_RegExpClass); - if (!obj) { - js_DestroyRegExp(cx, re); - return NULL; - } - obj->setPrivate(re); - obj->zeroRegExpLastIndex(); - return obj; -} - -JSObject * JS_FASTCALL -js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *proto) -{ - JS_ASSERT(obj->getClass() == &js_RegExpClass); - JS_ASSERT(proto); - JS_ASSERT(proto->getClass() == &js_RegExpClass); - JSObject *clone = NewNativeClassInstance(cx, &js_RegExpClass, proto, proto->getParent()); - if (!clone) - return NULL; - JSRegExp *re = static_cast(obj->getPrivate()); - clone->setPrivate(re); - clone->zeroRegExpLastIndex(); - HOLD_REGEXP(cx, re); - return clone; -} - -#ifdef JS_TRACER -JS_DEFINE_CALLINFO_3(extern, OBJECT, js_CloneRegExpObject, CONTEXT, OBJECT, OBJECT, 0, - ACCSET_STORE_ANY) -#endif - -bool -js_ContainsRegExpMetaChars(const jschar *chars, size_t length) -{ - for (size_t i = 0; i < length; ++i) { - jschar c = chars[i]; - switch (c) { - /* Taken from the PatternCharacter production in 15.10.1. */ - case '^': case '$': case '\\': case '.': case '*': case '+': - case '?': case '(': case ')': case '[': case ']': case '{': - case '}': case '|': - return true; - default:; - } - } - return false; -} - -JSBool -js_ObjectIsRegExp(JSObject *obj) -{ - return obj->isRegExp(); -} diff --git a/js/src/jsregexp.h b/js/src/jsregexp.h index 061179f2ab1e..d53580c7e04a 100644 --- a/js/src/jsregexp.h +++ b/js/src/jsregexp.h @@ -43,13 +43,21 @@ * JS regular expression interface. */ #include -#include "jspubtd.h" +#include "jsprvtd.h" #include "jsstr.h" #ifdef JS_THREADSAFE #include "jsdhash.h" #endif +extern js::Class js_RegExpClass; + +static inline bool +VALUE_IS_REGEXP(JSContext *cx, js::Value v) +{ + return !v.isPrimitive() && v.toObject().isRegExp(); +} + inline const js::Value & JSObject::getRegExpLastIndex() const { @@ -80,82 +88,6 @@ JSObject::zeroRegExpLastIndex() namespace js { class AutoStringRooter; } -extern JS_FRIEND_API(void) -js_SaveAndClearRegExpStatics(JSContext *cx, JSRegExpStatics *statics, - js::AutoStringRooter *tvr); - -extern JS_FRIEND_API(void) -js_RestoreRegExpStatics(JSContext *cx, JSRegExpStatics *statics); - -/* - * This struct holds a bitmap representation of a class from a regexp. - * There's a list of these referenced by the classList field in the JSRegExp - * struct below. The initial state has startIndex set to the offset in the - * original regexp source of the beginning of the class contents. The first - * use of the class converts the source representation into a bitmap. - * - */ -typedef struct RECharSet { - JSPackedBool converted; - JSPackedBool sense; - uint16 length; - union { - uint8 *bits; - struct { - size_t startIndex; - size_t length; - } src; - } u; -} RECharSet; - -typedef struct RENode RENode; - -struct JSRegExp { - jsrefcount nrefs; /* reference count */ - uint16 flags; /* flags, see jsapi.h's JSREG_* defines */ - size_t parenCount; /* number of parenthesized submatches */ - size_t classCount; /* count [...] bitmaps */ - RECharSet *classList; /* list of [...] bitmaps */ - JSString *source; /* locked source string, sans // */ - jsbytecode program[1]; /* regular expression bytecode */ -}; - -extern JSRegExp * -js_NewRegExp(JSContext *cx, js::TokenStream *ts, - JSString *str, uintN flags, JSBool flat); - -extern JSRegExp * -js_NewRegExpOpt(JSContext *cx, JSString *str, JSString *opt, JSBool flat); - -#define HOLD_REGEXP(cx, re) JS_ATOMIC_INCREMENT(&(re)->nrefs) -#define DROP_REGEXP(cx, re) js_DestroyRegExp(cx, re) - -extern void -js_DestroyRegExp(JSContext *cx, JSRegExp *re); - -/* - * Execute re on input str at *indexp, returning null in *rval on mismatch. - * On match, return true if test is true, otherwise return an array object. - * Update *indexp and cx->regExpStatics always on match. - */ -extern JSBool -js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, - JSBool test, js::Value *rval); - -extern void -js_InitRegExpStatics(JSContext *cx); - -extern void -js_TraceRegExpStatics(JSTracer *trc, JSContext *acx); - -extern void -js_FreeRegExpStatics(JSContext *cx); - -#define VALUE_IS_REGEXP(cx, v) \ - ((v).isObject() && v.toObject().isRegExp()) - -extern js::Class js_RegExpClass; - inline bool JSObject::isRegExp() const { @@ -174,21 +106,21 @@ js_InitRegExpClass(JSContext *cx, JSObject *obj); extern JSBool js_regexp_toString(JSContext *cx, JSObject *obj, js::Value *vp); +extern JS_FRIEND_API(JSObject *) JS_FASTCALL +js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *proto); + /* - * Create, serialize/deserialize, or clone a RegExp object. + * Move data from |cx|'s regexp statics to |statics| and root the input string in |tvr| if it's + * available. */ -extern JSObject * -js_NewRegExpObject(JSContext *cx, js::TokenStream *ts, - const jschar *chars, size_t length, uintN flags); +extern JS_FRIEND_API(void) +js_SaveAndClearRegExpStatics(JSContext *cx, js::RegExpStatics *res, js::AutoStringRooter *tvr); + +/* Move the data from |statics| into |cx|. */ +extern JS_FRIEND_API(void) +js_RestoreRegExpStatics(JSContext *cx, js::RegExpStatics *res); extern JSBool js_XDRRegExpObject(JSXDRState *xdr, JSObject **objp); -extern JS_FRIEND_API(JSObject *) JS_FASTCALL -js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *proto); - -/* Return whether the given character array contains RegExp meta-characters. */ -extern bool -js_ContainsRegExpMetaChars(const jschar *chars, size_t length); - #endif /* jsregexp_h___ */ diff --git a/js/src/jsregexpinlines.h b/js/src/jsregexpinlines.h new file mode 100644 index 000000000000..fc9c8642523d --- /dev/null +++ b/js/src/jsregexpinlines.h @@ -0,0 +1,404 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99 ft=cpp: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released + * June 12, 2009. + * + * The Initial Developer of the Original Code is + * the Mozilla Corporation. + * + * Contributor(s): + * Chris Leary + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsregexpinlines_h___ +#define jsregexpinlines_h___ + +#include "jsregexp.h" +#include "jscntxt.h" +#include "jsobjinlines.h" +#include "assembler/wtf/Platform.h" + +#if ENABLE_YARR_JIT +#include "yarr/yarr/RegexJIT.h" +#else +#include "yarr/pcre/pcre.h" +#endif + +namespace js { + +/* Defined in the inlines header to avoid Yarr dependency includes in main header. */ +class RegExp +{ + jsrefcount refCount; + JSString *source; +#if ENABLE_YARR_JIT + JSC::Yarr::RegexCodeBlock compiled; +#else + JSRegExp *compiled; +#endif + unsigned parenCount; + uint32 flags; + + RegExp(JSString *source, uint32 flags) + : refCount(1), source(source), compiled(), parenCount(), flags(flags) {} + bool compileHelper(JSContext *cx, UString &pattern); + bool compile(JSContext *cx); + static const uint32 allFlags = JSREG_FOLD | JSREG_GLOB | JSREG_MULTILINE | JSREG_STICKY; + void handlePCREError(JSContext *cx, int error); + void handleYarrError(JSContext *cx, int error); + + public: + ~RegExp() { +#if !ENABLE_YARR_JIT + if (compiled) + jsRegExpFree(compiled); +#endif + } + + static bool hasMetaChars(const jschar *chars, size_t length); + + /* + * Parse regexp flags. Report an error and return false if an invalid + * sequence of flags is encountered (repeat/invalid flag). + */ + static bool parseFlags(JSContext *cx, JSString *flagStr, uint32 &flagsOut); + + /* + * Execute regexp on |input| at |*lastIndex|. + * + * On match: Update |*lastIndex| and RegExp class statics. + * Return true if test is true. Place an array in |*rval| if test is false. + * On mismatch: Make |*rval| null. + */ + bool execute(JSContext *cx, JSString *input, size_t *lastIndex, bool test, Value *rval); + + /* Factories. */ + static RegExp *create(JSContext *cx, JSString *source, uint32 flags); + static RegExp *createFlagged(JSContext *cx, JSString *source, JSString *flags); + /* + * Create an object with new regular expression internals. + * @note The context's regexp statics flags are OR'd into the provided flags, + * so this function is really meant for object creation during code + * execution, as opposed to during something like XDR. + */ + static JSObject *createObject(JSContext *cx, const jschar *chars, size_t length, uint32 flags); + static RegExp *extractFrom(JSObject *obj); + static RegExp *clone(JSContext *cx, const RegExp &other); + + /* Mutators. */ + void incref(JSContext *cx) { JS_ATOMIC_INCREMENT(&refCount); } + void decref(JSContext *cx); + + /* Accessors. */ + JSString *getSource() const { return source; } + size_t getParenCount() const { return parenCount; } + bool ignoreCase() const { return flags & JSREG_FOLD; } + bool global() const { return flags & JSREG_GLOB; } + bool multiline() const { return flags & JSREG_MULTILINE; } + bool sticky() const { return flags & JSREG_STICKY; } + const uint32 &getFlags() const { JS_ASSERT((flags & allFlags) == flags); return flags; } + uint32 flagCount() const; +}; + +/* RegExp inlines. */ + +inline RegExp * +RegExp::create(JSContext *cx, JSString *source, uint32 flags) +{ + RegExp *self; + void *mem = cx->malloc(sizeof(*self)); + if (!mem) + return NULL; + self = new (mem) RegExp(source, flags); + if (!self->compile(cx)) { + cx->destroy(self); + return NULL; + } + return self; +} + +inline JSObject * +RegExp::createObject(JSContext *cx, const jschar *chars, size_t length, uint32 flags) +{ + JS_ASSERT((flags & allFlags) == flags); + JSString *str = js_NewStringCopyN(cx, chars, length); + if (!str) + return NULL; + AutoValueRooter tvr(cx, StringValue(str)); + uint32 staticsFlags = cx->regExpStatics.getFlags(); + JS_ASSERT((staticsFlags & allFlags) == staticsFlags); + RegExp *re = RegExp::create(cx, str, flags | staticsFlags); + if (!re) + return NULL; + JSObject *obj = NewBuiltinClassInstance(cx, &js_RegExpClass); + if (!obj) { + re->decref(cx); + return NULL; + } + obj->setPrivate(re); + obj->zeroRegExpLastIndex(); + return obj; +} + +inline bool +RegExp::compileHelper(JSContext *cx, UString &pattern) +{ +#if ENABLE_YARR_JIT + bool fellBack = false; + int error = 0; + jitCompileRegex(*cx->runtime->regExpAllocator, compiled, pattern, parenCount, error, fellBack, ignoreCase(), multiline()); + if (!error) + return true; + if (fellBack) + handlePCREError(cx, error); + else + handleYarrError(cx, error); + return false; +#else + int error = 0; + compiled = jsRegExpCompile(pattern.chars(), pattern.length(), + ignoreCase() ? JSRegExpIgnoreCase : JSRegExpDoNotIgnoreCase, + multiline() ? JSRegExpMultiline : JSRegExpSingleLine, + NULL, &error); + if (!error) + return true; + handlePCREError(cx, error); + return false; +#endif +} + +inline bool +RegExp::compile(JSContext *cx) +{ + if (!sticky()) + return compileHelper(cx, *source); + /* + * The sticky case we implement hackily by prepending a caret onto the front + * and relying on |::execute| to pseudo-slice the string when it sees a sticky regexp. + */ + JSString *fakeySource = js_ConcatStringsZ(cx, "^", source); + AutoValueRooter rooter(cx, StringValue(fakeySource)); + return compileHelper(cx, *fakeySource); +} + +inline bool +RegExp::hasMetaChars(const jschar *chars, size_t length) +{ + for (size_t i = 0; i < length; ++i) { + jschar c = chars[i]; + switch (c) { + /* Taken from the PatternCharacter production in 15.10.1. */ + case '^': case '$': case '\\': case '.': case '*': case '+': + case '?': case '(': case ')': case '[': case ']': case '{': + case '}': case '|': + return true; + default:; + } + } + return false; +} + +inline uint32 +RegExp::flagCount() const +{ + uint32 nflags = 0; + for (uint32 tmpFlags = flags; tmpFlags != 0; tmpFlags &= tmpFlags - 1) + nflags++; + return nflags; +} + +inline void +RegExp::decref(JSContext *cx) +{ + if (JS_ATOMIC_DECREMENT(&refCount) == 0) + cx->destroy(this); +} + +inline RegExp * +RegExp::extractFrom(JSObject *obj) +{ + JS_ASSERT_IF(obj, obj->isRegExp()); + return static_cast(obj->getPrivate()); +} + +inline RegExp * +RegExp::clone(JSContext *cx, const RegExp &other) +{ + return create(cx, other.source, other.flags); +} + +/* RegExpStatics inlines. */ + +inline void +RegExpStatics::clone(const RegExpStatics &other) +{ + JS_ASSERT(this != &other); + clear(); + input = other.input; + flags = other.flags; + JS_ASSERT((flags & allFlags) == flags); + matchPairs.append(other.matchPairs); +} + +inline bool +RegExpStatics::createDependent(size_t start, size_t end, Value *out) const +{ + JS_ASSERT(start <= end); + JS_ASSERT(end <= input->length()); + JSString *str = js_NewDependentString(cx, input, start, end - start); + if (!str) + return false; + *out = StringValue(str); + return true; +} + +inline bool +RegExpStatics::createInput(Value *out) const +{ + *out = input ? StringValue(input) : Valueify(JS_GetEmptyStringValue(cx)); + return true; +} + +inline bool +RegExpStatics::makeMatch(size_t checkValidIndex, size_t pairNum, Value *out) const +{ + if (checkValidIndex / 2 >= pairCount() || matchPairs[checkValidIndex] < 0) { + *out = Valueify(JS_GetEmptyStringValue(cx)); + return true; + } + return createDependent(get(pairNum, 0), get(pairNum, 1), out); +} + +inline bool +RegExpStatics::createLastParen(Value *out) const +{ + if (pairCount() <= 1) { + *out = Valueify(JS_GetEmptyStringValue(cx)); + return true; + } + size_t num = pairCount() - 1; + int start = get(num, 0); + int end = get(num, 1); + if (start == -1) { + JS_ASSERT(end == -1); + *out = Valueify(JS_GetEmptyStringValue(cx)); + return true; + } + JS_ASSERT(start >= 0 && end >= 0); + return createDependent(start, end, out); +} + +inline bool +RegExpStatics::createLeftContext(Value *out) const +{ + if (!pairCount()) { + *out = Valueify(JS_GetEmptyStringValue(cx)); + return true; + } + if (matchPairs[0] < 0) { + *out = UndefinedValue(); + return true; + } + return createDependent(0, matchPairs[0], out); +} + +inline bool +RegExpStatics::createRightContext(Value *out) const +{ + if (!pairCount()) { + *out = Valueify(JS_GetEmptyStringValue(cx)); + return true; + } + if (matchPairs[1] < 0) { + *out = UndefinedValue(); + return true; + } + return createDependent(matchPairs[1], input->length(), out); +} + +inline void +RegExpStatics::getParen(size_t num, JSSubString *out) const +{ + out->chars = input->chars() + get(num + 1, 0); + out->length = getParenLength(num); +} + +inline void +RegExpStatics::getLastMatch(JSSubString *out) const +{ + if (!pairCount()) { + *out = js_EmptySubString; + return; + } + JS_ASSERT(input); + out->chars = input->chars() + get(0, 0); + JS_ASSERT(get(0, 1) >= get(0, 0)); + out->length = get(0, 1) - get(0, 0); +} + +inline void +RegExpStatics::getLastParen(JSSubString *out) const +{ + if (!pairCount()) { + *out = js_EmptySubString; + return; + } + size_t num = pairCount() - 1; + out->chars = input->chars() + get(num, 0); + JS_ASSERT(get(num, 1) >= get(num, 0)); + out->length = get(num, 1) - get(num, 0); +} + +inline void +RegExpStatics::getLeftContext(JSSubString *out) const +{ + if (!pairCount()) { + *out = js_EmptySubString; + return; + } + out->chars = input->chars(); + out->length = get(0, 0); +} + +inline void +RegExpStatics::getRightContext(JSSubString *out) const +{ + if (!pairCount()) { + *out = js_EmptySubString; + return; + } + out->chars = input->chars() + get(0, 1); + JS_ASSERT(get(0, 1) <= int(input->length())); + out->length = input->length() - get(0, 1); +} + +} + +#endif /* jsregexpinlines_h___ */ diff --git a/js/src/jsscope.h b/js/src/jsscope.h index e81b0eb86734..926dc5c02e76 100644 --- a/js/src/jsscope.h +++ b/js/src/jsscope.h @@ -981,6 +981,22 @@ JSScopeProperty::isSharedPermanent() const extern JSScope * js_GetMutableScope(JSContext *cx, JSObject *obj); +namespace js { + +class AutoObjectLocker { + JSContext * const cx; + JSObject * const obj; + public: + AutoObjectLocker(JSContext *cx, JSObject *obj) + : cx(cx), obj(obj) { + JS_LOCK_OBJ(cx, obj); + } + + ~AutoObjectLocker() { JS_UNLOCK_OBJ(cx, obj); } +}; + +} + #ifdef _MSC_VER #pragma warning(pop) #pragma warning(pop) diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index ab74cf82e4ee..e6c2497232d2 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -79,6 +79,7 @@ #include "jscntxtinlines.h" #include "jsobjinlines.h" #include "jsstrinlines.h" +#include "jsregexpinlines.h" #include "jscntxtinlines.h" using namespace js; @@ -403,6 +404,20 @@ js_ConcatStrings(JSContext *cx, JSString *left, JSString *right) } } +JSString * JS_FASTCALL +js_ConcatStringsZ(JSContext *cx, const char *left, JSString *right) +{ + const size_t leftLength = strlen(left); + const size_t newLength = leftLength + right->length(); + const size_t newSize = (newLength + 1) * sizeof(jschar); + jschar *chars = static_cast(cx->malloc(newSize)); + for (size_t i = 0; i < leftLength; ++i) + chars[i] = left[i]; + js_strncpy(chars + leftLength, right->chars(), right->length()); + JSString *str = js_NewString(cx, chars, newLength); + return str; +} + const jschar * JSString::undepend(JSContext *cx) { @@ -1632,16 +1647,16 @@ class RegExpGuard RegExpGuard(const RegExpGuard &); void operator=(const RegExpGuard &); - JSContext *mCx; - JSObject *mReobj; - JSRegExp *mRe; + JSContext *mCx; + JSObject *mReobj; + js::RegExp *mRe; public: RegExpGuard(JSContext *cx) : mCx(cx), mRe(NULL) {} ~RegExpGuard() { if (mRe) - DROP_REGEXP(mCx, mRe); + mRe->decref(mCx); } JSContext* cx() const { return mCx; } @@ -1652,8 +1667,8 @@ class RegExpGuard { if (argc != 0 && VALUE_IS_REGEXP(mCx, vp[2])) { mReobj = &vp[2].toObject(); - mRe = (JSRegExp *) mReobj->getPrivate(); - HOLD_REGEXP(mCx, mRe); + mRe = static_cast(mReobj->getPrivate()); + mRe->incref(mCx); } else { patstr = ArgToRootedString(mCx, argc, vp, 0); if (!patstr) @@ -1680,7 +1695,7 @@ class RegExpGuard patstr->getCharsAndLength(pat, patlen); if (optarg < argc || (!flat && - (patlen > sMaxFlatPatLen || js_ContainsRegExpMetaChars(pat, patlen)))) { + (patlen > sMaxFlatPatLen || RegExp::hasMetaChars(pat, patlen)))) { return false; } /* @@ -1719,7 +1734,7 @@ class RegExpGuard } else { opt = NULL; } - mRe = js_NewRegExpOpt(mCx, patstr, opt, flat); + mRe = RegExp::createFlagged(mCx, patstr, opt); if (!mRe) return false; mReobj = NULL; @@ -1728,7 +1743,7 @@ class RegExpGuard /* Data available on successful return from |normalizeRegExp|. */ JSObject *reobj() const { return mReobj; } /* nullable */ - JSRegExp *re() const { return mRe; } /* non-null */ + js::RegExp *re() const { return mRe; } /* non-null */ }; /* js_ExecuteRegExp indicates success in two ways, based on the 'test' flag. */ @@ -1759,19 +1774,20 @@ static bool DoMatch(JSContext *cx, Value *vp, JSString *str, const RegExpGuard &g, DoMatchCallback callback, void *data, MatchControlFlags flags) { - if (g.re()->flags & JSREG_GLOB) { + RegExp &re = *g.re(); + if (re.global()) { /* global matching ('g') */ bool testGlobal = flags & TEST_GLOBAL_BIT; if (g.reobj()) g.reobj()->zeroRegExpLastIndex(); for (size_t count = 0, i = 0, length = str->length(); i <= length; ++count) { - if (!js_ExecuteRegExp(cx, g.re(), str, &i, testGlobal, vp)) + if (!re.execute(cx, str, &i, testGlobal, vp)) return false; if (!Matched(testGlobal, *vp)) break; if (!callback(cx, count, data)) return false; - if (cx->regExpStatics.lastMatch.length == 0) + if (!cx->regExpStatics.matched()) ++i; } } else { @@ -1779,48 +1795,14 @@ DoMatch(JSContext *cx, Value *vp, JSString *str, const RegExpGuard &g, bool testSingle = !!(flags & TEST_SINGLE_BIT), callbackOnSingle = !!(flags & CALLBACK_ON_SINGLE_BIT); size_t i = 0; - if (!js_ExecuteRegExp(cx, g.re(), str, &i, testSingle, vp)) + if (!re.execute(cx, str, &i, testSingle, vp)) return false; - if (callbackOnSingle && Matched(testSingle, *vp) && - !callback(cx, 0, data)) { + if (callbackOnSingle && Matched(testSingle, *vp) && !callback(cx, 0, data)) return false; - } } return true; } -typedef JSObject **MatchArgType; - -/* - * DoMatch will only callback on global matches, hence this function builds - * only the "array of matches" returned by match on global regexps. - */ -static bool -MatchCallback(JSContext *cx, size_t count, void *p) -{ - JS_ASSERT(count <= JSID_INT_MAX); /* by max string length */ - - JSObject *&arrayobj = *static_cast(p); - if (!arrayobj) { - arrayobj = js_NewArrayObject(cx, 0, NULL); - if (!arrayobj) - return false; - } - - JSString *str = cx->regExpStatics.input; - JSSubString &match = cx->regExpStatics.lastMatch; - ptrdiff_t off = match.chars - str->chars(); - JS_ASSERT(off >= 0 && size_t(off) <= str->length()); - JSString *matchstr = js_NewDependentString(cx, str, off, match.length); - if (!matchstr) - return false; - - Value v = StringValue(matchstr); - - JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING); - return !!arrayobj->setProperty(cx, INT_TO_JSID(count), &v); -} - static bool BuildFlatMatchArray(JSContext *cx, JSString *textstr, const RegExpGuard &g, Value *vp) @@ -1843,6 +1825,32 @@ BuildFlatMatchArray(JSContext *cx, JSString *textstr, const RegExpGuard &g, StringValue(textstr)); } +typedef JSObject **MatchArgType; + +/* + * DoMatch will only callback on global matches, hence this function builds + * only the "array of matches" returned by match on global regexps. + */ +static bool +MatchCallback(JSContext *cx, size_t count, void *p) +{ + JS_ASSERT(count <= JSID_INT_MAX); /* by max string length */ + + JSObject *&arrayobj = *static_cast(p); + if (!arrayobj) { + arrayobj = js_NewArrayObject(cx, 0, NULL); + if (!arrayobj) + return false; + } + + Value v; + if (!cx->regExpStatics.createLastMatch(&v)) + return false; + + JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING); + return !!arrayobj->setProperty(cx, INT_TO_JSID(count), &v); +} + static JSBool str_match(JSContext *cx, uintN argc, Value *vp) { @@ -1862,8 +1870,8 @@ str_match(JSContext *cx, uintN argc, Value *vp) if (!DoMatch(cx, vp, str, g, MatchCallback, arg, MATCH_ARGS)) return false; - /* When not global, DoMatch will leave |RegEx.exec()| in *vp. */ - if (g.re()->flags & JSREG_GLOB) + /* When not global, DoMatch will leave |RegExp.exec()| in *vp. */ + if (g.re()->global()) vp->setObjectOrNull(array.object()); return true; } @@ -1885,11 +1893,11 @@ str_search(JSContext *cx, uintN argc, Value *vp) return false; size_t i = 0; - if (!js_ExecuteRegExp(cx, g.re(), str, &i, true, vp)) + if (!g.re()->execute(cx, str, &i, true, vp)) return false; if (vp->isTrue()) - vp->setInt32(cx->regExpStatics.leftContext.length); + vp->setInt32(cx->regExpStatics.get(0, 0)); else vp->setInt32(-1); return true; @@ -1915,44 +1923,44 @@ struct ReplaceData JSCharBuffer cb; /* buffer built during DoMatch */ }; -static JSSubString * +static bool InterpretDollar(JSContext *cx, jschar *dp, jschar *ep, ReplaceData &rdata, - size_t *skip) + JSSubString *out, size_t *skip) { - JSRegExpStatics *res; - jschar dc, *cp; - uintN num, tmp; - JS_ASSERT(*dp == '$'); /* If there is only a dollar, bail now */ if (dp + 1 >= ep) - return NULL; + return false; /* Interpret all Perl match-induced dollar variables. */ - res = &cx->regExpStatics; - dc = dp[1]; + RegExpStatics &statics = cx->regExpStatics; + jschar dc = dp[1]; if (JS7_ISDEC(dc)) { /* ECMA-262 Edition 3: 1-9 or 01-99 */ - num = JS7_UNDEC(dc); - if (num > res->parens.length()) - return NULL; + uintN num = JS7_UNDEC(dc); + if (num > statics.getParenCount()) + return false; - cp = dp + 2; + jschar *cp = dp + 2; if (cp < ep && (dc = *cp, JS7_ISDEC(dc))) { - tmp = 10 * num + JS7_UNDEC(dc); - if (tmp <= res->parens.length()) { + uintN tmp = 10 * num + JS7_UNDEC(dc); + if (tmp <= statics.getParenCount()) { cp++; num = tmp; } } if (num == 0) - return NULL; + return false; /* Adjust num from 1 $n-origin to 0 array-index-origin. */ num--; *skip = cp - dp; - return (num < res->parens.length()) ? &res->parens[num] : &js_EmptySubString; + if (num < statics.getParenCount()) + statics.getParen(num, out); + else + *out = js_EmptySubString; + return true; } *skip = 2; @@ -1960,23 +1968,28 @@ InterpretDollar(JSContext *cx, jschar *dp, jschar *ep, ReplaceData &rdata, case '$': rdata.dollarStr.chars = dp; rdata.dollarStr.length = 1; - return &rdata.dollarStr; + *out = rdata.dollarStr; + return true; case '&': - return &res->lastMatch; + statics.getLastMatch(out); + return true; case '+': - return &res->lastParen; + statics.getLastParen(out); + return true; case '`': - return &res->leftContext; + statics.getLeftContext(out); + return true; case '\'': - return &res->rightContext; + statics.getRightContext(out); + return true; } - return NULL; + return false; } static JS_ALWAYS_INLINE bool PushRegExpSubstr(JSContext *cx, const JSSubString &sub, Value *sp) { - JSString *whole = cx->regExpStatics.input; + JSString *whole = cx->regExpStatics.getInput(); size_t off = sub.chars - whole->chars(); JSString *str = js_NewDependentString(cx, whole, off, sub.length); if (!str) @@ -1986,29 +1999,26 @@ PushRegExpSubstr(JSContext *cx, const JSSubString &sub, Value *sp) } class PreserveRegExpStatics { - JSContext *cx; - JSRegExpStatics save; + JSContext * const cx; + js::RegExpStatics container; public: - PreserveRegExpStatics(JSContext *cx) : cx(cx), save(cx) { - save.copy(cx->regExpStatics); + PreserveRegExpStatics(JSContext *cx) : cx(cx), container(cx) { + container.clone(cx->regExpStatics); + container.checkInvariants(); + cx->regExpStatics.checkInvariants(); } ~PreserveRegExpStatics() { - cx->regExpStatics.copy(save); + cx->regExpStatics.clone(container); + cx->regExpStatics.checkInvariants(); } }; static bool FindReplaceLength(JSContext *cx, ReplaceData &rdata, size_t *sizep) { - JSString *repstr; - size_t replen, skip; - jschar *dp, *ep; - JSSubString *sub; - JSObject *lambda; - - lambda = rdata.lambda; + JSObject *lambda = rdata.lambda; if (lambda) { LeaveTrace(cx); @@ -2020,7 +2030,8 @@ FindReplaceLength(JSContext *cx, ReplaceData &rdata, size_t *sizep) * For $&, etc., we must create string jsvals from cx->regExpStatics. * We grab up stack space to keep the newborn strings GC-rooted. */ - uintN p = rdata.g.re()->parenCount; + RegExpStatics &statics = cx->regExpStatics; + uintN p = statics.getParenCount(); uintN argc = 1 + p + 2; if (!rdata.args.pushed() && !cx->stack().pushInvokeArgs(cx, argc, rdata.args)) @@ -2036,21 +2047,16 @@ FindReplaceLength(JSContext *cx, ReplaceData &rdata, size_t *sizep) Value *sp = args.argv(); /* Push $&, $1, $2, ... */ - if (!PushRegExpSubstr(cx, cx->regExpStatics.lastMatch, sp++)) + if (!statics.createLastMatch(sp++)) return false; - uintN i = 0; - for (uintN n = cx->regExpStatics.parens.length(); i < n; i++) { - if (!PushRegExpSubstr(cx, cx->regExpStatics.parens[i], sp++)) + for (size_t i = 0; i < statics.getParenCount(); ++i) { + if (!statics.createParen(i, sp++)) return false; } - /* Make sure to push undefined for any unmatched parens. */ - for (; i < p; i++) - *sp++ = UndefinedValue(); - /* Push match index and input string. */ - sp++->setInt32(cx->regExpStatics.leftContext.length); + sp++->setInt32(statics.get(0, 0)); sp++->setString(rdata.str); if (!Invoke(cx, rdata.args, 0)) @@ -2061,7 +2067,7 @@ FindReplaceLength(JSContext *cx, ReplaceData &rdata, size_t *sizep) * created by this js_ValueToString that would otherwise be GC- * able, until we use rdata.repstr in DoReplace. */ - repstr = js_ValueToString(cx, args.rval()); + JSString *repstr = js_ValueToString(cx, args.rval()); if (!repstr) return false; @@ -2071,17 +2077,17 @@ FindReplaceLength(JSContext *cx, ReplaceData &rdata, size_t *sizep) return true; } - repstr = rdata.repstr; - replen = repstr->length(); - for (dp = rdata.dollar, ep = rdata.dollarEnd; dp; - dp = js_strchr_limit(dp, '$', ep)) { - sub = InterpretDollar(cx, dp, ep, rdata, &skip); - if (sub) { - replen += sub->length - skip; + JSString *repstr = rdata.repstr; + size_t replen = repstr->length(); + for (jschar *dp = rdata.dollar, *ep = rdata.dollarEnd; dp; dp = js_strchr_limit(dp, '$', ep)) { + JSSubString sub; + size_t skip; + if (InterpretDollar(cx, dp, ep, rdata, &sub, &skip)) { + replen += sub.length - skip; dp += skip; - } - else + } else { dp++; + } } *sizep = replen; return true; @@ -2090,23 +2096,20 @@ FindReplaceLength(JSContext *cx, ReplaceData &rdata, size_t *sizep) static void DoReplace(JSContext *cx, ReplaceData &rdata, jschar *chars) { - JSString *repstr; - jschar *bp, *cp, *dp, *ep; - size_t len, skip; - JSSubString *sub; - - repstr = rdata.repstr; - bp = cp = repstr->chars(); - for (dp = rdata.dollar, ep = rdata.dollarEnd; dp; - dp = js_strchr_limit(dp, '$', ep)) { - len = dp - cp; + JSString *repstr = rdata.repstr; + jschar *cp; + jschar *bp = cp = repstr->chars(); + for (jschar *dp = rdata.dollar, *ep = rdata.dollarEnd; dp; dp = js_strchr_limit(dp, '$', ep)) { + size_t len = dp - cp; js_strncpy(chars, cp, len); chars += len; cp = dp; - sub = InterpretDollar(cx, dp, ep, rdata, &skip); - if (sub) { - len = sub->length; - js_strncpy(chars, sub->chars, len); + + JSSubString sub; + size_t skip; + if (InterpretDollar(cx, dp, ep, rdata, &sub, &skip)) { + len = sub.length; + js_strncpy(chars, sub.chars, len); chars += len; cp += skip; dp += skip; @@ -2121,14 +2124,14 @@ static bool ReplaceCallback(JSContext *cx, size_t count, void *p) { ReplaceData &rdata = *static_cast(p); + RegExpStatics &statics = cx->regExpStatics; rdata.calledBack = true; JSString *str = rdata.str; size_t leftoff = rdata.leftIndex; const jschar *left = str->chars() + leftoff; - size_t leftlen = cx->regExpStatics.lastMatch.chars - left; - rdata.leftIndex = cx->regExpStatics.lastMatch.chars - str->chars(); - rdata.leftIndex += cx->regExpStatics.lastMatch.length; + size_t leftlen = statics.get(0, 0) - leftoff; + rdata.leftIndex = statics.get(0, 1); size_t replen = 0; /* silence 'unused' warning */ if (!FindReplaceLength(cx, rdata, &replen)) @@ -2270,8 +2273,9 @@ js::str_replace(JSContext *cx, uintN argc, Value *vp) return true; } - JSSubString *sub = &cx->regExpStatics.rightContext; - if (!rdata.cb.append(sub->chars, sub->length)) + JSSubString sub; + cx->regExpStatics.getRightContext(&sub); + if (!rdata.cb.append(sub.chars, sub.length)) return false; JSString *retstr = js_NewStringFromCharBuffer(cx, rdata.cb); @@ -2292,8 +2296,7 @@ js::str_replace(JSContext *cx, uintN argc, Value *vp) * separator occurrence if found, or str->length if no separator is found. */ static jsint -find_split(JSContext *cx, JSString *str, JSRegExp *re, jsint *ip, - JSSubString *sep) +find_split(JSContext *cx, JSString *str, js::RegExp *re, jsint *ip, JSSubString *sep) { jsint i; size_t length; @@ -2329,7 +2332,7 @@ find_split(JSContext *cx, JSString *str, JSRegExp *re, jsint *ip, again: /* JS1.2 deviated from Perl by never matching at end of string. */ index = (size_t)i; - if (!js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, &rval)) + if (!re->execute(cx, str, &index, true, &rval)) return -2; if (!rval.isTrue()) { /* Mismatch: ensure our caller advances i past end of string. */ @@ -2337,7 +2340,8 @@ find_split(JSContext *cx, JSString *str, JSRegExp *re, jsint *ip, return length; } i = (jsint)index; - *sep = cx->regExpStatics.lastMatch; + JS_ASSERT(sep); + cx->regExpStatics.getLastMatch(sep); if (sep->length == 0) { /* * Empty string match: never split on an empty match at the start @@ -2400,10 +2404,10 @@ str_split(JSContext *cx, uintN argc, Value *vp) return true; } - JSRegExp *re; + RegExp *re; JSSubString *sep, tmp; if (VALUE_IS_REGEXP(cx, vp[2])) { - re = (JSRegExp *) vp[2].toObject().getPrivate(); + re = static_cast(vp[2].toObject().getPrivate()); sep = &tmp; /* Set a magic value so we can detect a successful re match. */ @@ -2457,12 +2461,13 @@ str_split(JSContext *cx, uintN argc, Value *vp) * substring that was delimited. */ if (re && sep->chars) { - JSRegExpStatics *res = &cx->regExpStatics; - for (uintN num = 0; num < res->parens.length(); num++) { + RegExpStatics &statics = cx->regExpStatics; + for (uintN num = 0; num < statics.getParenCount(); num++) { if (limited && len >= limit) break; - JSSubString *parsub = &res->parens[num]; - sub = js_NewStringCopyN(cx, parsub->chars, parsub->length); + JSSubString parsub; + statics.getParen(num, &parsub); + sub = js_NewStringCopyN(cx, parsub.chars, parsub.length); if (!sub || !splits.append(StringValue(sub))) return false; len++; diff --git a/js/src/jsstr.h b/js/src/jsstr.h index e744f3cc3ecf..c9940888be29 100644 --- a/js/src/jsstr.h +++ b/js/src/jsstr.h @@ -73,6 +73,9 @@ js_GetDependentStringChars(JSString *str); extern JSString * JS_FASTCALL js_ConcatStrings(JSContext *cx, JSString *left, JSString *right); +extern JSString * JS_FASTCALL +js_ConcatStringsZ(JSContext *cx, const char *left, JSString *right); + JS_STATIC_ASSERT(JS_BITS_PER_WORD >= 32); struct JSRopeBufferInfo { diff --git a/js/src/jstl.h b/js/src/jstl.h index 96446967bc35..de8f86e01518 100644 --- a/js/src/jstl.h +++ b/js/src/jstl.h @@ -426,6 +426,14 @@ Reverse(T *beg, T *end) } } +template +void +ForEach(InputIterT begin, InputIterT end, CallableT f) +{ + for (; begin != end; ++begin) + f(*begin); +} + } /* namespace js */ #endif /* jstl_h_ */ diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 243f70d83ceb..860e622f4a01 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -2895,12 +2895,14 @@ NativeToValue(JSContext* cx, Value& v, JSValueType type, double* slot) debug_only_printf(LC_TMTracer, "string<%p> ", (void*)v.toString()); break; case JSVAL_TYPE_NULL: + JS_ASSERT(v.isNull()); debug_only_print0(LC_TMTracer, "null "); break; case JSVAL_TYPE_BOOLEAN: debug_only_printf(LC_TMTracer, "bool<%d> ", v.toBoolean()); break; case JSVAL_TYPE_UNDEFINED: + JS_ASSERT(v.isUndefined()); debug_only_print0(LC_TMTracer, "undefined "); break; case JSVAL_TYPE_MAGIC: @@ -11494,10 +11496,21 @@ TraceRecorder::callNative(uintN argc, JSOp mode) args[2] = cx_ins; newobj_ins = lir->insCall(&js_NewInstance_ci, args); guard(false, lir->insEqP_0(newobj_ins), OOM_EXIT); + + /* + * emitNativeCall may take a snapshot below. To avoid having a type + * mismatch (e.g., where get(&vp[1]) is an object and vp[1] is + * null), we make sure vp[1] is some object. The actual object + * doesn't matter; JSOP_NEW and InvokeConstructor both overwrite + * vp[1] without observing its value. + * + * N.B. tracing specializes for functions, so pick a non-function. + */ + vp[1].setObject(*globalObj); } this_ins = newobj_ins; } else if (JSFUN_BOUND_METHOD_TEST(fun->flags)) { - this_ins = INS_CONSTOBJ(funobj->getParent()); + RETURN_STOP("bound method test"); } else { this_ins = get(&vp[1]); diff --git a/js/src/jsvector.h b/js/src/jsvector.h index ef6d75fb2ee8..e60c32dad8d2 100644 --- a/js/src/jsvector.h +++ b/js/src/jsvector.h @@ -388,6 +388,7 @@ class Vector : AllocPolicy bool appendN(const T &t, size_t n); template bool append(const U *begin, const U *end); template bool append(const U *begin, size_t length); + template bool append(const Vector &other); void popBack(); @@ -407,6 +408,18 @@ class Vector : AllocPolicy * passed array. */ void replaceRawBuffer(T *p, size_t length); + + /* + * Places |val| at position |p|, shifting existing elements + * from |p| onward one position higher. + */ + bool insert(T *p, const T &val); + + /* + * Removes the element |t|, which must fall in the bounds [begin, end), + * shifting existing elements from |t + 1| onward one position lower. + */ + void erase(T *t); }; /* Helper functions */ @@ -686,6 +699,39 @@ Vector::appendN(const T &t, size_t needed) return true; } +template +inline bool +Vector::insert(T *p, const T &val) +{ + JS_ASSERT(begin() <= p && p < end()); + size_t pos = p - begin(); + JS_ASSERT(pos <= length()); + size_t oldLength = length(); + if (pos == oldLength) + return append(val); + { + T oldBack = back(); + if (!append(oldBack)) /* Dup the last element. */ + return false; + } + for (size_t i = oldLength; i > pos; --i) + (*this)[i] = (*this)[i - 1]; + (*this)[pos] = val; + return true; +} + +template +inline void +Vector::erase(T *it) +{ + JS_ASSERT(begin() <= it && it < end()); + while (it + 1 != end()) { + *it = *(it + 1); + ++it; + } + popBack(); +} + template template JS_ALWAYS_INLINE bool @@ -716,6 +762,14 @@ Vector::append(const U *insBegin, const U *insEnd) return true; } +template +template +inline bool +Vector::append(const Vector &other) +{ + return append(other.begin(), other.end()); +} + template template JS_ALWAYS_INLINE bool diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index 2c87c8abcd5c..9c7847507756 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -154,7 +154,7 @@ class AutoCompartment private: LazilyConstructed frame; JSFrameRegs regs; - JSRegExpStatics statics; + RegExpStatics statics; AutoStringRooter input; bool entered; diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 984c348ce937..a7a4a49d61bb 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -70,12 +70,6 @@ static const char *OpcodeNames[] = { }; #endif -// This probably does not belong here; adding here for now as a quick build fix. -#if ENABLE_ASSEMBLER && WTF_CPU_X86 && !WTF_PLATFORM_MAC -JSC::MacroAssemblerX86Common::SSE2CheckState JSC::MacroAssemblerX86Common::s_sse2CheckState = -NotCheckedSSE2; -#endif - mjit::Compiler::Compiler(JSContext *cx, JSScript *script, JSFunction *fun, JSObject *scopeChain) : cx(cx), script(script), scopeChain(scopeChain), globalObj(scopeChain->getGlobal()), fun(fun), analysis(cx, script), jumpMap(NULL), frame(cx, script, masm), diff --git a/js/src/methodjit/Logging.cpp b/js/src/methodjit/Logging.cpp index c7bffbf67386..761288559bb8 100644 --- a/js/src/methodjit/Logging.cpp +++ b/js/src/methodjit/Logging.cpp @@ -38,8 +38,11 @@ * * ***** END LICENSE BLOCK ***** */ +#include +#include #include #include +#include "jsutil.h" #include "MethodJIT.h" #include "Logging.h" diff --git a/js/src/methodjit/Logging.h b/js/src/methodjit/Logging.h index 792a7555800d..fed4a896bc10 100644 --- a/js/src/methodjit/Logging.h +++ b/js/src/methodjit/Logging.h @@ -38,7 +38,7 @@ * * ***** END LICENSE BLOCK ***** */ -#if !defined jsjaeger_logging_h__ && defined JS_METHODJIT +#if !defined jsjaeger_logging_h__ #define jsjaeger_logging_h__ #include "prmjtime.h" diff --git a/js/src/tests/js1_5/Exceptions/regress-332472.js b/js/src/tests/js1_5/Exceptions/regress-332472.js index 6d855dd38959..cd3e575c1ca7 100644 --- a/js/src/tests/js1_5/Exceptions/regress-332472.js +++ b/js/src/tests/js1_5/Exceptions/regress-332472.js @@ -39,7 +39,7 @@ var BUGNUMBER = 332472; var summary = 'new RegExp() ignores string boundaries when throwing exceptions'; var actual = ''; -var expect = 'SyntaxError: invalid quantifier ?asdf'; +var expect = 'SyntaxError: invalid quantifier'; printBugNumber(BUGNUMBER); printStatus (summary); diff --git a/js/src/tests/js1_5/extensions/jstests.list b/js/src/tests/js1_5/extensions/jstests.list index 643cdda5f53b..7004d6022fc9 100644 --- a/js/src/tests/js1_5/extensions/jstests.list +++ b/js/src/tests/js1_5/extensions/jstests.list @@ -43,7 +43,7 @@ script regress-325269.js script regress-327608.js script regress-328443.js script regress-328556.js -script regress-330569.js +skip script regress-330569.js # Yarr doesn't bail on complex regexps. script regress-333541.js skip script regress-335700.js # bug xxx - reftest hang, BigO skip-if(!xulRuntime.shell) script regress-336409-1.js # no results reported. @@ -71,7 +71,7 @@ skip script regress-350531.js # slow script regress-351102-01.js script regress-351102-02.js script regress-351102-06.js -script regress-351448.js +skip script regress-351448.js # Yarr doesn't have the same complexity errors at execution time. script regress-351463-01.js script regress-351973.js script regress-352261.js diff --git a/js/src/tests/js1_8_5/regress/jstests.list b/js/src/tests/js1_8_5/regress/jstests.list index 181230e30452..cc30cb2b3a89 100644 --- a/js/src/tests/js1_8_5/regress/jstests.list +++ b/js/src/tests/js1_8_5/regress/jstests.list @@ -27,3 +27,5 @@ script regress-571014.js script regress-577648-1.js script regress-577648-2.js script regress-583429.js +script regress-584355.js +script regress-yarr-regexp.js diff --git a/js/src/tests/js1_8_5/regress/regress-584355.js b/js/src/tests/js1_8_5/regress/regress-584355.js new file mode 100644 index 000000000000..3b24da193ae6 --- /dev/null +++ b/js/src/tests/js1_8_5/regress/regress-584355.js @@ -0,0 +1,7 @@ +var actual; +var expect = "function f() {\n ff();\n}"; +function fun() { + (new Function ("function ff () { actual = '' + ff. caller; } function f () { ff (); } f ();")) (); +} +fun(); +reportCompare(expect, actual, ""); diff --git a/js/src/tests/js1_8_5/regress/regress-yarr-regexp.js b/js/src/tests/js1_8_5/regress/regress-yarr-regexp.js new file mode 100644 index 000000000000..9a20d936debf --- /dev/null +++ b/js/src/tests/js1_8_5/regress/regress-yarr-regexp.js @@ -0,0 +1,12 @@ +function toSource(o) { return o === null ? "null" : o.toSource(); } + +var gcgcz = /((?:.)+)((?:.)*)/; /* Greedy capture, greedy capture zero. */ +reportCompare(["a", "a", ""].toSource(), gcgcz.exec("a").toSource()); +reportCompare(["ab", "ab", ""].toSource(), gcgcz.exec("ab").toSource()); +reportCompare(["abc", "abc", ""].toSource(), gcgcz.exec("abc").toSource()); + +reportCompare(["a", ""].toSource(), toSource(/((?:)*?)a/.exec("a"))); +reportCompare(["a", ""].toSource(), toSource(/((?:.)*?)a/.exec("a"))); +reportCompare(["a", ""].toSource(), toSource(/a((?:.)*)/.exec("a"))); + +reportCompare(["B", "B"].toSource(), toSource(/([A-Z])/.exec("fooBar"))); diff --git a/js/src/trace-test/tests/basic/bug586499-regexp.js b/js/src/trace-test/tests/basic/bug586499-regexp.js new file mode 100644 index 000000000000..3f7f451cffa9 --- /dev/null +++ b/js/src/trace-test/tests/basic/bug586499-regexp.js @@ -0,0 +1,2 @@ +// Don't crash. +var re = /^(?:\s*.){0,16}/; diff --git a/js/src/xpconnect/src/XPCSafeJSObjectWrapper.cpp b/js/src/xpconnect/src/XPCSafeJSObjectWrapper.cpp index fddf46e95828..f2606f8460dc 100644 --- a/js/src/xpconnect/src/XPCSafeJSObjectWrapper.cpp +++ b/js/src/xpconnect/src/XPCSafeJSObjectWrapper.cpp @@ -625,7 +625,7 @@ public: private: JSContext *cx; - JSRegExpStatics statics; + js::RegExpStatics statics; js::AutoStringRooter tvr; uint32 options; JSStackFrame *fp; diff --git a/js/src/xpconnect/src/xpcquickstubs.cpp b/js/src/xpconnect/src/xpcquickstubs.cpp index fe2e61445369..b6a82acebe07 100644 --- a/js/src/xpconnect/src/xpcquickstubs.cpp +++ b/js/src/xpconnect/src/xpcquickstubs.cpp @@ -1115,12 +1115,12 @@ xpc_qsXPCOMObjectToJsval(XPCLazyCallContext &lccx, qsObjectHelper* aHelper, const nsIID *iid, XPCNativeInterface **iface, jsval *rval) { + NS_PRECONDITION(iface, "Who did that and why?"); + // From the T_INTERFACE case in XPCConvert::NativeData2JS. // This is one of the slowest things quick stubs do. JSContext *cx = lccx.GetJSContext(); - if(!iface) - return xpc_qsThrow(cx, NS_ERROR_XPC_BAD_CONVERT_NATIVE); // XXX The OBJ_IS_NOT_GLOBAL here is not really right. In // fact, this code is depending on the fact that the diff --git a/js/src/xpconnect/tests/unit/test_import.js b/js/src/xpconnect/tests/unit/test_import.js index 242fe646ad24..18d30e8bd149 100644 --- a/js/src/xpconnect/tests/unit/test_import.js +++ b/js/src/xpconnect/tests/unit/test_import.js @@ -67,13 +67,12 @@ function run_test() { do_check_true(scope2.XPCOMUtils == scope.XPCOMUtils); - // try on a new object using a file URL + // try on a new object using the resolved URL var res = Components.classes["@mozilla.org/network/protocol;1?name=resource"] .getService(Components.interfaces.nsIResProtocolHandler); var resURI = res.newURI("resource://gre/modules/XPCOMUtils.jsm", null, null); dump("resURI: " + resURI + "\n"); var filePath = res.resolveURI(resURI); - do_check_eq(filePath.indexOf("file://"), 0); var scope3 = {}; Components.utils.import(filePath, scope3); do_check_eq(typeof(scope3.XPCOMUtils), "object"); diff --git a/js/src/yarr/Makefile b/js/src/yarr/Makefile new file mode 100644 index 000000000000..c824cdb96b6c --- /dev/null +++ b/js/src/yarr/Makefile @@ -0,0 +1,5 @@ +INCLUDES := -I. -Iyarr -Iwtf -I../assembler/assembler -I../assembler + +all: + $(CXX) -g3 -c $(INCLUDES) yarr/*.cpp + $(CXX) -g3 $(INCLUDES) TestMain.cpp *.o diff --git a/js/src/yarr/jswtfbridge.h b/js/src/yarr/jswtfbridge.h new file mode 100644 index 000000000000..2ed79102bf4e --- /dev/null +++ b/js/src/yarr/jswtfbridge.h @@ -0,0 +1,75 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99 ft=cpp: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released + * June 12, 2009. + * + * The Initial Developer of the Original Code is + * the Mozilla Corporation. + * + * Contributor(s): + * Chris Leary + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jswtfbridge_h__ +#define jswtfbridge_h__ + +/* + * The JS/WTF Bridge to Bona-fide Quality. + */ + +#include "assembler/wtf/Platform.h" +#include "jsstr.h" +#include "jsprvtd.h" +#include "jstl.h" + +typedef jschar UChar; +typedef JSString UString; + +template +class ValueDeleter +{ + public: + void operator()(T &t) { delete t; } +}; + +template +static inline void +deleteAllValues(js::Vector &vector) +{ + js::ForEach(vector.begin(), vector.end(), ValueDeleter()); +} + +class Unicode { + public: + static UChar toUpper(UChar c) { return JS_TOUPPER(c); } + static UChar toLower(UChar c) { return JS_TOLOWER(c); } +}; + +#endif diff --git a/js/src/yarr/pcre/AUTHORS b/js/src/yarr/pcre/AUTHORS new file mode 100644 index 000000000000..dbac2a54834b --- /dev/null +++ b/js/src/yarr/pcre/AUTHORS @@ -0,0 +1,12 @@ +Originally written by: Philip Hazel +Email local part: ph10 +Email domain: cam.ac.uk + +University of Cambridge Computing Service, +Cambridge, England. Phone: +44 1223 334714. + +Copyright (c) 1997-2005 University of Cambridge. All rights reserved. + +Adapted for JavaScriptCore and WebKit by Apple Inc. + +Copyright (c) 2005, 2006, 2007 Apple Inc. All rights reserved. diff --git a/js/src/yarr/pcre/COPYING b/js/src/yarr/pcre/COPYING new file mode 100644 index 000000000000..6ffdc24342d5 --- /dev/null +++ b/js/src/yarr/pcre/COPYING @@ -0,0 +1,35 @@ +PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + +This is JavaScriptCore's variant of the PCRE library. While this library +started out as a copy of PCRE, many of the features of PCRE have been +removed. + +Copyright (c) 1997-2005 University of Cambridge. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the name of Apple + Inc. nor the names of their contributors may be used to endorse or + promote products derived from this software without specific prior + written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/js/src/yarr/pcre/chartables.c b/js/src/yarr/pcre/chartables.c new file mode 100644 index 000000000000..5c99db0b980f --- /dev/null +++ b/js/src/yarr/pcre/chartables.c @@ -0,0 +1,96 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* This file is automatically written by the dftables auxiliary +program. If you edit it by hand, you might like to edit the Makefile to +prevent its ever being regenerated. + +This file contains the default tables for characters with codes less than +128 (ASCII characters). These tables are used when no external tables are +passed to PCRE. */ + +const unsigned char jsc_pcre_default_tables[480] = { + +/* This table is a lower casing table. */ + + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, + +/* This table is a case flipping table. */ + + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, + 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, + +/* This table contains bit maps for various character classes. +Each map is 32 bytes long and the bits run from the least +significant end of each byte. The classes are: space, digit, word. */ + + 0x00, 0x3E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x03, + 0xFE, 0xFF, 0xFF, 0x87, 0xFE, 0xFF, 0xFF, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + +/* This table identifies various classes of character by individual bits: + 0x01 white space character + 0x08 hexadecimal digit + 0x10 alphanumeric or '_' +*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0- 7 */ + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, /* 8- 15 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 16- 23 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 24- 31 */ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* - ' */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ( - / */ + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, /* 0 - 7 */ + 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 8 - ? */ + 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x10, /* @ - G */ + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, /* H - O */ + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, /* P - W */ + 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, /* X - _ */ + 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x10, /* ` - g */ + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, /* h - o */ + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, /* p - w */ + 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00}; /* x -127 */ + + +/* End of chartables.c */ diff --git a/js/src/yarr/pcre/dftables b/js/src/yarr/pcre/dftables new file mode 100644 index 000000000000..669b948ffc91 --- /dev/null +++ b/js/src/yarr/pcre/dftables @@ -0,0 +1,273 @@ +#!/usr/bin/perl -w +# +# This is JavaScriptCore's variant of the PCRE library. While this library +# started out as a copy of PCRE, many of the features of PCRE have been +# removed. This library now supports only the regular expression features +# required by the JavaScript language specification, and has only the functions +# needed by JavaScriptCore and the rest of WebKit. +# +# Originally written by Philip Hazel +# Copyright (c) 1997-2006 University of Cambridge +# Copyright (C) 2002, 2004, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. +# +# ----------------------------------------------------------------------------- +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the name of the University of Cambridge nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ----------------------------------------------------------------------------- + +# This is a freestanding support program to generate a file containing +# character tables. The tables are built according to the default C +# locale. + +use strict; + +use File::Basename; +use File::Spec; +use File::Temp qw(tempfile); +use Getopt::Long; + +sub readHeaderValues(); + +my %pcre_internal; + +if (scalar(@ARGV) < 1) { + print STDERR "Usage: ", basename($0), " [--preprocessor=program] output-file\n"; + exit 1; +} + +my $outputFile; +my $preprocessor; +GetOptions('preprocessor=s' => \$preprocessor); +if (not $preprocessor) { + $preprocessor = "cpp"; +} + +$outputFile = $ARGV[0]; +die('Must specify output file.') unless defined($outputFile); + +readHeaderValues(); + +open(OUT, ">", $outputFile) or die "$!"; +binmode(OUT); + +printf(OUT + "/*************************************************\n" . + "* Perl-Compatible Regular Expressions *\n" . + "*************************************************/\n\n" . + "/* This file is automatically written by the dftables auxiliary \n" . + "program. If you edit it by hand, you might like to edit the Makefile to \n" . + "prevent its ever being regenerated.\n\n"); +printf(OUT + "This file contains the default tables for characters with codes less than\n" . + "128 (ASCII characters). These tables are used when no external tables are\n" . + "passed to PCRE. */\n\n" . + "const unsigned char jsc_pcre_default_tables[%d] = {\n\n" . + "/* This table is a lower casing table. */\n\n", $pcre_internal{tables_length}); + +if ($pcre_internal{lcc_offset} != 0) { + die "lcc_offset != 0"; +} + +printf(OUT " "); +for (my $i = 0; $i < 128; $i++) { + if (($i & 7) == 0 && $i != 0) { + printf(OUT "\n "); + } + printf(OUT "0x%02X", ord(lc(chr($i)))); + if ($i != 127) { + printf(OUT ", "); + } +} +printf(OUT ",\n\n"); + +printf(OUT "/* This table is a case flipping table. */\n\n"); + +if ($pcre_internal{fcc_offset} != 128) { + die "fcc_offset != 128"; +} + +printf(OUT " "); +for (my $i = 0; $i < 128; $i++) { + if (($i & 7) == 0 && $i != 0) { + printf(OUT "\n "); + } + my $c = chr($i); + printf(OUT "0x%02X", $c =~ /[[:lower:]]/ ? ord(uc($c)) : ord(lc($c))); + if ($i != 127) { + printf(OUT ", "); + } +} +printf(OUT ",\n\n"); + +printf(OUT + "/* This table contains bit maps for various character classes.\n" . + "Each map is 32 bytes long and the bits run from the least\n" . + "significant end of each byte. The classes are: space, digit, word. */\n\n"); + +if ($pcre_internal{cbits_offset} != $pcre_internal{fcc_offset} + 128) { + die "cbits_offset != fcc_offset + 128"; +} + +my @cbit_table = (0) x $pcre_internal{cbit_length}; +for (my $i = ord('0'); $i <= ord('9'); $i++) { + $cbit_table[$pcre_internal{cbit_digit} + $i / 8] |= 1 << ($i & 7); +} +$cbit_table[$pcre_internal{cbit_word} + ord('_') / 8] |= 1 << (ord('_') & 7); +for (my $i = 0; $i < 128; $i++) { + my $c = chr($i); + if ($c =~ /[[:alnum:]]/) { + $cbit_table[$pcre_internal{cbit_word} + $i / 8] |= 1 << ($i & 7); + } + if ($c =~ /[[:space:]]/) { + $cbit_table[$pcre_internal{cbit_space} + $i / 8] |= 1 << ($i & 7); + } +} + +printf(OUT " "); +for (my $i = 0; $i < $pcre_internal{cbit_length}; $i++) { + if (($i & 7) == 0 && $i != 0) { + if (($i & 31) == 0) { + printf(OUT "\n"); + } + printf(OUT "\n "); + } + printf(OUT "0x%02X", $cbit_table[$i]); + if ($i != $pcre_internal{cbit_length} - 1) { + printf(OUT ", "); + } +} +printf(OUT ",\n\n"); + +printf(OUT + "/* This table identifies various classes of character by individual bits:\n" . + " 0x%02x white space character\n" . + " 0x%02x hexadecimal digit\n" . + " 0x%02x alphanumeric or '_'\n*/\n\n", + $pcre_internal{ctype_space}, $pcre_internal{ctype_xdigit}, $pcre_internal{ctype_word}); + +if ($pcre_internal{ctypes_offset} != $pcre_internal{cbits_offset} + $pcre_internal{cbit_length}) { + die "ctypes_offset != cbits_offset + cbit_length"; +} + +printf(OUT " "); +for (my $i = 0; $i < 128; $i++) { + my $x = 0; + my $c = chr($i); + if ($c =~ /[[:space:]]/) { + $x += $pcre_internal{ctype_space}; + } + if ($c =~ /[[:xdigit:]]/) { + $x += $pcre_internal{ctype_xdigit}; + } + if ($c =~ /[[:alnum:]_]/) { + $x += $pcre_internal{ctype_word}; + } + printf(OUT "0x%02X", $x); + if ($i != 127) { + printf(OUT ", "); + } else { + printf(OUT "};"); + } + if (($i & 7) == 7) { + printf(OUT " /* "); + my $d = chr($i - 7); + if ($d =~ /[[:print:]]/) { + printf(OUT " %c -", $i - 7); + } else { + printf(OUT "%3d-", $i - 7); + } + if ($c =~ m/[[:print:]]/) { + printf(OUT " %c ", $i); + } else { + printf(OUT "%3d", $i); + } + printf(OUT " */\n"); + if ($i != 127) { + printf(OUT " "); + } + } +} + +if ($pcre_internal{tables_length} != $pcre_internal{ctypes_offset} + 128) { + die "tables_length != ctypes_offset + 128"; +} + +printf(OUT "\n\n/* End of chartables.c */\n"); + +close(OUT); + +exit 0; + +sub readHeaderValues() +{ + my @variables = qw( + cbit_digit + cbit_length + cbit_space + cbit_word + cbits_offset + ctype_space + ctype_word + ctype_xdigit + ctypes_offset + fcc_offset + lcc_offset + tables_length + ); + + local $/ = undef; + + my $headerPath = File::Spec->catfile(dirname($0), "pcre_internal.h"); + + my ($fh, $tempFile) = tempfile( + basename($0) . "-XXXXXXXX", + DIR => File::Spec->tmpdir(), + SUFFIX => ".in", + UNLINK => 0, + ); + + print $fh "#define DFTABLES\n\n"; + + open(HEADER, "<", $headerPath) or die "$!"; + print $fh
; + close(HEADER); + + print $fh "\n\n"; + + for my $v (@variables) { + print $fh "\$pcre_internal{\"$v\"} = $v;\n"; + } + + close($fh); + + open(CPP, "$preprocessor \"$tempFile\" |") or die "$!"; + my $content = ; + close(CPP); + + eval $content; + die "$@" if $@; + unlink $tempFile; +} diff --git a/js/src/yarr/pcre/pcre.h b/js/src/yarr/pcre/pcre.h new file mode 100644 index 000000000000..a0036af61d19 --- /dev/null +++ b/js/src/yarr/pcre/pcre.h @@ -0,0 +1,69 @@ +/* This is the public header file for JavaScriptCore's variant of the PCRE +library. While this library started out as a copy of PCRE, many of the +features of PCRE have been removed. This library now supports only the +regular expression features required by the JavaScript language +specification, and has only the functions needed by JavaScriptCore and the +rest of WebKit. + + Copyright (c) 1997-2005 University of Cambridge + Copyright (C) 2002, 2004, 2006, 2007 Apple Inc. All rights reserved. + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + +// FIXME: This file needs to be renamed to JSRegExp.h; it's no longer PCRE. + +#ifndef JSRegExp_h +#define JSRegExp_h + +#include "yarr/jswtfbridge.h" + +struct JSRegExp; +struct JSContext; + +enum JSRegExpIgnoreCaseOption { JSRegExpDoNotIgnoreCase, JSRegExpIgnoreCase }; +enum JSRegExpMultilineOption { JSRegExpSingleLine, JSRegExpMultiline }; + +/* jsRegExpExecute error codes */ +const int JSRegExpErrorNoMatch = -1; +const int JSRegExpErrorHitLimit = -2; +const int JSRegExpErrorNoMemory = -3; +const int JSRegExpErrorInternal = -4; + +JSRegExp* jsRegExpCompile(const UChar* pattern, int patternLength, + JSRegExpIgnoreCaseOption, JSRegExpMultilineOption, + unsigned* numSubpatterns, int *error); + +int jsRegExpExecute(JSContext *, const JSRegExp*, + const UChar* subject, int subjectLength, int startOffset, + int* offsetsVector, int offsetsVectorLength); + +void jsRegExpFree(JSRegExp*); + +#endif diff --git a/js/src/yarr/pcre/pcre.pri b/js/src/yarr/pcre/pcre.pri new file mode 100644 index 000000000000..4f59e17f4d91 --- /dev/null +++ b/js/src/yarr/pcre/pcre.pri @@ -0,0 +1,12 @@ +# Perl Compatible Regular Expressions - Qt4 build info +VPATH += $$PWD +INCLUDEPATH += $$PWD $$OUTPUT_DIR/JavaScriptCore/tmp +DEPENDPATH += $$PWD + +SOURCES += \ + pcre_compile.cpp \ + pcre_exec.cpp \ + pcre_tables.cpp \ + pcre_ucp_searchfuncs.cpp \ + pcre_xclass.cpp + diff --git a/js/src/yarr/pcre/pcre_compile.cpp b/js/src/yarr/pcre/pcre_compile.cpp new file mode 100644 index 000000000000..643c76203473 --- /dev/null +++ b/js/src/yarr/pcre/pcre_compile.cpp @@ -0,0 +1,2702 @@ +/* This is JavaScriptCore's variant of the PCRE library. While this library +started out as a copy of PCRE, many of the features of PCRE have been +removed. This library now supports only the regular expression features +required by the JavaScript language specification, and has only the functions +needed by JavaScriptCore and the rest of WebKit. + + Originally written by Philip Hazel + Copyright (c) 1997-2006 University of Cambridge + Copyright (C) 2002, 2004, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + Copyright (C) 2007 Eric Seidel + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + +/* This module contains the external function jsRegExpExecute(), along with +supporting internal functions that are not used by other modules. */ + +#include "pcre_internal.h" + +#include +#include "yarr/wtf/ASCIICType.h" +#include "jsvector.h" + +using namespace WTF; + +/* Negative values for the firstchar and reqchar variables */ + +#define REQ_UNSET (-2) +#define REQ_NONE (-1) + +/************************************************* +* Code parameters and static tables * +*************************************************/ + +/* Maximum number of items on the nested bracket stacks at compile time. This +applies to the nesting of all kinds of parentheses. It does not limit +un-nested, non-capturing parentheses. This number can be made bigger if +necessary - it is used to dimension one int and one unsigned char vector at +compile time. */ + +#define BRASTACK_SIZE 200 + +/* Table for handling escaped characters in the range '0'-'z'. Positive returns +are simple data values; negative values are for special things like \d and so +on. Zero means further processing is needed (for things like \x), or the escape +is invalid. */ + +static const short escapes[] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - 7 */ + 0, 0, ':', ';', '<', '=', '>', '?', /* 8 - ? */ + '@', 0, -ESC_B, 0, -ESC_D, 0, 0, 0, /* @ - G */ + 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */ + 0, 0, 0, -ESC_S, 0, 0, 0, -ESC_W, /* P - W */ + 0, 0, 0, '[', '\\', ']', '^', '_', /* X - _ */ + '`', 7, -ESC_b, 0, -ESC_d, 0, '\f', 0, /* ` - g */ + 0, 0, 0, 0, 0, 0, '\n', 0, /* h - o */ + 0, 0, '\r', -ESC_s, '\t', 0, '\v', -ESC_w, /* p - w */ + 0, 0, 0 /* x - z */ +}; +static const unsigned OPCODE_LEN = 1; +static const unsigned BRA_NEST_SIZE = 2; +static const unsigned BRA_LEN = OPCODE_LEN + LINK_SIZE + BRA_NEST_SIZE; +static const unsigned KET_LEN = OPCODE_LEN + LINK_SIZE; + +/* Error code numbers. They are given names so that they can more easily be +tracked. */ + +enum ErrorCode { + ERR0, ERR1, ERR2, ERR3, ERR4, ERR5, ERR6, ERR7, ERR8, ERR9, + ERR10, ERR11, ERR12, ERR13, ERR14, ERR15, ERR16, ERR17 +}; + +/* These are the error texts that correspond to the above error codes: + // 1 + "\\ at end of pattern\0" + "\\c at end of pattern\0" + "character value in \\x{...} sequence is too large\0" + "numbers out of order in {} quantifier\0" + // 5 + "number too big in {} quantifier\0" + "missing terminating ] for character class\0" + "internal error: code overflow\0" + "range out of order in character class\0" + "nothing to repeat\0" + // 10 + "unmatched parentheses\0" + "internal error: unexpected repeat\0" + "unrecognized character after (?\0" + "failed to get memory\0" + "missing )\0" + // 15 + "reference to non-existent subpattern\0" + "regular expression too large\0" + "parentheses nested too deeply" */ + +/* Structure for passing "static" information around between the functions +doing the compiling. */ + +struct CompileData { + CompileData() { + topBackref = 0; + backrefMap = 0; + reqVaryOpt = 0; + needOuterBracket = false; + numCapturingBrackets = 0; + } + int topBackref; /* Maximum back reference */ + unsigned backrefMap; /* Bitmap of low back refs */ + int reqVaryOpt; /* "After variable item" flag for reqByte */ + bool needOuterBracket; + int numCapturingBrackets; +}; + +/* Definitions to allow mutual recursion */ + +static bool compileBracket(int, int*, unsigned char**, const UChar**, const UChar*, ErrorCode*, int, int*, int*, CompileData&); +static bool bracketIsAnchored(const unsigned char* code); +static bool bracketNeedsLineStart(const unsigned char* code, unsigned captureMap, unsigned backrefMap); +static int bracketFindFirstAssertedCharacter(const unsigned char* code, bool inassert); + +/************************************************* +* Handle escapes * +*************************************************/ + +/* This function is called when a \ has been encountered. It either returns a +positive value for a simple escape such as \n, or a negative value which +encodes one of the more complicated things such as \d. When UTF-8 is enabled, +a positive value greater than 255 may be returned. On entry, ptr is pointing at +the \. On exit, it is on the final character of the escape sequence. + +Arguments: + ptrPtr points to the pattern position pointer + errorCodePtr points to the errorcode variable + bracount number of previous extracting brackets + options the options bits + isClass true if inside a character class + +Returns: zero or positive => a data character + negative => a special escape sequence + on error, error is set +*/ + +static int checkEscape(const UChar** ptrPtr, const UChar* patternEnd, ErrorCode* errorCodePtr, int bracount, bool isClass) +{ + const UChar* ptr = *ptrPtr + 1; + + /* If backslash is at the end of the pattern, it's an error. */ + if (ptr == patternEnd) { + *errorCodePtr = ERR1; + *ptrPtr = ptr; + return 0; + } + + int c = *ptr; + + /* Non-alphamerics are literals. For digits or letters, do an initial lookup in + a table. A non-zero result is something that can be returned immediately. + Otherwise further processing may be required. */ + + if (c < '0' || c > 'z') { /* Not alphameric */ + } else if (int escapeValue = escapes[c - '0']) { + c = escapeValue; + if (isClass) { + if (-c == ESC_b) + c = '\b'; /* \b is backslash in a class */ + else if (-c == ESC_B) + c = 'B'; /* and \B is a capital B in a class (in browsers event though ECMAScript 15.10.2.19 says it raises an error) */ + } + /* Escapes that need further processing, or are illegal. */ + + } else { + switch (c) { + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + /* Escape sequences starting with a non-zero digit are backreferences, + unless there are insufficient brackets, in which case they are octal + escape sequences. Those sequences end on the first non-octal character + or when we overflow 0-255, whichever comes first. */ + + if (!isClass) { + const UChar* oldptr = ptr; + c -= '0'; + while ((ptr + 1 < patternEnd) && isASCIIDigit(ptr[1]) && c <= bracount) + c = c * 10 + *(++ptr) - '0'; + if (c <= bracount) { + c = -(ESC_REF + c); + break; + } + ptr = oldptr; /* Put the pointer back and fall through */ + } + + /* Handle an octal number following \. If the first digit is 8 or 9, + this is not octal. */ + + if ((c = *ptr) >= '8') { + c = '\\'; + ptr -= 1; + break; + } + + /* \0 always starts an octal number, but we may drop through to here with a + larger first octal digit. */ + + case '0': { + c -= '0'; + int i; + for (i = 1; i <= 2; ++i) { + if (ptr + i >= patternEnd || ptr[i] < '0' || ptr[i] > '7') + break; + int cc = c * 8 + ptr[i] - '0'; + if (cc > 255) + break; + c = cc; + } + ptr += i - 1; + break; + } + + case 'x': { + c = 0; + int i; + for (i = 1; i <= 2; ++i) { + if (ptr + i >= patternEnd || !isASCIIHexDigit(ptr[i])) { + c = 'x'; + i = 1; + break; + } + int cc = ptr[i]; + if (cc >= 'a') + cc -= 32; /* Convert to upper case */ + c = c * 16 + cc - ((cc < 'A') ? '0' : ('A' - 10)); + } + ptr += i - 1; + break; + } + + case 'u': { + c = 0; + int i; + for (i = 1; i <= 4; ++i) { + if (ptr + i >= patternEnd || !isASCIIHexDigit(ptr[i])) { + c = 'u'; + i = 1; + break; + } + int cc = ptr[i]; + if (cc >= 'a') + cc -= 32; /* Convert to upper case */ + c = c * 16 + cc - ((cc < 'A') ? '0' : ('A' - 10)); + } + ptr += i - 1; + break; + } + + case 'c': + if (++ptr == patternEnd) { + *errorCodePtr = ERR2; + return 0; + } + + c = *ptr; + + /* To match Firefox, inside a character class, we also accept + numbers and '_' as control characters */ + if ((!isClass && !isASCIIAlpha(c)) || (!isASCIIAlphanumeric(c) && c != '_')) { + c = '\\'; + ptr -= 2; + break; + } + + /* A letter is upper-cased; then the 0x40 bit is flipped. This coding + is ASCII-specific, but then the whole concept of \cx is ASCII-specific. */ + c = toASCIIUpper(c) ^ 0x40; + break; + } + } + + *ptrPtr = ptr; + return c; +} + +/************************************************* +* Check for counted repeat * +*************************************************/ + +/* This function is called when a '{' is encountered in a place where it might +start a quantifier. It looks ahead to see if it really is a quantifier or not. +It is only a quantifier if it is one of the forms {ddd} {ddd,} or {ddd,ddd} +where the ddds are digits. + +Arguments: + p pointer to the first char after '{' + +Returns: true or false +*/ + +static bool isCountedRepeat(const UChar* p, const UChar* patternEnd) +{ + if (p >= patternEnd || !isASCIIDigit(*p)) + return false; + p++; + while (p < patternEnd && isASCIIDigit(*p)) + p++; + if (p < patternEnd && *p == '}') + return true; + + if (p >= patternEnd || *p++ != ',') + return false; + if (p < patternEnd && *p == '}') + return true; + + if (p >= patternEnd || !isASCIIDigit(*p)) + return false; + p++; + while (p < patternEnd && isASCIIDigit(*p)) + p++; + + return (p < patternEnd && *p == '}'); +} + +/************************************************* +* Read repeat counts * +*************************************************/ + +/* Read an item of the form {n,m} and return the values. This is called only +after isCountedRepeat() has confirmed that a repeat-count quantifier exists, +so the syntax is guaranteed to be correct, but we need to check the values. + +Arguments: + p pointer to first char after '{' + minp pointer to int for min + maxp pointer to int for max + returned as -1 if no max + errorCodePtr points to error code variable + +Returns: pointer to '}' on success; + current ptr on error, with errorCodePtr set non-zero +*/ + +static const UChar* readRepeatCounts(const UChar* p, int* minp, int* maxp, ErrorCode* errorCodePtr) +{ + int min = 0; + int max = -1; + + /* Read the minimum value and do a paranoid check: a negative value indicates + an integer overflow. */ + + while (isASCIIDigit(*p)) + min = min * 10 + *p++ - '0'; + if (min < 0 || min > 65535) { + *errorCodePtr = ERR5; + return p; + } + + /* Read the maximum value if there is one, and again do a paranoid on its size. + Also, max must not be less than min. */ + + if (*p == '}') + max = min; + else { + if (*(++p) != '}') { + max = 0; + while (isASCIIDigit(*p)) + max = max * 10 + *p++ - '0'; + if (max < 0 || max > 65535) { + *errorCodePtr = ERR5; + return p; + } + if (max < min) { + *errorCodePtr = ERR4; + return p; + } + } + } + + /* Fill in the required variables, and pass back the pointer to the terminating + '}'. */ + + *minp = min; + *maxp = max; + return p; +} + +/************************************************* +* Find first significant op code * +*************************************************/ + +/* This is called by several functions that scan a compiled expression looking +for a fixed first character, or an anchoring op code etc. It skips over things +that do not influence this. + +Arguments: + code pointer to the start of the group +Returns: pointer to the first significant opcode +*/ + +static const unsigned char* firstSignificantOpcode(const unsigned char* code) +{ + while (*code == OP_BRANUMBER) + code += 3; + return code; +} + +static const unsigned char* firstSignificantOpcodeSkippingAssertions(const unsigned char* code) +{ + while (true) { + switch (*code) { + case OP_ASSERT_NOT: + advanceToEndOfBracket(code); + code += 1 + LINK_SIZE; + break; + case OP_WORD_BOUNDARY: + case OP_NOT_WORD_BOUNDARY: + ++code; + break; + case OP_BRANUMBER: + code += 3; + break; + default: + return code; + } + } +} + +/************************************************* +* Get othercase range * +*************************************************/ + +/* This function is passed the start and end of a class range, in UTF-8 mode +with UCP support. It searches up the characters, looking for internal ranges of +characters in the "other" case. Each call returns the next one, updating the +start address. + +Arguments: + cptr points to starting character value; updated + d end value + ocptr where to put start of othercase range + odptr where to put end of othercase range + +Yield: true when range returned; false when no more +*/ + +static bool getOthercaseRange(int* cptr, int d, int* ocptr, int* odptr) +{ + int c, othercase = 0; + + for (c = *cptr; c <= d; c++) { + if ((othercase = jsc_pcre_ucp_othercase(c)) >= 0) + break; + } + + if (c > d) + return false; + + *ocptr = othercase; + int next = othercase + 1; + + for (++c; c <= d; c++) { + if (jsc_pcre_ucp_othercase(c) != next) + break; + next++; + } + + *odptr = next - 1; + *cptr = c; + + return true; +} + +/************************************************* + * Convert character value to UTF-8 * + *************************************************/ + +/* This function takes an integer value in the range 0 - 0x7fffffff + and encodes it as a UTF-8 character in 0 to 6 bytes. + + Arguments: + cvalue the character value + buffer pointer to buffer for result - at least 6 bytes long + + Returns: number of characters placed in the buffer + */ + +static int encodeUTF8(int cvalue, unsigned char *buffer) +{ + int i; + for (i = 0; i < jsc_pcre_utf8_table1_size; i++) + if (cvalue <= jsc_pcre_utf8_table1[i]) + break; + buffer += i; + for (int j = i; j > 0; j--) { + *buffer-- = 0x80 | (cvalue & 0x3f); + cvalue >>= 6; + } + *buffer = jsc_pcre_utf8_table2[i] | cvalue; + return i + 1; +} + +/************************************************* +* Compile one branch * +*************************************************/ + +/* Scan the pattern, compiling it into the code vector. + +Arguments: + options the option bits + brackets points to number of extracting brackets used + codePtr points to the pointer to the current code point + ptrPtr points to the current pattern pointer + errorCodePtr points to error code variable + firstbyteptr set to initial literal character, or < 0 (REQ_UNSET, REQ_NONE) + reqbyteptr set to the last literal character required, else < 0 + cd contains pointers to tables etc. + +Returns: true on success + false, with *errorCodePtr set non-zero on error +*/ + +static inline bool safelyCheckNextChar(const UChar* ptr, const UChar* patternEnd, UChar expected) +{ + return ((ptr + 1 < patternEnd) && ptr[1] == expected); +} + +static bool +compileBranch(int options, int* brackets, unsigned char** codePtr, + const UChar** ptrPtr, const UChar* patternEnd, ErrorCode* errorCodePtr, int *firstbyteptr, + int* reqbyteptr, CompileData& cd) +{ + int repeatType, opType; + int repeatMin = 0, repeat_max = 0; /* To please picky compilers */ + int bravalue = 0; + int reqvary, tempreqvary; + int c; + unsigned char* code = *codePtr; + unsigned char* tempcode; + bool didGroupSetFirstByte = false; + const UChar* ptr = *ptrPtr; + const UChar* tempptr; + unsigned char* previous = NULL; + unsigned char classbits[32]; + + bool class_utf8; + unsigned char* class_utf8data; + unsigned char utf8_char[6]; + + /* Initialize no first byte, no required byte. REQ_UNSET means "no char + matching encountered yet". It gets changed to REQ_NONE if we hit something that + matches a non-fixed char first char; reqByte just remains unset if we never + find one. + + When we hit a repeat whose minimum is zero, we may have to adjust these values + to take the zero repeat into account. This is implemented by setting them to + zeroFirstByte and zeroReqByte when such a repeat is encountered. The individual + item types that can be repeated set these backoff variables appropriately. */ + + int firstByte = REQ_UNSET; + int reqByte = REQ_UNSET; + int zeroReqByte = REQ_UNSET; + int zeroFirstByte = REQ_UNSET; + + /* The variable reqCaseOpt contains either the REQ_IGNORE_CASE value or zero, + according to the current setting of the ignores-case flag. REQ_IGNORE_CASE is a bit + value > 255. It is added into the firstByte or reqByte variables to record the + case status of the value. This is used only for ASCII characters. */ + + int reqCaseOpt = (options & IgnoreCaseOption) ? REQ_IGNORE_CASE : 0; + + /* Switch on next character until the end of the branch */ + + for (;; ptr++) { + bool negateClass; + bool shouldFlipNegation; /* If a negative special such as \S is used, we should negate the whole class to properly support Unicode. */ + int classCharCount; + int classLastChar; + int skipBytes; + int subReqByte; + int subFirstByte; + int mcLength; + unsigned char mcbuffer[8]; + + /* Next byte in the pattern */ + + c = ptr < patternEnd ? *ptr : 0; + + /* Fill in length of a previous callout, except when the next thing is + a quantifier. */ + + bool isQuantifier = c == '*' || c == '+' || c == '?' || (c == '{' && isCountedRepeat(ptr + 1, patternEnd)); + + switch (c) { + /* The branch terminates at end of string, |, or ). */ + + case 0: + if (ptr < patternEnd) + goto NORMAL_CHAR; + // End of string; fall through + case '|': + case ')': + *firstbyteptr = firstByte; + *reqbyteptr = reqByte; + *codePtr = code; + *ptrPtr = ptr; + return true; + + /* Handle single-character metacharacters. In multiline mode, ^ disables + the setting of any following char as a first character. */ + + case '^': + if (options & MatchAcrossMultipleLinesOption) { + if (firstByte == REQ_UNSET) + firstByte = REQ_NONE; + *code++ = OP_BOL; + } else + *code++ = OP_CIRC; + previous = NULL; + break; + + case '$': + previous = NULL; + if (options & MatchAcrossMultipleLinesOption) + *code++ = OP_EOL; + else + *code++ = OP_DOLL; + break; + + /* There can never be a first char if '.' is first, whatever happens about + repeats. The value of reqByte doesn't change either. */ + + case '.': + if (firstByte == REQ_UNSET) + firstByte = REQ_NONE; + zeroFirstByte = firstByte; + zeroReqByte = reqByte; + previous = code; + *code++ = OP_NOT_NEWLINE; + break; + + /* Character classes. If the included characters are all < 256, we build a + 32-byte bitmap of the permitted characters, except in the special case + where there is only one such character. For negated classes, we build the + map as usual, then invert it at the end. However, we use a different opcode + so that data characters > 255 can be handled correctly. + + If the class contains characters outside the 0-255 range, a different + opcode is compiled. It may optionally have a bit map for characters < 256, + but those above are are explicitly listed afterwards. A flag byte tells + whether the bitmap is present, and whether this is a negated class or not. + */ + + case '[': { + previous = code; + shouldFlipNegation = false; + + /* PCRE supports POSIX class stuff inside a class. Perl gives an error if + they are encountered at the top level, so we'll do that too. */ + + /* If the first character is '^', set the negation flag and skip it. */ + + if (ptr + 1 >= patternEnd) { + *errorCodePtr = ERR6; + return false; + } + + if (ptr[1] == '^') { + negateClass = true; + ++ptr; + } else + negateClass = false; + + /* Keep a count of chars with values < 256 so that we can optimize the case + of just a single character (as long as it's < 256). For higher valued UTF-8 + characters, we don't yet do any optimization. */ + + classCharCount = 0; + classLastChar = -1; + + class_utf8 = false; /* No chars >= 256 */ + class_utf8data = code + LINK_SIZE + 34; /* For UTF-8 items */ + + /* Initialize the 32-char bit map to all zeros. We have to build the + map in a temporary bit of store, in case the class contains only 1 + character (< 256), because in that case the compiled code doesn't use the + bit map. */ + + memset(classbits, 0, 32 * sizeof(unsigned char)); + + /* Process characters until ] is reached. The first pass + through the regex checked the overall syntax, so we don't need to be very + strict here. At the start of the loop, c contains the first byte of the + character. */ + + while ((++ptr < patternEnd) && (c = *ptr) != ']') { + /* Backslash may introduce a single character, or it may introduce one + of the specials, which just set a flag. Escaped items are checked for + validity in the pre-compiling pass. The sequence \b is a special case. + Inside a class (and only there) it is treated as backspace. Elsewhere + it marks a word boundary. Other escapes have preset maps ready to + or into the one we are building. We assume they have more than one + character in them, so set classCharCount bigger than one. */ + + if (c == '\\') { + c = checkEscape(&ptr, patternEnd, errorCodePtr, cd.numCapturingBrackets, true); + if (c < 0) { + classCharCount += 2; /* Greater than 1 is what matters */ + switch (-c) { + case ESC_d: + for (c = 0; c < 32; c++) + classbits[c] |= classBitmapForChar(c + cbit_digit); + continue; + + case ESC_D: + shouldFlipNegation = true; + for (c = 0; c < 32; c++) + classbits[c] |= ~classBitmapForChar(c + cbit_digit); + continue; + + case ESC_w: + for (c = 0; c < 32; c++) + classbits[c] |= classBitmapForChar(c + cbit_word); + continue; + + case ESC_W: + shouldFlipNegation = true; + for (c = 0; c < 32; c++) + classbits[c] |= ~classBitmapForChar(c + cbit_word); + continue; + + case ESC_s: + for (c = 0; c < 32; c++) + classbits[c] |= classBitmapForChar(c + cbit_space); + continue; + + case ESC_S: + shouldFlipNegation = true; + for (c = 0; c < 32; c++) + classbits[c] |= ~classBitmapForChar(c + cbit_space); + continue; + + /* Unrecognized escapes are faulted if PCRE is running in its + strict mode. By default, for compatibility with Perl, they are + treated as literals. */ + + default: + c = *ptr; /* The final character */ + classCharCount -= 2; /* Undo the default count from above */ + } + } + + /* Fall through if we have a single character (c >= 0). This may be + > 256 in UTF-8 mode. */ + + } /* End of backslash handling */ + + /* A single character may be followed by '-' to form a range. However, + Perl does not permit ']' to be the end of the range. A '-' character + here is treated as a literal. */ + + if ((ptr + 2 < patternEnd) && ptr[1] == '-' && ptr[2] != ']') { + ptr += 2; + + int d = *ptr; + + /* The second part of a range can be a single-character escape, but + not any of the other escapes. Perl 5.6 treats a hyphen as a literal + in such circumstances. */ + + if (d == '\\') { + const UChar* oldptr = ptr; + d = checkEscape(&ptr, patternEnd, errorCodePtr, cd.numCapturingBrackets, true); + + /* \X is literal X; any other special means the '-' was literal */ + if (d < 0) { + ptr = oldptr - 2; + goto LONE_SINGLE_CHARACTER; /* A few lines below */ + } + } + + /* The check that the two values are in the correct order happens in + the pre-pass. Optimize one-character ranges */ + + if (d == c) + goto LONE_SINGLE_CHARACTER; /* A few lines below */ + + /* In UTF-8 mode, if the upper limit is > 255, or > 127 for caseless + matching, we have to use an XCLASS with extra data items. Caseless + matching for characters > 127 is available only if UCP support is + available. */ + + if ((d > 255 || ((options & IgnoreCaseOption) && d > 127))) { + class_utf8 = true; + + /* With UCP support, we can find the other case equivalents of + the relevant characters. There may be several ranges. Optimize how + they fit with the basic range. */ + + if (options & IgnoreCaseOption) { + int occ, ocd; + int cc = c; + int origd = d; + while (getOthercaseRange(&cc, origd, &occ, &ocd)) { + if (occ >= c && ocd <= d) + continue; /* Skip embedded ranges */ + + if (occ < c && ocd >= c - 1) /* Extend the basic range */ + { /* if there is overlap, */ + c = occ; /* noting that if occ < c */ + continue; /* we can't have ocd > d */ + } /* because a subrange is */ + if (ocd > d && occ <= d + 1) /* always shorter than */ + { /* the basic range. */ + d = ocd; + continue; + } + + if (occ == ocd) + *class_utf8data++ = XCL_SINGLE; + else { + *class_utf8data++ = XCL_RANGE; + class_utf8data += encodeUTF8(occ, class_utf8data); + } + class_utf8data += encodeUTF8(ocd, class_utf8data); + } + } + + /* Now record the original range, possibly modified for UCP caseless + overlapping ranges. */ + + *class_utf8data++ = XCL_RANGE; + class_utf8data += encodeUTF8(c, class_utf8data); + class_utf8data += encodeUTF8(d, class_utf8data); + + /* With UCP support, we are done. Without UCP support, there is no + caseless matching for UTF-8 characters > 127; we can use the bit map + for the smaller ones. */ + + continue; /* With next character in the class */ + } + + /* We use the bit map for all cases when not in UTF-8 mode; else + ranges that lie entirely within 0-127 when there is UCP support; else + for partial ranges without UCP support. */ + + for (; c <= d; c++) { + classbits[c/8] |= (1 << (c&7)); + if (options & IgnoreCaseOption) { + int uc = flipCase(c); + classbits[uc/8] |= (1 << (uc&7)); + } + classCharCount++; /* in case a one-char range */ + classLastChar = c; + } + + continue; /* Go get the next char in the class */ + } + + /* Handle a lone single character - we can get here for a normal + non-escape char, or after \ that introduces a single character or for an + apparent range that isn't. */ + + LONE_SINGLE_CHARACTER: + + /* Handle a character that cannot go in the bit map */ + + if ((c > 255 || ((options & IgnoreCaseOption) && c > 127))) { + class_utf8 = true; + *class_utf8data++ = XCL_SINGLE; + class_utf8data += encodeUTF8(c, class_utf8data); + + if (options & IgnoreCaseOption) { + int othercase; + if ((othercase = jsc_pcre_ucp_othercase(c)) >= 0) { + *class_utf8data++ = XCL_SINGLE; + class_utf8data += encodeUTF8(othercase, class_utf8data); + } + } + } else { + /* Handle a single-byte character */ + classbits[c/8] |= (1 << (c&7)); + if (options & IgnoreCaseOption) { + c = flipCase(c); + classbits[c/8] |= (1 << (c&7)); + } + classCharCount++; + classLastChar = c; + } + } + + /* If classCharCount is 1, we saw precisely one character whose value is + less than 256. In non-UTF-8 mode we can always optimize. In UTF-8 mode, we + can optimize the negative case only if there were no characters >= 128 + because OP_NOT and the related opcodes like OP_NOTSTAR operate on + single-bytes only. This is an historical hangover. Maybe one day we can + tidy these opcodes to handle multi-byte characters. + + The optimization throws away the bit map. We turn the item into a + 1-character OP_CHAR[NC] if it's positive, or OP_NOT if it's negative. Note + that OP_NOT does not support multibyte characters. In the positive case, it + can cause firstByte to be set. Otherwise, there can be no first char if + this item is first, whatever repeat count may follow. In the case of + reqByte, save the previous value for reinstating. */ + + if (classCharCount == 1 && (!class_utf8 && (!negateClass || classLastChar < 128))) { + zeroReqByte = reqByte; + + /* The OP_NOT opcode works on one-byte characters only. */ + + if (negateClass) { + if (firstByte == REQ_UNSET) + firstByte = REQ_NONE; + zeroFirstByte = firstByte; + *code++ = OP_NOT; + *code++ = classLastChar; + break; + } + + /* For a single, positive character, get the value into c, and + then we can handle this with the normal one-character code. */ + + c = classLastChar; + goto NORMAL_CHAR; + } /* End of 1-char optimization */ + + /* The general case - not the one-char optimization. If this is the first + thing in the branch, there can be no first char setting, whatever the + repeat count. Any reqByte setting must remain unchanged after any kind of + repeat. */ + + if (firstByte == REQ_UNSET) firstByte = REQ_NONE; + zeroFirstByte = firstByte; + zeroReqByte = reqByte; + + /* If there are characters with values > 255, we have to compile an + extended class, with its own opcode. If there are no characters < 256, + we can omit the bitmap. */ + + if (class_utf8 && !shouldFlipNegation) { + *class_utf8data++ = XCL_END; /* Marks the end of extra data */ + *code++ = OP_XCLASS; + code += LINK_SIZE; + *code = negateClass? XCL_NOT : 0; + + /* If the map is required, install it, and move on to the end of + the extra data */ + + if (classCharCount > 0) { + *code++ |= XCL_MAP; + memcpy(code, classbits, 32); + code = class_utf8data; + } + + /* If the map is not required, slide down the extra data. */ + + else { + int len = class_utf8data - (code + 33); + memmove(code + 1, code + 33, len); + code += len + 1; + } + + /* Now fill in the complete length of the item */ + + putLinkValue(previous + 1, code - previous); + break; /* End of class handling */ + } + + /* If there are no characters > 255, negate the 32-byte map if necessary, + and copy it into the code vector. If this is the first thing in the branch, + there can be no first char setting, whatever the repeat count. Any reqByte + setting must remain unchanged after any kind of repeat. */ + + *code++ = (negateClass == shouldFlipNegation) ? OP_CLASS : OP_NCLASS; + if (negateClass) + for (c = 0; c < 32; c++) + code[c] = ~classbits[c]; + else + memcpy(code, classbits, 32); + code += 32; + break; + } + + /* Various kinds of repeat; '{' is not necessarily a quantifier, but this + has been tested above. */ + + case '{': + if (!isQuantifier) + goto NORMAL_CHAR; + ptr = readRepeatCounts(ptr + 1, &repeatMin, &repeat_max, errorCodePtr); + if (*errorCodePtr) + goto FAILED; + goto REPEAT; + + case '*': + repeatMin = 0; + repeat_max = -1; + goto REPEAT; + + case '+': + repeatMin = 1; + repeat_max = -1; + goto REPEAT; + + case '?': + repeatMin = 0; + repeat_max = 1; + + REPEAT: + if (!previous) { + *errorCodePtr = ERR9; + goto FAILED; + } + + if (repeatMin == 0) { + firstByte = zeroFirstByte; /* Adjust for zero repeat */ + reqByte = zeroReqByte; /* Ditto */ + } + + /* Remember whether this is a variable length repeat */ + + reqvary = (repeatMin == repeat_max) ? 0 : REQ_VARY; + + opType = 0; /* Default single-char op codes */ + + /* Save start of previous item, in case we have to move it up to make space + for an inserted OP_ONCE for the additional '+' extension. */ + /* FIXME: Probably don't need this because we don't use OP_ONCE. */ + + tempcode = previous; + + /* If the next character is '+', we have a possessive quantifier. This + implies greediness, whatever the setting of the PCRE_UNGREEDY option. + If the next character is '?' this is a minimizing repeat, by default, + but if PCRE_UNGREEDY is set, it works the other way round. We change the + repeat type to the non-default. */ + + if (safelyCheckNextChar(ptr, patternEnd, '?')) { + repeatType = 1; + ptr++; + } else + repeatType = 0; + + /* If previous was a character match, abolish the item and generate a + repeat item instead. If a char item has a minumum of more than one, ensure + that it is set in reqByte - it might not be if a sequence such as x{3} is + the first thing in a branch because the x will have gone into firstByte + instead. */ + + if (*previous == OP_CHAR || *previous == OP_CHAR_IGNORING_CASE) { + /* Deal with UTF-8 characters that take up more than one byte. It's + easier to write this out separately than try to macrify it. Use c to + hold the length of the character in bytes, plus 0x80 to flag that it's a + length rather than a small character. */ + + if (code[-1] & 0x80) { + unsigned char *lastchar = code - 1; + while((*lastchar & 0xc0) == 0x80) + lastchar--; + c = code - lastchar; /* Length of UTF-8 character */ + memcpy(utf8_char, lastchar, c); /* Save the char */ + c |= 0x80; /* Flag c as a length */ + } + else { + c = code[-1]; + if (repeatMin > 1) + reqByte = c | reqCaseOpt | cd.reqVaryOpt; + } + + goto OUTPUT_SINGLE_REPEAT; /* Code shared with single character types */ + } + + else if (*previous == OP_ASCII_CHAR || *previous == OP_ASCII_LETTER_IGNORING_CASE) { + c = previous[1]; + if (repeatMin > 1) + reqByte = c | reqCaseOpt | cd.reqVaryOpt; + goto OUTPUT_SINGLE_REPEAT; + } + + /* If previous was a single negated character ([^a] or similar), we use + one of the special opcodes, replacing it. The code is shared with single- + character repeats by setting opt_type to add a suitable offset into + repeatType. OP_NOT is currently used only for single-byte chars. */ + + else if (*previous == OP_NOT) { + opType = OP_NOTSTAR - OP_STAR; /* Use "not" opcodes */ + c = previous[1]; + goto OUTPUT_SINGLE_REPEAT; + } + + /* If previous was a character type match (\d or similar), abolish it and + create a suitable repeat item. The code is shared with single-character + repeats by setting opType to add a suitable offset into repeatType. */ + + else if (*previous <= OP_NOT_NEWLINE) { + opType = OP_TYPESTAR - OP_STAR; /* Use type opcodes */ + c = *previous; + + OUTPUT_SINGLE_REPEAT: + int prop_type = -1; + int prop_value = -1; + + unsigned char* oldcode = code; + code = previous; /* Usually overwrite previous item */ + + /* If the maximum is zero then the minimum must also be zero; Perl allows + this case, so we do too - by simply omitting the item altogether. */ + + if (repeat_max == 0) + goto END_REPEAT; + + /* Combine the opType with the repeatType */ + + repeatType += opType; + + /* A minimum of zero is handled either as the special case * or ?, or as + an UPTO, with the maximum given. */ + + if (repeatMin == 0) { + if (repeat_max == -1) + *code++ = OP_STAR + repeatType; + else if (repeat_max == 1) + *code++ = OP_QUERY + repeatType; + else { + *code++ = OP_UPTO + repeatType; + put2ByteValueAndAdvance(code, repeat_max); + } + } + + /* A repeat minimum of 1 is optimized into some special cases. If the + maximum is unlimited, we use OP_PLUS. Otherwise, the original item it + left in place and, if the maximum is greater than 1, we use OP_UPTO with + one less than the maximum. */ + + else if (repeatMin == 1) { + if (repeat_max == -1) + *code++ = OP_PLUS + repeatType; + else { + code = oldcode; /* leave previous item in place */ + if (repeat_max == 1) + goto END_REPEAT; + *code++ = OP_UPTO + repeatType; + put2ByteValueAndAdvance(code, repeat_max - 1); + } + } + + /* The case {n,n} is just an EXACT, while the general case {n,m} is + handled as an EXACT followed by an UPTO. */ + + else { + *code++ = OP_EXACT + opType; /* NB EXACT doesn't have repeatType */ + put2ByteValueAndAdvance(code, repeatMin); + + /* If the maximum is unlimited, insert an OP_STAR. Before doing so, + we have to insert the character for the previous code. For a repeated + Unicode property match, there are two extra bytes that define the + required property. In UTF-8 mode, long characters have their length in + c, with the 0x80 bit as a flag. */ + + if (repeat_max < 0) { + if (c >= 128) { + memcpy(code, utf8_char, c & 7); + code += c & 7; + } else { + *code++ = c; + if (prop_type >= 0) { + *code++ = prop_type; + *code++ = prop_value; + } + } + *code++ = OP_STAR + repeatType; + } + + /* Else insert an UPTO if the max is greater than the min, again + preceded by the character, for the previously inserted code. */ + + else if (repeat_max != repeatMin) { + if (c >= 128) { + memcpy(code, utf8_char, c & 7); + code += c & 7; + } else + *code++ = c; + if (prop_type >= 0) { + *code++ = prop_type; + *code++ = prop_value; + } + repeat_max -= repeatMin; + *code++ = OP_UPTO + repeatType; + put2ByteValueAndAdvance(code, repeat_max); + } + } + + /* The character or character type itself comes last in all cases. */ + + if (c >= 128) { + memcpy(code, utf8_char, c & 7); + code += c & 7; + } else + *code++ = c; + + /* For a repeated Unicode property match, there are two extra bytes that + define the required property. */ + + if (prop_type >= 0) { + *code++ = prop_type; + *code++ = prop_value; + } + } + + /* If previous was a character class or a back reference, we put the repeat + stuff after it, but just skip the item if the repeat was {0,0}. */ + + else if (*previous == OP_CLASS || + *previous == OP_NCLASS || + *previous == OP_XCLASS || + *previous == OP_REF) + { + if (repeat_max == 0) { + code = previous; + goto END_REPEAT; + } + + if (repeatMin == 0 && repeat_max == -1) + *code++ = OP_CRSTAR + repeatType; + else if (repeatMin == 1 && repeat_max == -1) + *code++ = OP_CRPLUS + repeatType; + else if (repeatMin == 0 && repeat_max == 1) + *code++ = OP_CRQUERY + repeatType; + else { + *code++ = OP_CRRANGE + repeatType; + put2ByteValueAndAdvance(code, repeatMin); + if (repeat_max == -1) + repeat_max = 0; /* 2-byte encoding for max */ + put2ByteValueAndAdvance(code, repeat_max); + } + } + + /* If previous was a bracket group, we may have to replicate it in certain + cases. */ + + else if (*previous >= OP_BRA) { + int ketoffset = 0; + int len = code - previous; + unsigned char* bralink = NULL; + int nested = get2ByteValue(previous + 1 + LINK_SIZE); + + /* If the maximum repeat count is unlimited, find the end of the bracket + by scanning through from the start, and compute the offset back to it + from the current code pointer. There may be an OP_OPT setting following + the final KET, so we can't find the end just by going back from the code + pointer. */ + + if (repeat_max == -1) { + const unsigned char* ket = previous; + advanceToEndOfBracket(ket); + ketoffset = code - ket; + } + + /* The case of a zero minimum is special because of the need to stick + OP_BRAZERO in front of it, and because the group appears once in the + data, whereas in other cases it appears the minimum number of times. For + this reason, it is simplest to treat this case separately, as otherwise + the code gets far too messy. There are several special subcases when the + minimum is zero. */ + + if (repeatMin == 0) { + /* If the maximum is also zero, we just omit the group from the output + altogether. */ + + if (repeat_max == 0) { + code = previous; + goto END_REPEAT; + } + + /* If the maximum is 1 or unlimited, we just have to stick in the + BRAZERO and do no more at this point. However, we do need to adjust + any OP_RECURSE calls inside the group that refer to the group itself or + any internal group, because the offset is from the start of the whole + regex. Temporarily terminate the pattern while doing this. */ + + if (repeat_max <= 1) { + *code = OP_END; + memmove(previous+1, previous, len); + code++; + *previous++ = OP_BRAZERO + repeatType; + } + + /* If the maximum is greater than 1 and limited, we have to replicate + in a nested fashion, sticking OP_BRAZERO before each set of brackets. + The first one has to be handled carefully because it's the original + copy, which has to be moved up. The remainder can be handled by code + that is common with the non-zero minimum case below. We have to + adjust the value of repeat_max, since one less copy is required. */ + + else { + *code = OP_END; + memmove(previous + 4 + LINK_SIZE, previous, len); + code += 4 + LINK_SIZE; + *previous++ = OP_BRAZERO + repeatType; + *previous++ = OP_BRA; + + /* We chain together the bracket offset fields that have to be + filled in later when the ends of the brackets are reached. */ + + int offset = (!bralink) ? 0 : previous - bralink; + bralink = previous; + putLinkValueAllowZeroAndAdvance(previous, offset); + put2ByteValueAndAdvance(previous, nested); + } + + repeat_max--; + } + + /* If the minimum is greater than zero, replicate the group as many + times as necessary, and adjust the maximum to the number of subsequent + copies that we need. If we set a first char from the group, and didn't + set a required char, copy the latter from the former. */ + + else { + if (repeatMin > 1) { + if (didGroupSetFirstByte && reqByte < 0) + reqByte = firstByte; + for (int i = 1; i < repeatMin; i++) { + memcpy(code, previous, len); + code += len; + } + } + if (repeat_max > 0) + repeat_max -= repeatMin; + } + + /* This code is common to both the zero and non-zero minimum cases. If + the maximum is limited, it replicates the group in a nested fashion, + remembering the bracket starts on a stack. In the case of a zero minimum, + the first one was set up above. In all cases the repeat_max now specifies + the number of additional copies needed. */ + + if (repeat_max >= 0) { + for (int i = repeat_max - 1; i >= 0; i--) { + *code++ = OP_BRAZERO + repeatType; + + /* All but the final copy start a new nesting, maintaining the + chain of brackets outstanding. */ + + if (i != 0) { + *code++ = OP_BRA; + int offset = (!bralink) ? 0 : code - bralink; + bralink = code; + putLinkValueAllowZeroAndAdvance(code, offset); + put2ByteValueAndAdvance(code, nested); + } + + memcpy(code, previous, len); + code += len; + } + + /* Now chain through the pending brackets, and fill in their length + fields (which are holding the chain links pro tem). */ + + while (bralink) { + int offset = code - bralink + 1; + unsigned char* bra = code - offset; + int oldlinkoffset = getLinkValueAllowZero(bra + 1); + bralink = (!oldlinkoffset) ? 0 : bralink - oldlinkoffset; + *code++ = OP_KET; + putLinkValueAndAdvance(code, offset); + putLinkValue(bra + 1, offset); + } + } + + /* If the maximum is unlimited, set a repeater in the final copy. We + can't just offset backwards from the current code point, because we + don't know if there's been an options resetting after the ket. The + correct offset was computed above. */ + + else + code[-ketoffset] = OP_KETRMAX + repeatType; + } + + // A quantifier after an assertion is mostly meaningless, but it + // can nullify the assertion if it has a 0 minimum. + else if (*previous == OP_ASSERT || *previous == OP_ASSERT_NOT) { + if (repeatMin == 0) { + code = previous; + goto END_REPEAT; + } + } + + /* Else there's some kind of shambles */ + + else { + *errorCodePtr = ERR11; + goto FAILED; + } + + /* In all case we no longer have a previous item. We also set the + "follows varying string" flag for subsequently encountered reqbytes if + it isn't already set and we have just passed a varying length item. */ + + END_REPEAT: + previous = NULL; + cd.reqVaryOpt |= reqvary; + break; + + /* Start of nested bracket sub-expression, or comment or lookahead or + lookbehind or option setting or condition. First deal with special things + that can come after a bracket; all are introduced by ?, and the appearance + of any of them means that this is not a referencing group. They were + checked for validity in the first pass over the string, so we don't have to + check for syntax errors here. */ + + case '(': + { + skipBytes = 2; + unsigned minBracket = *brackets + 1; + if (*(++ptr) == '?') { + switch (*(++ptr)) { + case ':': /* Non-extracting bracket */ + bravalue = OP_BRA; + ptr++; + break; + + case '=': /* Positive lookahead */ + bravalue = OP_ASSERT; + ptr++; + break; + + case '!': /* Negative lookahead */ + bravalue = OP_ASSERT_NOT; + ptr++; + break; + + /* Character after (? not specially recognized */ + + default: + *errorCodePtr = ERR12; + goto FAILED; + } + } + + /* Else we have a referencing group; adjust the opcode. If the bracket + number is greater than EXTRACT_BASIC_MAX, we set the opcode one higher, and + arrange for the true number to follow later, in an OP_BRANUMBER item. */ + + else { + if (++(*brackets) > EXTRACT_BASIC_MAX) { + bravalue = OP_BRA + EXTRACT_BASIC_MAX + 1; + code[3 + LINK_SIZE] = OP_BRANUMBER; + put2ByteValue(code + 4 + LINK_SIZE, *brackets); + skipBytes = 5; + } + else + bravalue = OP_BRA + *brackets; + } + + /* Process nested bracketed re. We copy code into a non-variable + in order to be able to pass its address because some compilers + complain otherwise. Pass in a new setting for the ims options + if they have changed. */ + + previous = code; + *code = bravalue; + tempcode = code; + tempreqvary = cd.reqVaryOpt; /* Save value before bracket */ + { + unsigned bracketsBeforeRecursion = *brackets; + if (!compileBracket( + options, + brackets, /* Extracting bracket count */ + &tempcode, /* Where to put code (updated) */ + &ptr, /* Input pointer (updated) */ + patternEnd, + errorCodePtr, /* Where to put an error message */ + skipBytes, /* Skip over OP_BRANUMBER */ + &subFirstByte, /* For possible first char */ + &subReqByte, /* For possible last char */ + cd)) /* Tables block */ + goto FAILED; + unsigned enclosedBrackets = (*brackets - bracketsBeforeRecursion); + unsigned limitBracket = minBracket + enclosedBrackets + (bravalue > OP_BRA); + if (!((minBracket & 0xff) == minBracket && (limitBracket & 0xff) == limitBracket)) { + *errorCodePtr = ERR17; + return false; + } + JS_ASSERT(minBracket <= limitBracket); + put2ByteValue(code + 1 + LINK_SIZE, minBracket << 8 | limitBracket); + } + + /* At the end of compiling, code is still pointing to the start of the + group, while tempcode has been updated to point past the end of the group + and any option resetting that may follow it. The pattern pointer (ptr) + is on the bracket. */ + + /* Handle updating of the required and first characters. Update for normal + brackets of all kinds, and conditions with two branches (see code above). + If the bracket is followed by a quantifier with zero repeat, we have to + back off. Hence the definition of zeroReqByte and zeroFirstByte outside the + main loop so that they can be accessed for the back off. */ + + zeroReqByte = reqByte; + zeroFirstByte = firstByte; + didGroupSetFirstByte = false; + + if (bravalue >= OP_BRA) { + /* If we have not yet set a firstByte in this branch, take it from the + subpattern, remembering that it was set here so that a repeat of more + than one can replicate it as reqByte if necessary. If the subpattern has + no firstByte, set "none" for the whole branch. In both cases, a zero + repeat forces firstByte to "none". */ + + if (firstByte == REQ_UNSET) { + if (subFirstByte >= 0) { + firstByte = subFirstByte; + didGroupSetFirstByte = true; + } + else + firstByte = REQ_NONE; + zeroFirstByte = REQ_NONE; + } + + /* If firstByte was previously set, convert the subpattern's firstByte + into reqByte if there wasn't one, using the vary flag that was in + existence beforehand. */ + + else if (subFirstByte >= 0 && subReqByte < 0) + subReqByte = subFirstByte | tempreqvary; + + /* If the subpattern set a required byte (or set a first byte that isn't + really the first byte - see above), set it. */ + + if (subReqByte >= 0) + reqByte = subReqByte; + } + + /* For a forward assertion, we take the reqByte, if set. This can be + helpful if the pattern that follows the assertion doesn't set a different + char. For example, it's useful for /(?=abcde).+/. We can't set firstByte + for an assertion, however because it leads to incorrect effect for patterns + such as /(?=a)a.+/ when the "real" "a" would then become a reqByte instead + of a firstByte. This is overcome by a scan at the end if there's no + firstByte, looking for an asserted first char. */ + + else if (bravalue == OP_ASSERT && subReqByte >= 0) + reqByte = subReqByte; + + /* Now update the main code pointer to the end of the group. */ + + code = tempcode; + + /* Error if hit end of pattern */ + + if (ptr >= patternEnd || *ptr != ')') { + *errorCodePtr = ERR14; + goto FAILED; + } + break; + + } + /* Check \ for being a real metacharacter; if not, fall through and handle + it as a data character at the start of a string. Escape items are checked + for validity in the pre-compiling pass. */ + + case '\\': + tempptr = ptr; + c = checkEscape(&ptr, patternEnd, errorCodePtr, cd.numCapturingBrackets, false); + + /* Handle metacharacters introduced by \. For ones like \d, the ESC_ values + are arranged to be the negation of the corresponding OP_values. For the + back references, the values are ESC_REF plus the reference number. Only + back references and those types that consume a character may be repeated. + We can test for values between ESC_b and ESC_w for the latter; this may + have to change if any new ones are ever created. */ + + if (c < 0) { + /* For metasequences that actually match a character, we disable the + setting of a first character if it hasn't already been set. */ + + if (firstByte == REQ_UNSET && -c > ESC_b && -c <= ESC_w) + firstByte = REQ_NONE; + + /* Set values to reset to if this is followed by a zero repeat. */ + + zeroFirstByte = firstByte; + zeroReqByte = reqByte; + + /* Back references are handled specially */ + + if (-c >= ESC_REF) { + int number = -c - ESC_REF; + previous = code; + *code++ = OP_REF; + put2ByteValueAndAdvance(code, number); + } + + /* For the rest, we can obtain the OP value by negating the escape + value */ + + else { + previous = (-c > ESC_b && -c <= ESC_w) ? code : NULL; + *code++ = -c; + } + continue; + } + + /* Fall through. */ + + /* Handle a literal character. It is guaranteed not to be whitespace or # + when the extended flag is set. If we are in UTF-8 mode, it may be a + multi-byte literal character. */ + + default: + NORMAL_CHAR: + + previous = code; + + if (c < 128) { + mcLength = 1; + mcbuffer[0] = c; + + if ((options & IgnoreCaseOption) && (c | 0x20) >= 'a' && (c | 0x20) <= 'z') { + *code++ = OP_ASCII_LETTER_IGNORING_CASE; + *code++ = c | 0x20; + } else { + *code++ = OP_ASCII_CHAR; + *code++ = c; + } + } else { + mcLength = encodeUTF8(c, mcbuffer); + + *code++ = (options & IgnoreCaseOption) ? OP_CHAR_IGNORING_CASE : OP_CHAR; + for (c = 0; c < mcLength; c++) + *code++ = mcbuffer[c]; + } + + /* Set the first and required bytes appropriately. If no previous first + byte, set it from this character, but revert to none on a zero repeat. + Otherwise, leave the firstByte value alone, and don't change it on a zero + repeat. */ + + if (firstByte == REQ_UNSET) { + zeroFirstByte = REQ_NONE; + zeroReqByte = reqByte; + + /* If the character is more than one byte long, we can set firstByte + only if it is not to be matched caselessly. */ + + if (mcLength == 1 || reqCaseOpt == 0) { + firstByte = mcbuffer[0] | reqCaseOpt; + if (mcLength != 1) + reqByte = code[-1] | cd.reqVaryOpt; + } + else + firstByte = reqByte = REQ_NONE; + } + + /* firstByte was previously set; we can set reqByte only the length is + 1 or the matching is caseful. */ + + else { + zeroFirstByte = firstByte; + zeroReqByte = reqByte; + if (mcLength == 1 || reqCaseOpt == 0) + reqByte = code[-1] | reqCaseOpt | cd.reqVaryOpt; + } + + break; /* End of literal character handling */ + } + } /* end of big loop */ + + /* Control never reaches here by falling through, only by a goto for all the + error states. Pass back the position in the pattern so that it can be displayed + to the user for diagnosing the error. */ + +FAILED: + *ptrPtr = ptr; + return false; +} + +/************************************************* +* Compile sequence of alternatives * +*************************************************/ + +/* On entry, ptr is pointing past the bracket character, but on return +it points to the closing bracket, or vertical bar, or end of string. +The code variable is pointing at the byte into which the BRA operator has been +stored. If the ims options are changed at the start (for a (?ims: group) or +during any branch, we need to insert an OP_OPT item at the start of every +following branch to ensure they get set correctly at run time, and also pass +the new options into every subsequent branch compile. + +Argument: + options option bits, including any changes for this subpattern + brackets -> int containing the number of extracting brackets used + codePtr -> the address of the current code pointer + ptrPtr -> the address of the current pattern pointer + errorCodePtr -> pointer to error code variable + skipBytes skip this many bytes at start (for OP_BRANUMBER) + firstbyteptr place to put the first required character, or a negative number + reqbyteptr place to put the last required character, or a negative number + cd points to the data block with tables pointers etc. + +Returns: true on success +*/ + +static bool +compileBracket(int options, int* brackets, unsigned char** codePtr, + const UChar** ptrPtr, const UChar* patternEnd, ErrorCode* errorCodePtr, int skipBytes, + int* firstbyteptr, int* reqbyteptr, CompileData& cd) +{ + const UChar* ptr = *ptrPtr; + unsigned char* code = *codePtr; + unsigned char* lastBranch = code; + unsigned char* start_bracket = code; + int firstByte = REQ_UNSET; + int reqByte = REQ_UNSET; + + /* Offset is set zero to mark that this bracket is still open */ + + putLinkValueAllowZero(code + 1, 0); + code += 1 + LINK_SIZE + skipBytes; + + /* Loop for each alternative branch */ + + while (true) { + /* Now compile the branch */ + + int branchFirstByte; + int branchReqByte; + if (!compileBranch(options, brackets, &code, &ptr, patternEnd, errorCodePtr, + &branchFirstByte, &branchReqByte, cd)) { + *ptrPtr = ptr; + return false; + } + + /* If this is the first branch, the firstByte and reqByte values for the + branch become the values for the regex. */ + + if (*lastBranch != OP_ALT) { + firstByte = branchFirstByte; + reqByte = branchReqByte; + } + + /* If this is not the first branch, the first char and reqByte have to + match the values from all the previous branches, except that if the previous + value for reqByte didn't have REQ_VARY set, it can still match, and we set + REQ_VARY for the regex. */ + + else { + /* If we previously had a firstByte, but it doesn't match the new branch, + we have to abandon the firstByte for the regex, but if there was previously + no reqByte, it takes on the value of the old firstByte. */ + + if (firstByte >= 0 && firstByte != branchFirstByte) { + if (reqByte < 0) + reqByte = firstByte; + firstByte = REQ_NONE; + } + + /* If we (now or from before) have no firstByte, a firstByte from the + branch becomes a reqByte if there isn't a branch reqByte. */ + + if (firstByte < 0 && branchFirstByte >= 0 && branchReqByte < 0) + branchReqByte = branchFirstByte; + + /* Now ensure that the reqbytes match */ + + if ((reqByte & ~REQ_VARY) != (branchReqByte & ~REQ_VARY)) + reqByte = REQ_NONE; + else + reqByte |= branchReqByte; /* To "or" REQ_VARY */ + } + + /* Reached end of expression, either ')' or end of pattern. Go back through + the alternative branches and reverse the chain of offsets, with the field in + the BRA item now becoming an offset to the first alternative. If there are + no alternatives, it points to the end of the group. The length in the + terminating ket is always the length of the whole bracketed item. + Return leaving the pointer at the terminating char. */ + + if (ptr >= patternEnd || *ptr != '|') { + int length = code - lastBranch; + do { + int prevLength = getLinkValueAllowZero(lastBranch + 1); + putLinkValue(lastBranch + 1, length); + length = prevLength; + lastBranch -= length; + } while (length > 0); + + /* Fill in the ket */ + + *code = OP_KET; + putLinkValue(code + 1, code - start_bracket); + code += 1 + LINK_SIZE; + + /* Set values to pass back */ + + *codePtr = code; + *ptrPtr = ptr; + *firstbyteptr = firstByte; + *reqbyteptr = reqByte; + return true; + } + + /* Another branch follows; insert an "or" node. Its length field points back + to the previous branch while the bracket remains open. At the end the chain + is reversed. It's done like this so that the start of the bracket has a + zero offset until it is closed, making it possible to detect recursion. */ + + *code = OP_ALT; + putLinkValue(code + 1, code - lastBranch); + lastBranch = code; + code += 1 + LINK_SIZE; + ptr++; + } + JS_NOT_REACHED("No fallthru."); +} + +/************************************************* +* Check for anchored expression * +*************************************************/ + +/* Try to find out if this is an anchored regular expression. Consider each +alternative branch. If they all start OP_CIRC, or with a bracket +all of whose alternatives start OP_CIRC (recurse ad lib), then +it's anchored. + +Arguments: + code points to start of expression (the bracket) + captureMap a bitmap of which brackets we are inside while testing; this + handles up to substring 31; all brackets after that share + the zero bit + backrefMap the back reference bitmap +*/ + +static bool branchIsAnchored(const unsigned char* code) +{ + const unsigned char* scode = firstSignificantOpcode(code); + int op = *scode; + + /* Brackets */ + if (op >= OP_BRA || op == OP_ASSERT) + return bracketIsAnchored(scode); + + /* Check for explicit anchoring */ + return op == OP_CIRC; +} + +static bool bracketIsAnchored(const unsigned char* code) +{ + do { + if (!branchIsAnchored(code + 1 + LINK_SIZE)) + return false; + code += getLinkValue(code + 1); + } while (*code == OP_ALT); /* Loop for each alternative */ + return true; +} + +/************************************************* +* Check for starting with ^ or .* * +*************************************************/ + +/* This is called to find out if every branch starts with ^ or .* so that +"first char" processing can be done to speed things up in multiline +matching and for non-DOTALL patterns that start with .* (which must start at +the beginning or after \n) + +Except when the .* appears inside capturing parentheses, and there is a +subsequent back reference to those parentheses. By keeping a bitmap of the +first 31 back references, we can catch some of the more common cases more +precisely; all the greater back references share a single bit. + +Arguments: + code points to start of expression (the bracket) + captureMap a bitmap of which brackets we are inside while testing; this + handles up to substring 31; all brackets after that share + the zero bit + backrefMap the back reference bitmap +*/ + +static bool branchNeedsLineStart(const unsigned char* code, unsigned captureMap, unsigned backrefMap) +{ + const unsigned char* scode = firstSignificantOpcode(code); + int op = *scode; + + /* Capturing brackets */ + if (op > OP_BRA) { + int captureNum = op - OP_BRA; + if (captureNum > EXTRACT_BASIC_MAX) + captureNum = get2ByteValue(scode + 2 + LINK_SIZE); + int bracketMask = (captureNum < 32) ? (1 << captureNum) : 1; + return bracketNeedsLineStart(scode, captureMap | bracketMask, backrefMap); + } + + /* Other brackets */ + if (op == OP_BRA || op == OP_ASSERT) + return bracketNeedsLineStart(scode, captureMap, backrefMap); + + /* .* means "start at start or after \n" if it isn't in brackets that + may be referenced. */ + + if (op == OP_TYPESTAR || op == OP_TYPEMINSTAR) + return scode[1] == OP_NOT_NEWLINE && !(captureMap & backrefMap); + + /* Explicit ^ */ + return op == OP_CIRC || op == OP_BOL; +} + +static bool bracketNeedsLineStart(const unsigned char* code, unsigned captureMap, unsigned backrefMap) +{ + do { + if (!branchNeedsLineStart(code + 1 + LINK_SIZE, captureMap, backrefMap)) + return false; + code += getLinkValue(code + 1); + } while (*code == OP_ALT); /* Loop for each alternative */ + return true; +} + +/************************************************* +* Check for asserted fixed first char * +*************************************************/ + +/* During compilation, the "first char" settings from forward assertions are +discarded, because they can cause conflicts with actual literals that follow. +However, if we end up without a first char setting for an unanchored pattern, +it is worth scanning the regex to see if there is an initial asserted first +char. If all branches start with the same asserted char, or with a bracket all +of whose alternatives start with the same asserted char (recurse ad lib), then +we return that char, otherwise -1. + +Arguments: + code points to start of expression (the bracket) + options pointer to the options (used to check casing changes) + inassert true if in an assertion + +Returns: -1 or the fixed first char +*/ + +static int branchFindFirstAssertedCharacter(const unsigned char* code, bool inassert) +{ + const unsigned char* scode = firstSignificantOpcodeSkippingAssertions(code); + int op = *scode; + + if (op >= OP_BRA) + op = OP_BRA; + + switch (op) { + default: + return -1; + + case OP_BRA: + case OP_ASSERT: + return bracketFindFirstAssertedCharacter(scode, op == OP_ASSERT); + + case OP_EXACT: + scode += 2; + /* Fall through */ + + case OP_CHAR: + case OP_CHAR_IGNORING_CASE: + case OP_ASCII_CHAR: + case OP_ASCII_LETTER_IGNORING_CASE: + case OP_PLUS: + case OP_MINPLUS: + if (!inassert) + return -1; + return scode[1]; + } +} + +static int bracketFindFirstAssertedCharacter(const unsigned char* code, bool inassert) +{ + int c = -1; + do { + int d = branchFindFirstAssertedCharacter(code + 1 + LINK_SIZE, inassert); + if (d < 0) + return -1; + if (c < 0) + c = d; + else if (c != d) + return -1; + code += getLinkValue(code + 1); + } while (*code == OP_ALT); + return c; +} + +static inline int multiplyWithOverflowCheck(int a, int b) +{ + if (!a || !b) + return 0; + if (a > MAX_PATTERN_SIZE / b) + return -1; + return a * b; +} + +static int calculateCompiledPatternLength(const UChar* pattern, int patternLength, JSRegExpIgnoreCaseOption ignoreCase, + CompileData& cd, ErrorCode& errorcode) +{ + /* Make a pass over the pattern to compute the + amount of store required to hold the compiled code. This does not have to be + perfect as long as errors are overestimates. */ + + if (patternLength > MAX_PATTERN_SIZE) { + errorcode = ERR16; + return -1; + } + + int length = BRA_LEN; /* For initial BRA. */ + int branch_extra = 0; + int lastitemlength = 0; + unsigned brastackptr = 0; + int brastack[BRASTACK_SIZE]; + unsigned char bralenstack[BRASTACK_SIZE]; + int bracount = 0; + + const UChar* ptr = (const UChar*)(pattern - 1); + const UChar* patternEnd = (const UChar*)(pattern + patternLength); + + while (++ptr < patternEnd) { + int minRepeats = 0, maxRepeats = 0; + int c = *ptr; + + switch (c) { + /* A backslashed item may be an escaped data character or it may be a + character type. */ + + case '\\': + c = checkEscape(&ptr, patternEnd, &errorcode, cd.numCapturingBrackets, false); + if (errorcode != 0) + return -1; + + lastitemlength = 1; /* Default length of last item for repeats */ + + if (c >= 0) { /* Data character */ + length += 2; /* For a one-byte character */ + + if (c > 127) { + int i; + for (i = 0; i < jsc_pcre_utf8_table1_size; i++) + if (c <= jsc_pcre_utf8_table1[i]) break; + length += i; + lastitemlength += i; + } + + continue; + } + + /* Other escapes need one byte */ + + length++; + + /* A back reference needs an additional 2 bytes, plus either one or 5 + bytes for a repeat. We also need to keep the value of the highest + back reference. */ + + if (c <= -ESC_REF) { + int refnum = -c - ESC_REF; + cd.backrefMap |= (refnum < 32) ? (1 << refnum) : 1; + if (refnum > cd.topBackref) + cd.topBackref = refnum; + length += 2; /* For single back reference */ + if (safelyCheckNextChar(ptr, patternEnd, '{') && isCountedRepeat(ptr + 2, patternEnd)) { + ptr = readRepeatCounts(ptr + 2, &minRepeats, &maxRepeats, &errorcode); + if (errorcode) + return -1; + if ((minRepeats == 0 && (maxRepeats == 1 || maxRepeats == -1)) || + (minRepeats == 1 && maxRepeats == -1)) + length++; + else + length += 5; + if (safelyCheckNextChar(ptr, patternEnd, '?')) + ptr++; + } + } + continue; + + case '^': /* Single-byte metacharacters */ + case '.': + case '$': + length++; + lastitemlength = 1; + continue; + + case '*': /* These repeats won't be after brackets; */ + case '+': /* those are handled separately */ + case '?': + length++; + goto POSSESSIVE; + + /* This covers the cases of braced repeats after a single char, metachar, + class, or back reference. */ + + case '{': + if (!isCountedRepeat(ptr + 1, patternEnd)) + goto NORMAL_CHAR; + ptr = readRepeatCounts(ptr + 1, &minRepeats, &maxRepeats, &errorcode); + if (errorcode != 0) + return -1; + + /* These special cases just insert one extra opcode */ + + if ((minRepeats == 0 && (maxRepeats == 1 || maxRepeats == -1)) || + (minRepeats == 1 && maxRepeats == -1)) + length++; + + /* These cases might insert additional copies of a preceding character. */ + + else { + if (minRepeats != 1) { + length -= lastitemlength; /* Uncount the original char or metachar */ + if (minRepeats > 0) + length += 5 + lastitemlength; + } + length += lastitemlength + ((maxRepeats > 0) ? 5 : 1); + } + + if (safelyCheckNextChar(ptr, patternEnd, '?')) + ptr++; /* Needs no extra length */ + + POSSESSIVE: /* Test for possessive quantifier */ + if (safelyCheckNextChar(ptr, patternEnd, '+')) { + ptr++; + length += 2 + 2 * LINK_SIZE; /* Allow for atomic brackets */ + } + continue; + + /* An alternation contains an offset to the next branch or ket. If any ims + options changed in the previous branch(es), and/or if we are in a + lookbehind assertion, extra space will be needed at the start of the + branch. This is handled by branch_extra. */ + + case '|': + if (brastackptr == 0) + cd.needOuterBracket = true; + length += 1 + LINK_SIZE + branch_extra; + continue; + + /* A character class uses 33 characters provided that all the character + values are less than 256. Otherwise, it uses a bit map for low valued + characters, and individual items for others. Don't worry about character + types that aren't allowed in classes - they'll get picked up during the + compile. A character class that contains only one single-byte character + uses 2 or 3 bytes, depending on whether it is negated or not. Notice this + where we can. (In UTF-8 mode we can do this only for chars < 128.) */ + + case '[': { + int class_optcount; + if (*(++ptr) == '^') { + class_optcount = 10; /* Greater than one */ + ptr++; + } + else + class_optcount = 0; + + bool class_utf8 = false; + + for (; ptr < patternEnd && *ptr != ']'; ++ptr) { + /* Check for escapes */ + + if (*ptr == '\\') { + c = checkEscape(&ptr, patternEnd, &errorcode, cd.numCapturingBrackets, true); + if (errorcode != 0) + return -1; + + /* Handle escapes that turn into characters */ + + if (c >= 0) + goto NON_SPECIAL_CHARACTER; + + /* Escapes that are meta-things. The normal ones just affect the + bit map, but Unicode properties require an XCLASS extended item. */ + + else + class_optcount = 10; /* \d, \s etc; make sure > 1 */ + } + + /* Anything else increments the possible optimization count. We have to + detect ranges here so that we can compute the number of extra ranges for + caseless wide characters when UCP support is available. If there are wide + characters, we are going to have to use an XCLASS, even for single + characters. */ + + else { + c = *ptr; + + /* Come here from handling \ above when it escapes to a char value */ + + NON_SPECIAL_CHARACTER: + class_optcount++; + + int d = -1; + if (safelyCheckNextChar(ptr, patternEnd, '-')) { + const UChar* hyptr = ptr++; + if (safelyCheckNextChar(ptr, patternEnd, '\\')) { + ptr++; + d = checkEscape(&ptr, patternEnd, &errorcode, cd.numCapturingBrackets, true); + if (errorcode != 0) + return -1; + } + else if ((ptr + 1 < patternEnd) && ptr[1] != ']') + d = *++ptr; + if (d < 0) + ptr = hyptr; /* go back to hyphen as data */ + } + + /* If d >= 0 we have a range. In UTF-8 mode, if the end is > 255, or > + 127 for caseless matching, we will need to use an XCLASS. */ + + if (d >= 0) { + class_optcount = 10; /* Ensure > 1 */ + if (d < c) { + errorcode = ERR8; + return -1; + } + + if ((d > 255 || (ignoreCase && d > 127))) { + unsigned char buffer[6]; + if (!class_utf8) /* Allow for XCLASS overhead */ + { + class_utf8 = true; + length += LINK_SIZE + 2; + } + + /* If we have UCP support, find out how many extra ranges are + needed to map the other case of characters within this range. We + have to mimic the range optimization here, because extending the + range upwards might push d over a boundary that makes it use + another byte in the UTF-8 representation. */ + + if (ignoreCase) { + int occ, ocd; + int cc = c; + int origd = d; + while (getOthercaseRange(&cc, origd, &occ, &ocd)) { + if (occ >= c && ocd <= d) + continue; /* Skip embedded */ + + if (occ < c && ocd >= c - 1) /* Extend the basic range */ + { /* if there is overlap, */ + c = occ; /* noting that if occ < c */ + continue; /* we can't have ocd > d */ + } /* because a subrange is */ + if (ocd > d && occ <= d + 1) /* always shorter than */ + { /* the basic range. */ + d = ocd; + continue; + } + + /* An extra item is needed */ + + length += 1 + encodeUTF8(occ, buffer) + + ((occ == ocd) ? 0 : encodeUTF8(ocd, buffer)); + } + } + + /* The length of the (possibly extended) range */ + + length += 1 + encodeUTF8(c, buffer) + encodeUTF8(d, buffer); + } + + } + + /* We have a single character. There is nothing to be done unless we + are in UTF-8 mode. If the char is > 255, or 127 when caseless, we must + allow for an XCL_SINGLE item, doubled for caselessness if there is UCP + support. */ + + else { + if ((c > 255 || (ignoreCase && c > 127))) { + unsigned char buffer[6]; + class_optcount = 10; /* Ensure > 1 */ + if (!class_utf8) /* Allow for XCLASS overhead */ + { + class_utf8 = true; + length += LINK_SIZE + 2; + } + length += (ignoreCase ? 2 : 1) * (1 + encodeUTF8(c, buffer)); + } + } + } + } + + if (ptr >= patternEnd) { /* Missing terminating ']' */ + errorcode = ERR6; + return -1; + } + + /* We can optimize when there was only one optimizable character. + Note that this does not detect the case of a negated single character. + In that case we do an incorrect length computation, but it's not a serious + problem because the computed length is too large rather than too small. */ + + if (class_optcount == 1) + goto NORMAL_CHAR; + + /* Here, we handle repeats for the class opcodes. */ + { + length += 33; + + /* A repeat needs either 1 or 5 bytes. If it is a possessive quantifier, + we also need extra for wrapping the whole thing in a sub-pattern. */ + + if (safelyCheckNextChar(ptr, patternEnd, '{') && isCountedRepeat(ptr + 2, patternEnd)) { + ptr = readRepeatCounts(ptr + 2, &minRepeats, &maxRepeats, &errorcode); + if (errorcode != 0) + return -1; + if ((minRepeats == 0 && (maxRepeats == 1 || maxRepeats == -1)) || + (minRepeats == 1 && maxRepeats == -1)) + length++; + else + length += 5; + if (safelyCheckNextChar(ptr, patternEnd, '+')) { + ptr++; + length += 2 + 2 * LINK_SIZE; + } else if (safelyCheckNextChar(ptr, patternEnd, '?')) + ptr++; + } + } + continue; + } + + /* Brackets may be genuine groups or special things */ + + case '(': { + int branch_newextra = 0; + int bracket_length = BRA_LEN; + bool capturing = false; + + /* Handle special forms of bracket, which all start (? */ + + if (safelyCheckNextChar(ptr, patternEnd, '?')) { + switch (c = (ptr + 2 < patternEnd ? ptr[2] : 0)) { + /* Non-referencing groups and lookaheads just move the pointer on, and + then behave like a non-special bracket, except that they don't increment + the count of extracting brackets. Ditto for the "once only" bracket, + which is in Perl from version 5.005. */ + + case ':': + case '=': + case '!': + ptr += 2; + break; + + /* Else loop checking valid options until ) is met. Anything else is an + error. If we are without any brackets, i.e. at top level, the settings + act as if specified in the options, so massage the options immediately. + This is for backward compatibility with Perl 5.004. */ + + default: + errorcode = ERR12; + return -1; + } + } else + capturing = true; + + /* Capturing brackets must be counted so we can process escapes in a + Perlish way. If the number exceeds EXTRACT_BASIC_MAX we are going to need + an additional 3 bytes of memory per capturing bracket. */ + + if (capturing) { + bracount++; + if (bracount > EXTRACT_BASIC_MAX) + bracket_length += 3; + } + + /* Save length for computing whole length at end if there's a repeat that + requires duplication of the group. Also save the current value of + branch_extra, and start the new group with the new value. If non-zero, this + will either be 2 for a (?imsx: group, or 3 for a lookbehind assertion. */ + + if (brastackptr >= sizeof(brastack)/sizeof(int)) { + errorcode = ERR17; + return -1; + } + + bralenstack[brastackptr] = branch_extra; + branch_extra = branch_newextra; + + brastack[brastackptr++] = length; + length += bracket_length; + continue; + } + + /* Handle ket. Look for subsequent maxRepeats/minRepeats; for certain sets of values we + have to replicate this bracket up to that many times. If brastackptr is + 0 this is an unmatched bracket which will generate an error, but take care + not to try to access brastack[-1] when computing the length and restoring + the branch_extra value. */ + + case ')': { + int duplength; + length += KET_LEN; + if (brastackptr > 0) { + duplength = length - brastack[--brastackptr]; + branch_extra = bralenstack[brastackptr]; + } + else + duplength = 0; + + /* Leave ptr at the final char; for readRepeatCounts this happens + automatically; for the others we need an increment. */ + + if ((ptr + 1 < patternEnd) && (c = ptr[1]) == '{' && isCountedRepeat(ptr + 2, patternEnd)) { + ptr = readRepeatCounts(ptr + 2, &minRepeats, &maxRepeats, &errorcode); + if (errorcode) + return -1; + } else if (c == '*') { + minRepeats = 0; + maxRepeats = -1; + ptr++; + } else if (c == '+') { + minRepeats = 1; + maxRepeats = -1; + ptr++; + } else if (c == '?') { + minRepeats = 0; + maxRepeats = 1; + ptr++; + } else { + minRepeats = 1; + maxRepeats = 1; + } + + /* If the minimum is zero, we have to allow for an OP_BRAZERO before the + group, and if the maximum is greater than zero, we have to replicate + maxval-1 times; each replication acquires an OP_BRAZERO plus a nesting + bracket set. */ + + int repeatsLength; + if (minRepeats == 0) { + length++; + if (maxRepeats > 0) { + repeatsLength = multiplyWithOverflowCheck(maxRepeats - 1, duplength + BRA_LEN + KET_LEN + OPCODE_LEN); + if (repeatsLength < 0) { + errorcode = ERR16; + return -1; + } + length += repeatsLength; + if (length > MAX_PATTERN_SIZE) { + errorcode = ERR16; + return -1; + } + } + } + + /* When the minimum is greater than zero, we have to replicate up to + minval-1 times, with no additions required in the copies. Then, if there + is a limited maximum we have to replicate up to maxval-1 times allowing + for a BRAZERO item before each optional copy and nesting brackets for all + but one of the optional copies. */ + + else { + repeatsLength = multiplyWithOverflowCheck(minRepeats - 1, duplength); + if (repeatsLength < 0) { + errorcode = ERR16; + return -1; + } + length += repeatsLength; + if (maxRepeats > minRepeats) { /* Need this test as maxRepeats=-1 means no limit */ + repeatsLength = multiplyWithOverflowCheck(maxRepeats - minRepeats, duplength + BRA_LEN + KET_LEN); + if (repeatsLength < 0) { + errorcode = ERR16; + return -1; + } + length += repeatsLength - (2 + 2 * LINK_SIZE); + } + if (length > MAX_PATTERN_SIZE) { + errorcode = ERR16; + return -1; + } + } + + /* Allow space for once brackets for "possessive quantifier" */ + + if (safelyCheckNextChar(ptr, patternEnd, '+')) { + ptr++; + length += 2 + 2 * LINK_SIZE; + } + continue; + } + + /* Non-special character. It won't be space or # in extended mode, so it is + always a genuine character. If we are in a \Q...\E sequence, check for the + end; if not, we have a literal. */ + + default: + NORMAL_CHAR: + length += 2; /* For a one-byte character */ + lastitemlength = 1; /* Default length of last item for repeats */ + + if (c > 127) { + int i; + for (i = 0; i < jsc_pcre_utf8_table1_size; i++) + if (c <= jsc_pcre_utf8_table1[i]) + break; + length += i; + lastitemlength += i; + } + + continue; + } + } + + length += KET_LEN + OPCODE_LEN; /* For final KET and END */ + + cd.numCapturingBrackets = bracount; + return length; +} + +/************************************************* +* Compile a Regular Expression * +*************************************************/ + +/* This function takes a string and returns a pointer to a block of store +holding a compiled version of the expression. The original API for this +function had no error code return variable; it is retained for backwards +compatibility. The new function is given a new name. + +Arguments: + pattern the regular expression + options various option bits + errorCodePtr pointer to error code variable (pcre_compile2() only) + can be NULL if you don't want a code value + error pointer to pointer to error text + erroroffset ptr offset in pattern where error was detected + tables pointer to character tables or NULL + +Returns: pointer to compiled data block, or NULL on error, + with error and erroroffset set +*/ + +static inline JSRegExp* returnError(ErrorCode errorcode, int *error) +{ + *error = static_cast(errorcode); + return 0; +} + +JSRegExp* jsRegExpCompile(const UChar* pattern, int patternLength, + JSRegExpIgnoreCaseOption ignoreCase, JSRegExpMultilineOption multiline, + unsigned* numSubpatterns, int *error) +{ + /* We can't pass back an error message if error is NULL; I guess the best we + can do is just return NULL, but we can set a code value if there is a code pointer. */ + if (!error) + return 0; + *error = 0; + + CompileData cd; + + ErrorCode errorcode = ERR0; + /* Call this once just to count the brackets. */ + calculateCompiledPatternLength(pattern, patternLength, ignoreCase, cd, errorcode); + /* Call it again to compute the length. */ + int length = calculateCompiledPatternLength(pattern, patternLength, ignoreCase, cd, errorcode); + if (errorcode) + return returnError(errorcode, error); + + if (length > MAX_PATTERN_SIZE) + return returnError(ERR16, error); + + size_t size = length + sizeof(JSRegExp); + JSRegExp* re = reinterpret_cast(new char[size]); + + if (!re) + return returnError(ERR13, error); + + re->options = (ignoreCase ? IgnoreCaseOption : 0) | (multiline ? MatchAcrossMultipleLinesOption : 0); + + /* The starting points of the name/number translation table and of the code are + passed around in the compile data block. */ + + const unsigned char* codeStart = (const unsigned char*)(re + 1); + + /* Set up a starting, non-extracting bracket, then compile the expression. On + error, errorcode will be set non-zero, so we don't need to look at the result + of the function here. */ + + const UChar* ptr = (const UChar*)pattern; + const UChar* patternEnd = pattern + patternLength; + unsigned char* code = const_cast(codeStart); + int firstByte, reqByte; + int bracketCount = 0; + if (!cd.needOuterBracket) + compileBranch(re->options, &bracketCount, &code, &ptr, patternEnd, &errorcode, &firstByte, &reqByte, cd); + else { + *code = OP_BRA; + unsigned char * const codeBefore = code; + compileBracket(re->options, &bracketCount, &code, &ptr, patternEnd, &errorcode, 2, &firstByte, &reqByte, cd); + JS_ASSERT((bracketCount & 0xff) == bracketCount); + put2ByteValue(codeBefore + 1 + LINK_SIZE, 0 << 8 | (bracketCount & 0xff)); + } + re->topBracket = bracketCount; + re->topBackref = cd.topBackref; + + /* If not reached end of pattern on success, there's an excess bracket. */ + + if (errorcode == 0 && ptr < patternEnd) + errorcode = ERR10; + + /* Fill in the terminating state and check for disastrous overflow, but + if debugging, leave the test till after things are printed out. */ + + *code++ = OP_END; + + JS_ASSERT(code - codeStart <= length); + if (code - codeStart > length) + errorcode = ERR7; + + /* Give an error if there's back reference to a non-existent capturing + subpattern. */ + + if (re->topBackref > re->topBracket) + errorcode = ERR15; + + /* Failed to compile, or error while post-processing */ + + if (errorcode != ERR0) { + delete [] reinterpret_cast(re); + return returnError(errorcode, error); + } + + /* If the anchored option was not passed, set the flag if we can determine that + the pattern is anchored by virtue of ^ characters or \A or anything else (such + as starting with .* when DOTALL is set). + + Otherwise, if we know what the first character has to be, save it, because that + speeds up unanchored matches no end. If not, see if we can set the + UseMultiLineFirstByteOptimizationOption flag. This is helpful for multiline matches when all branches + start with ^. and also when all branches start with .* for non-DOTALL matches. + */ + + if (cd.needOuterBracket ? bracketIsAnchored(codeStart) : branchIsAnchored(codeStart)) + re->options |= IsAnchoredOption; + else { + if (firstByte < 0) { + firstByte = (cd.needOuterBracket + ? bracketFindFirstAssertedCharacter(codeStart, false) + : branchFindFirstAssertedCharacter(codeStart, false)) + | ((re->options & IgnoreCaseOption) ? REQ_IGNORE_CASE : 0); + } + if (firstByte >= 0) { + int ch = firstByte & 255; + if (ch < 127) { + re->firstByte = ((firstByte & REQ_IGNORE_CASE) && flipCase(ch) == ch) ? ch : firstByte; + re->options |= UseFirstByteOptimizationOption; + } + } else { + if (cd.needOuterBracket ? bracketNeedsLineStart(codeStart, 0, cd.backrefMap) : branchNeedsLineStart(codeStart, 0, cd.backrefMap)) + re->options |= UseMultiLineFirstByteOptimizationOption; + } + } + + /* For an anchored pattern, we use the "required byte" only if it follows a + variable length item in the regex. Remove the caseless flag for non-caseable + bytes. */ + + if (reqByte >= 0 && (!(re->options & IsAnchoredOption) || (reqByte & REQ_VARY))) { + int ch = reqByte & 255; + if (ch < 127) { + re->reqByte = ((reqByte & REQ_IGNORE_CASE) && flipCase(ch) == ch) ? (reqByte & ~REQ_IGNORE_CASE) : reqByte; + re->options |= UseRequiredByteOptimizationOption; + } + } + + if (numSubpatterns) + *numSubpatterns = re->topBracket; + + return re; +} + +void jsRegExpFree(JSRegExp* re) +{ + delete [] reinterpret_cast(re); +} diff --git a/js/src/yarr/pcre/pcre_exec.cpp b/js/src/yarr/pcre/pcre_exec.cpp new file mode 100644 index 000000000000..8f178ad9ac23 --- /dev/null +++ b/js/src/yarr/pcre/pcre_exec.cpp @@ -0,0 +1,2147 @@ +/* This is JavaScriptCore's variant of the PCRE library. While this library +started out as a copy of PCRE, many of the features of PCRE have been +removed. This library now supports only the regular expression features +required by the JavaScript language specification, and has only the functions +needed by JavaScriptCore and the rest of WebKit. + + Originally written by Philip Hazel + Copyright (c) 1997-2006 University of Cambridge + Copyright (C) 2002, 2004, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + Copyright (C) 2007 Eric Seidel + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + +/* This module contains jsRegExpExecute(), the externally visible function +that does pattern matching using an NFA algorithm, following the rules from +the JavaScript specification. There are also some supporting functions. */ + +#include "pcre_internal.h" + +#include +#include "yarr/jswtfbridge.h" +#include "yarr/wtf/ASCIICType.h" +#include "jsarena.h" +#include "jscntxt.h" + +using namespace WTF; + +#ifndef WTF_COMPILER_MSVC +#define USE_COMPUTED_GOTO_FOR_MATCH_RECURSION +#endif + +/* Note: Webkit sources have USE_COMPUTED_GOTO_FOR_MATCH_OPCODE_LOOP disabled. */ +/* Note: There are hardcoded constants all over the place, but in the port of + Yarr to TraceMonkey two bytes are added to the OP_BRA* opcodes, so the + instruction stream now looks like this at the start of a bracket group: + + OP_BRA* [link:LINK_SIZE] [minNestedBracket,maxNestedBracket:2] + + Both capturing and non-capturing brackets encode this information. */ + +/* Avoid warnings on Windows. */ +#undef min +#undef max + +#ifndef USE_COMPUTED_GOTO_FOR_MATCH_RECURSION +typedef int ReturnLocation; +#else +typedef void* ReturnLocation; +#endif + +/* Structure for building a chain of data for holding the values of +the subject pointer at the start of each bracket, used to detect when +an empty string has been matched by a bracket to break infinite loops. */ +struct BracketChainNode { + BracketChainNode* previousBracket; + const UChar* bracketStart; +}; + +struct MatchFrame { + ReturnLocation returnLocation; + struct MatchFrame* previousFrame; + int *savedOffsets; + /* The frame allocates saved offsets into the regular expression arena pool so + that they can be restored during backtracking. */ + size_t savedOffsetsSize; + JSArenaPool *regExpPool; + + MatchFrame() : savedOffsetsSize(0), regExpPool(0) {} + void init(JSArenaPool *regExpPool) { this->regExpPool = regExpPool; } + + /* Function arguments that may change */ + struct { + const UChar* subjectPtr; + const unsigned char* instructionPtr; + int offsetTop; + BracketChainNode* bracketChain; + bool groupMatched; + } args; + + + /* PCRE uses "fake" recursion built off of gotos, thus + stack-based local variables are not safe to use. Instead we have to + store local variables on the current MatchFrame. */ + struct { + const unsigned char* data; + const unsigned char* startOfRepeatingBracket; + const UChar* subjectPtrAtStartOfInstruction; // Several instrutions stash away a subjectPtr here for later compare + const unsigned char* instructionPtrAtStartOfOnce; + + int repeatOthercase; + int savedSubjectOffset; + + int ctype; + int fc; + int fi; + int length; + int max; + int number; + int offset; + int skipBytes; + int minBracket; + int limitBracket; + int bracketsBefore; + + BracketChainNode bracketChainNode; + } locals; + + void saveOffsets(int minBracket, int limitBracket, int *offsets, int offsetEnd) { + JS_ASSERT(regExpPool); + JS_ASSERT(minBracket >= 0); + JS_ASSERT(limitBracket >= minBracket); + JS_ASSERT(offsetEnd >= 0); + if (minBracket == limitBracket) + return; + const size_t newSavedOffsetCount = 3 * (limitBracket - minBracket); + /* Increase saved offset space if necessary. */ + { + size_t targetSize = sizeof(*savedOffsets) * newSavedOffsetCount; + if (savedOffsetsSize < targetSize) { + JS_ARENA_ALLOCATE_CAST(savedOffsets, int *, regExpPool, targetSize); + JS_ASSERT(savedOffsets); /* FIXME: error code, bug 574459. */ + savedOffsetsSize = targetSize; + } + } + for (unsigned i = 0; i < unsigned(limitBracket - minBracket); ++i) { + int bracketIter = minBracket + i; + JS_ASSERT(2 * bracketIter + 1 <= offsetEnd); + int start = offsets[2 * bracketIter]; + int end = offsets[2 * bracketIter + 1]; + JS_ASSERT(bracketIter <= offsetEnd); + int offset = offsets[offsetEnd - bracketIter]; + DPRINTF(("saving bracket %d; start: %d; end: %d; offset: %d\n", bracketIter, start, end, offset)); + JS_ASSERT(start <= end); + JS_ASSERT(i * 3 + 2 < newSavedOffsetCount); + savedOffsets[i * 3 + 0] = start; + savedOffsets[i * 3 + 1] = end; + savedOffsets[i * 3 + 2] = offset; + } + } + + void clobberOffsets(int minBracket, int limitBracket, int *offsets, int offsetEnd) { + for (int i = 0; i < limitBracket - minBracket; ++i) { + int bracketIter = minBracket + i; + JS_ASSERT(2 * bracketIter + 1 < offsetEnd); + offsets[2 * bracketIter + 0] = -1; + offsets[2 * bracketIter + 1] = -1; + } + } + + void restoreOffsets(int minBracket, int limitBracket, int *offsets, int offsetEnd) { + JS_ASSERT(regExpPool); + JS_ASSERT_IF(limitBracket > minBracket, savedOffsets); + for (int i = 0; i < limitBracket - minBracket; ++i) { + int bracketIter = minBracket + i; + int start = savedOffsets[i * 3 + 0]; + int end = savedOffsets[i * 3 + 1]; + int offset = savedOffsets[i * 3 + 2]; + DPRINTF(("restoring bracket %d; start: %d; end: %d; offset: %d\n", bracketIter, start, end, offset)); + JS_ASSERT(start <= end); + offsets[2 * bracketIter + 0] = start; + offsets[2 * bracketIter + 1] = end; + offsets[offsetEnd - bracketIter] = offset; + } + } + + /* Extract the bracket data after the current opcode/link at |instructionPtr| into the locals. */ + void extractBrackets(const unsigned char *instructionPtr) { + uint16 bracketMess = get2ByteValue(instructionPtr + 1 + LINK_SIZE); + locals.minBracket = (bracketMess >> 8) & 0xff; + locals.limitBracket = (bracketMess & 0xff); + JS_ASSERT(locals.minBracket <= locals.limitBracket); + } + + /* At the start of a bracketed group, add the current subject pointer to the + stack of such pointers, to be re-instated at the end of the group when we hit + the closing ket. When match() is called in other circumstances, we don't add to + this stack. */ + void startNewGroup() { + locals.bracketChainNode.previousBracket = args.bracketChain; + locals.bracketChainNode.bracketStart = args.subjectPtr; + args.bracketChain = &locals.bracketChainNode; + } +}; + +/* Structure for passing "static" information around between the functions +doing traditional NFA matching, so that they are thread-safe. */ + +struct MatchData { + int *offsetVector; /* Offset vector */ + int offsetEnd; /* One past the end */ + int offsetMax; /* The maximum usable for return data */ + bool offsetOverflow; /* Set if too many extractions */ + const UChar *startSubject; /* Start of the subject string */ + const UChar *endSubject; /* End of the subject string */ + const UChar *endMatchPtr; /* Subject position at end match */ + int endOffsetTop; /* Highwater mark at end of match */ + bool multiline; + bool ignoreCase; + + void setOffsetPair(size_t pairNum, int start, int end) { + JS_ASSERT(int(2 * pairNum + 1) < offsetEnd && int(pairNum) < offsetEnd); + JS_ASSERT(start <= end); + JS_ASSERT_IF(start < 0, start == end && start == -1); + DPRINTF(("setting offset pair at %u (%d, %d)\n", pairNum, start, end)); + offsetVector[2 * pairNum + 0] = start; + offsetVector[2 * pairNum + 1] = end; + } +}; + +/* The maximum remaining length of subject we are prepared to search for a +reqByte match. */ + +#define REQ_BYTE_MAX 1000 + +/* The below limit restricts the number of "recursive" match calls in order to +avoid spending exponential time on complex regular expressions. */ + +static const unsigned matchLimit = 1000000; + +/************************************************* +* Match a back-reference * +*************************************************/ + +/* If a back reference hasn't been set, the length that is passed is greater +than the number of characters left in the string, so the match fails. + +Arguments: + offset index into the offset vector + subjectPtr points into the subject + length length to be matched + md points to match data block + +Returns: true if matched +*/ + +static bool matchRef(int offset, const UChar* subjectPtr, int length, const MatchData& md) +{ + const UChar* p = md.startSubject + md.offsetVector[offset]; + + /* Always fail if not enough characters left */ + + if (length > md.endSubject - subjectPtr) + return false; + + /* Separate the caselesss case for speed */ + + if (md.ignoreCase) { + while (length-- > 0) { + UChar c = *p++; + int othercase = jsc_pcre_ucp_othercase(c); + UChar d = *subjectPtr++; + if (c != d && othercase != d) + return false; + } + } + else { + while (length-- > 0) + if (*p++ != *subjectPtr++) + return false; + } + + return true; +} + +#ifndef USE_COMPUTED_GOTO_FOR_MATCH_RECURSION + +/* Use numbered labels and switch statement at the bottom of the match function. */ + +#define RMATCH_WHERE(num) num +#define RRETURN_LABEL RRETURN_SWITCH + +#else + +/* Use GCC's computed goto extension. */ + +/* For one test case this is more than 40% faster than the switch statement. +We could avoid the use of the num argument entirely by using local labels, +but using it for the GCC case as well as the non-GCC case allows us to share +a bit more code and notice if we use conflicting numbers.*/ + +#define RMATCH_WHERE(num) JS_EXTENSION(&&RRETURN_##num) +#define RRETURN_LABEL *stack.currentFrame->returnLocation + +#endif + +#define RECURSIVE_MATCH_COMMON(num) \ + goto RECURSE;\ + RRETURN_##num: \ + stack.popCurrentFrame(); + +#define RECURSIVE_MATCH(num, ra, rb, gm) \ + do { \ + stack.pushNewFrame((ra), (rb), RMATCH_WHERE(num), (gm)); \ + RECURSIVE_MATCH_COMMON(num) \ + } while (0) + +#define RECURSIVE_MATCH_NEW_GROUP(num, ra, rb, gm) \ + do { \ + stack.pushNewFrame((ra), (rb), RMATCH_WHERE(num), (gm)); \ + stack.currentFrame->startNewGroup(); \ + RECURSIVE_MATCH_COMMON(num) \ + } while (0) + +#define RRETURN do { JS_EXTENSION_(goto RRETURN_LABEL); } while (0) + +#define RRETURN_NO_MATCH do { isMatch = false; RRETURN; } while (0) + +/************************************************* +* Match from current position * +*************************************************/ + +/* On entry instructionPtr points to the first opcode, and subjectPtr to the first character +in the subject string, while substringStart holds the value of subjectPtr at the start of the +last bracketed group - used for breaking infinite loops matching zero-length +strings. This function is called recursively in many circumstances. Whenever it +returns a negative (error) response, the outer match() call must also return the +same response. + +Arguments: + subjectPtr pointer in subject + instructionPtr position in code + offsetTop current top pointer + md pointer to "static" info for the match + +Returns: 1 if matched ) these values are >= 0 + 0 if failed to match ) + a negative error value if aborted by an error condition + (e.g. stopped by repeated call or recursion limit) +*/ + +static const unsigned numFramesOnStack = 16; + +struct MatchStack { + JSArenaPool *regExpPool; + void *regExpPoolMark; + + MatchStack(JSArenaPool *regExpPool) + : regExpPool(regExpPool) + , regExpPoolMark(JS_ARENA_MARK(regExpPool)) + , framesEnd(frames + numFramesOnStack) + , currentFrame(frames) + , size(1) // match() creates accesses the first frame w/o calling pushNewFrame + { + JS_ASSERT((sizeof(frames) / sizeof(frames[0])) == numFramesOnStack); + JS_ASSERT(regExpPool); + for (size_t i = 0; i < numFramesOnStack; ++i) + frames[i].init(regExpPool); + } + + ~MatchStack() { JS_ARENA_RELEASE(regExpPool, regExpPoolMark); } + + MatchFrame frames[numFramesOnStack]; + MatchFrame* framesEnd; + MatchFrame* currentFrame; + unsigned size; + + bool canUseStackBufferForNextFrame() { + return size < numFramesOnStack; + } + + MatchFrame* allocateNextFrame() { + if (canUseStackBufferForNextFrame()) + return currentFrame + 1; + MatchFrame *frame = new MatchFrame; + frame->init(regExpPool); + return frame; + } + + void pushNewFrame(const unsigned char* instructionPtr, BracketChainNode* bracketChain, ReturnLocation returnLocation, bool groupMatched) { + MatchFrame* newframe = allocateNextFrame(); + newframe->previousFrame = currentFrame; + + newframe->args.subjectPtr = currentFrame->args.subjectPtr; + newframe->args.offsetTop = currentFrame->args.offsetTop; + newframe->args.instructionPtr = instructionPtr; + newframe->args.bracketChain = bracketChain; + newframe->args.groupMatched = groupMatched; + newframe->returnLocation = returnLocation; + size++; + + currentFrame = newframe; + } + + void popCurrentFrame() { + MatchFrame* oldFrame = currentFrame; + currentFrame = currentFrame->previousFrame; + if (size > numFramesOnStack) + delete oldFrame; + size--; + } + + void popAllFrames() { + while (size) + popCurrentFrame(); + } +}; + +static int matchError(int errorCode, MatchStack& stack) +{ + stack.popAllFrames(); + return errorCode; +} + +/* Get the next UTF-8 character, not advancing the pointer, incrementing length + if there are extra bytes. This is called when we know we are in UTF-8 mode. */ + +static inline void getUTF8CharAndIncrementLength(int& c, const unsigned char* subjectPtr, int& len) +{ + c = *subjectPtr; + if ((c & 0xc0) == 0xc0) { + int gcaa = jsc_pcre_utf8_table4[c & 0x3f]; /* Number of additional bytes */ + int gcss = 6 * gcaa; + c = (c & jsc_pcre_utf8_table3[gcaa]) << gcss; + for (int gcii = 1; gcii <= gcaa; gcii++) { + gcss -= 6; + c |= (subjectPtr[gcii] & 0x3f) << gcss; + } + len += gcaa; + } +} + +static inline void repeatInformationFromInstructionOffset(short instructionOffset, bool& minimize, int& minimumRepeats, int& maximumRepeats) +{ + // Instruction offsets are based off of OP_CRSTAR, OP_STAR, OP_TYPESTAR, OP_NOTSTAR + static const char minimumRepeatsFromInstructionOffset[] = { 0, 0, 1, 1, 0, 0 }; + static const int maximumRepeatsFromInstructionOffset[] = { INT_MAX, INT_MAX, INT_MAX, INT_MAX, 1, 1 }; + + JS_ASSERT(instructionOffset >= 0); + JS_ASSERT(instructionOffset <= (OP_CRMINQUERY - OP_CRSTAR)); + + minimize = (instructionOffset & 1); // this assumes ordering: Instruction, MinimizeInstruction, Instruction2, MinimizeInstruction2 + minimumRepeats = minimumRepeatsFromInstructionOffset[instructionOffset]; + maximumRepeats = maximumRepeatsFromInstructionOffset[instructionOffset]; +} + +static int +match(JSArenaPool *regExpPool, const UChar* subjectPtr, const unsigned char* instructionPtr, int offsetTop, MatchData& md) +{ + bool isMatch = false; + int min; + bool minimize = false; /* Initialization not really needed, but some compilers think so. */ + unsigned remainingMatchCount = matchLimit; + int othercase; /* Declare here to avoid errors during jumps */ + + MatchStack stack(regExpPool); + + /* The opcode jump table. */ +#ifdef USE_COMPUTED_GOTO_FOR_MATCH_OPCODE_LOOP +#define EMIT_JUMP_TABLE_ENTRY(opcode) JS_EXTENSION(&&LABEL_OP_##opcode) + static void* opcodeJumpTable[256] = { FOR_EACH_OPCODE(EMIT_JUMP_TABLE_ENTRY) }; +#undef EMIT_JUMP_TABLE_ENTRY +#endif + + /* One-time setup of the opcode jump table. */ +#ifdef USE_COMPUTED_GOTO_FOR_MATCH_OPCODE_LOOP + for (int i = 255; !opcodeJumpTable[i]; i--) + opcodeJumpTable[i] = &&CAPTURING_BRACKET; +#endif + +#ifdef USE_COMPUTED_GOTO_FOR_MATCH_RECURSION + // Shark shows this as a hot line + // Using a static const here makes this line disappear, but makes later access hotter (not sure why) + stack.currentFrame->returnLocation = JS_EXTENSION(&&RETURN); +#else + stack.currentFrame->returnLocation = 0; +#endif + stack.currentFrame->args.subjectPtr = subjectPtr; + stack.currentFrame->args.instructionPtr = instructionPtr; + stack.currentFrame->args.offsetTop = offsetTop; + stack.currentFrame->args.bracketChain = 0; + stack.currentFrame->args.groupMatched = false; + stack.currentFrame->startNewGroup(); + + /* This is where control jumps back to to effect "recursion" */ + +RECURSE: + if (!--remainingMatchCount) + return matchError(JSRegExpErrorHitLimit, stack); + + /* Now start processing the operations. */ + +#ifndef USE_COMPUTED_GOTO_FOR_MATCH_OPCODE_LOOP + while (true) +#endif + { + +#ifdef USE_COMPUTED_GOTO_FOR_MATCH_OPCODE_LOOP +#define BEGIN_OPCODE(opcode) LABEL_OP_##opcode +#define NEXT_OPCODE goto *opcodeJumpTable[*stack.currentFrame->args.instructionPtr] +#else +#define BEGIN_OPCODE(opcode) case OP_##opcode +#define NEXT_OPCODE continue +#endif +#define LOCALS(__ident) (stack.currentFrame->locals.__ident) + +#ifdef USE_COMPUTED_GOTO_FOR_MATCH_OPCODE_LOOP + NEXT_OPCODE; +#else + switch (*stack.currentFrame->args.instructionPtr) +#endif + { + /* Non-capturing bracket: optimized */ + + BEGIN_OPCODE(BRA): + NON_CAPTURING_BRACKET: + DPRINTF(("start non-capturing bracket\n")); + stack.currentFrame->extractBrackets(stack.currentFrame->args.instructionPtr); + /* If we see no ALT, we have to skip three bytes of bracket data (link plus nested + bracket data. */ + stack.currentFrame->locals.skipBytes = 3; + do { + RECURSIVE_MATCH_NEW_GROUP(2, stack.currentFrame->args.instructionPtr + stack.currentFrame->locals.skipBytes + LINK_SIZE, stack.currentFrame->args.bracketChain, stack.currentFrame->args.groupMatched); + if (isMatch) { + DPRINTF(("non-capturing bracket succeeded\n")); + RRETURN; + } + stack.currentFrame->locals.skipBytes = 1; + stack.currentFrame->args.instructionPtr += getLinkValue(stack.currentFrame->args.instructionPtr + 1); + } while (*stack.currentFrame->args.instructionPtr == OP_ALT); + DPRINTF(("non-capturing bracket failed\n")); + for (size_t i = LOCALS(minBracket); i < size_t(LOCALS(limitBracket)); ++i) + md.setOffsetPair(i, -1, -1); + RRETURN; + + /* Skip over large extraction number data if encountered. */ + + BEGIN_OPCODE(BRANUMBER): + stack.currentFrame->args.instructionPtr += 3; + NEXT_OPCODE; + + /* End of the pattern. */ + + BEGIN_OPCODE(END): + md.endMatchPtr = stack.currentFrame->args.subjectPtr; /* Record where we ended */ + md.endOffsetTop = stack.currentFrame->args.offsetTop; /* and how many extracts were taken */ + isMatch = true; + RRETURN; + + /* Assertion brackets. Check the alternative branches in turn - the + matching won't pass the KET for an assertion. If any one branch matches, + the assertion is true. Lookbehind assertions have an OP_REVERSE item at the + start of each branch to move the current point backwards, so the code at + this level is identical to the lookahead case. */ + + BEGIN_OPCODE(ASSERT): + { + uint16 bracketMess = get2ByteValue(stack.currentFrame->args.instructionPtr + 1 + LINK_SIZE); + LOCALS(minBracket) = (bracketMess >> 8) & 0xff; + LOCALS(limitBracket) = bracketMess & 0xff; + JS_ASSERT(LOCALS(minBracket) <= LOCALS(limitBracket)); + } + stack.currentFrame->locals.skipBytes = 3; + do { + RECURSIVE_MATCH_NEW_GROUP(6, stack.currentFrame->args.instructionPtr + stack.currentFrame->locals.skipBytes + LINK_SIZE, NULL, false); + if (isMatch) + break; + stack.currentFrame->locals.skipBytes = 1; + stack.currentFrame->args.instructionPtr += getLinkValue(stack.currentFrame->args.instructionPtr + 1); + } while (*stack.currentFrame->args.instructionPtr == OP_ALT); + if (*stack.currentFrame->args.instructionPtr == OP_KET) { + for (size_t i = LOCALS(minBracket); i < size_t(LOCALS(limitBracket)); ++i) + md.setOffsetPair(i, -1, -1); + RRETURN_NO_MATCH; + } + + /* Continue from after the assertion, updating the offsets high water + mark, since extracts may have been taken during the assertion. */ + + advanceToEndOfBracket(stack.currentFrame->args.instructionPtr); + stack.currentFrame->args.instructionPtr += 1 + LINK_SIZE; + stack.currentFrame->args.offsetTop = md.endOffsetTop; + NEXT_OPCODE; + + /* Negative assertion: all branches must fail to match */ + + BEGIN_OPCODE(ASSERT_NOT): + stack.currentFrame->locals.skipBytes = 3; + { + unsigned bracketMess = get2ByteValue(stack.currentFrame->args.instructionPtr + 1 + LINK_SIZE); + LOCALS(minBracket) = (bracketMess >> 8) & 0xff; + LOCALS(limitBracket) = bracketMess & 0xff; + } + JS_ASSERT(LOCALS(minBracket) <= LOCALS(limitBracket)); + do { + RECURSIVE_MATCH_NEW_GROUP(7, stack.currentFrame->args.instructionPtr + stack.currentFrame->locals.skipBytes + LINK_SIZE, NULL, false); + if (isMatch) + RRETURN_NO_MATCH; + stack.currentFrame->locals.skipBytes = 1; + stack.currentFrame->args.instructionPtr += getLinkValue(stack.currentFrame->args.instructionPtr + 1); + } while (*stack.currentFrame->args.instructionPtr == OP_ALT); + + stack.currentFrame->args.instructionPtr += stack.currentFrame->locals.skipBytes + LINK_SIZE; + NEXT_OPCODE; + + /* An alternation is the end of a branch; scan along to find the end of the + bracketed group and go to there. */ + + BEGIN_OPCODE(ALT): + advanceToEndOfBracket(stack.currentFrame->args.instructionPtr); + NEXT_OPCODE; + + /* BRAZERO and BRAMINZERO occur just before a bracket group, indicating + that it may occur zero times. It may repeat infinitely, or not at all - + i.e. it could be ()* or ()? in the pattern. Brackets with fixed upper + repeat limits are compiled as a number of copies, with the optional ones + preceded by BRAZERO or BRAMINZERO. */ + + BEGIN_OPCODE(BRAZERO): { + stack.currentFrame->locals.startOfRepeatingBracket = stack.currentFrame->args.instructionPtr + 1; + stack.currentFrame->extractBrackets(stack.currentFrame->args.instructionPtr + 1); + stack.currentFrame->saveOffsets(LOCALS(minBracket), LOCALS(limitBracket), md.offsetVector, md.offsetEnd); + RECURSIVE_MATCH_NEW_GROUP(14, stack.currentFrame->locals.startOfRepeatingBracket, stack.currentFrame->args.bracketChain, true); + if (isMatch) + RRETURN; + stack.currentFrame->restoreOffsets(LOCALS(minBracket), LOCALS(limitBracket), md.offsetVector, md.offsetEnd); + advanceToEndOfBracket(stack.currentFrame->locals.startOfRepeatingBracket); + stack.currentFrame->args.instructionPtr = stack.currentFrame->locals.startOfRepeatingBracket + 1 + LINK_SIZE; + NEXT_OPCODE; + } + + BEGIN_OPCODE(BRAMINZERO): { + stack.currentFrame->locals.startOfRepeatingBracket = stack.currentFrame->args.instructionPtr + 1; + advanceToEndOfBracket(stack.currentFrame->locals.startOfRepeatingBracket); + RECURSIVE_MATCH_NEW_GROUP(15, stack.currentFrame->locals.startOfRepeatingBracket + 1 + LINK_SIZE, stack.currentFrame->args.bracketChain, false); + if (isMatch) + RRETURN; + stack.currentFrame->args.instructionPtr++; + NEXT_OPCODE; + } + + /* End of a group, repeated or non-repeating. If we are at the end of + an assertion "group", stop matching and return 1, but record the + current high water mark for use by positive assertions. Do this also + for the "once" (not-backup up) groups. */ + + BEGIN_OPCODE(KET): + BEGIN_OPCODE(KETRMIN): + BEGIN_OPCODE(KETRMAX): + stack.currentFrame->locals.instructionPtrAtStartOfOnce = stack.currentFrame->args.instructionPtr - getLinkValue(stack.currentFrame->args.instructionPtr + 1); + stack.currentFrame->locals.subjectPtrAtStartOfInstruction = stack.currentFrame->args.bracketChain->bracketStart; + + /* Back up the stack of bracket start pointers. */ + + stack.currentFrame->args.bracketChain = stack.currentFrame->args.bracketChain->previousBracket; + + if (*stack.currentFrame->locals.instructionPtrAtStartOfOnce == OP_ASSERT || *stack.currentFrame->locals.instructionPtrAtStartOfOnce == OP_ASSERT_NOT) { + md.endOffsetTop = stack.currentFrame->args.offsetTop; + isMatch = true; + RRETURN; + } + + /* In all other cases except a conditional group we have to check the + group number back at the start and if necessary complete handling an + extraction by setting the offsets and bumping the high water mark. */ + + stack.currentFrame->locals.number = *stack.currentFrame->locals.instructionPtrAtStartOfOnce - OP_BRA; + + /* For extended extraction brackets (large number), we have to fish out + the number from a dummy opcode at the start. */ + + if (stack.currentFrame->locals.number > EXTRACT_BASIC_MAX) + stack.currentFrame->locals.number = get2ByteValue(stack.currentFrame->locals.instructionPtrAtStartOfOnce + 4 + LINK_SIZE); + stack.currentFrame->locals.offset = 2 * stack.currentFrame->locals.number; + + DPRINTF(("end bracket %d\n", stack.currentFrame->locals.number)); + + /* Test for a numbered group. This includes groups called as a result + of recursion. Note that whole-pattern recursion is coded as a recurse + into group 0, so it won't be picked up here. Instead, we catch it when + the OP_END is reached. */ + + if (stack.currentFrame->locals.number > 0) { + if (stack.currentFrame->locals.offset >= md.offsetMax) + md.offsetOverflow = true; + else { + int start = md.offsetVector[md.offsetEnd - stack.currentFrame->locals.number]; + int end = stack.currentFrame->args.subjectPtr - md.startSubject; + if (start == end && stack.currentFrame->args.groupMatched) { + DPRINTF(("empty string while group already matched; bailing")); + RRETURN; + } + DPRINTF(("saving; start: %d; end: %d\n", start, end)); + JS_ASSERT(start <= end); + md.setOffsetPair(stack.currentFrame->locals.number, start, end); + if (stack.currentFrame->args.offsetTop <= stack.currentFrame->locals.offset) + stack.currentFrame->args.offsetTop = stack.currentFrame->locals.offset + 2; + } + } + + /* For a non-repeating ket, just continue at this level. This also + happens for a repeating ket if no characters were matched in the group. + This is the forcible breaking of infinite loops as implemented in Perl + 5.005. If there is an options reset, it will get obeyed in the normal + course of events. */ + + if (*stack.currentFrame->args.instructionPtr == OP_KET || stack.currentFrame->args.subjectPtr == stack.currentFrame->locals.subjectPtrAtStartOfInstruction) { + DPRINTF(("non-repeating ket or empty match\n")); + stack.currentFrame->args.instructionPtr += 1 + LINK_SIZE; + NEXT_OPCODE; + } + + /* The repeating kets try the rest of the pattern or restart from the + preceding bracket, in the appropriate order. */ + + stack.currentFrame->extractBrackets(LOCALS(instructionPtrAtStartOfOnce)); + JS_ASSERT_IF(LOCALS(number), LOCALS(minBracket) <= LOCALS(number) && LOCALS(number) < LOCALS(limitBracket)); + if (*stack.currentFrame->args.instructionPtr == OP_KETRMIN) { + stack.currentFrame->saveOffsets(LOCALS(minBracket), LOCALS(limitBracket), md.offsetVector, md.offsetEnd); + RECURSIVE_MATCH(16, stack.currentFrame->args.instructionPtr + 1 + LINK_SIZE, stack.currentFrame->args.bracketChain, true); + if (isMatch) + RRETURN; + else + stack.currentFrame->restoreOffsets(LOCALS(minBracket), LOCALS(limitBracket), md.offsetVector, md.offsetEnd); + DPRINTF(("recursively matching lazy group\n")); + RECURSIVE_MATCH_NEW_GROUP(17, LOCALS(instructionPtrAtStartOfOnce), stack.currentFrame->args.bracketChain, true); + } else { /* OP_KETRMAX */ + stack.currentFrame->saveOffsets(LOCALS(minBracket), LOCALS(limitBracket), md.offsetVector, md.offsetEnd); + stack.currentFrame->clobberOffsets(LOCALS(minBracket), LOCALS(limitBracket), md.offsetVector, md.offsetEnd); + DPRINTF(("recursively matching greedy group\n")); + RECURSIVE_MATCH_NEW_GROUP(18, LOCALS(instructionPtrAtStartOfOnce), stack.currentFrame->args.bracketChain, true); + if (isMatch) + RRETURN; + else + stack.currentFrame->restoreOffsets(LOCALS(minBracket), LOCALS(limitBracket), md.offsetVector, md.offsetEnd); + RECURSIVE_MATCH(19, stack.currentFrame->args.instructionPtr + 1 + LINK_SIZE, stack.currentFrame->args.bracketChain, false); + } + RRETURN; + + /* Start of subject. */ + + BEGIN_OPCODE(CIRC): + if (stack.currentFrame->args.subjectPtr != md.startSubject) + RRETURN_NO_MATCH; + stack.currentFrame->args.instructionPtr++; + NEXT_OPCODE; + + /* After internal newline if multiline. */ + + BEGIN_OPCODE(BOL): + if (stack.currentFrame->args.subjectPtr != md.startSubject && !isNewline(stack.currentFrame->args.subjectPtr[-1])) + RRETURN_NO_MATCH; + stack.currentFrame->args.instructionPtr++; + NEXT_OPCODE; + + /* End of subject. */ + + BEGIN_OPCODE(DOLL): + if (stack.currentFrame->args.subjectPtr < md.endSubject) + RRETURN_NO_MATCH; + stack.currentFrame->args.instructionPtr++; + NEXT_OPCODE; + + /* Before internal newline if multiline. */ + + BEGIN_OPCODE(EOL): + if (stack.currentFrame->args.subjectPtr < md.endSubject && !isNewline(*stack.currentFrame->args.subjectPtr)) + RRETURN_NO_MATCH; + stack.currentFrame->args.instructionPtr++; + NEXT_OPCODE; + + /* Word boundary assertions */ + + BEGIN_OPCODE(NOT_WORD_BOUNDARY): + BEGIN_OPCODE(WORD_BOUNDARY): { + bool currentCharIsWordChar = false; + bool previousCharIsWordChar = false; + + if (stack.currentFrame->args.subjectPtr > md.startSubject) + previousCharIsWordChar = isWordChar(stack.currentFrame->args.subjectPtr[-1]); + if (stack.currentFrame->args.subjectPtr < md.endSubject) + currentCharIsWordChar = isWordChar(*stack.currentFrame->args.subjectPtr); + + /* Now see if the situation is what we want */ + bool wordBoundaryDesired = (*stack.currentFrame->args.instructionPtr++ == OP_WORD_BOUNDARY); + if (wordBoundaryDesired ? currentCharIsWordChar == previousCharIsWordChar : currentCharIsWordChar != previousCharIsWordChar) + RRETURN_NO_MATCH; + NEXT_OPCODE; + } + + /* Match a single character type; inline for speed */ + + BEGIN_OPCODE(NOT_NEWLINE): + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN_NO_MATCH; + if (isNewline(*stack.currentFrame->args.subjectPtr++)) + RRETURN_NO_MATCH; + stack.currentFrame->args.instructionPtr++; + NEXT_OPCODE; + + BEGIN_OPCODE(NOT_DIGIT): + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN_NO_MATCH; + if (isASCIIDigit(*stack.currentFrame->args.subjectPtr++)) + RRETURN_NO_MATCH; + stack.currentFrame->args.instructionPtr++; + NEXT_OPCODE; + + BEGIN_OPCODE(DIGIT): + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN_NO_MATCH; + if (!isASCIIDigit(*stack.currentFrame->args.subjectPtr++)) + RRETURN_NO_MATCH; + stack.currentFrame->args.instructionPtr++; + NEXT_OPCODE; + + BEGIN_OPCODE(NOT_WHITESPACE): + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN_NO_MATCH; + if (isSpaceChar(*stack.currentFrame->args.subjectPtr++)) + RRETURN_NO_MATCH; + stack.currentFrame->args.instructionPtr++; + NEXT_OPCODE; + + BEGIN_OPCODE(WHITESPACE): + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN_NO_MATCH; + if (!isSpaceChar(*stack.currentFrame->args.subjectPtr++)) + RRETURN_NO_MATCH; + stack.currentFrame->args.instructionPtr++; + NEXT_OPCODE; + + BEGIN_OPCODE(NOT_WORDCHAR): + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN_NO_MATCH; + if (isWordChar(*stack.currentFrame->args.subjectPtr++)) + RRETURN_NO_MATCH; + stack.currentFrame->args.instructionPtr++; + NEXT_OPCODE; + + BEGIN_OPCODE(WORDCHAR): + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN_NO_MATCH; + if (!isWordChar(*stack.currentFrame->args.subjectPtr++)) + RRETURN_NO_MATCH; + stack.currentFrame->args.instructionPtr++; + NEXT_OPCODE; + + /* Match a back reference, possibly repeatedly. Look past the end of the + item to see if there is repeat information following. The code is similar + to that for character classes, but repeated for efficiency. Then obey + similar code to character type repeats - written out again for speed. + However, if the referenced string is the empty string, always treat + it as matched, any number of times (otherwise there could be infinite + loops). */ + + BEGIN_OPCODE(REF): + stack.currentFrame->locals.offset = get2ByteValue(stack.currentFrame->args.instructionPtr + 1) << 1; /* Doubled ref number */ + stack.currentFrame->args.instructionPtr += 3; /* Advance past item */ + + /* If the reference is unset, set the length to be longer than the amount + of subject left; this ensures that every attempt at a match fails. We + can't just fail here, because of the possibility of quantifiers with zero + minima. */ + + if (stack.currentFrame->locals.offset >= stack.currentFrame->args.offsetTop || md.offsetVector[stack.currentFrame->locals.offset] < 0) + stack.currentFrame->locals.length = 0; + else + stack.currentFrame->locals.length = md.offsetVector[stack.currentFrame->locals.offset+1] - md.offsetVector[stack.currentFrame->locals.offset]; + + /* Set up for repetition, or handle the non-repeated case */ + + switch (*stack.currentFrame->args.instructionPtr) { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + repeatInformationFromInstructionOffset(*stack.currentFrame->args.instructionPtr++ - OP_CRSTAR, minimize, min, stack.currentFrame->locals.max); + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + minimize = (*stack.currentFrame->args.instructionPtr == OP_CRMINRANGE); + min = get2ByteValue(stack.currentFrame->args.instructionPtr + 1); + stack.currentFrame->locals.max = get2ByteValue(stack.currentFrame->args.instructionPtr + 3); + if (stack.currentFrame->locals.max == 0) + stack.currentFrame->locals.max = INT_MAX; + stack.currentFrame->args.instructionPtr += 5; + break; + + default: /* No repeat follows */ + if (!matchRef(stack.currentFrame->locals.offset, stack.currentFrame->args.subjectPtr, stack.currentFrame->locals.length, md)) + RRETURN_NO_MATCH; + stack.currentFrame->args.subjectPtr += stack.currentFrame->locals.length; + NEXT_OPCODE; + } + + /* If the length of the reference is zero, just continue with the + main loop. */ + + if (stack.currentFrame->locals.length == 0) + NEXT_OPCODE; + + /* First, ensure the minimum number of matches are present. */ + + for (int i = 1; i <= min; i++) { + if (!matchRef(stack.currentFrame->locals.offset, stack.currentFrame->args.subjectPtr, stack.currentFrame->locals.length, md)) + RRETURN_NO_MATCH; + stack.currentFrame->args.subjectPtr += stack.currentFrame->locals.length; + } + + /* If min = max, continue at the same level without recursion. + They are not both allowed to be zero. */ + + if (min == stack.currentFrame->locals.max) + NEXT_OPCODE; + + /* If minimizing, keep trying and advancing the pointer */ + + if (minimize) { + for (stack.currentFrame->locals.fi = min;; stack.currentFrame->locals.fi++) { + RECURSIVE_MATCH(20, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain, false); + if (isMatch) + RRETURN; + if (stack.currentFrame->locals.fi >= stack.currentFrame->locals.max || !matchRef(stack.currentFrame->locals.offset, stack.currentFrame->args.subjectPtr, stack.currentFrame->locals.length, md)) + RRETURN; + stack.currentFrame->args.subjectPtr += stack.currentFrame->locals.length; + } + /* Control never reaches here */ + } + + /* If maximizing, find the longest string and work backwards */ + + else { + stack.currentFrame->locals.subjectPtrAtStartOfInstruction = stack.currentFrame->args.subjectPtr; + for (int i = min; i < stack.currentFrame->locals.max; i++) { + if (!matchRef(stack.currentFrame->locals.offset, stack.currentFrame->args.subjectPtr, stack.currentFrame->locals.length, md)) + break; + stack.currentFrame->args.subjectPtr += stack.currentFrame->locals.length; + } + while (stack.currentFrame->args.subjectPtr >= stack.currentFrame->locals.subjectPtrAtStartOfInstruction) { + RECURSIVE_MATCH(21, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain, false); + if (isMatch) + RRETURN; + stack.currentFrame->args.subjectPtr -= stack.currentFrame->locals.length; + } + RRETURN_NO_MATCH; + } + /* Control never reaches here */ + + /* Match a bit-mapped character class, possibly repeatedly. This op code is + used when all the characters in the class have values in the range 0-255, + and either the matching is caseful, or the characters are in the range + 0-127 when UTF-8 processing is enabled. The only difference between + OP_CLASS and OP_NCLASS occurs when a data character outside the range is + encountered. + + First, look past the end of the item to see if there is repeat information + following. Then obey similar code to character type repeats - written out + again for speed. */ + + BEGIN_OPCODE(NCLASS): + BEGIN_OPCODE(CLASS): + stack.currentFrame->locals.data = stack.currentFrame->args.instructionPtr + 1; /* Save for matching */ + stack.currentFrame->args.instructionPtr += 33; /* Advance past the item */ + + switch (*stack.currentFrame->args.instructionPtr) { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + repeatInformationFromInstructionOffset(*stack.currentFrame->args.instructionPtr++ - OP_CRSTAR, minimize, min, stack.currentFrame->locals.max); + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + minimize = (*stack.currentFrame->args.instructionPtr == OP_CRMINRANGE); + min = get2ByteValue(stack.currentFrame->args.instructionPtr + 1); + stack.currentFrame->locals.max = get2ByteValue(stack.currentFrame->args.instructionPtr + 3); + if (stack.currentFrame->locals.max == 0) + stack.currentFrame->locals.max = INT_MAX; + stack.currentFrame->args.instructionPtr += 5; + break; + + default: /* No repeat follows */ + min = stack.currentFrame->locals.max = 1; + break; + } + + /* First, ensure the minimum number of matches are present. */ + + for (int i = 1; i <= min; i++) { + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN_NO_MATCH; + int c = *stack.currentFrame->args.subjectPtr++; + if (c > 255) { + if (stack.currentFrame->locals.data[-1] == OP_CLASS) + RRETURN_NO_MATCH; + } else { + if (!(stack.currentFrame->locals.data[c / 8] & (1 << (c & 7)))) + RRETURN_NO_MATCH; + } + } + + /* If max == min we can continue with the main loop without the + need to recurse. */ + + if (min == stack.currentFrame->locals.max) + NEXT_OPCODE; + + /* If minimizing, keep testing the rest of the expression and advancing + the pointer while it matches the class. */ + if (minimize) { + for (stack.currentFrame->locals.fi = min;; stack.currentFrame->locals.fi++) { + RECURSIVE_MATCH(22, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain, false); + if (isMatch) + RRETURN; + if (stack.currentFrame->locals.fi >= stack.currentFrame->locals.max || stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN; + int c = *stack.currentFrame->args.subjectPtr++; + if (c > 255) { + if (stack.currentFrame->locals.data[-1] == OP_CLASS) + RRETURN; + } else { + if ((stack.currentFrame->locals.data[c/8] & (1 << (c&7))) == 0) + RRETURN; + } + } + /* Control never reaches here */ + } + /* If maximizing, find the longest possible run, then work backwards. */ + else { + stack.currentFrame->locals.subjectPtrAtStartOfInstruction = stack.currentFrame->args.subjectPtr; + + for (int i = min; i < stack.currentFrame->locals.max; i++) { + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + break; + int c = *stack.currentFrame->args.subjectPtr; + if (c > 255) { + if (stack.currentFrame->locals.data[-1] == OP_CLASS) + break; + } else { + if (!(stack.currentFrame->locals.data[c / 8] & (1 << (c & 7)))) + break; + } + ++stack.currentFrame->args.subjectPtr; + } + for (;;) { + RECURSIVE_MATCH(24, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain, false); + if (isMatch) + RRETURN; + if (stack.currentFrame->args.subjectPtr-- == stack.currentFrame->locals.subjectPtrAtStartOfInstruction) + break; /* Stop if tried at original pos */ + } + + RRETURN; + } + /* Control never reaches here */ + + /* Match an extended character class. */ + + BEGIN_OPCODE(XCLASS): + stack.currentFrame->locals.data = stack.currentFrame->args.instructionPtr + 1 + LINK_SIZE; /* Save for matching */ + stack.currentFrame->args.instructionPtr += getLinkValue(stack.currentFrame->args.instructionPtr + 1); /* Advance past the item */ + + switch (*stack.currentFrame->args.instructionPtr) { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + repeatInformationFromInstructionOffset(*stack.currentFrame->args.instructionPtr++ - OP_CRSTAR, minimize, min, stack.currentFrame->locals.max); + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + minimize = (*stack.currentFrame->args.instructionPtr == OP_CRMINRANGE); + min = get2ByteValue(stack.currentFrame->args.instructionPtr + 1); + stack.currentFrame->locals.max = get2ByteValue(stack.currentFrame->args.instructionPtr + 3); + if (stack.currentFrame->locals.max == 0) + stack.currentFrame->locals.max = INT_MAX; + stack.currentFrame->args.instructionPtr += 5; + break; + + default: /* No repeat follows */ + min = stack.currentFrame->locals.max = 1; + } + + /* First, ensure the minimum number of matches are present. */ + + for (int i = 1; i <= min; i++) { + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN_NO_MATCH; + int c = *stack.currentFrame->args.subjectPtr++; + if (!jsc_pcre_xclass(c, stack.currentFrame->locals.data)) + RRETURN_NO_MATCH; + } + + /* If max == min we can continue with the main loop without the + need to recurse. */ + + if (min == stack.currentFrame->locals.max) + NEXT_OPCODE; + + /* If minimizing, keep testing the rest of the expression and advancing + the pointer while it matches the class. */ + + if (minimize) { + for (stack.currentFrame->locals.fi = min;; stack.currentFrame->locals.fi++) { + RECURSIVE_MATCH(26, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain, false); + if (isMatch) + RRETURN; + if (stack.currentFrame->locals.fi >= stack.currentFrame->locals.max || stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN; + int c = *stack.currentFrame->args.subjectPtr++; + if (!jsc_pcre_xclass(c, stack.currentFrame->locals.data)) + RRETURN; + } + /* Control never reaches here */ + } + + /* If maximizing, find the longest possible run, then work backwards. */ + + else { + stack.currentFrame->locals.subjectPtrAtStartOfInstruction = stack.currentFrame->args.subjectPtr; + for (int i = min; i < stack.currentFrame->locals.max; i++) { + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + break; + int c = *stack.currentFrame->args.subjectPtr; + if (!jsc_pcre_xclass(c, stack.currentFrame->locals.data)) + break; + ++stack.currentFrame->args.subjectPtr; + } + for(;;) { + RECURSIVE_MATCH(27, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain, false); + if (isMatch) + RRETURN; + if (stack.currentFrame->args.subjectPtr-- == stack.currentFrame->locals.subjectPtrAtStartOfInstruction) + break; /* Stop if tried at original pos */ + } + RRETURN; + } + + /* Control never reaches here */ + + /* Match a single character, casefully */ + + BEGIN_OPCODE(CHAR): + stack.currentFrame->locals.length = 1; + stack.currentFrame->args.instructionPtr++; + getUTF8CharAndIncrementLength(stack.currentFrame->locals.fc, stack.currentFrame->args.instructionPtr, stack.currentFrame->locals.length); + stack.currentFrame->args.instructionPtr += stack.currentFrame->locals.length; + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN_NO_MATCH; + if (stack.currentFrame->locals.fc != *stack.currentFrame->args.subjectPtr++) + RRETURN_NO_MATCH; + NEXT_OPCODE; + + /* Match a single character, caselessly */ + + BEGIN_OPCODE(CHAR_IGNORING_CASE): { + stack.currentFrame->locals.length = 1; + stack.currentFrame->args.instructionPtr++; + getUTF8CharAndIncrementLength(stack.currentFrame->locals.fc, stack.currentFrame->args.instructionPtr, stack.currentFrame->locals.length); + stack.currentFrame->args.instructionPtr += stack.currentFrame->locals.length; + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN_NO_MATCH; + int dc = *stack.currentFrame->args.subjectPtr++; + if (stack.currentFrame->locals.fc != dc && jsc_pcre_ucp_othercase(stack.currentFrame->locals.fc) != dc) + RRETURN_NO_MATCH; + NEXT_OPCODE; + } + + /* Match a single ASCII character. */ + + BEGIN_OPCODE(ASCII_CHAR): + if (md.endSubject == stack.currentFrame->args.subjectPtr) + RRETURN_NO_MATCH; + if (*stack.currentFrame->args.subjectPtr != stack.currentFrame->args.instructionPtr[1]) + RRETURN_NO_MATCH; + ++stack.currentFrame->args.subjectPtr; + stack.currentFrame->args.instructionPtr += 2; + NEXT_OPCODE; + + /* Match one of two cases of an ASCII letter. */ + + BEGIN_OPCODE(ASCII_LETTER_IGNORING_CASE): + if (md.endSubject == stack.currentFrame->args.subjectPtr) + RRETURN_NO_MATCH; + if ((*stack.currentFrame->args.subjectPtr | 0x20) != stack.currentFrame->args.instructionPtr[1]) + RRETURN_NO_MATCH; + ++stack.currentFrame->args.subjectPtr; + stack.currentFrame->args.instructionPtr += 2; + NEXT_OPCODE; + + /* Match a single character repeatedly; different opcodes share code. */ + + BEGIN_OPCODE(EXACT): + min = stack.currentFrame->locals.max = get2ByteValue(stack.currentFrame->args.instructionPtr + 1); + minimize = false; + stack.currentFrame->args.instructionPtr += 3; + goto REPEATCHAR; + + BEGIN_OPCODE(UPTO): + BEGIN_OPCODE(MINUPTO): + min = 0; + stack.currentFrame->locals.max = get2ByteValue(stack.currentFrame->args.instructionPtr + 1); + minimize = *stack.currentFrame->args.instructionPtr == OP_MINUPTO; + stack.currentFrame->args.instructionPtr += 3; + goto REPEATCHAR; + + BEGIN_OPCODE(STAR): + BEGIN_OPCODE(MINSTAR): + BEGIN_OPCODE(PLUS): + BEGIN_OPCODE(MINPLUS): + BEGIN_OPCODE(QUERY): + BEGIN_OPCODE(MINQUERY): + repeatInformationFromInstructionOffset(*stack.currentFrame->args.instructionPtr++ - OP_STAR, minimize, min, stack.currentFrame->locals.max); + + /* Common code for all repeated single-character matches. We can give + up quickly if there are fewer than the minimum number of characters left in + the subject. */ + + REPEATCHAR: + + stack.currentFrame->locals.length = 1; + getUTF8CharAndIncrementLength(stack.currentFrame->locals.fc, stack.currentFrame->args.instructionPtr, stack.currentFrame->locals.length); + if (min * (stack.currentFrame->locals.fc > 0xFFFF ? 2 : 1) > md.endSubject - stack.currentFrame->args.subjectPtr) + RRETURN_NO_MATCH; + stack.currentFrame->args.instructionPtr += stack.currentFrame->locals.length; + + if (stack.currentFrame->locals.fc <= 0xFFFF) { + othercase = md.ignoreCase ? jsc_pcre_ucp_othercase(stack.currentFrame->locals.fc) : -1; + + for (int i = 1; i <= min; i++) { + if (*stack.currentFrame->args.subjectPtr != stack.currentFrame->locals.fc && *stack.currentFrame->args.subjectPtr != othercase) + RRETURN_NO_MATCH; + ++stack.currentFrame->args.subjectPtr; + } + + if (min == stack.currentFrame->locals.max) + NEXT_OPCODE; + + if (minimize) { + stack.currentFrame->locals.repeatOthercase = othercase; + for (stack.currentFrame->locals.fi = min;; stack.currentFrame->locals.fi++) { + RECURSIVE_MATCH(28, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain, false); + if (isMatch) + RRETURN; + if (stack.currentFrame->locals.fi >= stack.currentFrame->locals.max || stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN; + if (*stack.currentFrame->args.subjectPtr != stack.currentFrame->locals.fc && *stack.currentFrame->args.subjectPtr != stack.currentFrame->locals.repeatOthercase) + RRETURN; + ++stack.currentFrame->args.subjectPtr; + } + /* Control never reaches here */ + } else { + stack.currentFrame->locals.subjectPtrAtStartOfInstruction = stack.currentFrame->args.subjectPtr; + for (int i = min; i < stack.currentFrame->locals.max; i++) { + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + break; + if (*stack.currentFrame->args.subjectPtr != stack.currentFrame->locals.fc && *stack.currentFrame->args.subjectPtr != othercase) + break; + ++stack.currentFrame->args.subjectPtr; + } + while (stack.currentFrame->args.subjectPtr >= stack.currentFrame->locals.subjectPtrAtStartOfInstruction) { + RECURSIVE_MATCH(29, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain, stack.currentFrame->args.groupMatched); + if (isMatch) + RRETURN; + --stack.currentFrame->args.subjectPtr; + } + RRETURN_NO_MATCH; + } + /* Control never reaches here */ + } else { + /* No case on surrogate pairs, so no need to bother with "othercase". */ + + for (int i = 1; i <= min; i++) { + if (*stack.currentFrame->args.subjectPtr != stack.currentFrame->locals.fc) + RRETURN_NO_MATCH; + stack.currentFrame->args.subjectPtr += 2; + } + + if (min == stack.currentFrame->locals.max) + NEXT_OPCODE; + + if (minimize) { + for (stack.currentFrame->locals.fi = min;; stack.currentFrame->locals.fi++) { + RECURSIVE_MATCH(30, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain, false); + if (isMatch) + RRETURN; + if (stack.currentFrame->locals.fi >= stack.currentFrame->locals.max || stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN; + if (*stack.currentFrame->args.subjectPtr != stack.currentFrame->locals.fc) + RRETURN; + stack.currentFrame->args.subjectPtr += 2; + } + /* Control never reaches here */ + } else { + stack.currentFrame->locals.subjectPtrAtStartOfInstruction = stack.currentFrame->args.subjectPtr; + for (int i = min; i < stack.currentFrame->locals.max; i++) { + if (stack.currentFrame->args.subjectPtr > md.endSubject - 2) + break; + if (*stack.currentFrame->args.subjectPtr != stack.currentFrame->locals.fc) + break; + stack.currentFrame->args.subjectPtr += 2; + } + while (stack.currentFrame->args.subjectPtr >= stack.currentFrame->locals.subjectPtrAtStartOfInstruction) { + RECURSIVE_MATCH(31, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain, false); + if (isMatch) + RRETURN; + stack.currentFrame->args.subjectPtr -= 2; + } + RRETURN_NO_MATCH; + } + /* Control never reaches here */ + } + /* Control never reaches here */ + + /* Match a negated single one-byte character. */ + + BEGIN_OPCODE(NOT): { + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN_NO_MATCH; + int b = stack.currentFrame->args.instructionPtr[1]; + int c = *stack.currentFrame->args.subjectPtr++; + stack.currentFrame->args.instructionPtr += 2; + if (md.ignoreCase) { + if (c < 128) + c = toLowerCase(c); + if (toLowerCase(b) == c) + RRETURN_NO_MATCH; + } else { + if (b == c) + RRETURN_NO_MATCH; + } + NEXT_OPCODE; + } + + /* Match a negated single one-byte character repeatedly. This is almost a + repeat of the code for a repeated single character, but I haven't found a + nice way of commoning these up that doesn't require a test of the + positive/negative option for each character match. Maybe that wouldn't add + very much to the time taken, but character matching *is* what this is all + about... */ + + BEGIN_OPCODE(NOTEXACT): + min = stack.currentFrame->locals.max = get2ByteValue(stack.currentFrame->args.instructionPtr + 1); + minimize = false; + stack.currentFrame->args.instructionPtr += 3; + goto REPEATNOTCHAR; + + BEGIN_OPCODE(NOTUPTO): + BEGIN_OPCODE(NOTMINUPTO): + min = 0; + stack.currentFrame->locals.max = get2ByteValue(stack.currentFrame->args.instructionPtr + 1); + minimize = *stack.currentFrame->args.instructionPtr == OP_NOTMINUPTO; + stack.currentFrame->args.instructionPtr += 3; + goto REPEATNOTCHAR; + + BEGIN_OPCODE(NOTSTAR): + BEGIN_OPCODE(NOTMINSTAR): + BEGIN_OPCODE(NOTPLUS): + BEGIN_OPCODE(NOTMINPLUS): + BEGIN_OPCODE(NOTQUERY): + BEGIN_OPCODE(NOTMINQUERY): + repeatInformationFromInstructionOffset(*stack.currentFrame->args.instructionPtr++ - OP_NOTSTAR, minimize, min, stack.currentFrame->locals.max); + + /* Common code for all repeated single-byte matches. We can give up quickly + if there are fewer than the minimum number of bytes left in the + subject. */ + + REPEATNOTCHAR: + if (min > md.endSubject - stack.currentFrame->args.subjectPtr) + RRETURN_NO_MATCH; + stack.currentFrame->locals.fc = *stack.currentFrame->args.instructionPtr++; + + /* The code is duplicated for the caseless and caseful cases, for speed, + since matching characters is likely to be quite common. First, ensure the + minimum number of matches are present. If min = max, continue at the same + level without recursing. Otherwise, if minimizing, keep trying the rest of + the expression and advancing one matching character if failing, up to the + maximum. Alternatively, if maximizing, find the maximum number of + characters and work backwards. */ + + DPRINTF(("negative matching %c{%d,%d}\n", stack.currentFrame->locals.fc, min, stack.currentFrame->locals.max)); + + if (md.ignoreCase) { + if (stack.currentFrame->locals.fc < 128) + stack.currentFrame->locals.fc = toLowerCase(stack.currentFrame->locals.fc); + + for (int i = 1; i <= min; i++) { + int d = *stack.currentFrame->args.subjectPtr++; + if (d < 128) + d = toLowerCase(d); + if (stack.currentFrame->locals.fc == d) + RRETURN_NO_MATCH; + } + + if (min == stack.currentFrame->locals.max) + NEXT_OPCODE; + + if (minimize) { + for (stack.currentFrame->locals.fi = min;; stack.currentFrame->locals.fi++) { + RECURSIVE_MATCH(38, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain, false); + if (isMatch) + RRETURN; + int d = *stack.currentFrame->args.subjectPtr++; + if (d < 128) + d = toLowerCase(d); + if (stack.currentFrame->locals.fi >= stack.currentFrame->locals.max || stack.currentFrame->args.subjectPtr >= md.endSubject || stack.currentFrame->locals.fc == d) + RRETURN; + } + /* Control never reaches here */ + } + + /* Maximize case */ + + else { + stack.currentFrame->locals.subjectPtrAtStartOfInstruction = stack.currentFrame->args.subjectPtr; + + for (int i = min; i < stack.currentFrame->locals.max; i++) { + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + break; + int d = *stack.currentFrame->args.subjectPtr; + if (d < 128) + d = toLowerCase(d); + if (stack.currentFrame->locals.fc == d) + break; + ++stack.currentFrame->args.subjectPtr; + } + for (;;) { + RECURSIVE_MATCH(40, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain, false); + if (isMatch) + RRETURN; + if (stack.currentFrame->args.subjectPtr-- == stack.currentFrame->locals.subjectPtrAtStartOfInstruction) + break; /* Stop if tried at original pos */ + } + + RRETURN; + } + /* Control never reaches here */ + } + + /* Caseful comparisons */ + + else { + for (int i = 1; i <= min; i++) { + int d = *stack.currentFrame->args.subjectPtr++; + if (stack.currentFrame->locals.fc == d) + RRETURN_NO_MATCH; + } + + if (min == stack.currentFrame->locals.max) + NEXT_OPCODE; + + if (minimize) { + for (stack.currentFrame->locals.fi = min;; stack.currentFrame->locals.fi++) { + RECURSIVE_MATCH(42, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain, false); + if (isMatch) + RRETURN; + int d = *stack.currentFrame->args.subjectPtr++; + if (stack.currentFrame->locals.fi >= stack.currentFrame->locals.max || stack.currentFrame->args.subjectPtr >= md.endSubject || stack.currentFrame->locals.fc == d) + RRETURN; + } + /* Control never reaches here */ + } + + /* Maximize case */ + + else { + stack.currentFrame->locals.subjectPtrAtStartOfInstruction = stack.currentFrame->args.subjectPtr; + + for (int i = min; i < stack.currentFrame->locals.max; i++) { + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + break; + int d = *stack.currentFrame->args.subjectPtr; + if (stack.currentFrame->locals.fc == d) + break; + ++stack.currentFrame->args.subjectPtr; + } + for (;;) { + RECURSIVE_MATCH(44, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain, false); + if (isMatch) + RRETURN; + if (stack.currentFrame->args.subjectPtr-- == stack.currentFrame->locals.subjectPtrAtStartOfInstruction) + break; /* Stop if tried at original pos */ + } + + RRETURN; + } + } + /* Control never reaches here */ + + /* Match a single character type repeatedly; several different opcodes + share code. This is very similar to the code for single characters, but we + repeat it in the interests of efficiency. */ + + BEGIN_OPCODE(TYPEEXACT): + min = stack.currentFrame->locals.max = get2ByteValue(stack.currentFrame->args.instructionPtr + 1); + minimize = true; + stack.currentFrame->args.instructionPtr += 3; + goto REPEATTYPE; + + BEGIN_OPCODE(TYPEUPTO): + BEGIN_OPCODE(TYPEMINUPTO): + min = 0; + stack.currentFrame->locals.max = get2ByteValue(stack.currentFrame->args.instructionPtr + 1); + minimize = *stack.currentFrame->args.instructionPtr == OP_TYPEMINUPTO; + stack.currentFrame->args.instructionPtr += 3; + goto REPEATTYPE; + + BEGIN_OPCODE(TYPESTAR): + BEGIN_OPCODE(TYPEMINSTAR): + BEGIN_OPCODE(TYPEPLUS): + BEGIN_OPCODE(TYPEMINPLUS): + BEGIN_OPCODE(TYPEQUERY): + BEGIN_OPCODE(TYPEMINQUERY): + repeatInformationFromInstructionOffset(*stack.currentFrame->args.instructionPtr++ - OP_TYPESTAR, minimize, min, stack.currentFrame->locals.max); + + /* Common code for all repeated single character type matches. Note that + in UTF-8 mode, '.' matches a character of any length, but for the other + character types, the valid characters are all one-byte long. */ + + REPEATTYPE: + stack.currentFrame->locals.ctype = *stack.currentFrame->args.instructionPtr++; /* Code for the character type */ + + /* First, ensure the minimum number of matches are present. Use inline + code for maximizing the speed, and do the type test once at the start + (i.e. keep it out of the loop). Also we can test that there are at least + the minimum number of characters before we start. */ + + if (min > md.endSubject - stack.currentFrame->args.subjectPtr) + RRETURN_NO_MATCH; + if (min > 0) { + switch (stack.currentFrame->locals.ctype) { + case OP_NOT_NEWLINE: + for (int i = 1; i <= min; i++) { + if (isNewline(*stack.currentFrame->args.subjectPtr)) + RRETURN_NO_MATCH; + ++stack.currentFrame->args.subjectPtr; + } + break; + + case OP_NOT_DIGIT: + for (int i = 1; i <= min; i++) { + if (isASCIIDigit(*stack.currentFrame->args.subjectPtr)) + RRETURN_NO_MATCH; + ++stack.currentFrame->args.subjectPtr; + } + break; + + case OP_DIGIT: + for (int i = 1; i <= min; i++) { + if (!isASCIIDigit(*stack.currentFrame->args.subjectPtr)) + RRETURN_NO_MATCH; + ++stack.currentFrame->args.subjectPtr; + } + break; + + case OP_NOT_WHITESPACE: + for (int i = 1; i <= min; i++) { + if (isSpaceChar(*stack.currentFrame->args.subjectPtr)) + RRETURN_NO_MATCH; + ++stack.currentFrame->args.subjectPtr; + } + break; + + case OP_WHITESPACE: + for (int i = 1; i <= min; i++) { + if (!isSpaceChar(*stack.currentFrame->args.subjectPtr)) + RRETURN_NO_MATCH; + ++stack.currentFrame->args.subjectPtr; + } + break; + + case OP_NOT_WORDCHAR: + for (int i = 1; i <= min; i++) { + if (isWordChar(*stack.currentFrame->args.subjectPtr)) + RRETURN_NO_MATCH; + ++stack.currentFrame->args.subjectPtr; + } + break; + + case OP_WORDCHAR: + for (int i = 1; i <= min; i++) { + if (!isWordChar(*stack.currentFrame->args.subjectPtr)) + RRETURN_NO_MATCH; + ++stack.currentFrame->args.subjectPtr; + } + break; + + default: + JS_NOT_REACHED("Invalid character type."); + return matchError(JSRegExpErrorInternal, stack); + } /* End switch(stack.currentFrame->locals.ctype) */ + } + + /* If min = max, continue at the same level without recursing */ + + if (min == stack.currentFrame->locals.max) + NEXT_OPCODE; + + /* If minimizing, we have to test the rest of the pattern before each + subsequent match. */ + + if (minimize) { + for (stack.currentFrame->locals.fi = min;; stack.currentFrame->locals.fi++) { + RECURSIVE_MATCH(48, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain, false); + if (isMatch) + RRETURN; + if (stack.currentFrame->locals.fi >= stack.currentFrame->locals.max || stack.currentFrame->args.subjectPtr >= md.endSubject) + RRETURN; + + int c = *stack.currentFrame->args.subjectPtr++; + switch (stack.currentFrame->locals.ctype) { + case OP_NOT_NEWLINE: + if (isNewline(c)) + RRETURN; + break; + + case OP_NOT_DIGIT: + if (isASCIIDigit(c)) + RRETURN; + break; + + case OP_DIGIT: + if (!isASCIIDigit(c)) + RRETURN; + break; + + case OP_NOT_WHITESPACE: + if (isSpaceChar(c)) + RRETURN; + break; + + case OP_WHITESPACE: + if (!isSpaceChar(c)) + RRETURN; + break; + + case OP_NOT_WORDCHAR: + if (isWordChar(c)) + RRETURN; + break; + + case OP_WORDCHAR: + if (!isWordChar(c)) + RRETURN; + break; + + default: + JS_NOT_REACHED("Invalid character type."); + return matchError(JSRegExpErrorInternal, stack); + } + } + /* Control never reaches here */ + } + + /* If maximizing it is worth using inline code for speed, doing the type + test once at the start (i.e. keep it out of the loop). */ + + else { + stack.currentFrame->locals.subjectPtrAtStartOfInstruction = stack.currentFrame->args.subjectPtr; /* Remember where we started */ + + switch (stack.currentFrame->locals.ctype) { + case OP_NOT_NEWLINE: + for (int i = min; i < stack.currentFrame->locals.max; i++) { + if (stack.currentFrame->args.subjectPtr >= md.endSubject || isNewline(*stack.currentFrame->args.subjectPtr)) + break; + stack.currentFrame->args.subjectPtr++; + } + break; + + case OP_NOT_DIGIT: + for (int i = min; i < stack.currentFrame->locals.max; i++) { + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + break; + int c = *stack.currentFrame->args.subjectPtr; + if (isASCIIDigit(c)) + break; + ++stack.currentFrame->args.subjectPtr; + } + break; + + case OP_DIGIT: + for (int i = min; i < stack.currentFrame->locals.max; i++) { + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + break; + int c = *stack.currentFrame->args.subjectPtr; + if (!isASCIIDigit(c)) + break; + ++stack.currentFrame->args.subjectPtr; + } + break; + + case OP_NOT_WHITESPACE: + for (int i = min; i < stack.currentFrame->locals.max; i++) { + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + break; + int c = *stack.currentFrame->args.subjectPtr; + if (isSpaceChar(c)) + break; + ++stack.currentFrame->args.subjectPtr; + } + break; + + case OP_WHITESPACE: + for (int i = min; i < stack.currentFrame->locals.max; i++) { + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + break; + int c = *stack.currentFrame->args.subjectPtr; + if (!isSpaceChar(c)) + break; + ++stack.currentFrame->args.subjectPtr; + } + break; + + case OP_NOT_WORDCHAR: + for (int i = min; i < stack.currentFrame->locals.max; i++) { + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + break; + int c = *stack.currentFrame->args.subjectPtr; + if (isWordChar(c)) + break; + ++stack.currentFrame->args.subjectPtr; + } + break; + + case OP_WORDCHAR: + for (int i = min; i < stack.currentFrame->locals.max; i++) { + if (stack.currentFrame->args.subjectPtr >= md.endSubject) + break; + int c = *stack.currentFrame->args.subjectPtr; + if (!isWordChar(c)) + break; + ++stack.currentFrame->args.subjectPtr; + } + break; + + default: + JS_NOT_REACHED("Invalid character type."); + return matchError(JSRegExpErrorInternal, stack); + } + + /* stack.currentFrame->args.subjectPtr is now past the end of the maximum run */ + + for (;;) { + RECURSIVE_MATCH(52, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain, false); + if (isMatch) + RRETURN; + if (stack.currentFrame->args.subjectPtr-- == stack.currentFrame->locals.subjectPtrAtStartOfInstruction) + break; /* Stop if tried at original pos */ + } + + /* Get here if we can't make it match with any permitted repetitions */ + + RRETURN; + } + /* Control never reaches here */ + + BEGIN_OPCODE(CRMINPLUS): + BEGIN_OPCODE(CRMINQUERY): + BEGIN_OPCODE(CRMINRANGE): + BEGIN_OPCODE(CRMINSTAR): + BEGIN_OPCODE(CRPLUS): + BEGIN_OPCODE(CRQUERY): + BEGIN_OPCODE(CRRANGE): + BEGIN_OPCODE(CRSTAR): + JS_NOT_REACHED("Invalid opcode."); + return matchError(JSRegExpErrorInternal, stack); + +#ifdef USE_COMPUTED_GOTO_FOR_MATCH_OPCODE_LOOP + CAPTURING_BRACKET: +#else + default: +#endif + /* Opening capturing bracket. If there is space in the offset vector, save + the current subject position in the working slot at the top of the vector. We + mustn't change the current values of the data slot, because they may be set + from a previous iteration of this group, and be referred to by a reference + inside the group. + + If the bracket fails to match, we need to restore this value and also the + values of the final offsets, in case they were set by a previous iteration of + the same bracket. + + If there isn't enough space in the offset vector, treat this as if it were a + non-capturing bracket. Don't worry about setting the flag for the error case + here; that is handled in the code for KET. */ + + JS_ASSERT(*stack.currentFrame->args.instructionPtr > OP_BRA); + + LOCALS(number) = *stack.currentFrame->args.instructionPtr - OP_BRA; + stack.currentFrame->extractBrackets(stack.currentFrame->args.instructionPtr); + DPRINTF(("opening capturing bracket %d\n", stack.currentFrame->locals.number)); + + /* For extended extraction brackets (large number), we have to fish out the + number from a dummy opcode at the start. */ + + if (stack.currentFrame->locals.number > EXTRACT_BASIC_MAX) + stack.currentFrame->locals.number = get2ByteValue(stack.currentFrame->args.instructionPtr + 4 + LINK_SIZE); + stack.currentFrame->locals.offset = 2 * stack.currentFrame->locals.number; + + JS_ASSERT_IF(LOCALS(number), LOCALS(minBracket) <= LOCALS(number) && LOCALS(number) < LOCALS(limitBracket)); + + if (stack.currentFrame->locals.offset < md.offsetMax) { + stack.currentFrame->locals.savedSubjectOffset = md.offsetVector[md.offsetEnd - stack.currentFrame->locals.number]; + DPRINTF(("setting subject offset for bracket to %d\n", stack.currentFrame->args.subjectPtr - md.startSubject)); + md.offsetVector[md.offsetEnd - stack.currentFrame->locals.number] = stack.currentFrame->args.subjectPtr - md.startSubject; + stack.currentFrame->locals.skipBytes = 3; /* For OP_BRAs. */ + + do { + RECURSIVE_MATCH_NEW_GROUP(1, stack.currentFrame->args.instructionPtr + stack.currentFrame->locals.skipBytes + LINK_SIZE, stack.currentFrame->args.bracketChain, stack.currentFrame->args.groupMatched); + if (isMatch) + RRETURN; + stack.currentFrame->locals.skipBytes = 1; /* For OP_ALTs. */ + stack.currentFrame->args.instructionPtr += getLinkValue(stack.currentFrame->args.instructionPtr + 1); + } while (*stack.currentFrame->args.instructionPtr == OP_ALT); + + DPRINTF(("bracket %d failed\n", stack.currentFrame->locals.number)); + for (size_t i = LOCALS(minBracket); i < size_t(LOCALS(limitBracket)); ++i) + md.setOffsetPair(i, -1, -1); + DPRINTF(("restoring subject offset for bracket to %d\n", stack.currentFrame->locals.savedSubjectOffset)); + md.offsetVector[md.offsetEnd - stack.currentFrame->locals.number] = stack.currentFrame->locals.savedSubjectOffset; + + RRETURN; + } + + /* Insufficient room for saving captured contents */ + + goto NON_CAPTURING_BRACKET; + } + + /* Do not stick any code in here without much thought; it is assumed + that "continue" in the code above comes out to here to repeat the main + loop. */ + + } /* End of main loop */ + + JS_NOT_REACHED("Loop does not fallthru."); + +#ifndef USE_COMPUTED_GOTO_FOR_MATCH_RECURSION + +RRETURN_SWITCH: + switch (stack.currentFrame->returnLocation) { + case 0: goto RETURN; + case 1: goto RRETURN_1; + case 2: goto RRETURN_2; + case 6: goto RRETURN_6; + case 7: goto RRETURN_7; + case 14: goto RRETURN_14; + case 15: goto RRETURN_15; + case 16: goto RRETURN_16; + case 17: goto RRETURN_17; + case 18: goto RRETURN_18; + case 19: goto RRETURN_19; + case 20: goto RRETURN_20; + case 21: goto RRETURN_21; + case 22: goto RRETURN_22; + case 24: goto RRETURN_24; + case 26: goto RRETURN_26; + case 27: goto RRETURN_27; + case 28: goto RRETURN_28; + case 29: goto RRETURN_29; + case 30: goto RRETURN_30; + case 31: goto RRETURN_31; + case 38: goto RRETURN_38; + case 40: goto RRETURN_40; + case 42: goto RRETURN_42; + case 44: goto RRETURN_44; + case 48: goto RRETURN_48; + case 52: goto RRETURN_52; + } + + JS_NOT_REACHED("Bad computed return location."); + return matchError(JSRegExpErrorInternal, stack); + +#endif + +RETURN: + return isMatch; +} + + +/************************************************* +* Execute a Regular Expression * +*************************************************/ + +/* This function applies a compiled re to a subject string and picks out +portions of the string if it matches. Two elements in the vector are set for +each substring: the offsets to the start and end of the substring. + +Arguments: + re points to the compiled expression + extra_data points to extra data or is NULL + subject points to the subject string + length length of subject string (may contain binary zeros) + start_offset where to start in the subject string + options option bits + offsets points to a vector of ints to be filled in with offsets + offsetCount the number of elements in the vector + +Returns: > 0 => success; value is the number of elements filled in + = 0 => success, but offsets is not big enough + -1 => failed to match + < -1 => some kind of unexpected problem +*/ + +static void tryFirstByteOptimization(const UChar*& subjectPtr, const UChar* endSubject, int firstByte, bool firstByteIsCaseless, bool useMultiLineFirstCharOptimization, const UChar* originalSubjectStart) +{ + // If firstByte is set, try scanning to the first instance of that byte + // no need to try and match against any earlier part of the subject string. + if (firstByte >= 0) { + UChar firstChar = firstByte; + if (firstByteIsCaseless) + while (subjectPtr < endSubject) { + int c = *subjectPtr; + if (c > 127) + break; + if (toLowerCase(c) == firstChar) + break; + subjectPtr++; + } + else { + while (subjectPtr < endSubject && *subjectPtr != firstChar) + subjectPtr++; + } + } else if (useMultiLineFirstCharOptimization) { + /* Or to just after \n for a multiline match if possible */ + // I'm not sure why this != originalSubjectStart check is necessary -- ecs 11/18/07 + if (subjectPtr > originalSubjectStart) { + while (subjectPtr < endSubject && !isNewline(subjectPtr[-1])) + subjectPtr++; + } + } +} + +static bool tryRequiredByteOptimization(const UChar*& subjectPtr, const UChar* endSubject, int reqByte, int reqByte2, bool reqByteIsCaseless, bool hasFirstByte, const UChar*& reqBytePtr) +{ + /* If reqByte is set, we know that that character must appear in the subject + for the match to succeed. If the first character is set, reqByte must be + later in the subject; otherwise the test starts at the match point. This + optimization can save a huge amount of backtracking in patterns with nested + unlimited repeats that aren't going to match. Writing separate code for + cased/caseless versions makes it go faster, as does using an autoincrement + and backing off on a match. + + HOWEVER: when the subject string is very, very long, searching to its end can + take a long time, and give bad performance on quite ordinary patterns. This + showed up when somebody was matching /^C/ on a 32-megabyte string... so we + don't do this when the string is sufficiently long. + */ + + if (reqByte >= 0 && endSubject - subjectPtr < REQ_BYTE_MAX) { + const UChar* p = subjectPtr + (hasFirstByte ? 1 : 0); + + /* We don't need to repeat the search if we haven't yet reached the + place we found it at last time. */ + + if (p > reqBytePtr) { + if (reqByteIsCaseless) { + while (p < endSubject) { + int pp = *p++; + if (pp == reqByte || pp == reqByte2) { + p--; + break; + } + } + } else { + while (p < endSubject) { + if (*p++ == reqByte) { + p--; + break; + } + } + } + + /* If we can't find the required character, break the matching loop */ + + if (p >= endSubject) + return true; + + /* If we have found the required character, save the point where we + found it, so that we don't search again next time round the loop if + the start hasn't passed this character yet. */ + + reqBytePtr = p; + } + } + return false; +} + +int jsRegExpExecute(JSContext *cx, const JSRegExp* re, + const UChar* subject, int length, int start_offset, int* offsets, + int offsetCount) +{ + JS_ASSERT(re); + JS_ASSERT(subject || !length); + JS_ASSERT(offsetCount >= 0); + JS_ASSERT(offsets || offsetCount == 0); + + MatchData matchBlock; + matchBlock.startSubject = subject; + matchBlock.endSubject = matchBlock.startSubject + length; + const UChar* endSubject = matchBlock.endSubject; + + matchBlock.multiline = (re->options & MatchAcrossMultipleLinesOption); + matchBlock.ignoreCase = (re->options & IgnoreCaseOption); + + /* Use the vector supplied, rounding down its size to a multiple of 3. */ + int ocount = offsetCount - (offsetCount % 3); + + matchBlock.offsetVector = offsets; + matchBlock.offsetEnd = ocount; + matchBlock.offsetMax = (2*ocount)/3; + matchBlock.offsetOverflow = false; + + /* Compute the minimum number of offsets that we need to reset each time. Doing + this makes a huge difference to execution time when there aren't many brackets + in the pattern. */ + + int resetCount = 2 + re->topBracket * 2; + if (resetCount > offsetCount) + resetCount = ocount; + + /* Reset the working variable associated with each extraction. These should + never be used unless previously set, but they get saved and restored, and so we + initialize them to avoid reading uninitialized locations. */ + + if (matchBlock.offsetVector) { + int* iptr = matchBlock.offsetVector + ocount; + int* iend = iptr - resetCount/2 + 1; + while (--iptr >= iend) + *iptr = -1; + } + + /* Set up the first character to match, if available. The firstByte value is + never set for an anchored regular expression, but the anchoring may be forced + at run time, so we have to test for anchoring. The first char may be unset for + an unanchored pattern, of course. If there's no first char and the pattern was + studied, there may be a bitmap of possible first characters. */ + + bool firstByteIsCaseless = false; + int firstByte = -1; + if (re->options & UseFirstByteOptimizationOption) { + firstByte = re->firstByte & 255; + if ((firstByteIsCaseless = (re->firstByte & REQ_IGNORE_CASE))) + firstByte = toLowerCase(firstByte); + } + + /* For anchored or unanchored matches, there may be a "last known required + character" set. */ + + bool reqByteIsCaseless = false; + int reqByte = -1; + int reqByte2 = -1; + if (re->options & UseRequiredByteOptimizationOption) { + reqByte = re->reqByte & 255; + reqByteIsCaseless = (re->reqByte & REQ_IGNORE_CASE); + reqByte2 = flipCase(reqByte); + } + + /* Loop for handling unanchored repeated matching attempts; for anchored regexs + the loop runs just once. */ + + const UChar* startMatch = subject + start_offset; + const UChar* reqBytePtr = startMatch - 1; + bool useMultiLineFirstCharOptimization = re->options & UseMultiLineFirstByteOptimizationOption; + + do { + /* Reset the maximum number of extractions we might see. */ + if (matchBlock.offsetVector) { + int* iptr = matchBlock.offsetVector; + int* iend = iptr + resetCount; + while (iptr < iend) + *iptr++ = -1; + } + + tryFirstByteOptimization(startMatch, endSubject, firstByte, firstByteIsCaseless, useMultiLineFirstCharOptimization, matchBlock.startSubject + start_offset); + if (tryRequiredByteOptimization(startMatch, endSubject, reqByte, reqByte2, reqByteIsCaseless, firstByte >= 0, reqBytePtr)) + break; + + /* When a match occurs, substrings will be set for all internal extractions; + we just need to set up the whole thing as substring 0 before returning. If + there were too many extractions, set the return code to zero. In the case + where we had to get some local store to hold offsets for backreferences, copy + those back references that we can. In this case there need not be overflow + if certain parts of the pattern were not used. */ + + /* The code starts after the JSRegExp block and the capture name table. */ + const unsigned char* start_code = (const unsigned char*)(re + 1); + + int returnCode = match(&cx->regExpPool, startMatch, start_code, 2, matchBlock); + + /* When the result is no match, advance the pointer to the next character + and continue. */ + if (returnCode == 0) { + startMatch++; + continue; + } + + if (returnCode != 1) { + JS_ASSERT(returnCode == JSRegExpErrorHitLimit || returnCode == JSRegExpErrorNoMemory); + DPRINTF((">>>> error: returning %d\n", returnCode)); + return returnCode; + } + + /* We have a match! */ + + returnCode = matchBlock.offsetOverflow ? 0 : matchBlock.endOffsetTop / 2; + + if (offsetCount < 2) + returnCode = 0; + else { + offsets[0] = startMatch - matchBlock.startSubject; + offsets[1] = matchBlock.endMatchPtr - matchBlock.startSubject; + } + + DPRINTF((">>>> returning %d\n", returnCode)); + return returnCode; + } while (!(re->options & IsAnchoredOption) && startMatch <= endSubject); + + DPRINTF((">>>> returning PCRE_ERROR_NOMATCH\n")); + return JSRegExpErrorNoMatch; +} diff --git a/js/src/yarr/pcre/pcre_internal.h b/js/src/yarr/pcre/pcre_internal.h new file mode 100644 index 000000000000..d677cfcfa255 --- /dev/null +++ b/js/src/yarr/pcre/pcre_internal.h @@ -0,0 +1,434 @@ +/* This is JavaScriptCore's variant of the PCRE library. While this library +started out as a copy of PCRE, many of the features of PCRE have been +removed. This library now supports only the regular expression features +required by the JavaScript language specification, and has only the functions +needed by JavaScriptCore and the rest of WebKit. + + Originally written by Philip Hazel + Copyright (c) 1997-2006 University of Cambridge + Copyright (C) 2002, 2004, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + +/* This header contains definitions that are shared between the different +modules, but which are not relevant to the exported API. This includes some +functions whose names all begin with "_pcre_". */ + +#ifndef PCRE_INTERNAL_H +#define PCRE_INTERNAL_H + +/* Bit definitions for entries in the pcre_ctypes table. */ + +#define ctype_space 0x01 +#define ctype_xdigit 0x08 +#define ctype_word 0x10 /* alphameric or '_' */ + +/* Offsets for the bitmap tables in pcre_cbits. Each table contains a set +of bits for a class map. Some classes are built by combining these tables. */ + +#define cbit_space 0 /* \s */ +#define cbit_digit 32 /* \d */ +#define cbit_word 64 /* \w */ +#define cbit_length 96 /* Length of the cbits table */ + +/* Offsets of the various tables from the base tables pointer, and +total length. */ + +#define lcc_offset 0 +#define fcc_offset 128 +#define cbits_offset 256 +#define ctypes_offset (cbits_offset + cbit_length) +#define tables_length (ctypes_offset + 128) + +#ifndef DFTABLES + +#include "pcre.h" + +/* The value of LINK_SIZE determines the number of bytes used to store links as +offsets within the compiled regex. The default is 2, which allows for compiled +patterns up to 64K long. */ + +#define LINK_SIZE 3 + +/* Use a macro for debugging printing, 'cause that eliminates the use of #ifdef +inline, and there are *still* stupid compilers about that don't like indented +pre-processor statements, or at least there were when I first wrote this. After +all, it had only been about 10 years then... */ + +#ifdef DEBUG +#define DPRINTF(p) /*printf p; fflush(stdout);*/ +#else +#define DPRINTF(p) /*nothing*/ +#endif + +/* PCRE keeps offsets in its compiled code as 2-byte quantities (always stored +in big-endian order) by default. These are used, for example, to link from the +start of a subpattern to its alternatives and its end. The use of 2 bytes per +offset limits the size of the compiled regex to around 64K, which is big enough +for almost everybody. However, I received a request for an even bigger limit. +For this reason, and also to make the code easier to maintain, the storing and +loading of offsets from the byte string is now handled by the functions that are +defined here. */ + +/* PCRE uses some other 2-byte quantities that do not change when the size of +offsets changes. There are used for repeat counts and for other things such as +capturing parenthesis numbers in back references. */ + +static inline void put2ByteValue(unsigned char* opcodePtr, int value) +{ + JS_ASSERT(value >= 0 && value <= 0xFFFF); + opcodePtr[0] = value >> 8; + opcodePtr[1] = value; +} + +static inline void put3ByteValue(unsigned char* opcodePtr, int value) +{ + JS_ASSERT(value >= 0 && value <= 0xFFFFFF); + opcodePtr[0] = value >> 16; + opcodePtr[1] = value >> 8; + opcodePtr[2] = value; +} + +static inline int get2ByteValue(const unsigned char* opcodePtr) +{ + return (opcodePtr[0] << 8) | opcodePtr[1]; +} + +static inline int get3ByteValue(const unsigned char* opcodePtr) +{ + return (opcodePtr[0] << 16) | (opcodePtr[1] << 8) | opcodePtr[2]; +} + +static inline void put2ByteValueAndAdvance(unsigned char*& opcodePtr, int value) +{ + put2ByteValue(opcodePtr, value); + opcodePtr += 2; +} + +static inline void put3ByteValueAndAdvance(unsigned char*& opcodePtr, int value) +{ + put3ByteValue(opcodePtr, value); + opcodePtr += 3; +} + +static inline void putLinkValueAllowZero(unsigned char* opcodePtr, int value) +{ +#if LINK_SIZE == 3 + put3ByteValue(opcodePtr, value); +#elif LINK_SIZE == 2 + put2ByteValue(opcodePtr, value); +#else +# error LINK_SIZE not supported. +#endif +} + +static inline int getLinkValueAllowZero(const unsigned char* opcodePtr) +{ +#if LINK_SIZE == 3 + return get3ByteValue(opcodePtr); +#elif LINK_SIZE == 2 + return get2ByteValue(opcodePtr); +#else +# error LINK_SIZE not supported. +#endif +} + +#define MAX_PATTERN_SIZE 1024 * 1024 // Derived by empirical testing of compile time in PCRE and WREC. +JS_STATIC_ASSERT(MAX_PATTERN_SIZE < (1 << (8 * LINK_SIZE))); + +static inline void putLinkValue(unsigned char* opcodePtr, int value) +{ + JS_ASSERT(value); + putLinkValueAllowZero(opcodePtr, value); +} + +static inline int getLinkValue(const unsigned char* opcodePtr) +{ + int value = getLinkValueAllowZero(opcodePtr); + JS_ASSERT(value); + return value; +} + +static inline void putLinkValueAndAdvance(unsigned char*& opcodePtr, int value) +{ + putLinkValue(opcodePtr, value); + opcodePtr += LINK_SIZE; +} + +static inline void putLinkValueAllowZeroAndAdvance(unsigned char*& opcodePtr, int value) +{ + putLinkValueAllowZero(opcodePtr, value); + opcodePtr += LINK_SIZE; +} + +// FIXME: These are really more of a "compiled regexp state" than "regexp options" +enum RegExpOptions { + UseFirstByteOptimizationOption = 0x40000000, /* firstByte is set */ + UseRequiredByteOptimizationOption = 0x20000000, /* reqByte is set */ + UseMultiLineFirstByteOptimizationOption = 0x10000000, /* start after \n for multiline */ + IsAnchoredOption = 0x02000000, /* can't use partial with this regex */ + IgnoreCaseOption = 0x00000001, + MatchAcrossMultipleLinesOption = 0x00000002 +}; + +/* Flags added to firstByte or reqByte; a "non-literal" item is either a +variable-length repeat, or a anything other than literal characters. */ + +#define REQ_IGNORE_CASE 0x0100 /* indicates should ignore case */ +#define REQ_VARY 0x0200 /* reqByte followed non-literal item */ + +/* Miscellaneous definitions */ + +/* Flag bits and data types for the extended class (OP_XCLASS) for classes that +contain UTF-8 characters with values greater than 255. */ + +#define XCL_NOT 0x01 /* Flag: this is a negative class */ +#define XCL_MAP 0x02 /* Flag: a 32-byte map is present */ + +#define XCL_END 0 /* Marks end of individual items */ +#define XCL_SINGLE 1 /* Single item (one multibyte char) follows */ +#define XCL_RANGE 2 /* A range (two multibyte chars) follows */ + +/* These are escaped items that aren't just an encoding of a particular data +value such as \n. They must have non-zero values, as check_escape() returns +their negation. Also, they must appear in the same order as in the opcode +definitions below, up to ESC_w. The final one must be +ESC_REF as subsequent values are used for \1, \2, \3, etc. There is are two +tests in the code for an escape > ESC_b and <= ESC_w to +detect the types that may be repeated. These are the types that consume +characters. If any new escapes are put in between that don't consume a +character, that code will have to change. */ + +enum { ESC_B = 1, ESC_b, ESC_D, ESC_d, ESC_S, ESC_s, ESC_W, ESC_w, ESC_REF }; + +/* Opcode table: OP_BRA must be last, as all values >= it are used for brackets +that extract substrings. Starting from 1 (i.e. after OP_END), the values up to +OP_EOD must correspond in order to the list of escapes immediately above. +Note that whenever this list is updated, the two macro definitions that follow +must also be updated to match. */ + +#define FOR_EACH_OPCODE(macro) \ + macro(END) \ + \ + , macro(NOT_WORD_BOUNDARY) \ + , macro(WORD_BOUNDARY) \ + , macro(NOT_DIGIT) \ + , macro(DIGIT) \ + , macro(NOT_WHITESPACE) \ + , macro(WHITESPACE) \ + , macro(NOT_WORDCHAR) \ + , macro(WORDCHAR) \ + \ + , macro(NOT_NEWLINE) \ + \ + , macro(CIRC) \ + , macro(DOLL) \ + , macro(BOL) \ + , macro(EOL) \ + , macro(CHAR) \ + , macro(CHAR_IGNORING_CASE) \ + , macro(ASCII_CHAR) \ + , macro(ASCII_LETTER_IGNORING_CASE) \ + , macro(NOT) \ + \ + , macro(STAR) \ + , macro(MINSTAR) \ + , macro(PLUS) \ + , macro(MINPLUS) \ + , macro(QUERY) \ + , macro(MINQUERY) \ + , macro(UPTO) \ + , macro(MINUPTO) \ + , macro(EXACT) \ + \ + , macro(NOTSTAR) \ + , macro(NOTMINSTAR) \ + , macro(NOTPLUS) \ + , macro(NOTMINPLUS) \ + , macro(NOTQUERY) \ + , macro(NOTMINQUERY) \ + , macro(NOTUPTO) \ + , macro(NOTMINUPTO) \ + , macro(NOTEXACT) \ + \ + , macro(TYPESTAR) \ + , macro(TYPEMINSTAR) \ + , macro(TYPEPLUS) \ + , macro(TYPEMINPLUS) \ + , macro(TYPEQUERY) \ + , macro(TYPEMINQUERY) \ + , macro(TYPEUPTO) \ + , macro(TYPEMINUPTO) \ + , macro(TYPEEXACT) \ + \ + , macro(CRSTAR) \ + , macro(CRMINSTAR) \ + , macro(CRPLUS) \ + , macro(CRMINPLUS) \ + , macro(CRQUERY) \ + , macro(CRMINQUERY) \ + , macro(CRRANGE) \ + , macro(CRMINRANGE) \ + \ + , macro(CLASS) \ + , macro(NCLASS) \ + , macro(XCLASS) \ + \ + , macro(REF) \ + \ + , macro(ALT) \ + , macro(KET) \ + , macro(KETRMAX) \ + , macro(KETRMIN) \ + \ + , macro(ASSERT) \ + , macro(ASSERT_NOT) \ + \ + , macro(BRAZERO) \ + , macro(BRAMINZERO) \ + , macro(BRANUMBER) \ + , macro(BRA) + +#define OPCODE_ENUM_VALUE(opcode) OP_##opcode +enum { FOR_EACH_OPCODE(OPCODE_ENUM_VALUE) }; + +/* WARNING WARNING WARNING: There is an implicit assumption in pcre.c and +study.c that all opcodes are less than 128 in value. This makes handling UTF-8 +character sequences easier. */ + +/* The highest extraction number before we have to start using additional +bytes. (Originally PCRE didn't have support for extraction counts higher than +this number.) The value is limited by the number of opcodes left after OP_BRA, +i.e. 255 - OP_BRA. We actually set it a bit lower to leave room for additional +opcodes. */ + +/* FIXME: Note that OP_BRA + 100 is > 128, so the two comments above +are in conflict! */ + +#define EXTRACT_BASIC_MAX 100 + +/* The code vector runs on as long as necessary after the end. */ + +struct JSRegExp { + unsigned options; + + unsigned short topBracket; + unsigned short topBackref; + + unsigned short firstByte; + unsigned short reqByte; +}; + +/* Internal shared data tables. These are tables that are used by more than one + of the exported public functions. They have to be "external" in the C sense, + but are not part of the PCRE public API. The data for these tables is in the + pcre_tables.c module. */ + +#define jsc_pcre_utf8_table1_size 6 + +extern const int jsc_pcre_utf8_table1[6]; +extern const int jsc_pcre_utf8_table2[6]; +extern const int jsc_pcre_utf8_table3[6]; +extern const unsigned char jsc_pcre_utf8_table4[0x40]; + +extern const unsigned char jsc_pcre_default_tables[tables_length]; + +static inline unsigned char toLowerCase(unsigned char c) +{ + static const unsigned char* lowerCaseChars = jsc_pcre_default_tables + lcc_offset; + return lowerCaseChars[c]; +} + +static inline unsigned char flipCase(unsigned char c) +{ + static const unsigned char* flippedCaseChars = jsc_pcre_default_tables + fcc_offset; + return flippedCaseChars[c]; +} + +static inline unsigned char classBitmapForChar(unsigned char c) +{ + static const unsigned char* charClassBitmaps = jsc_pcre_default_tables + cbits_offset; + return charClassBitmaps[c]; +} + +static inline unsigned char charTypeForChar(unsigned char c) +{ + const unsigned char* charTypeMap = jsc_pcre_default_tables + ctypes_offset; + return charTypeMap[c]; +} + +static inline bool isWordChar(UChar c) +{ + return c < 128 && (charTypeForChar(c) & ctype_word); +} + +static inline bool isSpaceChar(UChar c) +{ + return (c < 128 && (charTypeForChar(c) & ctype_space)) || c == 0x00A0; +} + +static inline bool isNewline(UChar nl) +{ + return (nl == 0xA || nl == 0xD || nl == 0x2028 || nl == 0x2029); +} + +static inline bool isBracketStartOpcode(unsigned char opcode) +{ + if (opcode >= OP_BRA) + return true; + switch (opcode) { + case OP_ASSERT: + case OP_ASSERT_NOT: + return true; + default: + return false; + } +} + +static inline void advanceToEndOfBracket(const unsigned char*& opcodePtr) +{ + JS_ASSERT(isBracketStartOpcode(*opcodePtr) || *opcodePtr == OP_ALT); + do + opcodePtr += getLinkValue(opcodePtr + 1); + while (*opcodePtr == OP_ALT); +} + +/* Internal shared functions. These are functions that are used in more +that one of the source files. They have to have external linkage, but +but are not part of the public API and so not exported from the library. */ + +extern int jsc_pcre_ucp_othercase(unsigned); +extern bool jsc_pcre_xclass(int, const unsigned char*); + +#endif + +#endif + +/* End of pcre_internal.h */ diff --git a/js/src/yarr/pcre/pcre_tables.cpp b/js/src/yarr/pcre/pcre_tables.cpp new file mode 100644 index 000000000000..b1ac229d5912 --- /dev/null +++ b/js/src/yarr/pcre/pcre_tables.cpp @@ -0,0 +1,71 @@ +/* This is JavaScriptCore's variant of the PCRE library. While this library +started out as a copy of PCRE, many of the features of PCRE have been +removed. This library now supports only the regular expression features +required by the JavaScript language specification, and has only the functions +needed by JavaScriptCore and the rest of WebKit. + + Originally written by Philip Hazel + Copyright (c) 1997-2006 University of Cambridge + Copyright (C) 2002, 2004, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + +/* This module contains some fixed tables that are used by more than one of the +PCRE code modules. */ + +#include "pcre_internal.h" + +/************************************************* +* Tables for UTF-8 support * +*************************************************/ + +/* These are the breakpoints for different numbers of bytes in a UTF-8 +character. */ + +const int jsc_pcre_utf8_table1[6] = + { 0x7f, 0x7ff, 0xffff, 0x1fffff, 0x3ffffff, 0x7fffffff}; + +/* These are the indicator bits and the mask for the data bits to set in the +first byte of a character, indexed by the number of additional bytes. */ + +const int jsc_pcre_utf8_table2[6] = { 0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc}; +const int jsc_pcre_utf8_table3[6] = { 0xff, 0x1f, 0x0f, 0x07, 0x03, 0x01}; + +/* Table of the number of extra characters, indexed by the first character +masked with 0x3f. The highest number for a valid UTF-8 character is in fact +0x3d. */ + +const unsigned char jsc_pcre_utf8_table4[0x40] = { + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 }; + +#include "chartables.c" diff --git a/js/src/yarr/pcre/pcre_ucp_searchfuncs.cpp b/js/src/yarr/pcre/pcre_ucp_searchfuncs.cpp new file mode 100644 index 000000000000..b97db921c981 --- /dev/null +++ b/js/src/yarr/pcre/pcre_ucp_searchfuncs.cpp @@ -0,0 +1,98 @@ +/* This is JavaScriptCore's variant of the PCRE library. While this library +started out as a copy of PCRE, many of the features of PCRE have been +removed. This library now supports only the regular expression features +required by the JavaScript language specification, and has only the functions +needed by JavaScriptCore and the rest of WebKit. + + Originally written by Philip Hazel + Copyright (c) 1997-2006 University of Cambridge + Copyright (C) 2002, 2004, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains code for searching the table of Unicode character +properties. */ + +#include "pcre_internal.h" + +#include "ucpinternal.h" /* Internal table details */ +#include "ucptable.cpp" /* The table itself */ + +/************************************************* +* Search table and return other case * +*************************************************/ + +/* If the given character is a letter, and there is another case for the +letter, return the other case. Otherwise, return -1. + +Arguments: + c the character value + +Returns: the other case or -1 if none +*/ + +int jsc_pcre_ucp_othercase(unsigned c) +{ + int bot = 0; + int top = sizeof(ucp_table) / sizeof(cnode); + int mid; + + /* The table is searched using a binary chop. You might think that using + intermediate variables to hold some of the common expressions would speed + things up, but tests with gcc 3.4.4 on Linux showed that, on the contrary, it + makes things a lot slower. */ + + for (;;) { + if (top <= bot) + return -1; + mid = (bot + top) >> 1; + if (c == (ucp_table[mid].f0 & f0_charmask)) + break; + if (c < (ucp_table[mid].f0 & f0_charmask)) + top = mid; + else { + if ((ucp_table[mid].f0 & f0_rangeflag) && (c <= (ucp_table[mid].f0 & f0_charmask) + (ucp_table[mid].f1 & f1_rangemask))) + break; + bot = mid + 1; + } + } + + /* Found an entry in the table. Return -1 for a range entry. Otherwise return + the other case if there is one, else -1. */ + + if (ucp_table[mid].f0 & f0_rangeflag) + return -1; + + int offset = ucp_table[mid].f1 & f1_casemask; + if (offset & f1_caseneg) + offset |= f1_caseneg; + return !offset ? -1 : c + offset; +} diff --git a/js/src/yarr/pcre/pcre_xclass.cpp b/js/src/yarr/pcre/pcre_xclass.cpp new file mode 100644 index 000000000000..8e59018ead0c --- /dev/null +++ b/js/src/yarr/pcre/pcre_xclass.cpp @@ -0,0 +1,114 @@ +/* This is JavaScriptCore's variant of the PCRE library. While this library +started out as a copy of PCRE, many of the features of PCRE have been +removed. This library now supports only the regular expression features +required by the JavaScript language specification, and has only the functions +needed by JavaScriptCore and the rest of WebKit. + + Originally written by Philip Hazel + Copyright (c) 1997-2006 University of Cambridge + Copyright (C) 2002, 2004, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + +/* This module contains an internal function that is used to match an extended +class (one that contains characters whose values are > 255). */ + +#include "pcre_internal.h" + +/************************************************* +* Match character against an XCLASS * +*************************************************/ + +/* This function is called to match a character against an extended class that +might contain values > 255. + +Arguments: + c the character + data points to the flag byte of the XCLASS data + +Returns: true if character matches, else false +*/ + +/* Get the next UTF-8 character, advancing the pointer. This is called when we + know we are in UTF-8 mode. */ + +static inline void getUTF8CharAndAdvancePointer(int& c, const unsigned char*& subjectPtr) +{ + c = *subjectPtr++; + if ((c & 0xc0) == 0xc0) { + int gcaa = jsc_pcre_utf8_table4[c & 0x3f]; /* Number of additional bytes */ + int gcss = 6 * gcaa; + c = (c & jsc_pcre_utf8_table3[gcaa]) << gcss; + while (gcaa-- > 0) { + gcss -= 6; + c |= (*subjectPtr++ & 0x3f) << gcss; + } + } +} + +bool jsc_pcre_xclass(int c, const unsigned char* data) +{ + bool negated = (*data & XCL_NOT); + + /* Character values < 256 are matched against a bitmap, if one is present. If + not, we still carry on, because there may be ranges that start below 256 in the + additional data. */ + + if (c < 256) { + if ((*data & XCL_MAP) != 0 && (data[1 + c/8] & (1 << (c&7))) != 0) + return !negated; /* char found */ + } + + /* First skip the bit map if present. Then match against the list of Unicode + properties or large chars or ranges that end with a large char. We won't ever + encounter XCL_PROP or XCL_NOTPROP when UCP support is not compiled. */ + + if ((*data++ & XCL_MAP) != 0) + data += 32; + + int t; + while ((t = *data++) != XCL_END) { + if (t == XCL_SINGLE) { + int x; + getUTF8CharAndAdvancePointer(x, data); + if (c == x) + return !negated; + } + else if (t == XCL_RANGE) { + int x, y; + getUTF8CharAndAdvancePointer(x, data); + getUTF8CharAndAdvancePointer(y, data); + if (c >= x && c <= y) + return !negated; + } + } + + return negated; /* char did not match */ +} diff --git a/js/src/yarr/pcre/ucpinternal.h b/js/src/yarr/pcre/ucpinternal.h new file mode 100644 index 000000000000..c8bc4aab679c --- /dev/null +++ b/js/src/yarr/pcre/ucpinternal.h @@ -0,0 +1,126 @@ +/* This is JavaScriptCore's variant of the PCRE library. While this library +started out as a copy of PCRE, many of the features of PCRE have been +removed. This library now supports only the regular expression features +required by the JavaScript language specification, and has only the functions +needed by JavaScriptCore and the rest of WebKit. + + Originally written by Philip Hazel + Copyright (c) 1997-2006 University of Cambridge + Copyright (C) 2002, 2004, 2006, 2007 Apple Inc. All rights reserved. + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + +/************************************************* +* Unicode Property Table handler * +*************************************************/ + +/* Internal header file defining the layout of the bits in each pair of 32-bit +words that form a data item in the table. */ + +typedef struct cnode { + unsigned f0; + unsigned f1; +} cnode; + +/* Things for the f0 field */ + +#define f0_scriptmask 0xff000000 /* Mask for script field */ +#define f0_scriptshift 24 /* Shift for script value */ +#define f0_rangeflag 0x00f00000 /* Flag for a range item */ +#define f0_charmask 0x001fffff /* Mask for code point value */ + +/* Things for the f1 field */ + +#define f1_typemask 0xfc000000 /* Mask for char type field */ +#define f1_typeshift 26 /* Shift for the type field */ +#define f1_rangemask 0x0000ffff /* Mask for a range offset */ +#define f1_casemask 0x0000ffff /* Mask for a case offset */ +#define f1_caseneg 0xffff8000 /* Bits for negation */ + +/* The data consists of a vector of structures of type cnode. The two unsigned +32-bit integers are used as follows: + +(f0) (1) The most significant byte holds the script number. The numbers are + defined by the enum in ucp.h. + + (2) The 0x00800000 bit is set if this entry defines a range of characters. + It is not set if this entry defines a single character + + (3) The 0x00600000 bits are spare. + + (4) The 0x001fffff bits contain the code point. No Unicode code point will + ever be greater than 0x0010ffff, so this should be OK for ever. + +(f1) (1) The 0xfc000000 bits contain the character type number. The numbers are + defined by an enum in ucp.h. + + (2) The 0x03ff0000 bits are spare. + + (3) The 0x0000ffff bits contain EITHER the unsigned offset to the top of + range if this entry defines a range, OR the *signed* offset to the + character's "other case" partner if this entry defines a single + character. There is no partner if the value is zero. + +------------------------------------------------------------------------------- +| script (8) |.|.|.| codepoint (21) || type (6) |.|.| spare (8) | offset (16) | +------------------------------------------------------------------------------- + | | | | | + | | |-> spare | |-> spare + | | | + | |-> spare |-> spare + | + |-> range flag + +The upper/lower casing information is set only for characters that come in +pairs. The non-one-to-one mappings in the Unicode data are ignored. + +When searching the data, proceed as follows: + +(1) Set up for a binary chop search. + +(2) If the top is not greater than the bottom, the character is not in the + table. Its type must therefore be "Cn" ("Undefined"). + +(3) Find the middle vector element. + +(4) Extract the code point and compare. If equal, we are done. + +(5) If the test character is smaller, set the top to the current point, and + goto (2). + +(6) If the current entry defines a range, compute the last character by adding + the offset, and see if the test character is within the range. If it is, + we are done. + +(7) Otherwise, set the bottom to one element past the current point and goto + (2). +*/ + +/* End of ucpinternal.h */ diff --git a/js/src/yarr/pcre/ucptable.cpp b/js/src/yarr/pcre/ucptable.cpp new file mode 100644 index 000000000000..011f7f572443 --- /dev/null +++ b/js/src/yarr/pcre/ucptable.cpp @@ -0,0 +1,2968 @@ +/* This source module is automatically generated from the Unicode +property table. See ucpinternal.h for a description of the layout. */ + +static const cnode ucp_table[] = { + { 0x09800000, 0x0000001f }, + { 0x09000020, 0x74000000 }, + { 0x09800021, 0x54000002 }, + { 0x09000024, 0x5c000000 }, + { 0x09800025, 0x54000002 }, + { 0x09000028, 0x58000000 }, + { 0x09000029, 0x48000000 }, + { 0x0900002a, 0x54000000 }, + { 0x0900002b, 0x64000000 }, + { 0x0900002c, 0x54000000 }, + { 0x0900002d, 0x44000000 }, + { 0x0980002e, 0x54000001 }, + { 0x09800030, 0x34000009 }, + { 0x0980003a, 0x54000001 }, + { 0x0980003c, 0x64000002 }, + { 0x0980003f, 0x54000001 }, + { 0x21000041, 0x24000020 }, + { 0x21000042, 0x24000020 }, + { 0x21000043, 0x24000020 }, + { 0x21000044, 0x24000020 }, + { 0x21000045, 0x24000020 }, + { 0x21000046, 0x24000020 }, + { 0x21000047, 0x24000020 }, + { 0x21000048, 0x24000020 }, + { 0x21000049, 0x24000020 }, + { 0x2100004a, 0x24000020 }, + { 0x2100004b, 0x24000020 }, + { 0x2100004c, 0x24000020 }, + { 0x2100004d, 0x24000020 }, + { 0x2100004e, 0x24000020 }, + { 0x2100004f, 0x24000020 }, + { 0x21000050, 0x24000020 }, + { 0x21000051, 0x24000020 }, + { 0x21000052, 0x24000020 }, + { 0x21000053, 0x24000020 }, + { 0x21000054, 0x24000020 }, + { 0x21000055, 0x24000020 }, + { 0x21000056, 0x24000020 }, + { 0x21000057, 0x24000020 }, + { 0x21000058, 0x24000020 }, + { 0x21000059, 0x24000020 }, + { 0x2100005a, 0x24000020 }, + { 0x0900005b, 0x58000000 }, + { 0x0900005c, 0x54000000 }, + { 0x0900005d, 0x48000000 }, + { 0x0900005e, 0x60000000 }, + { 0x0900005f, 0x40000000 }, + { 0x09000060, 0x60000000 }, + { 0x21000061, 0x1400ffe0 }, + { 0x21000062, 0x1400ffe0 }, + { 0x21000063, 0x1400ffe0 }, + { 0x21000064, 0x1400ffe0 }, + { 0x21000065, 0x1400ffe0 }, + { 0x21000066, 0x1400ffe0 }, + { 0x21000067, 0x1400ffe0 }, + { 0x21000068, 0x1400ffe0 }, + { 0x21000069, 0x1400ffe0 }, + { 0x2100006a, 0x1400ffe0 }, + { 0x2100006b, 0x1400ffe0 }, + { 0x2100006c, 0x1400ffe0 }, + { 0x2100006d, 0x1400ffe0 }, + { 0x2100006e, 0x1400ffe0 }, + { 0x2100006f, 0x1400ffe0 }, + { 0x21000070, 0x1400ffe0 }, + { 0x21000071, 0x1400ffe0 }, + { 0x21000072, 0x1400ffe0 }, + { 0x21000073, 0x1400ffe0 }, + { 0x21000074, 0x1400ffe0 }, + { 0x21000075, 0x1400ffe0 }, + { 0x21000076, 0x1400ffe0 }, + { 0x21000077, 0x1400ffe0 }, + { 0x21000078, 0x1400ffe0 }, + { 0x21000079, 0x1400ffe0 }, + { 0x2100007a, 0x1400ffe0 }, + { 0x0900007b, 0x58000000 }, + { 0x0900007c, 0x64000000 }, + { 0x0900007d, 0x48000000 }, + { 0x0900007e, 0x64000000 }, + { 0x0980007f, 0x00000020 }, + { 0x090000a0, 0x74000000 }, + { 0x090000a1, 0x54000000 }, + { 0x098000a2, 0x5c000003 }, + { 0x098000a6, 0x68000001 }, + { 0x090000a8, 0x60000000 }, + { 0x090000a9, 0x68000000 }, + { 0x210000aa, 0x14000000 }, + { 0x090000ab, 0x50000000 }, + { 0x090000ac, 0x64000000 }, + { 0x090000ad, 0x04000000 }, + { 0x090000ae, 0x68000000 }, + { 0x090000af, 0x60000000 }, + { 0x090000b0, 0x68000000 }, + { 0x090000b1, 0x64000000 }, + { 0x098000b2, 0x3c000001 }, + { 0x090000b4, 0x60000000 }, + { 0x090000b5, 0x140002e7 }, + { 0x090000b6, 0x68000000 }, + { 0x090000b7, 0x54000000 }, + { 0x090000b8, 0x60000000 }, + { 0x090000b9, 0x3c000000 }, + { 0x210000ba, 0x14000000 }, + { 0x090000bb, 0x4c000000 }, + { 0x098000bc, 0x3c000002 }, + { 0x090000bf, 0x54000000 }, + { 0x210000c0, 0x24000020 }, + { 0x210000c1, 0x24000020 }, + { 0x210000c2, 0x24000020 }, + { 0x210000c3, 0x24000020 }, + { 0x210000c4, 0x24000020 }, + { 0x210000c5, 0x24000020 }, + { 0x210000c6, 0x24000020 }, + { 0x210000c7, 0x24000020 }, + { 0x210000c8, 0x24000020 }, + { 0x210000c9, 0x24000020 }, + { 0x210000ca, 0x24000020 }, + { 0x210000cb, 0x24000020 }, + { 0x210000cc, 0x24000020 }, + { 0x210000cd, 0x24000020 }, + { 0x210000ce, 0x24000020 }, + { 0x210000cf, 0x24000020 }, + { 0x210000d0, 0x24000020 }, + { 0x210000d1, 0x24000020 }, + { 0x210000d2, 0x24000020 }, + { 0x210000d3, 0x24000020 }, + { 0x210000d4, 0x24000020 }, + { 0x210000d5, 0x24000020 }, + { 0x210000d6, 0x24000020 }, + { 0x090000d7, 0x64000000 }, + { 0x210000d8, 0x24000020 }, + { 0x210000d9, 0x24000020 }, + { 0x210000da, 0x24000020 }, + { 0x210000db, 0x24000020 }, + { 0x210000dc, 0x24000020 }, + { 0x210000dd, 0x24000020 }, + { 0x210000de, 0x24000020 }, + { 0x210000df, 0x14000000 }, + { 0x210000e0, 0x1400ffe0 }, + { 0x210000e1, 0x1400ffe0 }, + { 0x210000e2, 0x1400ffe0 }, + { 0x210000e3, 0x1400ffe0 }, + { 0x210000e4, 0x1400ffe0 }, + { 0x210000e5, 0x1400ffe0 }, + { 0x210000e6, 0x1400ffe0 }, + { 0x210000e7, 0x1400ffe0 }, + { 0x210000e8, 0x1400ffe0 }, + { 0x210000e9, 0x1400ffe0 }, + { 0x210000ea, 0x1400ffe0 }, + { 0x210000eb, 0x1400ffe0 }, + { 0x210000ec, 0x1400ffe0 }, + { 0x210000ed, 0x1400ffe0 }, + { 0x210000ee, 0x1400ffe0 }, + { 0x210000ef, 0x1400ffe0 }, + { 0x210000f0, 0x1400ffe0 }, + { 0x210000f1, 0x1400ffe0 }, + { 0x210000f2, 0x1400ffe0 }, + { 0x210000f3, 0x1400ffe0 }, + { 0x210000f4, 0x1400ffe0 }, + { 0x210000f5, 0x1400ffe0 }, + { 0x210000f6, 0x1400ffe0 }, + { 0x090000f7, 0x64000000 }, + { 0x210000f8, 0x1400ffe0 }, + { 0x210000f9, 0x1400ffe0 }, + { 0x210000fa, 0x1400ffe0 }, + { 0x210000fb, 0x1400ffe0 }, + { 0x210000fc, 0x1400ffe0 }, + { 0x210000fd, 0x1400ffe0 }, + { 0x210000fe, 0x1400ffe0 }, + { 0x210000ff, 0x14000079 }, + { 0x21000100, 0x24000001 }, + { 0x21000101, 0x1400ffff }, + { 0x21000102, 0x24000001 }, + { 0x21000103, 0x1400ffff }, + { 0x21000104, 0x24000001 }, + { 0x21000105, 0x1400ffff }, + { 0x21000106, 0x24000001 }, + { 0x21000107, 0x1400ffff }, + { 0x21000108, 0x24000001 }, + { 0x21000109, 0x1400ffff }, + { 0x2100010a, 0x24000001 }, + { 0x2100010b, 0x1400ffff }, + { 0x2100010c, 0x24000001 }, + { 0x2100010d, 0x1400ffff }, + { 0x2100010e, 0x24000001 }, + { 0x2100010f, 0x1400ffff }, + { 0x21000110, 0x24000001 }, + { 0x21000111, 0x1400ffff }, + { 0x21000112, 0x24000001 }, + { 0x21000113, 0x1400ffff }, + { 0x21000114, 0x24000001 }, + { 0x21000115, 0x1400ffff }, + { 0x21000116, 0x24000001 }, + { 0x21000117, 0x1400ffff }, + { 0x21000118, 0x24000001 }, + { 0x21000119, 0x1400ffff }, + { 0x2100011a, 0x24000001 }, + { 0x2100011b, 0x1400ffff }, + { 0x2100011c, 0x24000001 }, + { 0x2100011d, 0x1400ffff }, + { 0x2100011e, 0x24000001 }, + { 0x2100011f, 0x1400ffff }, + { 0x21000120, 0x24000001 }, + { 0x21000121, 0x1400ffff }, + { 0x21000122, 0x24000001 }, + { 0x21000123, 0x1400ffff }, + { 0x21000124, 0x24000001 }, + { 0x21000125, 0x1400ffff }, + { 0x21000126, 0x24000001 }, + { 0x21000127, 0x1400ffff }, + { 0x21000128, 0x24000001 }, + { 0x21000129, 0x1400ffff }, + { 0x2100012a, 0x24000001 }, + { 0x2100012b, 0x1400ffff }, + { 0x2100012c, 0x24000001 }, + { 0x2100012d, 0x1400ffff }, + { 0x2100012e, 0x24000001 }, + { 0x2100012f, 0x1400ffff }, + { 0x21000130, 0x2400ff39 }, + { 0x21000131, 0x1400ff18 }, + { 0x21000132, 0x24000001 }, + { 0x21000133, 0x1400ffff }, + { 0x21000134, 0x24000001 }, + { 0x21000135, 0x1400ffff }, + { 0x21000136, 0x24000001 }, + { 0x21000137, 0x1400ffff }, + { 0x21000138, 0x14000000 }, + { 0x21000139, 0x24000001 }, + { 0x2100013a, 0x1400ffff }, + { 0x2100013b, 0x24000001 }, + { 0x2100013c, 0x1400ffff }, + { 0x2100013d, 0x24000001 }, + { 0x2100013e, 0x1400ffff }, + { 0x2100013f, 0x24000001 }, + { 0x21000140, 0x1400ffff }, + { 0x21000141, 0x24000001 }, + { 0x21000142, 0x1400ffff }, + { 0x21000143, 0x24000001 }, + { 0x21000144, 0x1400ffff }, + { 0x21000145, 0x24000001 }, + { 0x21000146, 0x1400ffff }, + { 0x21000147, 0x24000001 }, + { 0x21000148, 0x1400ffff }, + { 0x21000149, 0x14000000 }, + { 0x2100014a, 0x24000001 }, + { 0x2100014b, 0x1400ffff }, + { 0x2100014c, 0x24000001 }, + { 0x2100014d, 0x1400ffff }, + { 0x2100014e, 0x24000001 }, + { 0x2100014f, 0x1400ffff }, + { 0x21000150, 0x24000001 }, + { 0x21000151, 0x1400ffff }, + { 0x21000152, 0x24000001 }, + { 0x21000153, 0x1400ffff }, + { 0x21000154, 0x24000001 }, + { 0x21000155, 0x1400ffff }, + { 0x21000156, 0x24000001 }, + { 0x21000157, 0x1400ffff }, + { 0x21000158, 0x24000001 }, + { 0x21000159, 0x1400ffff }, + { 0x2100015a, 0x24000001 }, + { 0x2100015b, 0x1400ffff }, + { 0x2100015c, 0x24000001 }, + { 0x2100015d, 0x1400ffff }, + { 0x2100015e, 0x24000001 }, + { 0x2100015f, 0x1400ffff }, + { 0x21000160, 0x24000001 }, + { 0x21000161, 0x1400ffff }, + { 0x21000162, 0x24000001 }, + { 0x21000163, 0x1400ffff }, + { 0x21000164, 0x24000001 }, + { 0x21000165, 0x1400ffff }, + { 0x21000166, 0x24000001 }, + { 0x21000167, 0x1400ffff }, + { 0x21000168, 0x24000001 }, + { 0x21000169, 0x1400ffff }, + { 0x2100016a, 0x24000001 }, + { 0x2100016b, 0x1400ffff }, + { 0x2100016c, 0x24000001 }, + { 0x2100016d, 0x1400ffff }, + { 0x2100016e, 0x24000001 }, + { 0x2100016f, 0x1400ffff }, + { 0x21000170, 0x24000001 }, + { 0x21000171, 0x1400ffff }, + { 0x21000172, 0x24000001 }, + { 0x21000173, 0x1400ffff }, + { 0x21000174, 0x24000001 }, + { 0x21000175, 0x1400ffff }, + { 0x21000176, 0x24000001 }, + { 0x21000177, 0x1400ffff }, + { 0x21000178, 0x2400ff87 }, + { 0x21000179, 0x24000001 }, + { 0x2100017a, 0x1400ffff }, + { 0x2100017b, 0x24000001 }, + { 0x2100017c, 0x1400ffff }, + { 0x2100017d, 0x24000001 }, + { 0x2100017e, 0x1400ffff }, + { 0x2100017f, 0x1400fed4 }, + { 0x21000180, 0x14000000 }, + { 0x21000181, 0x240000d2 }, + { 0x21000182, 0x24000001 }, + { 0x21000183, 0x1400ffff }, + { 0x21000184, 0x24000001 }, + { 0x21000185, 0x1400ffff }, + { 0x21000186, 0x240000ce }, + { 0x21000187, 0x24000001 }, + { 0x21000188, 0x1400ffff }, + { 0x21000189, 0x240000cd }, + { 0x2100018a, 0x240000cd }, + { 0x2100018b, 0x24000001 }, + { 0x2100018c, 0x1400ffff }, + { 0x2100018d, 0x14000000 }, + { 0x2100018e, 0x2400004f }, + { 0x2100018f, 0x240000ca }, + { 0x21000190, 0x240000cb }, + { 0x21000191, 0x24000001 }, + { 0x21000192, 0x1400ffff }, + { 0x21000193, 0x240000cd }, + { 0x21000194, 0x240000cf }, + { 0x21000195, 0x14000061 }, + { 0x21000196, 0x240000d3 }, + { 0x21000197, 0x240000d1 }, + { 0x21000198, 0x24000001 }, + { 0x21000199, 0x1400ffff }, + { 0x2100019a, 0x140000a3 }, + { 0x2100019b, 0x14000000 }, + { 0x2100019c, 0x240000d3 }, + { 0x2100019d, 0x240000d5 }, + { 0x2100019e, 0x14000082 }, + { 0x2100019f, 0x240000d6 }, + { 0x210001a0, 0x24000001 }, + { 0x210001a1, 0x1400ffff }, + { 0x210001a2, 0x24000001 }, + { 0x210001a3, 0x1400ffff }, + { 0x210001a4, 0x24000001 }, + { 0x210001a5, 0x1400ffff }, + { 0x210001a6, 0x240000da }, + { 0x210001a7, 0x24000001 }, + { 0x210001a8, 0x1400ffff }, + { 0x210001a9, 0x240000da }, + { 0x218001aa, 0x14000001 }, + { 0x210001ac, 0x24000001 }, + { 0x210001ad, 0x1400ffff }, + { 0x210001ae, 0x240000da }, + { 0x210001af, 0x24000001 }, + { 0x210001b0, 0x1400ffff }, + { 0x210001b1, 0x240000d9 }, + { 0x210001b2, 0x240000d9 }, + { 0x210001b3, 0x24000001 }, + { 0x210001b4, 0x1400ffff }, + { 0x210001b5, 0x24000001 }, + { 0x210001b6, 0x1400ffff }, + { 0x210001b7, 0x240000db }, + { 0x210001b8, 0x24000001 }, + { 0x210001b9, 0x1400ffff }, + { 0x210001ba, 0x14000000 }, + { 0x210001bb, 0x1c000000 }, + { 0x210001bc, 0x24000001 }, + { 0x210001bd, 0x1400ffff }, + { 0x210001be, 0x14000000 }, + { 0x210001bf, 0x14000038 }, + { 0x218001c0, 0x1c000003 }, + { 0x210001c4, 0x24000002 }, + { 0x210001c5, 0x2000ffff }, + { 0x210001c6, 0x1400fffe }, + { 0x210001c7, 0x24000002 }, + { 0x210001c8, 0x2000ffff }, + { 0x210001c9, 0x1400fffe }, + { 0x210001ca, 0x24000002 }, + { 0x210001cb, 0x2000ffff }, + { 0x210001cc, 0x1400fffe }, + { 0x210001cd, 0x24000001 }, + { 0x210001ce, 0x1400ffff }, + { 0x210001cf, 0x24000001 }, + { 0x210001d0, 0x1400ffff }, + { 0x210001d1, 0x24000001 }, + { 0x210001d2, 0x1400ffff }, + { 0x210001d3, 0x24000001 }, + { 0x210001d4, 0x1400ffff }, + { 0x210001d5, 0x24000001 }, + { 0x210001d6, 0x1400ffff }, + { 0x210001d7, 0x24000001 }, + { 0x210001d8, 0x1400ffff }, + { 0x210001d9, 0x24000001 }, + { 0x210001da, 0x1400ffff }, + { 0x210001db, 0x24000001 }, + { 0x210001dc, 0x1400ffff }, + { 0x210001dd, 0x1400ffb1 }, + { 0x210001de, 0x24000001 }, + { 0x210001df, 0x1400ffff }, + { 0x210001e0, 0x24000001 }, + { 0x210001e1, 0x1400ffff }, + { 0x210001e2, 0x24000001 }, + { 0x210001e3, 0x1400ffff }, + { 0x210001e4, 0x24000001 }, + { 0x210001e5, 0x1400ffff }, + { 0x210001e6, 0x24000001 }, + { 0x210001e7, 0x1400ffff }, + { 0x210001e8, 0x24000001 }, + { 0x210001e9, 0x1400ffff }, + { 0x210001ea, 0x24000001 }, + { 0x210001eb, 0x1400ffff }, + { 0x210001ec, 0x24000001 }, + { 0x210001ed, 0x1400ffff }, + { 0x210001ee, 0x24000001 }, + { 0x210001ef, 0x1400ffff }, + { 0x210001f0, 0x14000000 }, + { 0x210001f1, 0x24000002 }, + { 0x210001f2, 0x2000ffff }, + { 0x210001f3, 0x1400fffe }, + { 0x210001f4, 0x24000001 }, + { 0x210001f5, 0x1400ffff }, + { 0x210001f6, 0x2400ff9f }, + { 0x210001f7, 0x2400ffc8 }, + { 0x210001f8, 0x24000001 }, + { 0x210001f9, 0x1400ffff }, + { 0x210001fa, 0x24000001 }, + { 0x210001fb, 0x1400ffff }, + { 0x210001fc, 0x24000001 }, + { 0x210001fd, 0x1400ffff }, + { 0x210001fe, 0x24000001 }, + { 0x210001ff, 0x1400ffff }, + { 0x21000200, 0x24000001 }, + { 0x21000201, 0x1400ffff }, + { 0x21000202, 0x24000001 }, + { 0x21000203, 0x1400ffff }, + { 0x21000204, 0x24000001 }, + { 0x21000205, 0x1400ffff }, + { 0x21000206, 0x24000001 }, + { 0x21000207, 0x1400ffff }, + { 0x21000208, 0x24000001 }, + { 0x21000209, 0x1400ffff }, + { 0x2100020a, 0x24000001 }, + { 0x2100020b, 0x1400ffff }, + { 0x2100020c, 0x24000001 }, + { 0x2100020d, 0x1400ffff }, + { 0x2100020e, 0x24000001 }, + { 0x2100020f, 0x1400ffff }, + { 0x21000210, 0x24000001 }, + { 0x21000211, 0x1400ffff }, + { 0x21000212, 0x24000001 }, + { 0x21000213, 0x1400ffff }, + { 0x21000214, 0x24000001 }, + { 0x21000215, 0x1400ffff }, + { 0x21000216, 0x24000001 }, + { 0x21000217, 0x1400ffff }, + { 0x21000218, 0x24000001 }, + { 0x21000219, 0x1400ffff }, + { 0x2100021a, 0x24000001 }, + { 0x2100021b, 0x1400ffff }, + { 0x2100021c, 0x24000001 }, + { 0x2100021d, 0x1400ffff }, + { 0x2100021e, 0x24000001 }, + { 0x2100021f, 0x1400ffff }, + { 0x21000220, 0x2400ff7e }, + { 0x21000221, 0x14000000 }, + { 0x21000222, 0x24000001 }, + { 0x21000223, 0x1400ffff }, + { 0x21000224, 0x24000001 }, + { 0x21000225, 0x1400ffff }, + { 0x21000226, 0x24000001 }, + { 0x21000227, 0x1400ffff }, + { 0x21000228, 0x24000001 }, + { 0x21000229, 0x1400ffff }, + { 0x2100022a, 0x24000001 }, + { 0x2100022b, 0x1400ffff }, + { 0x2100022c, 0x24000001 }, + { 0x2100022d, 0x1400ffff }, + { 0x2100022e, 0x24000001 }, + { 0x2100022f, 0x1400ffff }, + { 0x21000230, 0x24000001 }, + { 0x21000231, 0x1400ffff }, + { 0x21000232, 0x24000001 }, + { 0x21000233, 0x1400ffff }, + { 0x21800234, 0x14000005 }, + { 0x2100023a, 0x24000000 }, + { 0x2100023b, 0x24000001 }, + { 0x2100023c, 0x1400ffff }, + { 0x2100023d, 0x2400ff5d }, + { 0x2100023e, 0x24000000 }, + { 0x2180023f, 0x14000001 }, + { 0x21000241, 0x24000053 }, + { 0x21800250, 0x14000002 }, + { 0x21000253, 0x1400ff2e }, + { 0x21000254, 0x1400ff32 }, + { 0x21000255, 0x14000000 }, + { 0x21000256, 0x1400ff33 }, + { 0x21000257, 0x1400ff33 }, + { 0x21000258, 0x14000000 }, + { 0x21000259, 0x1400ff36 }, + { 0x2100025a, 0x14000000 }, + { 0x2100025b, 0x1400ff35 }, + { 0x2180025c, 0x14000003 }, + { 0x21000260, 0x1400ff33 }, + { 0x21800261, 0x14000001 }, + { 0x21000263, 0x1400ff31 }, + { 0x21800264, 0x14000003 }, + { 0x21000268, 0x1400ff2f }, + { 0x21000269, 0x1400ff2d }, + { 0x2180026a, 0x14000004 }, + { 0x2100026f, 0x1400ff2d }, + { 0x21800270, 0x14000001 }, + { 0x21000272, 0x1400ff2b }, + { 0x21800273, 0x14000001 }, + { 0x21000275, 0x1400ff2a }, + { 0x21800276, 0x14000009 }, + { 0x21000280, 0x1400ff26 }, + { 0x21800281, 0x14000001 }, + { 0x21000283, 0x1400ff26 }, + { 0x21800284, 0x14000003 }, + { 0x21000288, 0x1400ff26 }, + { 0x21000289, 0x14000000 }, + { 0x2100028a, 0x1400ff27 }, + { 0x2100028b, 0x1400ff27 }, + { 0x2180028c, 0x14000005 }, + { 0x21000292, 0x1400ff25 }, + { 0x21000293, 0x14000000 }, + { 0x21000294, 0x1400ffad }, + { 0x21800295, 0x1400001a }, + { 0x218002b0, 0x18000011 }, + { 0x098002c2, 0x60000003 }, + { 0x098002c6, 0x1800000b }, + { 0x098002d2, 0x6000000d }, + { 0x218002e0, 0x18000004 }, + { 0x098002e5, 0x60000008 }, + { 0x090002ee, 0x18000000 }, + { 0x098002ef, 0x60000010 }, + { 0x1b800300, 0x30000044 }, + { 0x1b000345, 0x30000054 }, + { 0x1b800346, 0x30000029 }, + { 0x13800374, 0x60000001 }, + { 0x1300037a, 0x18000000 }, + { 0x0900037e, 0x54000000 }, + { 0x13800384, 0x60000001 }, + { 0x13000386, 0x24000026 }, + { 0x09000387, 0x54000000 }, + { 0x13000388, 0x24000025 }, + { 0x13000389, 0x24000025 }, + { 0x1300038a, 0x24000025 }, + { 0x1300038c, 0x24000040 }, + { 0x1300038e, 0x2400003f }, + { 0x1300038f, 0x2400003f }, + { 0x13000390, 0x14000000 }, + { 0x13000391, 0x24000020 }, + { 0x13000392, 0x24000020 }, + { 0x13000393, 0x24000020 }, + { 0x13000394, 0x24000020 }, + { 0x13000395, 0x24000020 }, + { 0x13000396, 0x24000020 }, + { 0x13000397, 0x24000020 }, + { 0x13000398, 0x24000020 }, + { 0x13000399, 0x24000020 }, + { 0x1300039a, 0x24000020 }, + { 0x1300039b, 0x24000020 }, + { 0x1300039c, 0x24000020 }, + { 0x1300039d, 0x24000020 }, + { 0x1300039e, 0x24000020 }, + { 0x1300039f, 0x24000020 }, + { 0x130003a0, 0x24000020 }, + { 0x130003a1, 0x24000020 }, + { 0x130003a3, 0x24000020 }, + { 0x130003a4, 0x24000020 }, + { 0x130003a5, 0x24000020 }, + { 0x130003a6, 0x24000020 }, + { 0x130003a7, 0x24000020 }, + { 0x130003a8, 0x24000020 }, + { 0x130003a9, 0x24000020 }, + { 0x130003aa, 0x24000020 }, + { 0x130003ab, 0x24000020 }, + { 0x130003ac, 0x1400ffda }, + { 0x130003ad, 0x1400ffdb }, + { 0x130003ae, 0x1400ffdb }, + { 0x130003af, 0x1400ffdb }, + { 0x130003b0, 0x14000000 }, + { 0x130003b1, 0x1400ffe0 }, + { 0x130003b2, 0x1400ffe0 }, + { 0x130003b3, 0x1400ffe0 }, + { 0x130003b4, 0x1400ffe0 }, + { 0x130003b5, 0x1400ffe0 }, + { 0x130003b6, 0x1400ffe0 }, + { 0x130003b7, 0x1400ffe0 }, + { 0x130003b8, 0x1400ffe0 }, + { 0x130003b9, 0x1400ffe0 }, + { 0x130003ba, 0x1400ffe0 }, + { 0x130003bb, 0x1400ffe0 }, + { 0x130003bc, 0x1400ffe0 }, + { 0x130003bd, 0x1400ffe0 }, + { 0x130003be, 0x1400ffe0 }, + { 0x130003bf, 0x1400ffe0 }, + { 0x130003c0, 0x1400ffe0 }, + { 0x130003c1, 0x1400ffe0 }, + { 0x130003c2, 0x1400ffe1 }, + { 0x130003c3, 0x1400ffe0 }, + { 0x130003c4, 0x1400ffe0 }, + { 0x130003c5, 0x1400ffe0 }, + { 0x130003c6, 0x1400ffe0 }, + { 0x130003c7, 0x1400ffe0 }, + { 0x130003c8, 0x1400ffe0 }, + { 0x130003c9, 0x1400ffe0 }, + { 0x130003ca, 0x1400ffe0 }, + { 0x130003cb, 0x1400ffe0 }, + { 0x130003cc, 0x1400ffc0 }, + { 0x130003cd, 0x1400ffc1 }, + { 0x130003ce, 0x1400ffc1 }, + { 0x130003d0, 0x1400ffc2 }, + { 0x130003d1, 0x1400ffc7 }, + { 0x138003d2, 0x24000002 }, + { 0x130003d5, 0x1400ffd1 }, + { 0x130003d6, 0x1400ffca }, + { 0x130003d7, 0x14000000 }, + { 0x130003d8, 0x24000001 }, + { 0x130003d9, 0x1400ffff }, + { 0x130003da, 0x24000001 }, + { 0x130003db, 0x1400ffff }, + { 0x130003dc, 0x24000001 }, + { 0x130003dd, 0x1400ffff }, + { 0x130003de, 0x24000001 }, + { 0x130003df, 0x1400ffff }, + { 0x130003e0, 0x24000001 }, + { 0x130003e1, 0x1400ffff }, + { 0x0a0003e2, 0x24000001 }, + { 0x0a0003e3, 0x1400ffff }, + { 0x0a0003e4, 0x24000001 }, + { 0x0a0003e5, 0x1400ffff }, + { 0x0a0003e6, 0x24000001 }, + { 0x0a0003e7, 0x1400ffff }, + { 0x0a0003e8, 0x24000001 }, + { 0x0a0003e9, 0x1400ffff }, + { 0x0a0003ea, 0x24000001 }, + { 0x0a0003eb, 0x1400ffff }, + { 0x0a0003ec, 0x24000001 }, + { 0x0a0003ed, 0x1400ffff }, + { 0x0a0003ee, 0x24000001 }, + { 0x0a0003ef, 0x1400ffff }, + { 0x130003f0, 0x1400ffaa }, + { 0x130003f1, 0x1400ffb0 }, + { 0x130003f2, 0x14000007 }, + { 0x130003f3, 0x14000000 }, + { 0x130003f4, 0x2400ffc4 }, + { 0x130003f5, 0x1400ffa0 }, + { 0x130003f6, 0x64000000 }, + { 0x130003f7, 0x24000001 }, + { 0x130003f8, 0x1400ffff }, + { 0x130003f9, 0x2400fff9 }, + { 0x130003fa, 0x24000001 }, + { 0x130003fb, 0x1400ffff }, + { 0x130003fc, 0x14000000 }, + { 0x138003fd, 0x24000002 }, + { 0x0c000400, 0x24000050 }, + { 0x0c000401, 0x24000050 }, + { 0x0c000402, 0x24000050 }, + { 0x0c000403, 0x24000050 }, + { 0x0c000404, 0x24000050 }, + { 0x0c000405, 0x24000050 }, + { 0x0c000406, 0x24000050 }, + { 0x0c000407, 0x24000050 }, + { 0x0c000408, 0x24000050 }, + { 0x0c000409, 0x24000050 }, + { 0x0c00040a, 0x24000050 }, + { 0x0c00040b, 0x24000050 }, + { 0x0c00040c, 0x24000050 }, + { 0x0c00040d, 0x24000050 }, + { 0x0c00040e, 0x24000050 }, + { 0x0c00040f, 0x24000050 }, + { 0x0c000410, 0x24000020 }, + { 0x0c000411, 0x24000020 }, + { 0x0c000412, 0x24000020 }, + { 0x0c000413, 0x24000020 }, + { 0x0c000414, 0x24000020 }, + { 0x0c000415, 0x24000020 }, + { 0x0c000416, 0x24000020 }, + { 0x0c000417, 0x24000020 }, + { 0x0c000418, 0x24000020 }, + { 0x0c000419, 0x24000020 }, + { 0x0c00041a, 0x24000020 }, + { 0x0c00041b, 0x24000020 }, + { 0x0c00041c, 0x24000020 }, + { 0x0c00041d, 0x24000020 }, + { 0x0c00041e, 0x24000020 }, + { 0x0c00041f, 0x24000020 }, + { 0x0c000420, 0x24000020 }, + { 0x0c000421, 0x24000020 }, + { 0x0c000422, 0x24000020 }, + { 0x0c000423, 0x24000020 }, + { 0x0c000424, 0x24000020 }, + { 0x0c000425, 0x24000020 }, + { 0x0c000426, 0x24000020 }, + { 0x0c000427, 0x24000020 }, + { 0x0c000428, 0x24000020 }, + { 0x0c000429, 0x24000020 }, + { 0x0c00042a, 0x24000020 }, + { 0x0c00042b, 0x24000020 }, + { 0x0c00042c, 0x24000020 }, + { 0x0c00042d, 0x24000020 }, + { 0x0c00042e, 0x24000020 }, + { 0x0c00042f, 0x24000020 }, + { 0x0c000430, 0x1400ffe0 }, + { 0x0c000431, 0x1400ffe0 }, + { 0x0c000432, 0x1400ffe0 }, + { 0x0c000433, 0x1400ffe0 }, + { 0x0c000434, 0x1400ffe0 }, + { 0x0c000435, 0x1400ffe0 }, + { 0x0c000436, 0x1400ffe0 }, + { 0x0c000437, 0x1400ffe0 }, + { 0x0c000438, 0x1400ffe0 }, + { 0x0c000439, 0x1400ffe0 }, + { 0x0c00043a, 0x1400ffe0 }, + { 0x0c00043b, 0x1400ffe0 }, + { 0x0c00043c, 0x1400ffe0 }, + { 0x0c00043d, 0x1400ffe0 }, + { 0x0c00043e, 0x1400ffe0 }, + { 0x0c00043f, 0x1400ffe0 }, + { 0x0c000440, 0x1400ffe0 }, + { 0x0c000441, 0x1400ffe0 }, + { 0x0c000442, 0x1400ffe0 }, + { 0x0c000443, 0x1400ffe0 }, + { 0x0c000444, 0x1400ffe0 }, + { 0x0c000445, 0x1400ffe0 }, + { 0x0c000446, 0x1400ffe0 }, + { 0x0c000447, 0x1400ffe0 }, + { 0x0c000448, 0x1400ffe0 }, + { 0x0c000449, 0x1400ffe0 }, + { 0x0c00044a, 0x1400ffe0 }, + { 0x0c00044b, 0x1400ffe0 }, + { 0x0c00044c, 0x1400ffe0 }, + { 0x0c00044d, 0x1400ffe0 }, + { 0x0c00044e, 0x1400ffe0 }, + { 0x0c00044f, 0x1400ffe0 }, + { 0x0c000450, 0x1400ffb0 }, + { 0x0c000451, 0x1400ffb0 }, + { 0x0c000452, 0x1400ffb0 }, + { 0x0c000453, 0x1400ffb0 }, + { 0x0c000454, 0x1400ffb0 }, + { 0x0c000455, 0x1400ffb0 }, + { 0x0c000456, 0x1400ffb0 }, + { 0x0c000457, 0x1400ffb0 }, + { 0x0c000458, 0x1400ffb0 }, + { 0x0c000459, 0x1400ffb0 }, + { 0x0c00045a, 0x1400ffb0 }, + { 0x0c00045b, 0x1400ffb0 }, + { 0x0c00045c, 0x1400ffb0 }, + { 0x0c00045d, 0x1400ffb0 }, + { 0x0c00045e, 0x1400ffb0 }, + { 0x0c00045f, 0x1400ffb0 }, + { 0x0c000460, 0x24000001 }, + { 0x0c000461, 0x1400ffff }, + { 0x0c000462, 0x24000001 }, + { 0x0c000463, 0x1400ffff }, + { 0x0c000464, 0x24000001 }, + { 0x0c000465, 0x1400ffff }, + { 0x0c000466, 0x24000001 }, + { 0x0c000467, 0x1400ffff }, + { 0x0c000468, 0x24000001 }, + { 0x0c000469, 0x1400ffff }, + { 0x0c00046a, 0x24000001 }, + { 0x0c00046b, 0x1400ffff }, + { 0x0c00046c, 0x24000001 }, + { 0x0c00046d, 0x1400ffff }, + { 0x0c00046e, 0x24000001 }, + { 0x0c00046f, 0x1400ffff }, + { 0x0c000470, 0x24000001 }, + { 0x0c000471, 0x1400ffff }, + { 0x0c000472, 0x24000001 }, + { 0x0c000473, 0x1400ffff }, + { 0x0c000474, 0x24000001 }, + { 0x0c000475, 0x1400ffff }, + { 0x0c000476, 0x24000001 }, + { 0x0c000477, 0x1400ffff }, + { 0x0c000478, 0x24000001 }, + { 0x0c000479, 0x1400ffff }, + { 0x0c00047a, 0x24000001 }, + { 0x0c00047b, 0x1400ffff }, + { 0x0c00047c, 0x24000001 }, + { 0x0c00047d, 0x1400ffff }, + { 0x0c00047e, 0x24000001 }, + { 0x0c00047f, 0x1400ffff }, + { 0x0c000480, 0x24000001 }, + { 0x0c000481, 0x1400ffff }, + { 0x0c000482, 0x68000000 }, + { 0x0c800483, 0x30000003 }, + { 0x0c800488, 0x2c000001 }, + { 0x0c00048a, 0x24000001 }, + { 0x0c00048b, 0x1400ffff }, + { 0x0c00048c, 0x24000001 }, + { 0x0c00048d, 0x1400ffff }, + { 0x0c00048e, 0x24000001 }, + { 0x0c00048f, 0x1400ffff }, + { 0x0c000490, 0x24000001 }, + { 0x0c000491, 0x1400ffff }, + { 0x0c000492, 0x24000001 }, + { 0x0c000493, 0x1400ffff }, + { 0x0c000494, 0x24000001 }, + { 0x0c000495, 0x1400ffff }, + { 0x0c000496, 0x24000001 }, + { 0x0c000497, 0x1400ffff }, + { 0x0c000498, 0x24000001 }, + { 0x0c000499, 0x1400ffff }, + { 0x0c00049a, 0x24000001 }, + { 0x0c00049b, 0x1400ffff }, + { 0x0c00049c, 0x24000001 }, + { 0x0c00049d, 0x1400ffff }, + { 0x0c00049e, 0x24000001 }, + { 0x0c00049f, 0x1400ffff }, + { 0x0c0004a0, 0x24000001 }, + { 0x0c0004a1, 0x1400ffff }, + { 0x0c0004a2, 0x24000001 }, + { 0x0c0004a3, 0x1400ffff }, + { 0x0c0004a4, 0x24000001 }, + { 0x0c0004a5, 0x1400ffff }, + { 0x0c0004a6, 0x24000001 }, + { 0x0c0004a7, 0x1400ffff }, + { 0x0c0004a8, 0x24000001 }, + { 0x0c0004a9, 0x1400ffff }, + { 0x0c0004aa, 0x24000001 }, + { 0x0c0004ab, 0x1400ffff }, + { 0x0c0004ac, 0x24000001 }, + { 0x0c0004ad, 0x1400ffff }, + { 0x0c0004ae, 0x24000001 }, + { 0x0c0004af, 0x1400ffff }, + { 0x0c0004b0, 0x24000001 }, + { 0x0c0004b1, 0x1400ffff }, + { 0x0c0004b2, 0x24000001 }, + { 0x0c0004b3, 0x1400ffff }, + { 0x0c0004b4, 0x24000001 }, + { 0x0c0004b5, 0x1400ffff }, + { 0x0c0004b6, 0x24000001 }, + { 0x0c0004b7, 0x1400ffff }, + { 0x0c0004b8, 0x24000001 }, + { 0x0c0004b9, 0x1400ffff }, + { 0x0c0004ba, 0x24000001 }, + { 0x0c0004bb, 0x1400ffff }, + { 0x0c0004bc, 0x24000001 }, + { 0x0c0004bd, 0x1400ffff }, + { 0x0c0004be, 0x24000001 }, + { 0x0c0004bf, 0x1400ffff }, + { 0x0c0004c0, 0x24000000 }, + { 0x0c0004c1, 0x24000001 }, + { 0x0c0004c2, 0x1400ffff }, + { 0x0c0004c3, 0x24000001 }, + { 0x0c0004c4, 0x1400ffff }, + { 0x0c0004c5, 0x24000001 }, + { 0x0c0004c6, 0x1400ffff }, + { 0x0c0004c7, 0x24000001 }, + { 0x0c0004c8, 0x1400ffff }, + { 0x0c0004c9, 0x24000001 }, + { 0x0c0004ca, 0x1400ffff }, + { 0x0c0004cb, 0x24000001 }, + { 0x0c0004cc, 0x1400ffff }, + { 0x0c0004cd, 0x24000001 }, + { 0x0c0004ce, 0x1400ffff }, + { 0x0c0004d0, 0x24000001 }, + { 0x0c0004d1, 0x1400ffff }, + { 0x0c0004d2, 0x24000001 }, + { 0x0c0004d3, 0x1400ffff }, + { 0x0c0004d4, 0x24000001 }, + { 0x0c0004d5, 0x1400ffff }, + { 0x0c0004d6, 0x24000001 }, + { 0x0c0004d7, 0x1400ffff }, + { 0x0c0004d8, 0x24000001 }, + { 0x0c0004d9, 0x1400ffff }, + { 0x0c0004da, 0x24000001 }, + { 0x0c0004db, 0x1400ffff }, + { 0x0c0004dc, 0x24000001 }, + { 0x0c0004dd, 0x1400ffff }, + { 0x0c0004de, 0x24000001 }, + { 0x0c0004df, 0x1400ffff }, + { 0x0c0004e0, 0x24000001 }, + { 0x0c0004e1, 0x1400ffff }, + { 0x0c0004e2, 0x24000001 }, + { 0x0c0004e3, 0x1400ffff }, + { 0x0c0004e4, 0x24000001 }, + { 0x0c0004e5, 0x1400ffff }, + { 0x0c0004e6, 0x24000001 }, + { 0x0c0004e7, 0x1400ffff }, + { 0x0c0004e8, 0x24000001 }, + { 0x0c0004e9, 0x1400ffff }, + { 0x0c0004ea, 0x24000001 }, + { 0x0c0004eb, 0x1400ffff }, + { 0x0c0004ec, 0x24000001 }, + { 0x0c0004ed, 0x1400ffff }, + { 0x0c0004ee, 0x24000001 }, + { 0x0c0004ef, 0x1400ffff }, + { 0x0c0004f0, 0x24000001 }, + { 0x0c0004f1, 0x1400ffff }, + { 0x0c0004f2, 0x24000001 }, + { 0x0c0004f3, 0x1400ffff }, + { 0x0c0004f4, 0x24000001 }, + { 0x0c0004f5, 0x1400ffff }, + { 0x0c0004f6, 0x24000001 }, + { 0x0c0004f7, 0x1400ffff }, + { 0x0c0004f8, 0x24000001 }, + { 0x0c0004f9, 0x1400ffff }, + { 0x0c000500, 0x24000001 }, + { 0x0c000501, 0x1400ffff }, + { 0x0c000502, 0x24000001 }, + { 0x0c000503, 0x1400ffff }, + { 0x0c000504, 0x24000001 }, + { 0x0c000505, 0x1400ffff }, + { 0x0c000506, 0x24000001 }, + { 0x0c000507, 0x1400ffff }, + { 0x0c000508, 0x24000001 }, + { 0x0c000509, 0x1400ffff }, + { 0x0c00050a, 0x24000001 }, + { 0x0c00050b, 0x1400ffff }, + { 0x0c00050c, 0x24000001 }, + { 0x0c00050d, 0x1400ffff }, + { 0x0c00050e, 0x24000001 }, + { 0x0c00050f, 0x1400ffff }, + { 0x01000531, 0x24000030 }, + { 0x01000532, 0x24000030 }, + { 0x01000533, 0x24000030 }, + { 0x01000534, 0x24000030 }, + { 0x01000535, 0x24000030 }, + { 0x01000536, 0x24000030 }, + { 0x01000537, 0x24000030 }, + { 0x01000538, 0x24000030 }, + { 0x01000539, 0x24000030 }, + { 0x0100053a, 0x24000030 }, + { 0x0100053b, 0x24000030 }, + { 0x0100053c, 0x24000030 }, + { 0x0100053d, 0x24000030 }, + { 0x0100053e, 0x24000030 }, + { 0x0100053f, 0x24000030 }, + { 0x01000540, 0x24000030 }, + { 0x01000541, 0x24000030 }, + { 0x01000542, 0x24000030 }, + { 0x01000543, 0x24000030 }, + { 0x01000544, 0x24000030 }, + { 0x01000545, 0x24000030 }, + { 0x01000546, 0x24000030 }, + { 0x01000547, 0x24000030 }, + { 0x01000548, 0x24000030 }, + { 0x01000549, 0x24000030 }, + { 0x0100054a, 0x24000030 }, + { 0x0100054b, 0x24000030 }, + { 0x0100054c, 0x24000030 }, + { 0x0100054d, 0x24000030 }, + { 0x0100054e, 0x24000030 }, + { 0x0100054f, 0x24000030 }, + { 0x01000550, 0x24000030 }, + { 0x01000551, 0x24000030 }, + { 0x01000552, 0x24000030 }, + { 0x01000553, 0x24000030 }, + { 0x01000554, 0x24000030 }, + { 0x01000555, 0x24000030 }, + { 0x01000556, 0x24000030 }, + { 0x01000559, 0x18000000 }, + { 0x0180055a, 0x54000005 }, + { 0x01000561, 0x1400ffd0 }, + { 0x01000562, 0x1400ffd0 }, + { 0x01000563, 0x1400ffd0 }, + { 0x01000564, 0x1400ffd0 }, + { 0x01000565, 0x1400ffd0 }, + { 0x01000566, 0x1400ffd0 }, + { 0x01000567, 0x1400ffd0 }, + { 0x01000568, 0x1400ffd0 }, + { 0x01000569, 0x1400ffd0 }, + { 0x0100056a, 0x1400ffd0 }, + { 0x0100056b, 0x1400ffd0 }, + { 0x0100056c, 0x1400ffd0 }, + { 0x0100056d, 0x1400ffd0 }, + { 0x0100056e, 0x1400ffd0 }, + { 0x0100056f, 0x1400ffd0 }, + { 0x01000570, 0x1400ffd0 }, + { 0x01000571, 0x1400ffd0 }, + { 0x01000572, 0x1400ffd0 }, + { 0x01000573, 0x1400ffd0 }, + { 0x01000574, 0x1400ffd0 }, + { 0x01000575, 0x1400ffd0 }, + { 0x01000576, 0x1400ffd0 }, + { 0x01000577, 0x1400ffd0 }, + { 0x01000578, 0x1400ffd0 }, + { 0x01000579, 0x1400ffd0 }, + { 0x0100057a, 0x1400ffd0 }, + { 0x0100057b, 0x1400ffd0 }, + { 0x0100057c, 0x1400ffd0 }, + { 0x0100057d, 0x1400ffd0 }, + { 0x0100057e, 0x1400ffd0 }, + { 0x0100057f, 0x1400ffd0 }, + { 0x01000580, 0x1400ffd0 }, + { 0x01000581, 0x1400ffd0 }, + { 0x01000582, 0x1400ffd0 }, + { 0x01000583, 0x1400ffd0 }, + { 0x01000584, 0x1400ffd0 }, + { 0x01000585, 0x1400ffd0 }, + { 0x01000586, 0x1400ffd0 }, + { 0x01000587, 0x14000000 }, + { 0x09000589, 0x54000000 }, + { 0x0100058a, 0x44000000 }, + { 0x19800591, 0x30000028 }, + { 0x198005bb, 0x30000002 }, + { 0x190005be, 0x54000000 }, + { 0x190005bf, 0x30000000 }, + { 0x190005c0, 0x54000000 }, + { 0x198005c1, 0x30000001 }, + { 0x190005c3, 0x54000000 }, + { 0x198005c4, 0x30000001 }, + { 0x190005c6, 0x54000000 }, + { 0x190005c7, 0x30000000 }, + { 0x198005d0, 0x1c00001a }, + { 0x198005f0, 0x1c000002 }, + { 0x198005f3, 0x54000001 }, + { 0x09800600, 0x04000003 }, + { 0x0000060b, 0x5c000000 }, + { 0x0980060c, 0x54000001 }, + { 0x0080060e, 0x68000001 }, + { 0x00800610, 0x30000005 }, + { 0x0900061b, 0x54000000 }, + { 0x0080061e, 0x54000001 }, + { 0x00800621, 0x1c000019 }, + { 0x09000640, 0x18000000 }, + { 0x00800641, 0x1c000009 }, + { 0x1b80064b, 0x30000013 }, + { 0x09800660, 0x34000009 }, + { 0x0080066a, 0x54000003 }, + { 0x0080066e, 0x1c000001 }, + { 0x1b000670, 0x30000000 }, + { 0x00800671, 0x1c000062 }, + { 0x000006d4, 0x54000000 }, + { 0x000006d5, 0x1c000000 }, + { 0x008006d6, 0x30000006 }, + { 0x090006dd, 0x04000000 }, + { 0x000006de, 0x2c000000 }, + { 0x008006df, 0x30000005 }, + { 0x008006e5, 0x18000001 }, + { 0x008006e7, 0x30000001 }, + { 0x000006e9, 0x68000000 }, + { 0x008006ea, 0x30000003 }, + { 0x008006ee, 0x1c000001 }, + { 0x008006f0, 0x34000009 }, + { 0x008006fa, 0x1c000002 }, + { 0x008006fd, 0x68000001 }, + { 0x000006ff, 0x1c000000 }, + { 0x31800700, 0x5400000d }, + { 0x3100070f, 0x04000000 }, + { 0x31000710, 0x1c000000 }, + { 0x31000711, 0x30000000 }, + { 0x31800712, 0x1c00001d }, + { 0x31800730, 0x3000001a }, + { 0x3180074d, 0x1c000020 }, + { 0x37800780, 0x1c000025 }, + { 0x378007a6, 0x3000000a }, + { 0x370007b1, 0x1c000000 }, + { 0x0e800901, 0x30000001 }, + { 0x0e000903, 0x28000000 }, + { 0x0e800904, 0x1c000035 }, + { 0x0e00093c, 0x30000000 }, + { 0x0e00093d, 0x1c000000 }, + { 0x0e80093e, 0x28000002 }, + { 0x0e800941, 0x30000007 }, + { 0x0e800949, 0x28000003 }, + { 0x0e00094d, 0x30000000 }, + { 0x0e000950, 0x1c000000 }, + { 0x0e800951, 0x30000003 }, + { 0x0e800958, 0x1c000009 }, + { 0x0e800962, 0x30000001 }, + { 0x09800964, 0x54000001 }, + { 0x0e800966, 0x34000009 }, + { 0x09000970, 0x54000000 }, + { 0x0e00097d, 0x1c000000 }, + { 0x02000981, 0x30000000 }, + { 0x02800982, 0x28000001 }, + { 0x02800985, 0x1c000007 }, + { 0x0280098f, 0x1c000001 }, + { 0x02800993, 0x1c000015 }, + { 0x028009aa, 0x1c000006 }, + { 0x020009b2, 0x1c000000 }, + { 0x028009b6, 0x1c000003 }, + { 0x020009bc, 0x30000000 }, + { 0x020009bd, 0x1c000000 }, + { 0x028009be, 0x28000002 }, + { 0x028009c1, 0x30000003 }, + { 0x028009c7, 0x28000001 }, + { 0x028009cb, 0x28000001 }, + { 0x020009cd, 0x30000000 }, + { 0x020009ce, 0x1c000000 }, + { 0x020009d7, 0x28000000 }, + { 0x028009dc, 0x1c000001 }, + { 0x028009df, 0x1c000002 }, + { 0x028009e2, 0x30000001 }, + { 0x028009e6, 0x34000009 }, + { 0x028009f0, 0x1c000001 }, + { 0x028009f2, 0x5c000001 }, + { 0x028009f4, 0x3c000005 }, + { 0x020009fa, 0x68000000 }, + { 0x15800a01, 0x30000001 }, + { 0x15000a03, 0x28000000 }, + { 0x15800a05, 0x1c000005 }, + { 0x15800a0f, 0x1c000001 }, + { 0x15800a13, 0x1c000015 }, + { 0x15800a2a, 0x1c000006 }, + { 0x15800a32, 0x1c000001 }, + { 0x15800a35, 0x1c000001 }, + { 0x15800a38, 0x1c000001 }, + { 0x15000a3c, 0x30000000 }, + { 0x15800a3e, 0x28000002 }, + { 0x15800a41, 0x30000001 }, + { 0x15800a47, 0x30000001 }, + { 0x15800a4b, 0x30000002 }, + { 0x15800a59, 0x1c000003 }, + { 0x15000a5e, 0x1c000000 }, + { 0x15800a66, 0x34000009 }, + { 0x15800a70, 0x30000001 }, + { 0x15800a72, 0x1c000002 }, + { 0x14800a81, 0x30000001 }, + { 0x14000a83, 0x28000000 }, + { 0x14800a85, 0x1c000008 }, + { 0x14800a8f, 0x1c000002 }, + { 0x14800a93, 0x1c000015 }, + { 0x14800aaa, 0x1c000006 }, + { 0x14800ab2, 0x1c000001 }, + { 0x14800ab5, 0x1c000004 }, + { 0x14000abc, 0x30000000 }, + { 0x14000abd, 0x1c000000 }, + { 0x14800abe, 0x28000002 }, + { 0x14800ac1, 0x30000004 }, + { 0x14800ac7, 0x30000001 }, + { 0x14000ac9, 0x28000000 }, + { 0x14800acb, 0x28000001 }, + { 0x14000acd, 0x30000000 }, + { 0x14000ad0, 0x1c000000 }, + { 0x14800ae0, 0x1c000001 }, + { 0x14800ae2, 0x30000001 }, + { 0x14800ae6, 0x34000009 }, + { 0x14000af1, 0x5c000000 }, + { 0x2b000b01, 0x30000000 }, + { 0x2b800b02, 0x28000001 }, + { 0x2b800b05, 0x1c000007 }, + { 0x2b800b0f, 0x1c000001 }, + { 0x2b800b13, 0x1c000015 }, + { 0x2b800b2a, 0x1c000006 }, + { 0x2b800b32, 0x1c000001 }, + { 0x2b800b35, 0x1c000004 }, + { 0x2b000b3c, 0x30000000 }, + { 0x2b000b3d, 0x1c000000 }, + { 0x2b000b3e, 0x28000000 }, + { 0x2b000b3f, 0x30000000 }, + { 0x2b000b40, 0x28000000 }, + { 0x2b800b41, 0x30000002 }, + { 0x2b800b47, 0x28000001 }, + { 0x2b800b4b, 0x28000001 }, + { 0x2b000b4d, 0x30000000 }, + { 0x2b000b56, 0x30000000 }, + { 0x2b000b57, 0x28000000 }, + { 0x2b800b5c, 0x1c000001 }, + { 0x2b800b5f, 0x1c000002 }, + { 0x2b800b66, 0x34000009 }, + { 0x2b000b70, 0x68000000 }, + { 0x2b000b71, 0x1c000000 }, + { 0x35000b82, 0x30000000 }, + { 0x35000b83, 0x1c000000 }, + { 0x35800b85, 0x1c000005 }, + { 0x35800b8e, 0x1c000002 }, + { 0x35800b92, 0x1c000003 }, + { 0x35800b99, 0x1c000001 }, + { 0x35000b9c, 0x1c000000 }, + { 0x35800b9e, 0x1c000001 }, + { 0x35800ba3, 0x1c000001 }, + { 0x35800ba8, 0x1c000002 }, + { 0x35800bae, 0x1c00000b }, + { 0x35800bbe, 0x28000001 }, + { 0x35000bc0, 0x30000000 }, + { 0x35800bc1, 0x28000001 }, + { 0x35800bc6, 0x28000002 }, + { 0x35800bca, 0x28000002 }, + { 0x35000bcd, 0x30000000 }, + { 0x35000bd7, 0x28000000 }, + { 0x35800be6, 0x34000009 }, + { 0x35800bf0, 0x3c000002 }, + { 0x35800bf3, 0x68000005 }, + { 0x35000bf9, 0x5c000000 }, + { 0x35000bfa, 0x68000000 }, + { 0x36800c01, 0x28000002 }, + { 0x36800c05, 0x1c000007 }, + { 0x36800c0e, 0x1c000002 }, + { 0x36800c12, 0x1c000016 }, + { 0x36800c2a, 0x1c000009 }, + { 0x36800c35, 0x1c000004 }, + { 0x36800c3e, 0x30000002 }, + { 0x36800c41, 0x28000003 }, + { 0x36800c46, 0x30000002 }, + { 0x36800c4a, 0x30000003 }, + { 0x36800c55, 0x30000001 }, + { 0x36800c60, 0x1c000001 }, + { 0x36800c66, 0x34000009 }, + { 0x1c800c82, 0x28000001 }, + { 0x1c800c85, 0x1c000007 }, + { 0x1c800c8e, 0x1c000002 }, + { 0x1c800c92, 0x1c000016 }, + { 0x1c800caa, 0x1c000009 }, + { 0x1c800cb5, 0x1c000004 }, + { 0x1c000cbc, 0x30000000 }, + { 0x1c000cbd, 0x1c000000 }, + { 0x1c000cbe, 0x28000000 }, + { 0x1c000cbf, 0x30000000 }, + { 0x1c800cc0, 0x28000004 }, + { 0x1c000cc6, 0x30000000 }, + { 0x1c800cc7, 0x28000001 }, + { 0x1c800cca, 0x28000001 }, + { 0x1c800ccc, 0x30000001 }, + { 0x1c800cd5, 0x28000001 }, + { 0x1c000cde, 0x1c000000 }, + { 0x1c800ce0, 0x1c000001 }, + { 0x1c800ce6, 0x34000009 }, + { 0x24800d02, 0x28000001 }, + { 0x24800d05, 0x1c000007 }, + { 0x24800d0e, 0x1c000002 }, + { 0x24800d12, 0x1c000016 }, + { 0x24800d2a, 0x1c00000f }, + { 0x24800d3e, 0x28000002 }, + { 0x24800d41, 0x30000002 }, + { 0x24800d46, 0x28000002 }, + { 0x24800d4a, 0x28000002 }, + { 0x24000d4d, 0x30000000 }, + { 0x24000d57, 0x28000000 }, + { 0x24800d60, 0x1c000001 }, + { 0x24800d66, 0x34000009 }, + { 0x2f800d82, 0x28000001 }, + { 0x2f800d85, 0x1c000011 }, + { 0x2f800d9a, 0x1c000017 }, + { 0x2f800db3, 0x1c000008 }, + { 0x2f000dbd, 0x1c000000 }, + { 0x2f800dc0, 0x1c000006 }, + { 0x2f000dca, 0x30000000 }, + { 0x2f800dcf, 0x28000002 }, + { 0x2f800dd2, 0x30000002 }, + { 0x2f000dd6, 0x30000000 }, + { 0x2f800dd8, 0x28000007 }, + { 0x2f800df2, 0x28000001 }, + { 0x2f000df4, 0x54000000 }, + { 0x38800e01, 0x1c00002f }, + { 0x38000e31, 0x30000000 }, + { 0x38800e32, 0x1c000001 }, + { 0x38800e34, 0x30000006 }, + { 0x09000e3f, 0x5c000000 }, + { 0x38800e40, 0x1c000005 }, + { 0x38000e46, 0x18000000 }, + { 0x38800e47, 0x30000007 }, + { 0x38000e4f, 0x54000000 }, + { 0x38800e50, 0x34000009 }, + { 0x38800e5a, 0x54000001 }, + { 0x20800e81, 0x1c000001 }, + { 0x20000e84, 0x1c000000 }, + { 0x20800e87, 0x1c000001 }, + { 0x20000e8a, 0x1c000000 }, + { 0x20000e8d, 0x1c000000 }, + { 0x20800e94, 0x1c000003 }, + { 0x20800e99, 0x1c000006 }, + { 0x20800ea1, 0x1c000002 }, + { 0x20000ea5, 0x1c000000 }, + { 0x20000ea7, 0x1c000000 }, + { 0x20800eaa, 0x1c000001 }, + { 0x20800ead, 0x1c000003 }, + { 0x20000eb1, 0x30000000 }, + { 0x20800eb2, 0x1c000001 }, + { 0x20800eb4, 0x30000005 }, + { 0x20800ebb, 0x30000001 }, + { 0x20000ebd, 0x1c000000 }, + { 0x20800ec0, 0x1c000004 }, + { 0x20000ec6, 0x18000000 }, + { 0x20800ec8, 0x30000005 }, + { 0x20800ed0, 0x34000009 }, + { 0x20800edc, 0x1c000001 }, + { 0x39000f00, 0x1c000000 }, + { 0x39800f01, 0x68000002 }, + { 0x39800f04, 0x5400000e }, + { 0x39800f13, 0x68000004 }, + { 0x39800f18, 0x30000001 }, + { 0x39800f1a, 0x68000005 }, + { 0x39800f20, 0x34000009 }, + { 0x39800f2a, 0x3c000009 }, + { 0x39000f34, 0x68000000 }, + { 0x39000f35, 0x30000000 }, + { 0x39000f36, 0x68000000 }, + { 0x39000f37, 0x30000000 }, + { 0x39000f38, 0x68000000 }, + { 0x39000f39, 0x30000000 }, + { 0x39000f3a, 0x58000000 }, + { 0x39000f3b, 0x48000000 }, + { 0x39000f3c, 0x58000000 }, + { 0x39000f3d, 0x48000000 }, + { 0x39800f3e, 0x28000001 }, + { 0x39800f40, 0x1c000007 }, + { 0x39800f49, 0x1c000021 }, + { 0x39800f71, 0x3000000d }, + { 0x39000f7f, 0x28000000 }, + { 0x39800f80, 0x30000004 }, + { 0x39000f85, 0x54000000 }, + { 0x39800f86, 0x30000001 }, + { 0x39800f88, 0x1c000003 }, + { 0x39800f90, 0x30000007 }, + { 0x39800f99, 0x30000023 }, + { 0x39800fbe, 0x68000007 }, + { 0x39000fc6, 0x30000000 }, + { 0x39800fc7, 0x68000005 }, + { 0x39000fcf, 0x68000000 }, + { 0x39800fd0, 0x54000001 }, + { 0x26801000, 0x1c000021 }, + { 0x26801023, 0x1c000004 }, + { 0x26801029, 0x1c000001 }, + { 0x2600102c, 0x28000000 }, + { 0x2680102d, 0x30000003 }, + { 0x26001031, 0x28000000 }, + { 0x26001032, 0x30000000 }, + { 0x26801036, 0x30000001 }, + { 0x26001038, 0x28000000 }, + { 0x26001039, 0x30000000 }, + { 0x26801040, 0x34000009 }, + { 0x2680104a, 0x54000005 }, + { 0x26801050, 0x1c000005 }, + { 0x26801056, 0x28000001 }, + { 0x26801058, 0x30000001 }, + { 0x100010a0, 0x24001c60 }, + { 0x100010a1, 0x24001c60 }, + { 0x100010a2, 0x24001c60 }, + { 0x100010a3, 0x24001c60 }, + { 0x100010a4, 0x24001c60 }, + { 0x100010a5, 0x24001c60 }, + { 0x100010a6, 0x24001c60 }, + { 0x100010a7, 0x24001c60 }, + { 0x100010a8, 0x24001c60 }, + { 0x100010a9, 0x24001c60 }, + { 0x100010aa, 0x24001c60 }, + { 0x100010ab, 0x24001c60 }, + { 0x100010ac, 0x24001c60 }, + { 0x100010ad, 0x24001c60 }, + { 0x100010ae, 0x24001c60 }, + { 0x100010af, 0x24001c60 }, + { 0x100010b0, 0x24001c60 }, + { 0x100010b1, 0x24001c60 }, + { 0x100010b2, 0x24001c60 }, + { 0x100010b3, 0x24001c60 }, + { 0x100010b4, 0x24001c60 }, + { 0x100010b5, 0x24001c60 }, + { 0x100010b6, 0x24001c60 }, + { 0x100010b7, 0x24001c60 }, + { 0x100010b8, 0x24001c60 }, + { 0x100010b9, 0x24001c60 }, + { 0x100010ba, 0x24001c60 }, + { 0x100010bb, 0x24001c60 }, + { 0x100010bc, 0x24001c60 }, + { 0x100010bd, 0x24001c60 }, + { 0x100010be, 0x24001c60 }, + { 0x100010bf, 0x24001c60 }, + { 0x100010c0, 0x24001c60 }, + { 0x100010c1, 0x24001c60 }, + { 0x100010c2, 0x24001c60 }, + { 0x100010c3, 0x24001c60 }, + { 0x100010c4, 0x24001c60 }, + { 0x100010c5, 0x24001c60 }, + { 0x108010d0, 0x1c00002a }, + { 0x090010fb, 0x54000000 }, + { 0x100010fc, 0x18000000 }, + { 0x17801100, 0x1c000059 }, + { 0x1780115f, 0x1c000043 }, + { 0x178011a8, 0x1c000051 }, + { 0x0f801200, 0x1c000048 }, + { 0x0f80124a, 0x1c000003 }, + { 0x0f801250, 0x1c000006 }, + { 0x0f001258, 0x1c000000 }, + { 0x0f80125a, 0x1c000003 }, + { 0x0f801260, 0x1c000028 }, + { 0x0f80128a, 0x1c000003 }, + { 0x0f801290, 0x1c000020 }, + { 0x0f8012b2, 0x1c000003 }, + { 0x0f8012b8, 0x1c000006 }, + { 0x0f0012c0, 0x1c000000 }, + { 0x0f8012c2, 0x1c000003 }, + { 0x0f8012c8, 0x1c00000e }, + { 0x0f8012d8, 0x1c000038 }, + { 0x0f801312, 0x1c000003 }, + { 0x0f801318, 0x1c000042 }, + { 0x0f00135f, 0x30000000 }, + { 0x0f001360, 0x68000000 }, + { 0x0f801361, 0x54000007 }, + { 0x0f801369, 0x3c000013 }, + { 0x0f801380, 0x1c00000f }, + { 0x0f801390, 0x68000009 }, + { 0x088013a0, 0x1c000054 }, + { 0x07801401, 0x1c00026b }, + { 0x0780166d, 0x54000001 }, + { 0x0780166f, 0x1c000007 }, + { 0x28001680, 0x74000000 }, + { 0x28801681, 0x1c000019 }, + { 0x2800169b, 0x58000000 }, + { 0x2800169c, 0x48000000 }, + { 0x2d8016a0, 0x1c00004a }, + { 0x098016eb, 0x54000002 }, + { 0x2d8016ee, 0x38000002 }, + { 0x32801700, 0x1c00000c }, + { 0x3280170e, 0x1c000003 }, + { 0x32801712, 0x30000002 }, + { 0x18801720, 0x1c000011 }, + { 0x18801732, 0x30000002 }, + { 0x09801735, 0x54000001 }, + { 0x06801740, 0x1c000011 }, + { 0x06801752, 0x30000001 }, + { 0x33801760, 0x1c00000c }, + { 0x3380176e, 0x1c000002 }, + { 0x33801772, 0x30000001 }, + { 0x1f801780, 0x1c000033 }, + { 0x1f8017b4, 0x04000001 }, + { 0x1f0017b6, 0x28000000 }, + { 0x1f8017b7, 0x30000006 }, + { 0x1f8017be, 0x28000007 }, + { 0x1f0017c6, 0x30000000 }, + { 0x1f8017c7, 0x28000001 }, + { 0x1f8017c9, 0x3000000a }, + { 0x1f8017d4, 0x54000002 }, + { 0x1f0017d7, 0x18000000 }, + { 0x1f8017d8, 0x54000002 }, + { 0x1f0017db, 0x5c000000 }, + { 0x1f0017dc, 0x1c000000 }, + { 0x1f0017dd, 0x30000000 }, + { 0x1f8017e0, 0x34000009 }, + { 0x1f8017f0, 0x3c000009 }, + { 0x25801800, 0x54000005 }, + { 0x25001806, 0x44000000 }, + { 0x25801807, 0x54000003 }, + { 0x2580180b, 0x30000002 }, + { 0x2500180e, 0x74000000 }, + { 0x25801810, 0x34000009 }, + { 0x25801820, 0x1c000022 }, + { 0x25001843, 0x18000000 }, + { 0x25801844, 0x1c000033 }, + { 0x25801880, 0x1c000028 }, + { 0x250018a9, 0x30000000 }, + { 0x22801900, 0x1c00001c }, + { 0x22801920, 0x30000002 }, + { 0x22801923, 0x28000003 }, + { 0x22801927, 0x30000001 }, + { 0x22801929, 0x28000002 }, + { 0x22801930, 0x28000001 }, + { 0x22001932, 0x30000000 }, + { 0x22801933, 0x28000005 }, + { 0x22801939, 0x30000002 }, + { 0x22001940, 0x68000000 }, + { 0x22801944, 0x54000001 }, + { 0x22801946, 0x34000009 }, + { 0x34801950, 0x1c00001d }, + { 0x34801970, 0x1c000004 }, + { 0x27801980, 0x1c000029 }, + { 0x278019b0, 0x28000010 }, + { 0x278019c1, 0x1c000006 }, + { 0x278019c8, 0x28000001 }, + { 0x278019d0, 0x34000009 }, + { 0x278019de, 0x54000001 }, + { 0x1f8019e0, 0x6800001f }, + { 0x05801a00, 0x1c000016 }, + { 0x05801a17, 0x30000001 }, + { 0x05801a19, 0x28000002 }, + { 0x05801a1e, 0x54000001 }, + { 0x21801d00, 0x1400002b }, + { 0x21801d2c, 0x18000035 }, + { 0x21801d62, 0x14000015 }, + { 0x0c001d78, 0x18000000 }, + { 0x21801d79, 0x14000021 }, + { 0x21801d9b, 0x18000024 }, + { 0x1b801dc0, 0x30000003 }, + { 0x21001e00, 0x24000001 }, + { 0x21001e01, 0x1400ffff }, + { 0x21001e02, 0x24000001 }, + { 0x21001e03, 0x1400ffff }, + { 0x21001e04, 0x24000001 }, + { 0x21001e05, 0x1400ffff }, + { 0x21001e06, 0x24000001 }, + { 0x21001e07, 0x1400ffff }, + { 0x21001e08, 0x24000001 }, + { 0x21001e09, 0x1400ffff }, + { 0x21001e0a, 0x24000001 }, + { 0x21001e0b, 0x1400ffff }, + { 0x21001e0c, 0x24000001 }, + { 0x21001e0d, 0x1400ffff }, + { 0x21001e0e, 0x24000001 }, + { 0x21001e0f, 0x1400ffff }, + { 0x21001e10, 0x24000001 }, + { 0x21001e11, 0x1400ffff }, + { 0x21001e12, 0x24000001 }, + { 0x21001e13, 0x1400ffff }, + { 0x21001e14, 0x24000001 }, + { 0x21001e15, 0x1400ffff }, + { 0x21001e16, 0x24000001 }, + { 0x21001e17, 0x1400ffff }, + { 0x21001e18, 0x24000001 }, + { 0x21001e19, 0x1400ffff }, + { 0x21001e1a, 0x24000001 }, + { 0x21001e1b, 0x1400ffff }, + { 0x21001e1c, 0x24000001 }, + { 0x21001e1d, 0x1400ffff }, + { 0x21001e1e, 0x24000001 }, + { 0x21001e1f, 0x1400ffff }, + { 0x21001e20, 0x24000001 }, + { 0x21001e21, 0x1400ffff }, + { 0x21001e22, 0x24000001 }, + { 0x21001e23, 0x1400ffff }, + { 0x21001e24, 0x24000001 }, + { 0x21001e25, 0x1400ffff }, + { 0x21001e26, 0x24000001 }, + { 0x21001e27, 0x1400ffff }, + { 0x21001e28, 0x24000001 }, + { 0x21001e29, 0x1400ffff }, + { 0x21001e2a, 0x24000001 }, + { 0x21001e2b, 0x1400ffff }, + { 0x21001e2c, 0x24000001 }, + { 0x21001e2d, 0x1400ffff }, + { 0x21001e2e, 0x24000001 }, + { 0x21001e2f, 0x1400ffff }, + { 0x21001e30, 0x24000001 }, + { 0x21001e31, 0x1400ffff }, + { 0x21001e32, 0x24000001 }, + { 0x21001e33, 0x1400ffff }, + { 0x21001e34, 0x24000001 }, + { 0x21001e35, 0x1400ffff }, + { 0x21001e36, 0x24000001 }, + { 0x21001e37, 0x1400ffff }, + { 0x21001e38, 0x24000001 }, + { 0x21001e39, 0x1400ffff }, + { 0x21001e3a, 0x24000001 }, + { 0x21001e3b, 0x1400ffff }, + { 0x21001e3c, 0x24000001 }, + { 0x21001e3d, 0x1400ffff }, + { 0x21001e3e, 0x24000001 }, + { 0x21001e3f, 0x1400ffff }, + { 0x21001e40, 0x24000001 }, + { 0x21001e41, 0x1400ffff }, + { 0x21001e42, 0x24000001 }, + { 0x21001e43, 0x1400ffff }, + { 0x21001e44, 0x24000001 }, + { 0x21001e45, 0x1400ffff }, + { 0x21001e46, 0x24000001 }, + { 0x21001e47, 0x1400ffff }, + { 0x21001e48, 0x24000001 }, + { 0x21001e49, 0x1400ffff }, + { 0x21001e4a, 0x24000001 }, + { 0x21001e4b, 0x1400ffff }, + { 0x21001e4c, 0x24000001 }, + { 0x21001e4d, 0x1400ffff }, + { 0x21001e4e, 0x24000001 }, + { 0x21001e4f, 0x1400ffff }, + { 0x21001e50, 0x24000001 }, + { 0x21001e51, 0x1400ffff }, + { 0x21001e52, 0x24000001 }, + { 0x21001e53, 0x1400ffff }, + { 0x21001e54, 0x24000001 }, + { 0x21001e55, 0x1400ffff }, + { 0x21001e56, 0x24000001 }, + { 0x21001e57, 0x1400ffff }, + { 0x21001e58, 0x24000001 }, + { 0x21001e59, 0x1400ffff }, + { 0x21001e5a, 0x24000001 }, + { 0x21001e5b, 0x1400ffff }, + { 0x21001e5c, 0x24000001 }, + { 0x21001e5d, 0x1400ffff }, + { 0x21001e5e, 0x24000001 }, + { 0x21001e5f, 0x1400ffff }, + { 0x21001e60, 0x24000001 }, + { 0x21001e61, 0x1400ffff }, + { 0x21001e62, 0x24000001 }, + { 0x21001e63, 0x1400ffff }, + { 0x21001e64, 0x24000001 }, + { 0x21001e65, 0x1400ffff }, + { 0x21001e66, 0x24000001 }, + { 0x21001e67, 0x1400ffff }, + { 0x21001e68, 0x24000001 }, + { 0x21001e69, 0x1400ffff }, + { 0x21001e6a, 0x24000001 }, + { 0x21001e6b, 0x1400ffff }, + { 0x21001e6c, 0x24000001 }, + { 0x21001e6d, 0x1400ffff }, + { 0x21001e6e, 0x24000001 }, + { 0x21001e6f, 0x1400ffff }, + { 0x21001e70, 0x24000001 }, + { 0x21001e71, 0x1400ffff }, + { 0x21001e72, 0x24000001 }, + { 0x21001e73, 0x1400ffff }, + { 0x21001e74, 0x24000001 }, + { 0x21001e75, 0x1400ffff }, + { 0x21001e76, 0x24000001 }, + { 0x21001e77, 0x1400ffff }, + { 0x21001e78, 0x24000001 }, + { 0x21001e79, 0x1400ffff }, + { 0x21001e7a, 0x24000001 }, + { 0x21001e7b, 0x1400ffff }, + { 0x21001e7c, 0x24000001 }, + { 0x21001e7d, 0x1400ffff }, + { 0x21001e7e, 0x24000001 }, + { 0x21001e7f, 0x1400ffff }, + { 0x21001e80, 0x24000001 }, + { 0x21001e81, 0x1400ffff }, + { 0x21001e82, 0x24000001 }, + { 0x21001e83, 0x1400ffff }, + { 0x21001e84, 0x24000001 }, + { 0x21001e85, 0x1400ffff }, + { 0x21001e86, 0x24000001 }, + { 0x21001e87, 0x1400ffff }, + { 0x21001e88, 0x24000001 }, + { 0x21001e89, 0x1400ffff }, + { 0x21001e8a, 0x24000001 }, + { 0x21001e8b, 0x1400ffff }, + { 0x21001e8c, 0x24000001 }, + { 0x21001e8d, 0x1400ffff }, + { 0x21001e8e, 0x24000001 }, + { 0x21001e8f, 0x1400ffff }, + { 0x21001e90, 0x24000001 }, + { 0x21001e91, 0x1400ffff }, + { 0x21001e92, 0x24000001 }, + { 0x21001e93, 0x1400ffff }, + { 0x21001e94, 0x24000001 }, + { 0x21001e95, 0x1400ffff }, + { 0x21801e96, 0x14000004 }, + { 0x21001e9b, 0x1400ffc5 }, + { 0x21001ea0, 0x24000001 }, + { 0x21001ea1, 0x1400ffff }, + { 0x21001ea2, 0x24000001 }, + { 0x21001ea3, 0x1400ffff }, + { 0x21001ea4, 0x24000001 }, + { 0x21001ea5, 0x1400ffff }, + { 0x21001ea6, 0x24000001 }, + { 0x21001ea7, 0x1400ffff }, + { 0x21001ea8, 0x24000001 }, + { 0x21001ea9, 0x1400ffff }, + { 0x21001eaa, 0x24000001 }, + { 0x21001eab, 0x1400ffff }, + { 0x21001eac, 0x24000001 }, + { 0x21001ead, 0x1400ffff }, + { 0x21001eae, 0x24000001 }, + { 0x21001eaf, 0x1400ffff }, + { 0x21001eb0, 0x24000001 }, + { 0x21001eb1, 0x1400ffff }, + { 0x21001eb2, 0x24000001 }, + { 0x21001eb3, 0x1400ffff }, + { 0x21001eb4, 0x24000001 }, + { 0x21001eb5, 0x1400ffff }, + { 0x21001eb6, 0x24000001 }, + { 0x21001eb7, 0x1400ffff }, + { 0x21001eb8, 0x24000001 }, + { 0x21001eb9, 0x1400ffff }, + { 0x21001eba, 0x24000001 }, + { 0x21001ebb, 0x1400ffff }, + { 0x21001ebc, 0x24000001 }, + { 0x21001ebd, 0x1400ffff }, + { 0x21001ebe, 0x24000001 }, + { 0x21001ebf, 0x1400ffff }, + { 0x21001ec0, 0x24000001 }, + { 0x21001ec1, 0x1400ffff }, + { 0x21001ec2, 0x24000001 }, + { 0x21001ec3, 0x1400ffff }, + { 0x21001ec4, 0x24000001 }, + { 0x21001ec5, 0x1400ffff }, + { 0x21001ec6, 0x24000001 }, + { 0x21001ec7, 0x1400ffff }, + { 0x21001ec8, 0x24000001 }, + { 0x21001ec9, 0x1400ffff }, + { 0x21001eca, 0x24000001 }, + { 0x21001ecb, 0x1400ffff }, + { 0x21001ecc, 0x24000001 }, + { 0x21001ecd, 0x1400ffff }, + { 0x21001ece, 0x24000001 }, + { 0x21001ecf, 0x1400ffff }, + { 0x21001ed0, 0x24000001 }, + { 0x21001ed1, 0x1400ffff }, + { 0x21001ed2, 0x24000001 }, + { 0x21001ed3, 0x1400ffff }, + { 0x21001ed4, 0x24000001 }, + { 0x21001ed5, 0x1400ffff }, + { 0x21001ed6, 0x24000001 }, + { 0x21001ed7, 0x1400ffff }, + { 0x21001ed8, 0x24000001 }, + { 0x21001ed9, 0x1400ffff }, + { 0x21001eda, 0x24000001 }, + { 0x21001edb, 0x1400ffff }, + { 0x21001edc, 0x24000001 }, + { 0x21001edd, 0x1400ffff }, + { 0x21001ede, 0x24000001 }, + { 0x21001edf, 0x1400ffff }, + { 0x21001ee0, 0x24000001 }, + { 0x21001ee1, 0x1400ffff }, + { 0x21001ee2, 0x24000001 }, + { 0x21001ee3, 0x1400ffff }, + { 0x21001ee4, 0x24000001 }, + { 0x21001ee5, 0x1400ffff }, + { 0x21001ee6, 0x24000001 }, + { 0x21001ee7, 0x1400ffff }, + { 0x21001ee8, 0x24000001 }, + { 0x21001ee9, 0x1400ffff }, + { 0x21001eea, 0x24000001 }, + { 0x21001eeb, 0x1400ffff }, + { 0x21001eec, 0x24000001 }, + { 0x21001eed, 0x1400ffff }, + { 0x21001eee, 0x24000001 }, + { 0x21001eef, 0x1400ffff }, + { 0x21001ef0, 0x24000001 }, + { 0x21001ef1, 0x1400ffff }, + { 0x21001ef2, 0x24000001 }, + { 0x21001ef3, 0x1400ffff }, + { 0x21001ef4, 0x24000001 }, + { 0x21001ef5, 0x1400ffff }, + { 0x21001ef6, 0x24000001 }, + { 0x21001ef7, 0x1400ffff }, + { 0x21001ef8, 0x24000001 }, + { 0x21001ef9, 0x1400ffff }, + { 0x13001f00, 0x14000008 }, + { 0x13001f01, 0x14000008 }, + { 0x13001f02, 0x14000008 }, + { 0x13001f03, 0x14000008 }, + { 0x13001f04, 0x14000008 }, + { 0x13001f05, 0x14000008 }, + { 0x13001f06, 0x14000008 }, + { 0x13001f07, 0x14000008 }, + { 0x13001f08, 0x2400fff8 }, + { 0x13001f09, 0x2400fff8 }, + { 0x13001f0a, 0x2400fff8 }, + { 0x13001f0b, 0x2400fff8 }, + { 0x13001f0c, 0x2400fff8 }, + { 0x13001f0d, 0x2400fff8 }, + { 0x13001f0e, 0x2400fff8 }, + { 0x13001f0f, 0x2400fff8 }, + { 0x13001f10, 0x14000008 }, + { 0x13001f11, 0x14000008 }, + { 0x13001f12, 0x14000008 }, + { 0x13001f13, 0x14000008 }, + { 0x13001f14, 0x14000008 }, + { 0x13001f15, 0x14000008 }, + { 0x13001f18, 0x2400fff8 }, + { 0x13001f19, 0x2400fff8 }, + { 0x13001f1a, 0x2400fff8 }, + { 0x13001f1b, 0x2400fff8 }, + { 0x13001f1c, 0x2400fff8 }, + { 0x13001f1d, 0x2400fff8 }, + { 0x13001f20, 0x14000008 }, + { 0x13001f21, 0x14000008 }, + { 0x13001f22, 0x14000008 }, + { 0x13001f23, 0x14000008 }, + { 0x13001f24, 0x14000008 }, + { 0x13001f25, 0x14000008 }, + { 0x13001f26, 0x14000008 }, + { 0x13001f27, 0x14000008 }, + { 0x13001f28, 0x2400fff8 }, + { 0x13001f29, 0x2400fff8 }, + { 0x13001f2a, 0x2400fff8 }, + { 0x13001f2b, 0x2400fff8 }, + { 0x13001f2c, 0x2400fff8 }, + { 0x13001f2d, 0x2400fff8 }, + { 0x13001f2e, 0x2400fff8 }, + { 0x13001f2f, 0x2400fff8 }, + { 0x13001f30, 0x14000008 }, + { 0x13001f31, 0x14000008 }, + { 0x13001f32, 0x14000008 }, + { 0x13001f33, 0x14000008 }, + { 0x13001f34, 0x14000008 }, + { 0x13001f35, 0x14000008 }, + { 0x13001f36, 0x14000008 }, + { 0x13001f37, 0x14000008 }, + { 0x13001f38, 0x2400fff8 }, + { 0x13001f39, 0x2400fff8 }, + { 0x13001f3a, 0x2400fff8 }, + { 0x13001f3b, 0x2400fff8 }, + { 0x13001f3c, 0x2400fff8 }, + { 0x13001f3d, 0x2400fff8 }, + { 0x13001f3e, 0x2400fff8 }, + { 0x13001f3f, 0x2400fff8 }, + { 0x13001f40, 0x14000008 }, + { 0x13001f41, 0x14000008 }, + { 0x13001f42, 0x14000008 }, + { 0x13001f43, 0x14000008 }, + { 0x13001f44, 0x14000008 }, + { 0x13001f45, 0x14000008 }, + { 0x13001f48, 0x2400fff8 }, + { 0x13001f49, 0x2400fff8 }, + { 0x13001f4a, 0x2400fff8 }, + { 0x13001f4b, 0x2400fff8 }, + { 0x13001f4c, 0x2400fff8 }, + { 0x13001f4d, 0x2400fff8 }, + { 0x13001f50, 0x14000000 }, + { 0x13001f51, 0x14000008 }, + { 0x13001f52, 0x14000000 }, + { 0x13001f53, 0x14000008 }, + { 0x13001f54, 0x14000000 }, + { 0x13001f55, 0x14000008 }, + { 0x13001f56, 0x14000000 }, + { 0x13001f57, 0x14000008 }, + { 0x13001f59, 0x2400fff8 }, + { 0x13001f5b, 0x2400fff8 }, + { 0x13001f5d, 0x2400fff8 }, + { 0x13001f5f, 0x2400fff8 }, + { 0x13001f60, 0x14000008 }, + { 0x13001f61, 0x14000008 }, + { 0x13001f62, 0x14000008 }, + { 0x13001f63, 0x14000008 }, + { 0x13001f64, 0x14000008 }, + { 0x13001f65, 0x14000008 }, + { 0x13001f66, 0x14000008 }, + { 0x13001f67, 0x14000008 }, + { 0x13001f68, 0x2400fff8 }, + { 0x13001f69, 0x2400fff8 }, + { 0x13001f6a, 0x2400fff8 }, + { 0x13001f6b, 0x2400fff8 }, + { 0x13001f6c, 0x2400fff8 }, + { 0x13001f6d, 0x2400fff8 }, + { 0x13001f6e, 0x2400fff8 }, + { 0x13001f6f, 0x2400fff8 }, + { 0x13001f70, 0x1400004a }, + { 0x13001f71, 0x1400004a }, + { 0x13001f72, 0x14000056 }, + { 0x13001f73, 0x14000056 }, + { 0x13001f74, 0x14000056 }, + { 0x13001f75, 0x14000056 }, + { 0x13001f76, 0x14000064 }, + { 0x13001f77, 0x14000064 }, + { 0x13001f78, 0x14000080 }, + { 0x13001f79, 0x14000080 }, + { 0x13001f7a, 0x14000070 }, + { 0x13001f7b, 0x14000070 }, + { 0x13001f7c, 0x1400007e }, + { 0x13001f7d, 0x1400007e }, + { 0x13001f80, 0x14000008 }, + { 0x13001f81, 0x14000008 }, + { 0x13001f82, 0x14000008 }, + { 0x13001f83, 0x14000008 }, + { 0x13001f84, 0x14000008 }, + { 0x13001f85, 0x14000008 }, + { 0x13001f86, 0x14000008 }, + { 0x13001f87, 0x14000008 }, + { 0x13001f88, 0x2000fff8 }, + { 0x13001f89, 0x2000fff8 }, + { 0x13001f8a, 0x2000fff8 }, + { 0x13001f8b, 0x2000fff8 }, + { 0x13001f8c, 0x2000fff8 }, + { 0x13001f8d, 0x2000fff8 }, + { 0x13001f8e, 0x2000fff8 }, + { 0x13001f8f, 0x2000fff8 }, + { 0x13001f90, 0x14000008 }, + { 0x13001f91, 0x14000008 }, + { 0x13001f92, 0x14000008 }, + { 0x13001f93, 0x14000008 }, + { 0x13001f94, 0x14000008 }, + { 0x13001f95, 0x14000008 }, + { 0x13001f96, 0x14000008 }, + { 0x13001f97, 0x14000008 }, + { 0x13001f98, 0x2000fff8 }, + { 0x13001f99, 0x2000fff8 }, + { 0x13001f9a, 0x2000fff8 }, + { 0x13001f9b, 0x2000fff8 }, + { 0x13001f9c, 0x2000fff8 }, + { 0x13001f9d, 0x2000fff8 }, + { 0x13001f9e, 0x2000fff8 }, + { 0x13001f9f, 0x2000fff8 }, + { 0x13001fa0, 0x14000008 }, + { 0x13001fa1, 0x14000008 }, + { 0x13001fa2, 0x14000008 }, + { 0x13001fa3, 0x14000008 }, + { 0x13001fa4, 0x14000008 }, + { 0x13001fa5, 0x14000008 }, + { 0x13001fa6, 0x14000008 }, + { 0x13001fa7, 0x14000008 }, + { 0x13001fa8, 0x2000fff8 }, + { 0x13001fa9, 0x2000fff8 }, + { 0x13001faa, 0x2000fff8 }, + { 0x13001fab, 0x2000fff8 }, + { 0x13001fac, 0x2000fff8 }, + { 0x13001fad, 0x2000fff8 }, + { 0x13001fae, 0x2000fff8 }, + { 0x13001faf, 0x2000fff8 }, + { 0x13001fb0, 0x14000008 }, + { 0x13001fb1, 0x14000008 }, + { 0x13001fb2, 0x14000000 }, + { 0x13001fb3, 0x14000009 }, + { 0x13001fb4, 0x14000000 }, + { 0x13801fb6, 0x14000001 }, + { 0x13001fb8, 0x2400fff8 }, + { 0x13001fb9, 0x2400fff8 }, + { 0x13001fba, 0x2400ffb6 }, + { 0x13001fbb, 0x2400ffb6 }, + { 0x13001fbc, 0x2000fff7 }, + { 0x13001fbd, 0x60000000 }, + { 0x13001fbe, 0x1400e3db }, + { 0x13801fbf, 0x60000002 }, + { 0x13001fc2, 0x14000000 }, + { 0x13001fc3, 0x14000009 }, + { 0x13001fc4, 0x14000000 }, + { 0x13801fc6, 0x14000001 }, + { 0x13001fc8, 0x2400ffaa }, + { 0x13001fc9, 0x2400ffaa }, + { 0x13001fca, 0x2400ffaa }, + { 0x13001fcb, 0x2400ffaa }, + { 0x13001fcc, 0x2000fff7 }, + { 0x13801fcd, 0x60000002 }, + { 0x13001fd0, 0x14000008 }, + { 0x13001fd1, 0x14000008 }, + { 0x13801fd2, 0x14000001 }, + { 0x13801fd6, 0x14000001 }, + { 0x13001fd8, 0x2400fff8 }, + { 0x13001fd9, 0x2400fff8 }, + { 0x13001fda, 0x2400ff9c }, + { 0x13001fdb, 0x2400ff9c }, + { 0x13801fdd, 0x60000002 }, + { 0x13001fe0, 0x14000008 }, + { 0x13001fe1, 0x14000008 }, + { 0x13801fe2, 0x14000002 }, + { 0x13001fe5, 0x14000007 }, + { 0x13801fe6, 0x14000001 }, + { 0x13001fe8, 0x2400fff8 }, + { 0x13001fe9, 0x2400fff8 }, + { 0x13001fea, 0x2400ff90 }, + { 0x13001feb, 0x2400ff90 }, + { 0x13001fec, 0x2400fff9 }, + { 0x13801fed, 0x60000002 }, + { 0x13001ff2, 0x14000000 }, + { 0x13001ff3, 0x14000009 }, + { 0x13001ff4, 0x14000000 }, + { 0x13801ff6, 0x14000001 }, + { 0x13001ff8, 0x2400ff80 }, + { 0x13001ff9, 0x2400ff80 }, + { 0x13001ffa, 0x2400ff82 }, + { 0x13001ffb, 0x2400ff82 }, + { 0x13001ffc, 0x2000fff7 }, + { 0x13801ffd, 0x60000001 }, + { 0x09802000, 0x7400000a }, + { 0x0980200b, 0x04000004 }, + { 0x09802010, 0x44000005 }, + { 0x09802016, 0x54000001 }, + { 0x09002018, 0x50000000 }, + { 0x09002019, 0x4c000000 }, + { 0x0900201a, 0x58000000 }, + { 0x0980201b, 0x50000001 }, + { 0x0900201d, 0x4c000000 }, + { 0x0900201e, 0x58000000 }, + { 0x0900201f, 0x50000000 }, + { 0x09802020, 0x54000007 }, + { 0x09002028, 0x6c000000 }, + { 0x09002029, 0x70000000 }, + { 0x0980202a, 0x04000004 }, + { 0x0900202f, 0x74000000 }, + { 0x09802030, 0x54000008 }, + { 0x09002039, 0x50000000 }, + { 0x0900203a, 0x4c000000 }, + { 0x0980203b, 0x54000003 }, + { 0x0980203f, 0x40000001 }, + { 0x09802041, 0x54000002 }, + { 0x09002044, 0x64000000 }, + { 0x09002045, 0x58000000 }, + { 0x09002046, 0x48000000 }, + { 0x09802047, 0x5400000a }, + { 0x09002052, 0x64000000 }, + { 0x09002053, 0x54000000 }, + { 0x09002054, 0x40000000 }, + { 0x09802055, 0x54000009 }, + { 0x0900205f, 0x74000000 }, + { 0x09802060, 0x04000003 }, + { 0x0980206a, 0x04000005 }, + { 0x09002070, 0x3c000000 }, + { 0x21002071, 0x14000000 }, + { 0x09802074, 0x3c000005 }, + { 0x0980207a, 0x64000002 }, + { 0x0900207d, 0x58000000 }, + { 0x0900207e, 0x48000000 }, + { 0x2100207f, 0x14000000 }, + { 0x09802080, 0x3c000009 }, + { 0x0980208a, 0x64000002 }, + { 0x0900208d, 0x58000000 }, + { 0x0900208e, 0x48000000 }, + { 0x21802090, 0x18000004 }, + { 0x098020a0, 0x5c000015 }, + { 0x1b8020d0, 0x3000000c }, + { 0x1b8020dd, 0x2c000003 }, + { 0x1b0020e1, 0x30000000 }, + { 0x1b8020e2, 0x2c000002 }, + { 0x1b8020e5, 0x30000006 }, + { 0x09802100, 0x68000001 }, + { 0x09002102, 0x24000000 }, + { 0x09802103, 0x68000003 }, + { 0x09002107, 0x24000000 }, + { 0x09802108, 0x68000001 }, + { 0x0900210a, 0x14000000 }, + { 0x0980210b, 0x24000002 }, + { 0x0980210e, 0x14000001 }, + { 0x09802110, 0x24000002 }, + { 0x09002113, 0x14000000 }, + { 0x09002114, 0x68000000 }, + { 0x09002115, 0x24000000 }, + { 0x09802116, 0x68000002 }, + { 0x09802119, 0x24000004 }, + { 0x0980211e, 0x68000005 }, + { 0x09002124, 0x24000000 }, + { 0x09002125, 0x68000000 }, + { 0x13002126, 0x2400e2a3 }, + { 0x09002127, 0x68000000 }, + { 0x09002128, 0x24000000 }, + { 0x09002129, 0x68000000 }, + { 0x2100212a, 0x2400df41 }, + { 0x2100212b, 0x2400dfba }, + { 0x0980212c, 0x24000001 }, + { 0x0900212e, 0x68000000 }, + { 0x0900212f, 0x14000000 }, + { 0x09802130, 0x24000001 }, + { 0x09002132, 0x68000000 }, + { 0x09002133, 0x24000000 }, + { 0x09002134, 0x14000000 }, + { 0x09802135, 0x1c000003 }, + { 0x09002139, 0x14000000 }, + { 0x0980213a, 0x68000001 }, + { 0x0980213c, 0x14000001 }, + { 0x0980213e, 0x24000001 }, + { 0x09802140, 0x64000004 }, + { 0x09002145, 0x24000000 }, + { 0x09802146, 0x14000003 }, + { 0x0900214a, 0x68000000 }, + { 0x0900214b, 0x64000000 }, + { 0x0900214c, 0x68000000 }, + { 0x09802153, 0x3c00000c }, + { 0x09002160, 0x38000010 }, + { 0x09002161, 0x38000010 }, + { 0x09002162, 0x38000010 }, + { 0x09002163, 0x38000010 }, + { 0x09002164, 0x38000010 }, + { 0x09002165, 0x38000010 }, + { 0x09002166, 0x38000010 }, + { 0x09002167, 0x38000010 }, + { 0x09002168, 0x38000010 }, + { 0x09002169, 0x38000010 }, + { 0x0900216a, 0x38000010 }, + { 0x0900216b, 0x38000010 }, + { 0x0900216c, 0x38000010 }, + { 0x0900216d, 0x38000010 }, + { 0x0900216e, 0x38000010 }, + { 0x0900216f, 0x38000010 }, + { 0x09002170, 0x3800fff0 }, + { 0x09002171, 0x3800fff0 }, + { 0x09002172, 0x3800fff0 }, + { 0x09002173, 0x3800fff0 }, + { 0x09002174, 0x3800fff0 }, + { 0x09002175, 0x3800fff0 }, + { 0x09002176, 0x3800fff0 }, + { 0x09002177, 0x3800fff0 }, + { 0x09002178, 0x3800fff0 }, + { 0x09002179, 0x3800fff0 }, + { 0x0900217a, 0x3800fff0 }, + { 0x0900217b, 0x3800fff0 }, + { 0x0900217c, 0x3800fff0 }, + { 0x0900217d, 0x3800fff0 }, + { 0x0900217e, 0x3800fff0 }, + { 0x0900217f, 0x3800fff0 }, + { 0x09802180, 0x38000003 }, + { 0x09802190, 0x64000004 }, + { 0x09802195, 0x68000004 }, + { 0x0980219a, 0x64000001 }, + { 0x0980219c, 0x68000003 }, + { 0x090021a0, 0x64000000 }, + { 0x098021a1, 0x68000001 }, + { 0x090021a3, 0x64000000 }, + { 0x098021a4, 0x68000001 }, + { 0x090021a6, 0x64000000 }, + { 0x098021a7, 0x68000006 }, + { 0x090021ae, 0x64000000 }, + { 0x098021af, 0x6800001e }, + { 0x098021ce, 0x64000001 }, + { 0x098021d0, 0x68000001 }, + { 0x090021d2, 0x64000000 }, + { 0x090021d3, 0x68000000 }, + { 0x090021d4, 0x64000000 }, + { 0x098021d5, 0x6800001e }, + { 0x098021f4, 0x6400010b }, + { 0x09802300, 0x68000007 }, + { 0x09802308, 0x64000003 }, + { 0x0980230c, 0x68000013 }, + { 0x09802320, 0x64000001 }, + { 0x09802322, 0x68000006 }, + { 0x09002329, 0x58000000 }, + { 0x0900232a, 0x48000000 }, + { 0x0980232b, 0x68000050 }, + { 0x0900237c, 0x64000000 }, + { 0x0980237d, 0x6800001d }, + { 0x0980239b, 0x64000018 }, + { 0x090023b4, 0x58000000 }, + { 0x090023b5, 0x48000000 }, + { 0x090023b6, 0x54000000 }, + { 0x098023b7, 0x68000024 }, + { 0x09802400, 0x68000026 }, + { 0x09802440, 0x6800000a }, + { 0x09802460, 0x3c00003b }, + { 0x0980249c, 0x68000019 }, + { 0x090024b6, 0x6800001a }, + { 0x090024b7, 0x6800001a }, + { 0x090024b8, 0x6800001a }, + { 0x090024b9, 0x6800001a }, + { 0x090024ba, 0x6800001a }, + { 0x090024bb, 0x6800001a }, + { 0x090024bc, 0x6800001a }, + { 0x090024bd, 0x6800001a }, + { 0x090024be, 0x6800001a }, + { 0x090024bf, 0x6800001a }, + { 0x090024c0, 0x6800001a }, + { 0x090024c1, 0x6800001a }, + { 0x090024c2, 0x6800001a }, + { 0x090024c3, 0x6800001a }, + { 0x090024c4, 0x6800001a }, + { 0x090024c5, 0x6800001a }, + { 0x090024c6, 0x6800001a }, + { 0x090024c7, 0x6800001a }, + { 0x090024c8, 0x6800001a }, + { 0x090024c9, 0x6800001a }, + { 0x090024ca, 0x6800001a }, + { 0x090024cb, 0x6800001a }, + { 0x090024cc, 0x6800001a }, + { 0x090024cd, 0x6800001a }, + { 0x090024ce, 0x6800001a }, + { 0x090024cf, 0x6800001a }, + { 0x090024d0, 0x6800ffe6 }, + { 0x090024d1, 0x6800ffe6 }, + { 0x090024d2, 0x6800ffe6 }, + { 0x090024d3, 0x6800ffe6 }, + { 0x090024d4, 0x6800ffe6 }, + { 0x090024d5, 0x6800ffe6 }, + { 0x090024d6, 0x6800ffe6 }, + { 0x090024d7, 0x6800ffe6 }, + { 0x090024d8, 0x6800ffe6 }, + { 0x090024d9, 0x6800ffe6 }, + { 0x090024da, 0x6800ffe6 }, + { 0x090024db, 0x6800ffe6 }, + { 0x090024dc, 0x6800ffe6 }, + { 0x090024dd, 0x6800ffe6 }, + { 0x090024de, 0x6800ffe6 }, + { 0x090024df, 0x6800ffe6 }, + { 0x090024e0, 0x6800ffe6 }, + { 0x090024e1, 0x6800ffe6 }, + { 0x090024e2, 0x6800ffe6 }, + { 0x090024e3, 0x6800ffe6 }, + { 0x090024e4, 0x6800ffe6 }, + { 0x090024e5, 0x6800ffe6 }, + { 0x090024e6, 0x6800ffe6 }, + { 0x090024e7, 0x6800ffe6 }, + { 0x090024e8, 0x6800ffe6 }, + { 0x090024e9, 0x6800ffe6 }, + { 0x098024ea, 0x3c000015 }, + { 0x09802500, 0x680000b6 }, + { 0x090025b7, 0x64000000 }, + { 0x098025b8, 0x68000008 }, + { 0x090025c1, 0x64000000 }, + { 0x098025c2, 0x68000035 }, + { 0x098025f8, 0x64000007 }, + { 0x09802600, 0x6800006e }, + { 0x0900266f, 0x64000000 }, + { 0x09802670, 0x6800002c }, + { 0x098026a0, 0x68000011 }, + { 0x09802701, 0x68000003 }, + { 0x09802706, 0x68000003 }, + { 0x0980270c, 0x6800001b }, + { 0x09802729, 0x68000022 }, + { 0x0900274d, 0x68000000 }, + { 0x0980274f, 0x68000003 }, + { 0x09002756, 0x68000000 }, + { 0x09802758, 0x68000006 }, + { 0x09802761, 0x68000006 }, + { 0x09002768, 0x58000000 }, + { 0x09002769, 0x48000000 }, + { 0x0900276a, 0x58000000 }, + { 0x0900276b, 0x48000000 }, + { 0x0900276c, 0x58000000 }, + { 0x0900276d, 0x48000000 }, + { 0x0900276e, 0x58000000 }, + { 0x0900276f, 0x48000000 }, + { 0x09002770, 0x58000000 }, + { 0x09002771, 0x48000000 }, + { 0x09002772, 0x58000000 }, + { 0x09002773, 0x48000000 }, + { 0x09002774, 0x58000000 }, + { 0x09002775, 0x48000000 }, + { 0x09802776, 0x3c00001d }, + { 0x09002794, 0x68000000 }, + { 0x09802798, 0x68000017 }, + { 0x098027b1, 0x6800000d }, + { 0x098027c0, 0x64000004 }, + { 0x090027c5, 0x58000000 }, + { 0x090027c6, 0x48000000 }, + { 0x098027d0, 0x64000015 }, + { 0x090027e6, 0x58000000 }, + { 0x090027e7, 0x48000000 }, + { 0x090027e8, 0x58000000 }, + { 0x090027e9, 0x48000000 }, + { 0x090027ea, 0x58000000 }, + { 0x090027eb, 0x48000000 }, + { 0x098027f0, 0x6400000f }, + { 0x04802800, 0x680000ff }, + { 0x09802900, 0x64000082 }, + { 0x09002983, 0x58000000 }, + { 0x09002984, 0x48000000 }, + { 0x09002985, 0x58000000 }, + { 0x09002986, 0x48000000 }, + { 0x09002987, 0x58000000 }, + { 0x09002988, 0x48000000 }, + { 0x09002989, 0x58000000 }, + { 0x0900298a, 0x48000000 }, + { 0x0900298b, 0x58000000 }, + { 0x0900298c, 0x48000000 }, + { 0x0900298d, 0x58000000 }, + { 0x0900298e, 0x48000000 }, + { 0x0900298f, 0x58000000 }, + { 0x09002990, 0x48000000 }, + { 0x09002991, 0x58000000 }, + { 0x09002992, 0x48000000 }, + { 0x09002993, 0x58000000 }, + { 0x09002994, 0x48000000 }, + { 0x09002995, 0x58000000 }, + { 0x09002996, 0x48000000 }, + { 0x09002997, 0x58000000 }, + { 0x09002998, 0x48000000 }, + { 0x09802999, 0x6400003e }, + { 0x090029d8, 0x58000000 }, + { 0x090029d9, 0x48000000 }, + { 0x090029da, 0x58000000 }, + { 0x090029db, 0x48000000 }, + { 0x098029dc, 0x6400001f }, + { 0x090029fc, 0x58000000 }, + { 0x090029fd, 0x48000000 }, + { 0x098029fe, 0x64000101 }, + { 0x09802b00, 0x68000013 }, + { 0x11002c00, 0x24000030 }, + { 0x11002c01, 0x24000030 }, + { 0x11002c02, 0x24000030 }, + { 0x11002c03, 0x24000030 }, + { 0x11002c04, 0x24000030 }, + { 0x11002c05, 0x24000030 }, + { 0x11002c06, 0x24000030 }, + { 0x11002c07, 0x24000030 }, + { 0x11002c08, 0x24000030 }, + { 0x11002c09, 0x24000030 }, + { 0x11002c0a, 0x24000030 }, + { 0x11002c0b, 0x24000030 }, + { 0x11002c0c, 0x24000030 }, + { 0x11002c0d, 0x24000030 }, + { 0x11002c0e, 0x24000030 }, + { 0x11002c0f, 0x24000030 }, + { 0x11002c10, 0x24000030 }, + { 0x11002c11, 0x24000030 }, + { 0x11002c12, 0x24000030 }, + { 0x11002c13, 0x24000030 }, + { 0x11002c14, 0x24000030 }, + { 0x11002c15, 0x24000030 }, + { 0x11002c16, 0x24000030 }, + { 0x11002c17, 0x24000030 }, + { 0x11002c18, 0x24000030 }, + { 0x11002c19, 0x24000030 }, + { 0x11002c1a, 0x24000030 }, + { 0x11002c1b, 0x24000030 }, + { 0x11002c1c, 0x24000030 }, + { 0x11002c1d, 0x24000030 }, + { 0x11002c1e, 0x24000030 }, + { 0x11002c1f, 0x24000030 }, + { 0x11002c20, 0x24000030 }, + { 0x11002c21, 0x24000030 }, + { 0x11002c22, 0x24000030 }, + { 0x11002c23, 0x24000030 }, + { 0x11002c24, 0x24000030 }, + { 0x11002c25, 0x24000030 }, + { 0x11002c26, 0x24000030 }, + { 0x11002c27, 0x24000030 }, + { 0x11002c28, 0x24000030 }, + { 0x11002c29, 0x24000030 }, + { 0x11002c2a, 0x24000030 }, + { 0x11002c2b, 0x24000030 }, + { 0x11002c2c, 0x24000030 }, + { 0x11002c2d, 0x24000030 }, + { 0x11002c2e, 0x24000030 }, + { 0x11002c30, 0x1400ffd0 }, + { 0x11002c31, 0x1400ffd0 }, + { 0x11002c32, 0x1400ffd0 }, + { 0x11002c33, 0x1400ffd0 }, + { 0x11002c34, 0x1400ffd0 }, + { 0x11002c35, 0x1400ffd0 }, + { 0x11002c36, 0x1400ffd0 }, + { 0x11002c37, 0x1400ffd0 }, + { 0x11002c38, 0x1400ffd0 }, + { 0x11002c39, 0x1400ffd0 }, + { 0x11002c3a, 0x1400ffd0 }, + { 0x11002c3b, 0x1400ffd0 }, + { 0x11002c3c, 0x1400ffd0 }, + { 0x11002c3d, 0x1400ffd0 }, + { 0x11002c3e, 0x1400ffd0 }, + { 0x11002c3f, 0x1400ffd0 }, + { 0x11002c40, 0x1400ffd0 }, + { 0x11002c41, 0x1400ffd0 }, + { 0x11002c42, 0x1400ffd0 }, + { 0x11002c43, 0x1400ffd0 }, + { 0x11002c44, 0x1400ffd0 }, + { 0x11002c45, 0x1400ffd0 }, + { 0x11002c46, 0x1400ffd0 }, + { 0x11002c47, 0x1400ffd0 }, + { 0x11002c48, 0x1400ffd0 }, + { 0x11002c49, 0x1400ffd0 }, + { 0x11002c4a, 0x1400ffd0 }, + { 0x11002c4b, 0x1400ffd0 }, + { 0x11002c4c, 0x1400ffd0 }, + { 0x11002c4d, 0x1400ffd0 }, + { 0x11002c4e, 0x1400ffd0 }, + { 0x11002c4f, 0x1400ffd0 }, + { 0x11002c50, 0x1400ffd0 }, + { 0x11002c51, 0x1400ffd0 }, + { 0x11002c52, 0x1400ffd0 }, + { 0x11002c53, 0x1400ffd0 }, + { 0x11002c54, 0x1400ffd0 }, + { 0x11002c55, 0x1400ffd0 }, + { 0x11002c56, 0x1400ffd0 }, + { 0x11002c57, 0x1400ffd0 }, + { 0x11002c58, 0x1400ffd0 }, + { 0x11002c59, 0x1400ffd0 }, + { 0x11002c5a, 0x1400ffd0 }, + { 0x11002c5b, 0x1400ffd0 }, + { 0x11002c5c, 0x1400ffd0 }, + { 0x11002c5d, 0x1400ffd0 }, + { 0x11002c5e, 0x1400ffd0 }, + { 0x0a002c80, 0x24000001 }, + { 0x0a002c81, 0x1400ffff }, + { 0x0a002c82, 0x24000001 }, + { 0x0a002c83, 0x1400ffff }, + { 0x0a002c84, 0x24000001 }, + { 0x0a002c85, 0x1400ffff }, + { 0x0a002c86, 0x24000001 }, + { 0x0a002c87, 0x1400ffff }, + { 0x0a002c88, 0x24000001 }, + { 0x0a002c89, 0x1400ffff }, + { 0x0a002c8a, 0x24000001 }, + { 0x0a002c8b, 0x1400ffff }, + { 0x0a002c8c, 0x24000001 }, + { 0x0a002c8d, 0x1400ffff }, + { 0x0a002c8e, 0x24000001 }, + { 0x0a002c8f, 0x1400ffff }, + { 0x0a002c90, 0x24000001 }, + { 0x0a002c91, 0x1400ffff }, + { 0x0a002c92, 0x24000001 }, + { 0x0a002c93, 0x1400ffff }, + { 0x0a002c94, 0x24000001 }, + { 0x0a002c95, 0x1400ffff }, + { 0x0a002c96, 0x24000001 }, + { 0x0a002c97, 0x1400ffff }, + { 0x0a002c98, 0x24000001 }, + { 0x0a002c99, 0x1400ffff }, + { 0x0a002c9a, 0x24000001 }, + { 0x0a002c9b, 0x1400ffff }, + { 0x0a002c9c, 0x24000001 }, + { 0x0a002c9d, 0x1400ffff }, + { 0x0a002c9e, 0x24000001 }, + { 0x0a002c9f, 0x1400ffff }, + { 0x0a002ca0, 0x24000001 }, + { 0x0a002ca1, 0x1400ffff }, + { 0x0a002ca2, 0x24000001 }, + { 0x0a002ca3, 0x1400ffff }, + { 0x0a002ca4, 0x24000001 }, + { 0x0a002ca5, 0x1400ffff }, + { 0x0a002ca6, 0x24000001 }, + { 0x0a002ca7, 0x1400ffff }, + { 0x0a002ca8, 0x24000001 }, + { 0x0a002ca9, 0x1400ffff }, + { 0x0a002caa, 0x24000001 }, + { 0x0a002cab, 0x1400ffff }, + { 0x0a002cac, 0x24000001 }, + { 0x0a002cad, 0x1400ffff }, + { 0x0a002cae, 0x24000001 }, + { 0x0a002caf, 0x1400ffff }, + { 0x0a002cb0, 0x24000001 }, + { 0x0a002cb1, 0x1400ffff }, + { 0x0a002cb2, 0x24000001 }, + { 0x0a002cb3, 0x1400ffff }, + { 0x0a002cb4, 0x24000001 }, + { 0x0a002cb5, 0x1400ffff }, + { 0x0a002cb6, 0x24000001 }, + { 0x0a002cb7, 0x1400ffff }, + { 0x0a002cb8, 0x24000001 }, + { 0x0a002cb9, 0x1400ffff }, + { 0x0a002cba, 0x24000001 }, + { 0x0a002cbb, 0x1400ffff }, + { 0x0a002cbc, 0x24000001 }, + { 0x0a002cbd, 0x1400ffff }, + { 0x0a002cbe, 0x24000001 }, + { 0x0a002cbf, 0x1400ffff }, + { 0x0a002cc0, 0x24000001 }, + { 0x0a002cc1, 0x1400ffff }, + { 0x0a002cc2, 0x24000001 }, + { 0x0a002cc3, 0x1400ffff }, + { 0x0a002cc4, 0x24000001 }, + { 0x0a002cc5, 0x1400ffff }, + { 0x0a002cc6, 0x24000001 }, + { 0x0a002cc7, 0x1400ffff }, + { 0x0a002cc8, 0x24000001 }, + { 0x0a002cc9, 0x1400ffff }, + { 0x0a002cca, 0x24000001 }, + { 0x0a002ccb, 0x1400ffff }, + { 0x0a002ccc, 0x24000001 }, + { 0x0a002ccd, 0x1400ffff }, + { 0x0a002cce, 0x24000001 }, + { 0x0a002ccf, 0x1400ffff }, + { 0x0a002cd0, 0x24000001 }, + { 0x0a002cd1, 0x1400ffff }, + { 0x0a002cd2, 0x24000001 }, + { 0x0a002cd3, 0x1400ffff }, + { 0x0a002cd4, 0x24000001 }, + { 0x0a002cd5, 0x1400ffff }, + { 0x0a002cd6, 0x24000001 }, + { 0x0a002cd7, 0x1400ffff }, + { 0x0a002cd8, 0x24000001 }, + { 0x0a002cd9, 0x1400ffff }, + { 0x0a002cda, 0x24000001 }, + { 0x0a002cdb, 0x1400ffff }, + { 0x0a002cdc, 0x24000001 }, + { 0x0a002cdd, 0x1400ffff }, + { 0x0a002cde, 0x24000001 }, + { 0x0a002cdf, 0x1400ffff }, + { 0x0a002ce0, 0x24000001 }, + { 0x0a002ce1, 0x1400ffff }, + { 0x0a002ce2, 0x24000001 }, + { 0x0a002ce3, 0x1400ffff }, + { 0x0a002ce4, 0x14000000 }, + { 0x0a802ce5, 0x68000005 }, + { 0x0a802cf9, 0x54000003 }, + { 0x0a002cfd, 0x3c000000 }, + { 0x0a802cfe, 0x54000001 }, + { 0x10002d00, 0x1400e3a0 }, + { 0x10002d01, 0x1400e3a0 }, + { 0x10002d02, 0x1400e3a0 }, + { 0x10002d03, 0x1400e3a0 }, + { 0x10002d04, 0x1400e3a0 }, + { 0x10002d05, 0x1400e3a0 }, + { 0x10002d06, 0x1400e3a0 }, + { 0x10002d07, 0x1400e3a0 }, + { 0x10002d08, 0x1400e3a0 }, + { 0x10002d09, 0x1400e3a0 }, + { 0x10002d0a, 0x1400e3a0 }, + { 0x10002d0b, 0x1400e3a0 }, + { 0x10002d0c, 0x1400e3a0 }, + { 0x10002d0d, 0x1400e3a0 }, + { 0x10002d0e, 0x1400e3a0 }, + { 0x10002d0f, 0x1400e3a0 }, + { 0x10002d10, 0x1400e3a0 }, + { 0x10002d11, 0x1400e3a0 }, + { 0x10002d12, 0x1400e3a0 }, + { 0x10002d13, 0x1400e3a0 }, + { 0x10002d14, 0x1400e3a0 }, + { 0x10002d15, 0x1400e3a0 }, + { 0x10002d16, 0x1400e3a0 }, + { 0x10002d17, 0x1400e3a0 }, + { 0x10002d18, 0x1400e3a0 }, + { 0x10002d19, 0x1400e3a0 }, + { 0x10002d1a, 0x1400e3a0 }, + { 0x10002d1b, 0x1400e3a0 }, + { 0x10002d1c, 0x1400e3a0 }, + { 0x10002d1d, 0x1400e3a0 }, + { 0x10002d1e, 0x1400e3a0 }, + { 0x10002d1f, 0x1400e3a0 }, + { 0x10002d20, 0x1400e3a0 }, + { 0x10002d21, 0x1400e3a0 }, + { 0x10002d22, 0x1400e3a0 }, + { 0x10002d23, 0x1400e3a0 }, + { 0x10002d24, 0x1400e3a0 }, + { 0x10002d25, 0x1400e3a0 }, + { 0x3a802d30, 0x1c000035 }, + { 0x3a002d6f, 0x18000000 }, + { 0x0f802d80, 0x1c000016 }, + { 0x0f802da0, 0x1c000006 }, + { 0x0f802da8, 0x1c000006 }, + { 0x0f802db0, 0x1c000006 }, + { 0x0f802db8, 0x1c000006 }, + { 0x0f802dc0, 0x1c000006 }, + { 0x0f802dc8, 0x1c000006 }, + { 0x0f802dd0, 0x1c000006 }, + { 0x0f802dd8, 0x1c000006 }, + { 0x09802e00, 0x54000001 }, + { 0x09002e02, 0x50000000 }, + { 0x09002e03, 0x4c000000 }, + { 0x09002e04, 0x50000000 }, + { 0x09002e05, 0x4c000000 }, + { 0x09802e06, 0x54000002 }, + { 0x09002e09, 0x50000000 }, + { 0x09002e0a, 0x4c000000 }, + { 0x09002e0b, 0x54000000 }, + { 0x09002e0c, 0x50000000 }, + { 0x09002e0d, 0x4c000000 }, + { 0x09802e0e, 0x54000008 }, + { 0x09002e17, 0x44000000 }, + { 0x09002e1c, 0x50000000 }, + { 0x09002e1d, 0x4c000000 }, + { 0x16802e80, 0x68000019 }, + { 0x16802e9b, 0x68000058 }, + { 0x16802f00, 0x680000d5 }, + { 0x09802ff0, 0x6800000b }, + { 0x09003000, 0x74000000 }, + { 0x09803001, 0x54000002 }, + { 0x09003004, 0x68000000 }, + { 0x16003005, 0x18000000 }, + { 0x09003006, 0x1c000000 }, + { 0x16003007, 0x38000000 }, + { 0x09003008, 0x58000000 }, + { 0x09003009, 0x48000000 }, + { 0x0900300a, 0x58000000 }, + { 0x0900300b, 0x48000000 }, + { 0x0900300c, 0x58000000 }, + { 0x0900300d, 0x48000000 }, + { 0x0900300e, 0x58000000 }, + { 0x0900300f, 0x48000000 }, + { 0x09003010, 0x58000000 }, + { 0x09003011, 0x48000000 }, + { 0x09803012, 0x68000001 }, + { 0x09003014, 0x58000000 }, + { 0x09003015, 0x48000000 }, + { 0x09003016, 0x58000000 }, + { 0x09003017, 0x48000000 }, + { 0x09003018, 0x58000000 }, + { 0x09003019, 0x48000000 }, + { 0x0900301a, 0x58000000 }, + { 0x0900301b, 0x48000000 }, + { 0x0900301c, 0x44000000 }, + { 0x0900301d, 0x58000000 }, + { 0x0980301e, 0x48000001 }, + { 0x09003020, 0x68000000 }, + { 0x16803021, 0x38000008 }, + { 0x1b80302a, 0x30000005 }, + { 0x09003030, 0x44000000 }, + { 0x09803031, 0x18000004 }, + { 0x09803036, 0x68000001 }, + { 0x16803038, 0x38000002 }, + { 0x1600303b, 0x18000000 }, + { 0x0900303c, 0x1c000000 }, + { 0x0900303d, 0x54000000 }, + { 0x0980303e, 0x68000001 }, + { 0x1a803041, 0x1c000055 }, + { 0x1b803099, 0x30000001 }, + { 0x0980309b, 0x60000001 }, + { 0x1a80309d, 0x18000001 }, + { 0x1a00309f, 0x1c000000 }, + { 0x090030a0, 0x44000000 }, + { 0x1d8030a1, 0x1c000059 }, + { 0x090030fb, 0x54000000 }, + { 0x098030fc, 0x18000002 }, + { 0x1d0030ff, 0x1c000000 }, + { 0x03803105, 0x1c000027 }, + { 0x17803131, 0x1c00005d }, + { 0x09803190, 0x68000001 }, + { 0x09803192, 0x3c000003 }, + { 0x09803196, 0x68000009 }, + { 0x038031a0, 0x1c000017 }, + { 0x098031c0, 0x6800000f }, + { 0x1d8031f0, 0x1c00000f }, + { 0x17803200, 0x6800001e }, + { 0x09803220, 0x3c000009 }, + { 0x0980322a, 0x68000019 }, + { 0x09003250, 0x68000000 }, + { 0x09803251, 0x3c00000e }, + { 0x17803260, 0x6800001f }, + { 0x09803280, 0x3c000009 }, + { 0x0980328a, 0x68000026 }, + { 0x098032b1, 0x3c00000e }, + { 0x098032c0, 0x6800003e }, + { 0x09803300, 0x680000ff }, + { 0x16803400, 0x1c0019b5 }, + { 0x09804dc0, 0x6800003f }, + { 0x16804e00, 0x1c0051bb }, + { 0x3c80a000, 0x1c000014 }, + { 0x3c00a015, 0x18000000 }, + { 0x3c80a016, 0x1c000476 }, + { 0x3c80a490, 0x68000036 }, + { 0x0980a700, 0x60000016 }, + { 0x3080a800, 0x1c000001 }, + { 0x3000a802, 0x28000000 }, + { 0x3080a803, 0x1c000002 }, + { 0x3000a806, 0x30000000 }, + { 0x3080a807, 0x1c000003 }, + { 0x3000a80b, 0x30000000 }, + { 0x3080a80c, 0x1c000016 }, + { 0x3080a823, 0x28000001 }, + { 0x3080a825, 0x30000001 }, + { 0x3000a827, 0x28000000 }, + { 0x3080a828, 0x68000003 }, + { 0x1780ac00, 0x1c002ba3 }, + { 0x0980d800, 0x1000037f }, + { 0x0980db80, 0x1000007f }, + { 0x0980dc00, 0x100003ff }, + { 0x0980e000, 0x0c0018ff }, + { 0x1680f900, 0x1c00012d }, + { 0x1680fa30, 0x1c00003a }, + { 0x1680fa70, 0x1c000069 }, + { 0x2180fb00, 0x14000006 }, + { 0x0180fb13, 0x14000004 }, + { 0x1900fb1d, 0x1c000000 }, + { 0x1900fb1e, 0x30000000 }, + { 0x1980fb1f, 0x1c000009 }, + { 0x1900fb29, 0x64000000 }, + { 0x1980fb2a, 0x1c00000c }, + { 0x1980fb38, 0x1c000004 }, + { 0x1900fb3e, 0x1c000000 }, + { 0x1980fb40, 0x1c000001 }, + { 0x1980fb43, 0x1c000001 }, + { 0x1980fb46, 0x1c00006b }, + { 0x0080fbd3, 0x1c00016a }, + { 0x0900fd3e, 0x58000000 }, + { 0x0900fd3f, 0x48000000 }, + { 0x0080fd50, 0x1c00003f }, + { 0x0080fd92, 0x1c000035 }, + { 0x0080fdf0, 0x1c00000b }, + { 0x0000fdfc, 0x5c000000 }, + { 0x0900fdfd, 0x68000000 }, + { 0x1b80fe00, 0x3000000f }, + { 0x0980fe10, 0x54000006 }, + { 0x0900fe17, 0x58000000 }, + { 0x0900fe18, 0x48000000 }, + { 0x0900fe19, 0x54000000 }, + { 0x1b80fe20, 0x30000003 }, + { 0x0900fe30, 0x54000000 }, + { 0x0980fe31, 0x44000001 }, + { 0x0980fe33, 0x40000001 }, + { 0x0900fe35, 0x58000000 }, + { 0x0900fe36, 0x48000000 }, + { 0x0900fe37, 0x58000000 }, + { 0x0900fe38, 0x48000000 }, + { 0x0900fe39, 0x58000000 }, + { 0x0900fe3a, 0x48000000 }, + { 0x0900fe3b, 0x58000000 }, + { 0x0900fe3c, 0x48000000 }, + { 0x0900fe3d, 0x58000000 }, + { 0x0900fe3e, 0x48000000 }, + { 0x0900fe3f, 0x58000000 }, + { 0x0900fe40, 0x48000000 }, + { 0x0900fe41, 0x58000000 }, + { 0x0900fe42, 0x48000000 }, + { 0x0900fe43, 0x58000000 }, + { 0x0900fe44, 0x48000000 }, + { 0x0980fe45, 0x54000001 }, + { 0x0900fe47, 0x58000000 }, + { 0x0900fe48, 0x48000000 }, + { 0x0980fe49, 0x54000003 }, + { 0x0980fe4d, 0x40000002 }, + { 0x0980fe50, 0x54000002 }, + { 0x0980fe54, 0x54000003 }, + { 0x0900fe58, 0x44000000 }, + { 0x0900fe59, 0x58000000 }, + { 0x0900fe5a, 0x48000000 }, + { 0x0900fe5b, 0x58000000 }, + { 0x0900fe5c, 0x48000000 }, + { 0x0900fe5d, 0x58000000 }, + { 0x0900fe5e, 0x48000000 }, + { 0x0980fe5f, 0x54000002 }, + { 0x0900fe62, 0x64000000 }, + { 0x0900fe63, 0x44000000 }, + { 0x0980fe64, 0x64000002 }, + { 0x0900fe68, 0x54000000 }, + { 0x0900fe69, 0x5c000000 }, + { 0x0980fe6a, 0x54000001 }, + { 0x0080fe70, 0x1c000004 }, + { 0x0080fe76, 0x1c000086 }, + { 0x0900feff, 0x04000000 }, + { 0x0980ff01, 0x54000002 }, + { 0x0900ff04, 0x5c000000 }, + { 0x0980ff05, 0x54000002 }, + { 0x0900ff08, 0x58000000 }, + { 0x0900ff09, 0x48000000 }, + { 0x0900ff0a, 0x54000000 }, + { 0x0900ff0b, 0x64000000 }, + { 0x0900ff0c, 0x54000000 }, + { 0x0900ff0d, 0x44000000 }, + { 0x0980ff0e, 0x54000001 }, + { 0x0980ff10, 0x34000009 }, + { 0x0980ff1a, 0x54000001 }, + { 0x0980ff1c, 0x64000002 }, + { 0x0980ff1f, 0x54000001 }, + { 0x2100ff21, 0x24000020 }, + { 0x2100ff22, 0x24000020 }, + { 0x2100ff23, 0x24000020 }, + { 0x2100ff24, 0x24000020 }, + { 0x2100ff25, 0x24000020 }, + { 0x2100ff26, 0x24000020 }, + { 0x2100ff27, 0x24000020 }, + { 0x2100ff28, 0x24000020 }, + { 0x2100ff29, 0x24000020 }, + { 0x2100ff2a, 0x24000020 }, + { 0x2100ff2b, 0x24000020 }, + { 0x2100ff2c, 0x24000020 }, + { 0x2100ff2d, 0x24000020 }, + { 0x2100ff2e, 0x24000020 }, + { 0x2100ff2f, 0x24000020 }, + { 0x2100ff30, 0x24000020 }, + { 0x2100ff31, 0x24000020 }, + { 0x2100ff32, 0x24000020 }, + { 0x2100ff33, 0x24000020 }, + { 0x2100ff34, 0x24000020 }, + { 0x2100ff35, 0x24000020 }, + { 0x2100ff36, 0x24000020 }, + { 0x2100ff37, 0x24000020 }, + { 0x2100ff38, 0x24000020 }, + { 0x2100ff39, 0x24000020 }, + { 0x2100ff3a, 0x24000020 }, + { 0x0900ff3b, 0x58000000 }, + { 0x0900ff3c, 0x54000000 }, + { 0x0900ff3d, 0x48000000 }, + { 0x0900ff3e, 0x60000000 }, + { 0x0900ff3f, 0x40000000 }, + { 0x0900ff40, 0x60000000 }, + { 0x2100ff41, 0x1400ffe0 }, + { 0x2100ff42, 0x1400ffe0 }, + { 0x2100ff43, 0x1400ffe0 }, + { 0x2100ff44, 0x1400ffe0 }, + { 0x2100ff45, 0x1400ffe0 }, + { 0x2100ff46, 0x1400ffe0 }, + { 0x2100ff47, 0x1400ffe0 }, + { 0x2100ff48, 0x1400ffe0 }, + { 0x2100ff49, 0x1400ffe0 }, + { 0x2100ff4a, 0x1400ffe0 }, + { 0x2100ff4b, 0x1400ffe0 }, + { 0x2100ff4c, 0x1400ffe0 }, + { 0x2100ff4d, 0x1400ffe0 }, + { 0x2100ff4e, 0x1400ffe0 }, + { 0x2100ff4f, 0x1400ffe0 }, + { 0x2100ff50, 0x1400ffe0 }, + { 0x2100ff51, 0x1400ffe0 }, + { 0x2100ff52, 0x1400ffe0 }, + { 0x2100ff53, 0x1400ffe0 }, + { 0x2100ff54, 0x1400ffe0 }, + { 0x2100ff55, 0x1400ffe0 }, + { 0x2100ff56, 0x1400ffe0 }, + { 0x2100ff57, 0x1400ffe0 }, + { 0x2100ff58, 0x1400ffe0 }, + { 0x2100ff59, 0x1400ffe0 }, + { 0x2100ff5a, 0x1400ffe0 }, + { 0x0900ff5b, 0x58000000 }, + { 0x0900ff5c, 0x64000000 }, + { 0x0900ff5d, 0x48000000 }, + { 0x0900ff5e, 0x64000000 }, + { 0x0900ff5f, 0x58000000 }, + { 0x0900ff60, 0x48000000 }, + { 0x0900ff61, 0x54000000 }, + { 0x0900ff62, 0x58000000 }, + { 0x0900ff63, 0x48000000 }, + { 0x0980ff64, 0x54000001 }, + { 0x1d80ff66, 0x1c000009 }, + { 0x0900ff70, 0x18000000 }, + { 0x1d80ff71, 0x1c00002c }, + { 0x0980ff9e, 0x18000001 }, + { 0x1780ffa0, 0x1c00001e }, + { 0x1780ffc2, 0x1c000005 }, + { 0x1780ffca, 0x1c000005 }, + { 0x1780ffd2, 0x1c000005 }, + { 0x1780ffda, 0x1c000002 }, + { 0x0980ffe0, 0x5c000001 }, + { 0x0900ffe2, 0x64000000 }, + { 0x0900ffe3, 0x60000000 }, + { 0x0900ffe4, 0x68000000 }, + { 0x0980ffe5, 0x5c000001 }, + { 0x0900ffe8, 0x68000000 }, + { 0x0980ffe9, 0x64000003 }, + { 0x0980ffed, 0x68000001 }, + { 0x0980fff9, 0x04000002 }, + { 0x0980fffc, 0x68000001 }, + { 0x23810000, 0x1c00000b }, + { 0x2381000d, 0x1c000019 }, + { 0x23810028, 0x1c000012 }, + { 0x2381003c, 0x1c000001 }, + { 0x2381003f, 0x1c00000e }, + { 0x23810050, 0x1c00000d }, + { 0x23810080, 0x1c00007a }, + { 0x09810100, 0x54000001 }, + { 0x09010102, 0x68000000 }, + { 0x09810107, 0x3c00002c }, + { 0x09810137, 0x68000008 }, + { 0x13810140, 0x38000034 }, + { 0x13810175, 0x3c000003 }, + { 0x13810179, 0x68000010 }, + { 0x1301018a, 0x3c000000 }, + { 0x29810300, 0x1c00001e }, + { 0x29810320, 0x3c000003 }, + { 0x12810330, 0x1c000019 }, + { 0x1201034a, 0x38000000 }, + { 0x3b810380, 0x1c00001d }, + { 0x3b01039f, 0x54000000 }, + { 0x2a8103a0, 0x1c000023 }, + { 0x2a8103c8, 0x1c000007 }, + { 0x2a0103d0, 0x68000000 }, + { 0x2a8103d1, 0x38000004 }, + { 0x0d010400, 0x24000028 }, + { 0x0d010401, 0x24000028 }, + { 0x0d010402, 0x24000028 }, + { 0x0d010403, 0x24000028 }, + { 0x0d010404, 0x24000028 }, + { 0x0d010405, 0x24000028 }, + { 0x0d010406, 0x24000028 }, + { 0x0d010407, 0x24000028 }, + { 0x0d010408, 0x24000028 }, + { 0x0d010409, 0x24000028 }, + { 0x0d01040a, 0x24000028 }, + { 0x0d01040b, 0x24000028 }, + { 0x0d01040c, 0x24000028 }, + { 0x0d01040d, 0x24000028 }, + { 0x0d01040e, 0x24000028 }, + { 0x0d01040f, 0x24000028 }, + { 0x0d010410, 0x24000028 }, + { 0x0d010411, 0x24000028 }, + { 0x0d010412, 0x24000028 }, + { 0x0d010413, 0x24000028 }, + { 0x0d010414, 0x24000028 }, + { 0x0d010415, 0x24000028 }, + { 0x0d010416, 0x24000028 }, + { 0x0d010417, 0x24000028 }, + { 0x0d010418, 0x24000028 }, + { 0x0d010419, 0x24000028 }, + { 0x0d01041a, 0x24000028 }, + { 0x0d01041b, 0x24000028 }, + { 0x0d01041c, 0x24000028 }, + { 0x0d01041d, 0x24000028 }, + { 0x0d01041e, 0x24000028 }, + { 0x0d01041f, 0x24000028 }, + { 0x0d010420, 0x24000028 }, + { 0x0d010421, 0x24000028 }, + { 0x0d010422, 0x24000028 }, + { 0x0d010423, 0x24000028 }, + { 0x0d010424, 0x24000028 }, + { 0x0d010425, 0x24000028 }, + { 0x0d010426, 0x24000028 }, + { 0x0d010427, 0x24000028 }, + { 0x0d010428, 0x1400ffd8 }, + { 0x0d010429, 0x1400ffd8 }, + { 0x0d01042a, 0x1400ffd8 }, + { 0x0d01042b, 0x1400ffd8 }, + { 0x0d01042c, 0x1400ffd8 }, + { 0x0d01042d, 0x1400ffd8 }, + { 0x0d01042e, 0x1400ffd8 }, + { 0x0d01042f, 0x1400ffd8 }, + { 0x0d010430, 0x1400ffd8 }, + { 0x0d010431, 0x1400ffd8 }, + { 0x0d010432, 0x1400ffd8 }, + { 0x0d010433, 0x1400ffd8 }, + { 0x0d010434, 0x1400ffd8 }, + { 0x0d010435, 0x1400ffd8 }, + { 0x0d010436, 0x1400ffd8 }, + { 0x0d010437, 0x1400ffd8 }, + { 0x0d010438, 0x1400ffd8 }, + { 0x0d010439, 0x1400ffd8 }, + { 0x0d01043a, 0x1400ffd8 }, + { 0x0d01043b, 0x1400ffd8 }, + { 0x0d01043c, 0x1400ffd8 }, + { 0x0d01043d, 0x1400ffd8 }, + { 0x0d01043e, 0x1400ffd8 }, + { 0x0d01043f, 0x1400ffd8 }, + { 0x0d010440, 0x1400ffd8 }, + { 0x0d010441, 0x1400ffd8 }, + { 0x0d010442, 0x1400ffd8 }, + { 0x0d010443, 0x1400ffd8 }, + { 0x0d010444, 0x1400ffd8 }, + { 0x0d010445, 0x1400ffd8 }, + { 0x0d010446, 0x1400ffd8 }, + { 0x0d010447, 0x1400ffd8 }, + { 0x0d010448, 0x1400ffd8 }, + { 0x0d010449, 0x1400ffd8 }, + { 0x0d01044a, 0x1400ffd8 }, + { 0x0d01044b, 0x1400ffd8 }, + { 0x0d01044c, 0x1400ffd8 }, + { 0x0d01044d, 0x1400ffd8 }, + { 0x0d01044e, 0x1400ffd8 }, + { 0x0d01044f, 0x1400ffd8 }, + { 0x2e810450, 0x1c00004d }, + { 0x2c8104a0, 0x34000009 }, + { 0x0b810800, 0x1c000005 }, + { 0x0b010808, 0x1c000000 }, + { 0x0b81080a, 0x1c00002b }, + { 0x0b810837, 0x1c000001 }, + { 0x0b01083c, 0x1c000000 }, + { 0x0b01083f, 0x1c000000 }, + { 0x1e010a00, 0x1c000000 }, + { 0x1e810a01, 0x30000002 }, + { 0x1e810a05, 0x30000001 }, + { 0x1e810a0c, 0x30000003 }, + { 0x1e810a10, 0x1c000003 }, + { 0x1e810a15, 0x1c000002 }, + { 0x1e810a19, 0x1c00001a }, + { 0x1e810a38, 0x30000002 }, + { 0x1e010a3f, 0x30000000 }, + { 0x1e810a40, 0x3c000007 }, + { 0x1e810a50, 0x54000008 }, + { 0x0981d000, 0x680000f5 }, + { 0x0981d100, 0x68000026 }, + { 0x0981d12a, 0x6800003a }, + { 0x0981d165, 0x28000001 }, + { 0x1b81d167, 0x30000002 }, + { 0x0981d16a, 0x68000002 }, + { 0x0981d16d, 0x28000005 }, + { 0x0981d173, 0x04000007 }, + { 0x1b81d17b, 0x30000007 }, + { 0x0981d183, 0x68000001 }, + { 0x1b81d185, 0x30000006 }, + { 0x0981d18c, 0x6800001d }, + { 0x1b81d1aa, 0x30000003 }, + { 0x0981d1ae, 0x6800002f }, + { 0x1381d200, 0x68000041 }, + { 0x1381d242, 0x30000002 }, + { 0x1301d245, 0x68000000 }, + { 0x0981d300, 0x68000056 }, + { 0x0981d400, 0x24000019 }, + { 0x0981d41a, 0x14000019 }, + { 0x0981d434, 0x24000019 }, + { 0x0981d44e, 0x14000006 }, + { 0x0981d456, 0x14000011 }, + { 0x0981d468, 0x24000019 }, + { 0x0981d482, 0x14000019 }, + { 0x0901d49c, 0x24000000 }, + { 0x0981d49e, 0x24000001 }, + { 0x0901d4a2, 0x24000000 }, + { 0x0981d4a5, 0x24000001 }, + { 0x0981d4a9, 0x24000003 }, + { 0x0981d4ae, 0x24000007 }, + { 0x0981d4b6, 0x14000003 }, + { 0x0901d4bb, 0x14000000 }, + { 0x0981d4bd, 0x14000006 }, + { 0x0981d4c5, 0x1400000a }, + { 0x0981d4d0, 0x24000019 }, + { 0x0981d4ea, 0x14000019 }, + { 0x0981d504, 0x24000001 }, + { 0x0981d507, 0x24000003 }, + { 0x0981d50d, 0x24000007 }, + { 0x0981d516, 0x24000006 }, + { 0x0981d51e, 0x14000019 }, + { 0x0981d538, 0x24000001 }, + { 0x0981d53b, 0x24000003 }, + { 0x0981d540, 0x24000004 }, + { 0x0901d546, 0x24000000 }, + { 0x0981d54a, 0x24000006 }, + { 0x0981d552, 0x14000019 }, + { 0x0981d56c, 0x24000019 }, + { 0x0981d586, 0x14000019 }, + { 0x0981d5a0, 0x24000019 }, + { 0x0981d5ba, 0x14000019 }, + { 0x0981d5d4, 0x24000019 }, + { 0x0981d5ee, 0x14000019 }, + { 0x0981d608, 0x24000019 }, + { 0x0981d622, 0x14000019 }, + { 0x0981d63c, 0x24000019 }, + { 0x0981d656, 0x14000019 }, + { 0x0981d670, 0x24000019 }, + { 0x0981d68a, 0x1400001b }, + { 0x0981d6a8, 0x24000018 }, + { 0x0901d6c1, 0x64000000 }, + { 0x0981d6c2, 0x14000018 }, + { 0x0901d6db, 0x64000000 }, + { 0x0981d6dc, 0x14000005 }, + { 0x0981d6e2, 0x24000018 }, + { 0x0901d6fb, 0x64000000 }, + { 0x0981d6fc, 0x14000018 }, + { 0x0901d715, 0x64000000 }, + { 0x0981d716, 0x14000005 }, + { 0x0981d71c, 0x24000018 }, + { 0x0901d735, 0x64000000 }, + { 0x0981d736, 0x14000018 }, + { 0x0901d74f, 0x64000000 }, + { 0x0981d750, 0x14000005 }, + { 0x0981d756, 0x24000018 }, + { 0x0901d76f, 0x64000000 }, + { 0x0981d770, 0x14000018 }, + { 0x0901d789, 0x64000000 }, + { 0x0981d78a, 0x14000005 }, + { 0x0981d790, 0x24000018 }, + { 0x0901d7a9, 0x64000000 }, + { 0x0981d7aa, 0x14000018 }, + { 0x0901d7c3, 0x64000000 }, + { 0x0981d7c4, 0x14000005 }, + { 0x0981d7ce, 0x34000031 }, + { 0x16820000, 0x1c00a6d6 }, + { 0x1682f800, 0x1c00021d }, + { 0x090e0001, 0x04000000 }, + { 0x098e0020, 0x0400005f }, + { 0x1b8e0100, 0x300000ef }, + { 0x098f0000, 0x0c00fffd }, + { 0x09900000, 0x0c00fffd }, +}; diff --git a/js/src/yarr/wtf/ASCIICType.h b/js/src/yarr/wtf/ASCIICType.h new file mode 100644 index 000000000000..cf53d9ac0c87 --- /dev/null +++ b/js/src/yarr/wtf/ASCIICType.h @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WTF_ASCIICType_h +#define WTF_ASCIICType_h + +#include "yarr/jswtfbridge.h" + +// The behavior of many of the functions in the header is dependent +// on the current locale. But in the WebKit project, all uses of those functions +// are in code processing something that's not locale-specific. These equivalents +// for some of the functions are named more explicitly, not dependent +// on the C library locale, and we should also optimize them as needed. + +// All functions return false or leave the character unchanged if passed a character +// that is outside the range 0-7F. So they can be used on Unicode strings or +// characters if the intent is to do processing only if the character is ASCII. + +namespace WTF { + + inline bool isASCII(char c) { return !(c & ~0x7F); } + inline bool isASCII(unsigned short c) { return !(c & ~0x7F); } +#if !WTF_COMPILER_MSVC || defined(_NATIVE_WCHAR_T_DEFINED) + inline bool isASCII(wchar_t c) { return !(c & ~0x7F); } +#endif + inline bool isASCII(int c) { return !(c & ~0x7F); } + + inline bool isASCIIAlpha(char c) { return (c | 0x20) >= 'a' && (c | 0x20) <= 'z'; } + inline bool isASCIIAlpha(unsigned short c) { return (c | 0x20) >= 'a' && (c | 0x20) <= 'z'; } +#if !WTF_COMPILER_MSVC || defined(_NATIVE_WCHAR_T_DEFINED) + inline bool isASCIIAlpha(wchar_t c) { return (c | 0x20) >= 'a' && (c | 0x20) <= 'z'; } +#endif + inline bool isASCIIAlpha(int c) { return (c | 0x20) >= 'a' && (c | 0x20) <= 'z'; } + + inline bool isASCIIAlphanumeric(char c) { return (c >= '0' && c <= '9') || ((c | 0x20) >= 'a' && (c | 0x20) <= 'z'); } + inline bool isASCIIAlphanumeric(unsigned short c) { return (c >= '0' && c <= '9') || ((c | 0x20) >= 'a' && (c | 0x20) <= 'z'); } +#if !WTF_COMPILER_MSVC || defined(_NATIVE_WCHAR_T_DEFINED) + inline bool isASCIIAlphanumeric(wchar_t c) { return (c >= '0' && c <= '9') || ((c | 0x20) >= 'a' && (c | 0x20) <= 'z'); } +#endif + inline bool isASCIIAlphanumeric(int c) { return (c >= '0' && c <= '9') || ((c | 0x20) >= 'a' && (c | 0x20) <= 'z'); } + + inline bool isASCIIDigit(char c) { return (c >= '0') & (c <= '9'); } + inline bool isASCIIDigit(unsigned short c) { return (c >= '0') & (c <= '9'); } +#if !WTF_COMPILER_MSVC || defined(_NATIVE_WCHAR_T_DEFINED) + inline bool isASCIIDigit(wchar_t c) { return (c >= '0') & (c <= '9'); } +#endif + inline bool isASCIIDigit(int c) { return (c >= '0') & (c <= '9'); } + + inline bool isASCIIHexDigit(char c) { return (c >= '0' && c <= '9') || ((c | 0x20) >= 'a' && (c | 0x20) <= 'f'); } + inline bool isASCIIHexDigit(unsigned short c) { return (c >= '0' && c <= '9') || ((c | 0x20) >= 'a' && (c | 0x20) <= 'f'); } +#if !WTF_COMPILER_MSVC || defined(_NATIVE_WCHAR_T_DEFINED) + inline bool isASCIIHexDigit(wchar_t c) { return (c >= '0' && c <= '9') || ((c | 0x20) >= 'a' && (c | 0x20) <= 'f'); } +#endif + inline bool isASCIIHexDigit(int c) { return (c >= '0' && c <= '9') || ((c | 0x20) >= 'a' && (c | 0x20) <= 'f'); } + + inline bool isASCIIOctalDigit(char c) { return (c >= '0') & (c <= '7'); } + inline bool isASCIIOctalDigit(unsigned short c) { return (c >= '0') & (c <= '7'); } +#if !WTF_COMPILER_MSVC || defined(_NATIVE_WCHAR_T_DEFINED) + inline bool isASCIIOctalDigit(wchar_t c) { return (c >= '0') & (c <= '7'); } +#endif + inline bool isASCIIOctalDigit(int c) { return (c >= '0') & (c <= '7'); } + + inline bool isASCIILower(char c) { return c >= 'a' && c <= 'z'; } + inline bool isASCIILower(unsigned short c) { return c >= 'a' && c <= 'z'; } +#if !WTF_COMPILER_MSVC || defined(_NATIVE_WCHAR_T_DEFINED) + inline bool isASCIILower(wchar_t c) { return c >= 'a' && c <= 'z'; } +#endif + inline bool isASCIILower(int c) { return c >= 'a' && c <= 'z'; } + + inline bool isASCIIUpper(char c) { return c >= 'A' && c <= 'Z'; } + inline bool isASCIIUpper(unsigned short c) { return c >= 'A' && c <= 'Z'; } +#if !WTF_COMPILER_MSVC || defined(_NATIVE_WCHAR_T_DEFINED) + inline bool isASCIIUpper(wchar_t c) { return c >= 'A' && c <= 'Z'; } +#endif + inline bool isASCIIUpper(int c) { return c >= 'A' && c <= 'Z'; } + + /* + Statistics from a run of Apple's page load test for callers of isASCIISpace: + + character count + --------- ----- + non-spaces 689383 + 20 space 294720 + 0A \n 89059 + 09 \t 28320 + 0D \r 0 + 0C \f 0 + 0B \v 0 + */ + inline bool isASCIISpace(char c) { return c <= ' ' && (c == ' ' || (c <= 0xD && c >= 0x9)); } + inline bool isASCIISpace(unsigned short c) { return c <= ' ' && (c == ' ' || (c <= 0xD && c >= 0x9)); } +#if !WTF_COMPILER_MSVC || defined(_NATIVE_WCHAR_T_DEFINED) + inline bool isASCIISpace(wchar_t c) { return c <= ' ' && (c == ' ' || (c <= 0xD && c >= 0x9)); } +#endif + inline bool isASCIISpace(int c) { return c <= ' ' && (c == ' ' || (c <= 0xD && c >= 0x9)); } + + inline char toASCIILower(char c) { return c | ((c >= 'A' && c <= 'Z') << 5); } + inline unsigned short toASCIILower(unsigned short c) { return c | ((c >= 'A' && c <= 'Z') << 5); } +#if !WTF_COMPILER_MSVC || defined(_NATIVE_WCHAR_T_DEFINED) + inline wchar_t toASCIILower(wchar_t c) { return c | ((c >= 'A' && c <= 'Z') << 5); } +#endif + inline int toASCIILower(int c) { return c | ((c >= 'A' && c <= 'Z') << 5); } + + inline char toASCIIUpper(char c) { return static_cast(c & ~((c >= 'a' && c <= 'z') << 5)); } + inline unsigned short toASCIIUpper(unsigned short c) { return static_cast(c & ~((c >= 'a' && c <= 'z') << 5)); } +#if !WTF_COMPILER_MSVC || defined(_NATIVE_WCHAR_T_DEFINED) + inline wchar_t toASCIIUpper(wchar_t c) { return static_cast(c & ~((c >= 'a' && c <= 'z') << 5)); } +#endif + inline int toASCIIUpper(int c) { return static_cast(c & ~((c >= 'a' && c <= 'z') << 5)); } + + inline int toASCIIHexValue(char c) { JS_ASSERT(isASCIIHexDigit(c)); return c < 'A' ? c - '0' : (c - 'A' + 10) & 0xF; } + inline int toASCIIHexValue(unsigned short c) { JS_ASSERT(isASCIIHexDigit(c)); return c < 'A' ? c - '0' : (c - 'A' + 10) & 0xF; } +#if !WTF_COMPILER_MSVC || defined(_NATIVE_WCHAR_T_DEFINED) + inline int toASCIIHexValue(wchar_t c) { JS_ASSERT(isASCIIHexDigit(c)); return c < 'A' ? c - '0' : (c - 'A' + 10) & 0xF; } +#endif + inline int toASCIIHexValue(int c) { JS_ASSERT(isASCIIHexDigit(c)); return c < 'A' ? c - '0' : (c - 'A' + 10) & 0xF; } + + inline bool isASCIIPrintable(char c) { return c >= ' ' && c <= '~'; } + inline bool isASCIIPrintable(unsigned short c) { return c >= ' ' && c <= '~'; } +#if !WTF_COMPILER_MSVC || defined(_NATIVE_WCHAR_T_DEFINED) + inline bool isASCIIPrintable(wchar_t c) { return c >= ' ' && c <= '~'; } +#endif + inline bool isASCIIPrintable(int c) { return c >= ' ' && c <= '~'; } + +} + +using WTF::isASCII; +using WTF::isASCIIAlpha; +using WTF::isASCIIAlphanumeric; +using WTF::isASCIIDigit; +using WTF::isASCIIHexDigit; +using WTF::isASCIILower; +using WTF::isASCIIOctalDigit; +using WTF::isASCIIPrintable; +using WTF::isASCIISpace; +using WTF::isASCIIUpper; +using WTF::toASCIIHexValue; +using WTF::toASCIILower; +using WTF::toASCIIUpper; + +#endif diff --git a/js/src/yarr/yarr/RegExpJitTables.h b/js/src/yarr/yarr/RegExpJitTables.h new file mode 100644 index 000000000000..7aae85dc75b6 --- /dev/null +++ b/js/src/yarr/yarr/RegExpJitTables.h @@ -0,0 +1,2713 @@ +static const char _spacesData[65536] = { +0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1, +1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + +static const char _wordcharData[65536] = { +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1, +1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,0,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + + +CharacterClass* digitsCreate() +{ + CharacterClass* characterClass = new CharacterClass(0); + characterClass->m_ranges.append(CharacterRange(0x30, 0x39)); + return characterClass; +} + +CharacterClass* nondigitsCreate() +{ + CharacterClass* characterClass = new CharacterClass(0); + characterClass->m_ranges.append(CharacterRange(0x00, 0x2f)); + characterClass->m_ranges.append(CharacterRange(0x3a, 0x7f)); + characterClass->m_rangesUnicode.append(CharacterRange(0x0080, 0xffff)); + return characterClass; +} + +CharacterClass* newlineCreate() +{ + CharacterClass* characterClass = new CharacterClass(0); + characterClass->m_matches.append(0x0a); + characterClass->m_matches.append(0x0d); + characterClass->m_matchesUnicode.append(0x2028); + characterClass->m_matchesUnicode.append(0x2029); + return characterClass; +} + +CharacterClass* spacesCreate() +{ + CharacterClass* characterClass = new CharacterClass(CharacterClassTable::create(_spacesData, false)); + characterClass->m_ranges.append(CharacterRange(0x09, 0x0d)); + characterClass->m_matches.append(0x20); + characterClass->m_matchesUnicode.append(0x00a0); + characterClass->m_matchesUnicode.append(0x1680); + characterClass->m_matchesUnicode.append(0x180e); + characterClass->m_rangesUnicode.append(CharacterRange(0x2000, 0x200a)); + characterClass->m_matchesUnicode.append(0x2028); + characterClass->m_matchesUnicode.append(0x2029); + characterClass->m_matchesUnicode.append(0x202f); + characterClass->m_matchesUnicode.append(0x205f); + characterClass->m_matchesUnicode.append(0x3000); + return characterClass; +} + +CharacterClass* nonspacesCreate() +{ + CharacterClass* characterClass = new CharacterClass(CharacterClassTable::create(_spacesData, true)); + characterClass->m_ranges.append(CharacterRange(0x00, 0x08)); + characterClass->m_ranges.append(CharacterRange(0x0e, 0x1f)); + characterClass->m_ranges.append(CharacterRange(0x21, 0x7f)); + characterClass->m_rangesUnicode.append(CharacterRange(0x0080, 0x009f)); + characterClass->m_rangesUnicode.append(CharacterRange(0x00a1, 0x167f)); + characterClass->m_rangesUnicode.append(CharacterRange(0x1681, 0x180d)); + characterClass->m_rangesUnicode.append(CharacterRange(0x180f, 0x1fff)); + characterClass->m_rangesUnicode.append(CharacterRange(0x200b, 0x2027)); + characterClass->m_rangesUnicode.append(CharacterRange(0x202a, 0x202e)); + characterClass->m_rangesUnicode.append(CharacterRange(0x2030, 0x205e)); + characterClass->m_rangesUnicode.append(CharacterRange(0x2060, 0x2fff)); + characterClass->m_rangesUnicode.append(CharacterRange(0x3001, 0xffff)); + return characterClass; +} + +CharacterClass* nonwordcharCreate() +{ + CharacterClass* characterClass = new CharacterClass(CharacterClassTable::create(_wordcharData, true)); + characterClass->m_ranges.append(CharacterRange(0x00, 0x2f)); + characterClass->m_ranges.append(CharacterRange(0x3a, 0x40)); + characterClass->m_ranges.append(CharacterRange(0x5b, 0x5e)); + characterClass->m_matches.append(0x60); + characterClass->m_ranges.append(CharacterRange(0x7b, 0x7f)); + characterClass->m_rangesUnicode.append(CharacterRange(0x0080, 0xffff)); + return characterClass; +} + +CharacterClass* wordcharCreate() +{ + CharacterClass* characterClass = new CharacterClass(CharacterClassTable::create(_wordcharData, false)); + characterClass->m_ranges.append(CharacterRange(0x30, 0x39)); + characterClass->m_ranges.append(CharacterRange(0x41, 0x5a)); + characterClass->m_matches.append(0x5f); + characterClass->m_ranges.append(CharacterRange(0x61, 0x7a)); + return characterClass; +} + + diff --git a/js/src/yarr/yarr/RegexCompiler.cpp b/js/src/yarr/yarr/RegexCompiler.cpp new file mode 100644 index 000000000000..a6e98d742450 --- /dev/null +++ b/js/src/yarr/yarr/RegexCompiler.cpp @@ -0,0 +1,628 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "jsinttypes.h" +#include "RegexCompiler.h" + +#include "RegexPattern.h" + +using namespace WTF; + +namespace JSC { namespace Yarr { + +#include "RegExpJitTables.h" + +class CharacterClassConstructor { +public: + CharacterClassConstructor(bool isCaseInsensitive = false) + : m_isCaseInsensitive(isCaseInsensitive) + { + } + + void reset() + { + m_matches.clear(); + m_ranges.clear(); + m_matchesUnicode.clear(); + m_rangesUnicode.clear(); + } + + void append(const CharacterClass* other) + { + for (size_t i = 0; i < other->m_matches.length(); ++i) + addSorted(m_matches, other->m_matches[i]); + for (size_t i = 0; i < other->m_ranges.length(); ++i) + addSortedRange(m_ranges, other->m_ranges[i].begin, other->m_ranges[i].end); + for (size_t i = 0; i < other->m_matchesUnicode.length(); ++i) + addSorted(m_matchesUnicode, other->m_matchesUnicode[i]); + for (size_t i = 0; i < other->m_rangesUnicode.length(); ++i) + addSortedRange(m_rangesUnicode, other->m_rangesUnicode[i].begin, other->m_rangesUnicode[i].end); + } + + void putChar(UChar ch) + { + if (ch <= 0x7f) { + if (m_isCaseInsensitive && isASCIIAlpha(ch)) { + addSorted(m_matches, toASCIIUpper(ch)); + addSorted(m_matches, toASCIILower(ch)); + } else + addSorted(m_matches, ch); + } else { + UChar upper, lower; + if (m_isCaseInsensitive && ((upper = Unicode::toUpper(ch)) != (lower = Unicode::toLower(ch)))) { + addSorted(m_matchesUnicode, upper); + addSorted(m_matchesUnicode, lower); + } else + addSorted(m_matchesUnicode, ch); + } + } + + // returns true if this character has another case, and 'ch' is the upper case form. + static inline bool isUnicodeUpper(UChar ch) + { + return ch != Unicode::toLower(ch); + } + + // returns true if this character has another case, and 'ch' is the lower case form. + static inline bool isUnicodeLower(UChar ch) + { + return ch != Unicode::toUpper(ch); + } + + void putRange(UChar lo, UChar hi) + { + if (lo <= 0x7f) { + char asciiLo = lo; + char asciiHi = JS_MIN(hi, (UChar)0x7f); + addSortedRange(m_ranges, lo, asciiHi); + + if (m_isCaseInsensitive) { + if ((asciiLo <= 'Z') && (asciiHi >= 'A')) + addSortedRange(m_ranges, JS_MAX(asciiLo, 'A')+('a'-'A'), JS_MIN(asciiHi, 'Z')+('a'-'A')); + if ((asciiLo <= 'z') && (asciiHi >= 'a')) + addSortedRange(m_ranges, JS_MAX(asciiLo, 'a')+('A'-'a'), JS_MIN(asciiHi, 'z')+('A'-'a')); + } + } + if (hi >= 0x80) { + uint32 unicodeCurr = JS_MAX(lo, (UChar)0x80); + addSortedRange(m_rangesUnicode, unicodeCurr, hi); + + if (m_isCaseInsensitive) { + while (unicodeCurr <= hi) { + // If the upper bound of the range (hi) is 0xffff, the increments to + // unicodeCurr in this loop may take it to 0x10000. This is fine + // (if so we won't re-enter the loop, since the loop condition above + // will definitely fail) - but this does mean we cannot use a UChar + // to represent unicodeCurr, we must use a 32-bit value instead. + JS_ASSERT(unicodeCurr <= 0xffff); + + if (isUnicodeUpper(unicodeCurr)) { + UChar lowerCaseRangeBegin = Unicode::toLower(unicodeCurr); + UChar lowerCaseRangeEnd = lowerCaseRangeBegin; + while ((++unicodeCurr <= hi) && isUnicodeUpper(unicodeCurr) && (Unicode::toLower(unicodeCurr) == (lowerCaseRangeEnd + 1))) + lowerCaseRangeEnd++; + addSortedRange(m_rangesUnicode, lowerCaseRangeBegin, lowerCaseRangeEnd); + } else if (isUnicodeLower(unicodeCurr)) { + UChar upperCaseRangeBegin = Unicode::toUpper(unicodeCurr); + UChar upperCaseRangeEnd = upperCaseRangeBegin; + while ((++unicodeCurr <= hi) && isUnicodeLower(unicodeCurr) && (Unicode::toUpper(unicodeCurr) == (upperCaseRangeEnd + 1))) + upperCaseRangeEnd++; + addSortedRange(m_rangesUnicode, upperCaseRangeBegin, upperCaseRangeEnd); + } else + ++unicodeCurr; + } + } + } + } + + CharacterClass* charClass() + { + CharacterClass* characterClass = new CharacterClass(0); + + characterClass->m_matches.append(m_matches); + characterClass->m_ranges.append(m_ranges); + characterClass->m_matchesUnicode.append(m_matchesUnicode); + characterClass->m_rangesUnicode.append(m_rangesUnicode); + + reset(); + + return characterClass; + } + +private: + typedef js::Vector UChars; + typedef js::Vector CharacterRanges; + void addSorted(UChars& matches, UChar ch) + { + unsigned pos = 0; + unsigned range = matches.length(); + + // binary chop, find position to insert char. + while (range) { + unsigned index = range >> 1; + + int val = matches[pos+index] - ch; + if (!val) + return; + else if (val > 0) + range = index; + else { + pos += (index+1); + range -= (index+1); + } + } + + if (pos == matches.length()) + matches.append(ch); + else + matches.insert(matches.begin() + pos, ch); + } + + void addSortedRange(CharacterRanges& ranges, UChar lo, UChar hi) + { + unsigned end = ranges.length(); + + // Simple linear scan - I doubt there are that many ranges anyway... + // feel free to fix this with something faster (eg binary chop). + for (unsigned i = 0; i < end; ++i) { + // does the new range fall before the current position in the array + if (hi < ranges[i].begin) { + // optional optimization: concatenate appending ranges? - may not be worthwhile. + if (hi == (ranges[i].begin - 1)) { + ranges[i].begin = lo; + return; + } + ranges.insert(ranges.begin() + i, CharacterRange(lo, hi)); + return; + } + // Okay, since we didn't hit the last case, the end of the new range is definitely at or after the begining + // If the new range start at or before the end of the last range, then the overlap (if it starts one after the + // end of the last range they concatenate, which is just as good. + if (lo <= (ranges[i].end + 1)) { + // found an intersect! we'll replace this entry in the array. + ranges[i].begin = JS_MIN(ranges[i].begin, lo); + ranges[i].end = JS_MAX(ranges[i].end, hi); + + // now check if the new range can subsume any subsequent ranges. + unsigned next = i+1; + // each iteration of the loop we will either remove something from the list, or break the loop. + while (next < ranges.length()) { + if (ranges[next].begin <= (ranges[i].end + 1)) { + // the next entry now overlaps / concatenates this one. + ranges[i].end = JS_MAX(ranges[i].end, ranges[next].end); + ranges.erase(ranges.begin() + next); + } else + break; + } + + return; + } + } + + // CharacterRange comes after all existing ranges. + ranges.append(CharacterRange(lo, hi)); + } + + bool m_isCaseInsensitive; + + UChars m_matches; + CharacterRanges m_ranges; + UChars m_matchesUnicode; + CharacterRanges m_rangesUnicode; +}; + +class RegexPatternConstructor { +public: + RegexPatternConstructor(RegexPattern& pattern) + : m_pattern(pattern) + , m_characterClassConstructor(pattern.m_ignoreCase) + { + } + + ~RegexPatternConstructor() + { + } + + void reset() + { + m_pattern.reset(); + m_characterClassConstructor.reset(); + } + + void assertionBOL() + { + m_alternative->m_terms.append(PatternTerm::BOL()); + } + void assertionEOL() + { + m_alternative->m_terms.append(PatternTerm::EOL()); + } + void assertionWordBoundary(bool invert) + { + m_alternative->m_terms.append(PatternTerm::WordBoundary(invert)); + } + + void atomPatternCharacter(UChar ch) + { + // We handle case-insensitive checking of unicode characters which do have both + // cases by handling them as if they were defined using a CharacterClass. + if (m_pattern.m_ignoreCase && !isASCII(ch) && (Unicode::toUpper(ch) != Unicode::toLower(ch))) { + atomCharacterClassBegin(); + atomCharacterClassAtom(ch); + atomCharacterClassEnd(); + } else + m_alternative->m_terms.append(PatternTerm(ch)); + } + + void atomBuiltInCharacterClass(BuiltInCharacterClassID classID, bool invert) + { + switch (classID) { + case DigitClassID: + m_alternative->m_terms.append(PatternTerm(m_pattern.digitsCharacterClass(), invert)); + break; + case SpaceClassID: + m_alternative->m_terms.append(PatternTerm(m_pattern.spacesCharacterClass(), invert)); + break; + case WordClassID: + m_alternative->m_terms.append(PatternTerm(m_pattern.wordcharCharacterClass(), invert)); + break; + case NewlineClassID: + m_alternative->m_terms.append(PatternTerm(m_pattern.newlineCharacterClass(), invert)); + break; + } + } + + void atomCharacterClassBegin(bool invert = false) + { + m_invertCharacterClass = invert; + } + + void atomCharacterClassAtom(UChar ch) + { + m_characterClassConstructor.putChar(ch); + } + + void atomCharacterClassRange(UChar begin, UChar end) + { + m_characterClassConstructor.putRange(begin, end); + } + + void atomCharacterClassBuiltIn(BuiltInCharacterClassID classID, bool invert) + { + JS_ASSERT(classID != NewlineClassID); + + switch (classID) { + case DigitClassID: + m_characterClassConstructor.append(invert ? m_pattern.nondigitsCharacterClass() : m_pattern.digitsCharacterClass()); + break; + + case SpaceClassID: + m_characterClassConstructor.append(invert ? m_pattern.nonspacesCharacterClass() : m_pattern.spacesCharacterClass()); + break; + + case WordClassID: + m_characterClassConstructor.append(invert ? m_pattern.nonwordcharCharacterClass() : m_pattern.wordcharCharacterClass()); + break; + + default: + JS_NOT_REACHED("Invalid character class."); + } + } + + void atomCharacterClassEnd() + { + CharacterClass* newCharacterClass = m_characterClassConstructor.charClass(); + m_pattern.m_userCharacterClasses.append(newCharacterClass); + m_alternative->m_terms.append(PatternTerm(newCharacterClass, m_invertCharacterClass)); + } + + void atomParenthesesSubpatternBegin(bool capture = true) + { + unsigned subpatternId = m_pattern.m_numSubpatterns + 1; + if (capture) + m_pattern.m_numSubpatterns++; + + PatternDisjunction* parenthesesDisjunction = new PatternDisjunction(m_alternative); + m_pattern.m_disjunctions.append(parenthesesDisjunction); + m_alternative->m_terms.append(PatternTerm(PatternTerm::TypeParenthesesSubpattern, subpatternId, parenthesesDisjunction, capture)); + m_alternative = parenthesesDisjunction->addNewAlternative(); + } + + void atomParentheticalAssertionBegin(bool invert = false) + { + PatternDisjunction* parenthesesDisjunction = new PatternDisjunction(m_alternative); + m_pattern.m_disjunctions.append(parenthesesDisjunction); + m_alternative->m_terms.append(PatternTerm(PatternTerm::TypeParentheticalAssertion, m_pattern.m_numSubpatterns + 1, parenthesesDisjunction, invert)); + m_alternative = parenthesesDisjunction->addNewAlternative(); + } + + void atomParenthesesEnd() + { + JS_ASSERT(m_alternative->m_parent); + JS_ASSERT(m_alternative->m_parent->m_parent); + m_alternative = m_alternative->m_parent->m_parent; + + m_alternative->lastTerm().parentheses.lastSubpatternId = m_pattern.m_numSubpatterns; + } + + void atomBackReference(unsigned subpatternId) + { + JS_ASSERT(subpatternId); + m_pattern.m_containsBackreferences = true; + m_pattern.m_maxBackReference = JS_MAX(m_pattern.m_maxBackReference, subpatternId); + + if (subpatternId > m_pattern.m_numSubpatterns) { + m_alternative->m_terms.append(PatternTerm::ForwardReference()); + return; + } + + PatternAlternative* currentAlternative = m_alternative; + JS_ASSERT(currentAlternative); + + // Note to self: if we waited until the AST was baked, we could also remove forwards refs + while ((currentAlternative = currentAlternative->m_parent->m_parent)) { + PatternTerm& term = currentAlternative->lastTerm(); + JS_ASSERT((term.type == PatternTerm::TypeParenthesesSubpattern) || (term.type == PatternTerm::TypeParentheticalAssertion)); + + if ((term.type == PatternTerm::TypeParenthesesSubpattern) && term.invertOrCapture && (subpatternId == term.subpatternId)) { + m_alternative->m_terms.append(PatternTerm::ForwardReference()); + return; + } + } + + m_alternative->m_terms.append(PatternTerm(subpatternId)); + } + + PatternDisjunction* copyDisjunction(PatternDisjunction* disjunction) + { + PatternDisjunction* newDisjunction = new PatternDisjunction(); + + newDisjunction->m_parent = disjunction->m_parent; + for (unsigned alt = 0; alt < disjunction->m_alternatives.length(); ++alt) { + PatternAlternative* alternative = disjunction->m_alternatives[alt]; + PatternAlternative* newAlternative = newDisjunction->addNewAlternative(); + for (unsigned i = 0; i < alternative->m_terms.length(); ++i) + newAlternative->m_terms.append(copyTerm(alternative->m_terms[i])); + } + + m_pattern.m_disjunctions.append(newDisjunction); + return newDisjunction; + } + + PatternTerm copyTerm(PatternTerm& term) + { + if ((term.type != PatternTerm::TypeParenthesesSubpattern) && (term.type != PatternTerm::TypeParentheticalAssertion)) + return PatternTerm(term); + + PatternTerm termCopy = term; + termCopy.parentheses.disjunction = copyDisjunction(termCopy.parentheses.disjunction); + return termCopy; + } + + void quantifyAtom(unsigned min, unsigned max, bool greedy) + { + JS_ASSERT(min <= max); + JS_ASSERT(m_alternative->m_terms.length()); + + if (!max) { + m_alternative->removeLastTerm(); + return; + } + + PatternTerm& term = m_alternative->lastTerm(); + JS_ASSERT(term.type > PatternTerm::TypeAssertionWordBoundary); + JS_ASSERT((term.quantityCount == 1) && (term.quantityType == QuantifierFixedCount)); + + // For any assertion with a zero minimum, not matching is valid and has no effect, + // remove it. Otherwise, we need to match as least once, but there is no point + // matching more than once, so remove the quantifier. It is not entirely clear + // from the spec whether or not this behavior is correct, but I believe this + // matches Firefox. :-/ + if (term.type == PatternTerm::TypeParentheticalAssertion) { + if (!min) + m_alternative->removeLastTerm(); + return; + } + + if (min == 0) + term.quantify(max, greedy ? QuantifierGreedy : QuantifierNonGreedy); + else if (min == max) + term.quantify(min, QuantifierFixedCount); + else { + term.quantify(min, QuantifierFixedCount); + m_alternative->m_terms.append(copyTerm(term)); + // NOTE: this term is interesting from an analysis perspective, in that it can be ignored..... + m_alternative->lastTerm().quantify((max == UINT_MAX) ? max : max - min, greedy ? QuantifierGreedy : QuantifierNonGreedy); + if (m_alternative->lastTerm().type == PatternTerm::TypeParenthesesSubpattern) + m_alternative->lastTerm().parentheses.isCopy = true; + } + } + + void disjunction() + { + m_alternative = m_alternative->m_parent->addNewAlternative(); + } + + void regexBegin() + { + m_pattern.m_body = new PatternDisjunction(); + m_alternative = m_pattern.m_body->addNewAlternative(); + m_pattern.m_disjunctions.append(m_pattern.m_body); + } + void regexEnd() + { + } + void regexError() + { + } + + unsigned setupAlternativeOffsets(PatternAlternative* alternative, unsigned currentCallFrameSize, unsigned initialInputPosition) + { + alternative->m_hasFixedSize = true; + unsigned currentInputPosition = initialInputPosition; + + for (unsigned i = 0; i < alternative->m_terms.length(); ++i) { + PatternTerm& term = alternative->m_terms[i]; + + switch (term.type) { + case PatternTerm::TypeAssertionBOL: + case PatternTerm::TypeAssertionEOL: + case PatternTerm::TypeAssertionWordBoundary: + term.inputPosition = currentInputPosition; + break; + + case PatternTerm::TypeBackReference: + term.inputPosition = currentInputPosition; + term.frameLocation = currentCallFrameSize; + currentCallFrameSize += RegexStackSpaceForBackTrackInfoBackReference; + alternative->m_hasFixedSize = false; + break; + + case PatternTerm::TypeForwardReference: + break; + + case PatternTerm::TypePatternCharacter: + term.inputPosition = currentInputPosition; + if (term.quantityType != QuantifierFixedCount) { + term.frameLocation = currentCallFrameSize; + currentCallFrameSize += RegexStackSpaceForBackTrackInfoPatternCharacter; + alternative->m_hasFixedSize = false; + } else + currentInputPosition += term.quantityCount; + break; + + case PatternTerm::TypeCharacterClass: + term.inputPosition = currentInputPosition; + if (term.quantityType != QuantifierFixedCount) { + term.frameLocation = currentCallFrameSize; + currentCallFrameSize += RegexStackSpaceForBackTrackInfoCharacterClass; + alternative->m_hasFixedSize = false; + } else + currentInputPosition += term.quantityCount; + break; + + case PatternTerm::TypeParenthesesSubpattern: + // Note: for fixed once parentheses we will ensure at least the minimum is available; others are on their own. + term.frameLocation = currentCallFrameSize; + if ((term.quantityCount == 1) && !term.parentheses.isCopy) { + if (term.quantityType == QuantifierFixedCount) { + currentCallFrameSize = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition); + currentInputPosition += term.parentheses.disjunction->m_minimumSize; + } else { + currentCallFrameSize += RegexStackSpaceForBackTrackInfoParenthesesOnce; + currentCallFrameSize = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition); + } + term.inputPosition = currentInputPosition; + } else { + term.inputPosition = currentInputPosition; + setupDisjunctionOffsets(term.parentheses.disjunction, 0, currentInputPosition); + currentCallFrameSize += RegexStackSpaceForBackTrackInfoParentheses; + } + // Fixed count of 1 could be accepted, if they have a fixed size *AND* if all alternatives are of the same length. + alternative->m_hasFixedSize = false; + break; + + case PatternTerm::TypeParentheticalAssertion: + term.inputPosition = currentInputPosition; + term.frameLocation = currentCallFrameSize; + currentCallFrameSize = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize + RegexStackSpaceForBackTrackInfoParentheticalAssertion, currentInputPosition); + break; + } + } + + alternative->m_minimumSize = currentInputPosition - initialInputPosition; + return currentCallFrameSize; + } + + unsigned setupDisjunctionOffsets(PatternDisjunction* disjunction, unsigned initialCallFrameSize, unsigned initialInputPosition) + { + if ((disjunction != m_pattern.m_body) && (disjunction->m_alternatives.length() > 1)) + initialCallFrameSize += RegexStackSpaceForBackTrackInfoAlternative; + + unsigned minimumInputSize = UINT_MAX; + unsigned maximumCallFrameSize = 0; + bool hasFixedSize = true; + + for (unsigned alt = 0; alt < disjunction->m_alternatives.length(); ++alt) { + PatternAlternative* alternative = disjunction->m_alternatives[alt]; + unsigned currentAlternativeCallFrameSize = setupAlternativeOffsets(alternative, initialCallFrameSize, initialInputPosition); + minimumInputSize = JS_MIN(minimumInputSize, alternative->m_minimumSize); + maximumCallFrameSize = JS_MAX(maximumCallFrameSize, currentAlternativeCallFrameSize); + hasFixedSize &= alternative->m_hasFixedSize; + } + + JS_ASSERT(minimumInputSize != UINT_MAX); + JS_ASSERT(maximumCallFrameSize >= initialCallFrameSize); + + disjunction->m_hasFixedSize = hasFixedSize; + disjunction->m_minimumSize = minimumInputSize; + disjunction->m_callFrameSize = maximumCallFrameSize; + return maximumCallFrameSize; + } + + void setupOffsets() + { + setupDisjunctionOffsets(m_pattern.m_body, 0, 0); + } + +private: + RegexPattern& m_pattern; + PatternAlternative* m_alternative; + CharacterClassConstructor m_characterClassConstructor; + bool m_invertCharacterClass; +}; + + +int compileRegex(const UString& patternString, RegexPattern& pattern) +{ + RegexPatternConstructor constructor(pattern); + + if (int error = parse(constructor, patternString)) + return error; + + // If the pattern contains illegal backreferences reset & reparse. + // Quoting Netscape's "What's new in JavaScript 1.2", + // "Note: if the number of left parentheses is less than the number specified + // in \#, the \# is taken as an octal escape as described in the next row." + if (pattern.containsIllegalBackReference()) { + unsigned numSubpatterns = pattern.m_numSubpatterns; + + constructor.reset(); +#ifdef DEBUG + int error = +#endif + parse(constructor, patternString, numSubpatterns); + + JS_ASSERT(!error); + JS_ASSERT(numSubpatterns == pattern.m_numSubpatterns); + } + + constructor.setupOffsets(); + + return 0; +} + + +} } diff --git a/js/src/yarr/yarr/RegexCompiler.h b/js/src/yarr/yarr/RegexCompiler.h new file mode 100644 index 000000000000..307c15866e59 --- /dev/null +++ b/js/src/yarr/yarr/RegexCompiler.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RegexCompiler_h +#define RegexCompiler_h + +#include "RegexParser.h" +#include "RegexPattern.h" + +namespace JSC { namespace Yarr { + +int compileRegex(const UString& patternString, RegexPattern& pattern); + +} } // namespace JSC::Yarr + +#endif // RegexCompiler_h diff --git a/js/src/yarr/yarr/RegexJIT.cpp b/js/src/yarr/yarr/RegexJIT.cpp new file mode 100644 index 000000000000..7160991e8285 --- /dev/null +++ b/js/src/yarr/yarr/RegexJIT.cpp @@ -0,0 +1,1516 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "RegexJIT.h" + +#include "assembler/assembler/LinkBuffer.h" +#include "assembler/assembler/MacroAssembler.h" +#include "RegexCompiler.h" + +#include "yarr/pcre/pcre.h" // temporary, remove when fallback is removed. + +#if ENABLE_YARR_JIT + +using namespace WTF; + +namespace JSC { namespace Yarr { + +class JSGlobalData; + +class RegexGenerator : private MacroAssembler { + friend void jitCompileRegex(JSGlobalData* globalData, RegexCodeBlock& jitObject, const UString& pattern, unsigned& numSubpatterns, const char*& error, bool ignoreCase, bool multiline); + +#if WTF_CPU_ARM + static const RegisterID input = ARMRegisters::r0; + static const RegisterID index = ARMRegisters::r1; + static const RegisterID length = ARMRegisters::r2; + static const RegisterID output = ARMRegisters::r4; + + static const RegisterID regT0 = ARMRegisters::r5; + static const RegisterID regT1 = ARMRegisters::r6; + + static const RegisterID returnRegister = ARMRegisters::r0; +#elif WTF_CPU_MIPS + static const RegisterID input = MIPSRegisters::a0; + static const RegisterID index = MIPSRegisters::a1; + static const RegisterID length = MIPSRegisters::a2; + static const RegisterID output = MIPSRegisters::a3; + + static const RegisterID regT0 = MIPSRegisters::t4; + static const RegisterID regT1 = MIPSRegisters::t5; + + static const RegisterID returnRegister = MIPSRegisters::v0; +#elif WTF_CPU_X86 + static const RegisterID input = X86Registers::eax; + static const RegisterID index = X86Registers::edx; + static const RegisterID length = X86Registers::ecx; + static const RegisterID output = X86Registers::edi; + + static const RegisterID regT0 = X86Registers::ebx; + static const RegisterID regT1 = X86Registers::esi; + + static const RegisterID returnRegister = X86Registers::eax; +#elif WTF_CPU_X86_64 + static const RegisterID input = X86Registers::edi; + static const RegisterID index = X86Registers::esi; + static const RegisterID length = X86Registers::edx; + static const RegisterID output = X86Registers::ecx; + + static const RegisterID regT0 = X86Registers::eax; + static const RegisterID regT1 = X86Registers::ebx; + + static const RegisterID returnRegister = X86Registers::eax; +#endif + + void optimizeAlternative(PatternAlternative* alternative) + { + if (!alternative->m_terms.length()) + return; + + for (unsigned i = 0; i < alternative->m_terms.length() - 1; ++i) { + PatternTerm& term = alternative->m_terms[i]; + PatternTerm& nextTerm = alternative->m_terms[i + 1]; + + if ((term.type == PatternTerm::TypeCharacterClass) + && (term.quantityType == QuantifierFixedCount) + && (nextTerm.type == PatternTerm::TypePatternCharacter) + && (nextTerm.quantityType == QuantifierFixedCount)) { + PatternTerm termCopy = term; + alternative->m_terms[i] = nextTerm; + alternative->m_terms[i + 1] = termCopy; + } + } + } + + void matchCharacterClassRange(RegisterID character, JumpList& failures, JumpList& matchDest, const CharacterRange* ranges, unsigned count, unsigned* matchIndex, const UChar* matches, unsigned matchCount) + { + do { + // pick which range we're going to generate + int which = count >> 1; + char lo = ranges[which].begin; + char hi = ranges[which].end; + + // check if there are any ranges or matches below lo. If not, just jl to failure - + // if there is anything else to check, check that first, if it falls through jmp to failure. + if ((*matchIndex < matchCount) && (matches[*matchIndex] < lo)) { + Jump loOrAbove = branch32(GreaterThanOrEqual, character, Imm32((unsigned short)lo)); + + // generate code for all ranges before this one + if (which) + matchCharacterClassRange(character, failures, matchDest, ranges, which, matchIndex, matches, matchCount); + + while ((*matchIndex < matchCount) && (matches[*matchIndex] < lo)) { + matchDest.append(branch32(Equal, character, Imm32((unsigned short)matches[*matchIndex]))); + ++*matchIndex; + } + failures.append(jump()); + + loOrAbove.link(this); + } else if (which) { + Jump loOrAbove = branch32(GreaterThanOrEqual, character, Imm32((unsigned short)lo)); + + matchCharacterClassRange(character, failures, matchDest, ranges, which, matchIndex, matches, matchCount); + failures.append(jump()); + + loOrAbove.link(this); + } else + failures.append(branch32(LessThan, character, Imm32((unsigned short)lo))); + + while ((*matchIndex < matchCount) && (matches[*matchIndex] <= hi)) + ++*matchIndex; + + matchDest.append(branch32(LessThanOrEqual, character, Imm32((unsigned short)hi))); + // fall through to here, the value is above hi. + + // shuffle along & loop around if there are any more matches to handle. + unsigned next = which + 1; + ranges += next; + count -= next; + } while (count); + } + + void matchCharacterClass(RegisterID character, JumpList& matchDest, const CharacterClass* charClass) + { + if (charClass->m_table) { + ExtendedAddress tableEntry(character, reinterpret_cast(charClass->m_table->m_table)); + matchDest.append(branchTest8(charClass->m_table->m_inverted ? Zero : NonZero, tableEntry)); + return; + } + Jump unicodeFail; + if (charClass->m_matchesUnicode.length() || charClass->m_rangesUnicode.length()) { + Jump isAscii = branch32(LessThanOrEqual, character, Imm32(0x7f)); + + if (charClass->m_matchesUnicode.length()) { + for (unsigned i = 0; i < charClass->m_matchesUnicode.length(); ++i) { + UChar ch = charClass->m_matchesUnicode[i]; + matchDest.append(branch32(Equal, character, Imm32(ch))); + } + } + + if (charClass->m_rangesUnicode.length()) { + for (unsigned i = 0; i < charClass->m_rangesUnicode.length(); ++i) { + UChar lo = charClass->m_rangesUnicode[i].begin; + UChar hi = charClass->m_rangesUnicode[i].end; + + Jump below = branch32(LessThan, character, Imm32(lo)); + matchDest.append(branch32(LessThanOrEqual, character, Imm32(hi))); + below.link(this); + } + } + + unicodeFail = jump(); + isAscii.link(this); + } + + if (charClass->m_ranges.length()) { + unsigned matchIndex = 0; + JumpList failures; + matchCharacterClassRange(character, failures, matchDest, charClass->m_ranges.begin(), charClass->m_ranges.length(), &matchIndex, charClass->m_matches.begin(), charClass->m_matches.length()); + while (matchIndex < charClass->m_matches.length()) + matchDest.append(branch32(Equal, character, Imm32((unsigned short)charClass->m_matches[matchIndex++]))); + + failures.link(this); + } else if (charClass->m_matches.length()) { + // optimization: gather 'a','A' etc back together, can mask & test once. + js::Vector matchesAZaz; + + for (unsigned i = 0; i < charClass->m_matches.length(); ++i) { + char ch = charClass->m_matches[i]; + if (m_pattern.m_ignoreCase) { + if (isASCIILower(ch)) { + matchesAZaz.append(ch); + continue; + } + if (isASCIIUpper(ch)) + continue; + } + matchDest.append(branch32(Equal, character, Imm32((unsigned short)ch))); + } + + if (unsigned countAZaz = matchesAZaz.length()) { + or32(Imm32(32), character); + for (unsigned i = 0; i < countAZaz; ++i) + matchDest.append(branch32(Equal, character, Imm32(matchesAZaz[i]))); + } + } + + if (charClass->m_matchesUnicode.length() || charClass->m_rangesUnicode.length()) + unicodeFail.link(this); + } + + // Jumps if input not available; will have (incorrectly) incremented already! + Jump jumpIfNoAvailableInput(unsigned countToCheck) + { + add32(Imm32(countToCheck), index); + return branch32(Above, index, length); + } + + Jump jumpIfAvailableInput(unsigned countToCheck) + { + add32(Imm32(countToCheck), index); + return branch32(BelowOrEqual, index, length); + } + + Jump checkInput() + { + return branch32(BelowOrEqual, index, length); + } + + Jump atEndOfInput() + { + return branch32(Equal, index, length); + } + + Jump notAtEndOfInput() + { + return branch32(NotEqual, index, length); + } + + Jump jumpIfCharEquals(UChar ch, int inputPosition) + { + return branch16(Equal, BaseIndex(input, index, TimesTwo, inputPosition * sizeof(UChar)), Imm32(ch)); + } + + Jump jumpIfCharNotEquals(UChar ch, int inputPosition) + { + return branch16(NotEqual, BaseIndex(input, index, TimesTwo, inputPosition * sizeof(UChar)), Imm32(ch)); + } + + void readCharacter(int inputPosition, RegisterID reg) + { + load16(BaseIndex(input, index, TimesTwo, inputPosition * sizeof(UChar)), reg); + } + + void storeToFrame(RegisterID reg, unsigned frameLocation) + { + poke(reg, frameLocation); + } + + void storeToFrame(Imm32 imm, unsigned frameLocation) + { + poke(imm, frameLocation); + } + + DataLabelPtr storeToFrameWithPatch(unsigned frameLocation) + { + return storePtrWithPatch(ImmPtr(0), Address(stackPointerRegister, frameLocation * sizeof(void*))); + } + + void loadFromFrame(unsigned frameLocation, RegisterID reg) + { + peek(reg, frameLocation); + } + + void loadFromFrameAndJump(unsigned frameLocation) + { + jump(Address(stackPointerRegister, frameLocation * sizeof(void*))); + } + + struct AlternativeBacktrackRecord { + DataLabelPtr dataLabel; + Label backtrackLocation; + + AlternativeBacktrackRecord(DataLabelPtr dataLabel, Label backtrackLocation) + : dataLabel(dataLabel) + , backtrackLocation(backtrackLocation) + { + } + }; + + struct TermGenerationState { + TermGenerationState(PatternDisjunction* disjunction, unsigned checkedTotal) + : disjunction(disjunction) + , checkedTotal(checkedTotal) + { + } + + void resetAlternative() + { + isBackTrackGenerated = false; + alt = 0; + } + bool alternativeValid() + { + return alt < disjunction->m_alternatives.length(); + } + void nextAlternative() + { + ++alt; + } + PatternAlternative* alternative() + { + return disjunction->m_alternatives[alt]; + } + + void resetTerm() + { + ASSERT(alternativeValid()); + t = 0; + } + bool termValid() + { + ASSERT(alternativeValid()); + return t < alternative()->m_terms.length(); + } + void nextTerm() + { + ASSERT(alternativeValid()); + ++t; + } + PatternTerm& term() + { + ASSERT(alternativeValid()); + return alternative()->m_terms[t]; + } + bool isLastTerm() + { + ASSERT(alternativeValid()); + return (t + 1) == alternative()->m_terms.length(); + } + bool isMainDisjunction() + { + return !disjunction->m_parent; + } + + PatternTerm& lookaheadTerm() + { + ASSERT(alternativeValid()); + ASSERT((t + 1) < alternative()->m_terms.length()); + return alternative()->m_terms[t + 1]; + } + bool isSinglePatternCharacterLookaheadTerm() + { + ASSERT(alternativeValid()); + return ((t + 1) < alternative()->m_terms.length()) + && (lookaheadTerm().type == PatternTerm::TypePatternCharacter) + && (lookaheadTerm().quantityType == QuantifierFixedCount) + && (lookaheadTerm().quantityCount == 1); + } + + int inputOffset() + { + return term().inputPosition - checkedTotal; + } + + void jumpToBacktrack(Jump jump, MacroAssembler* masm) + { + if (isBackTrackGenerated) + jump.linkTo(backtrackLabel, masm); + else + backTrackJumps.append(jump); + } + void jumpToBacktrack(JumpList& jumps, MacroAssembler* masm) + { + if (isBackTrackGenerated) + jumps.linkTo(backtrackLabel, masm); + else + backTrackJumps.append(jumps); + } + bool plantJumpToBacktrackIfExists(MacroAssembler* masm) + { + if (isBackTrackGenerated) { + masm->jump(backtrackLabel); + return true; + } + return false; + } + void addBacktrackJump(Jump jump) + { + backTrackJumps.append(jump); + } + void setBacktrackGenerated(Label label) + { + isBackTrackGenerated = true; + backtrackLabel = label; + } + void linkAlternativeBacktracks(MacroAssembler* masm) + { + isBackTrackGenerated = false; + backTrackJumps.link(masm); + } + void linkAlternativeBacktracksTo(Label label, MacroAssembler* masm) + { + isBackTrackGenerated = false; + backTrackJumps.linkTo(label, masm); + } + void propagateBacktrackingFrom(TermGenerationState& nestedParenthesesState, MacroAssembler* masm) + { + jumpToBacktrack(nestedParenthesesState.backTrackJumps, masm); + if (nestedParenthesesState.isBackTrackGenerated) + setBacktrackGenerated(nestedParenthesesState.backtrackLabel); + } + + PatternDisjunction* disjunction; + int checkedTotal; + private: + unsigned alt; + unsigned t; + JumpList backTrackJumps; + Label backtrackLabel; + bool isBackTrackGenerated; + }; + + void generateAssertionBOL(TermGenerationState& state) + { + PatternTerm& term = state.term(); + + if (m_pattern.m_multiline) { + const RegisterID character = regT0; + + JumpList matchDest; + if (!term.inputPosition) + matchDest.append(branch32(Equal, index, Imm32(state.checkedTotal))); + + readCharacter(state.inputOffset() - 1, character); + matchCharacterClass(character, matchDest, m_pattern.newlineCharacterClass()); + state.jumpToBacktrack(jump(), this); + + matchDest.link(this); + } else { + // Erk, really should poison out these alternatives early. :-/ + if (term.inputPosition) + state.jumpToBacktrack(jump(), this); + else + state.jumpToBacktrack(branch32(NotEqual, index, Imm32(state.checkedTotal)), this); + } + } + + void generateAssertionEOL(TermGenerationState& state) + { + PatternTerm& term = state.term(); + + if (m_pattern.m_multiline) { + const RegisterID character = regT0; + + JumpList matchDest; + if (term.inputPosition == state.checkedTotal) + matchDest.append(atEndOfInput()); + + readCharacter(state.inputOffset(), character); + matchCharacterClass(character, matchDest, m_pattern.newlineCharacterClass()); + state.jumpToBacktrack(jump(), this); + + matchDest.link(this); + } else { + if (term.inputPosition == state.checkedTotal) + state.jumpToBacktrack(notAtEndOfInput(), this); + // Erk, really should poison out these alternatives early. :-/ + else + state.jumpToBacktrack(jump(), this); + } + } + + // Also falls though on nextIsNotWordChar. + void matchAssertionWordchar(TermGenerationState& state, JumpList& nextIsWordChar, JumpList& nextIsNotWordChar) + { + const RegisterID character = regT0; + PatternTerm& term = state.term(); + + if (term.inputPosition == state.checkedTotal) + nextIsNotWordChar.append(atEndOfInput()); + + readCharacter(state.inputOffset(), character); + matchCharacterClass(character, nextIsWordChar, m_pattern.wordcharCharacterClass()); + } + + void generateAssertionWordBoundary(TermGenerationState& state) + { + const RegisterID character = regT0; + PatternTerm& term = state.term(); + + Jump atBegin; + JumpList matchDest; + if (!term.inputPosition) + atBegin = branch32(Equal, index, Imm32(state.checkedTotal)); + readCharacter(state.inputOffset() - 1, character); + matchCharacterClass(character, matchDest, m_pattern.wordcharCharacterClass()); + if (!term.inputPosition) + atBegin.link(this); + + // We fall through to here if the last character was not a wordchar. + JumpList nonWordCharThenWordChar; + JumpList nonWordCharThenNonWordChar; + if (term.invertOrCapture) { + matchAssertionWordchar(state, nonWordCharThenNonWordChar, nonWordCharThenWordChar); + nonWordCharThenWordChar.append(jump()); + } else { + matchAssertionWordchar(state, nonWordCharThenWordChar, nonWordCharThenNonWordChar); + nonWordCharThenNonWordChar.append(jump()); + } + state.jumpToBacktrack(nonWordCharThenNonWordChar, this); + + // We jump here if the last character was a wordchar. + matchDest.link(this); + JumpList wordCharThenWordChar; + JumpList wordCharThenNonWordChar; + if (term.invertOrCapture) { + matchAssertionWordchar(state, wordCharThenNonWordChar, wordCharThenWordChar); + wordCharThenWordChar.append(jump()); + } else { + matchAssertionWordchar(state, wordCharThenWordChar, wordCharThenNonWordChar); + // This can fall-though! + } + + state.jumpToBacktrack(wordCharThenWordChar, this); + + nonWordCharThenWordChar.link(this); + wordCharThenNonWordChar.link(this); + } + + void generatePatternCharacterSingle(TermGenerationState& state) + { + const RegisterID character = regT0; + UChar ch = state.term().patternCharacter; + + if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) { + readCharacter(state.inputOffset(), character); + or32(Imm32(32), character); + state.jumpToBacktrack(branch32(NotEqual, character, Imm32(Unicode::toLower(ch))), this); + } else { + ASSERT(!m_pattern.m_ignoreCase || (Unicode::toLower(ch) == Unicode::toUpper(ch))); + state.jumpToBacktrack(jumpIfCharNotEquals(ch, state.inputOffset()), this); + } + } + + void generatePatternCharacterPair(TermGenerationState& state) + { + const RegisterID character = regT0; + UChar ch1 = state.term().patternCharacter; + UChar ch2 = state.lookaheadTerm().patternCharacter; + + int mask = 0; + int chPair = ch1 | (ch2 << 16); + + if (m_pattern.m_ignoreCase) { + if (isASCIIAlpha(ch1)) + mask |= 32; + if (isASCIIAlpha(ch2)) + mask |= 32 << 16; + } + + if (mask) { + load32WithUnalignedHalfWords(BaseIndex(input, index, TimesTwo, state.inputOffset() * sizeof(UChar)), character); + or32(Imm32(mask), character); + state.jumpToBacktrack(branch32(NotEqual, character, Imm32(chPair | mask)), this); + } else + state.jumpToBacktrack(branch32WithUnalignedHalfWords(NotEqual, BaseIndex(input, index, TimesTwo, state.inputOffset() * sizeof(UChar)), Imm32(chPair)), this); + } + + void generatePatternCharacterFixed(TermGenerationState& state) + { + const RegisterID character = regT0; + const RegisterID countRegister = regT1; + PatternTerm& term = state.term(); + UChar ch = term.patternCharacter; + + move(index, countRegister); + sub32(Imm32(term.quantityCount), countRegister); + + Label loop(this); + if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) { + load16(BaseIndex(input, countRegister, TimesTwo, (state.inputOffset() + term.quantityCount) * sizeof(UChar)), character); + or32(Imm32(32), character); + state.jumpToBacktrack(branch32(NotEqual, character, Imm32(Unicode::toLower(ch))), this); + } else { + ASSERT(!m_pattern.m_ignoreCase || (Unicode::toLower(ch) == Unicode::toUpper(ch))); + state.jumpToBacktrack(branch16(NotEqual, BaseIndex(input, countRegister, TimesTwo, (state.inputOffset() + term.quantityCount) * sizeof(UChar)), Imm32(ch)), this); + } + add32(Imm32(1), countRegister); + branch32(NotEqual, countRegister, index).linkTo(loop, this); + } + + void generatePatternCharacterGreedy(TermGenerationState& state) + { + const RegisterID character = regT0; + const RegisterID countRegister = regT1; + PatternTerm& term = state.term(); + UChar ch = term.patternCharacter; + + move(Imm32(0), countRegister); + + JumpList failures; + Label loop(this); + failures.append(atEndOfInput()); + if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) { + readCharacter(state.inputOffset(), character); + or32(Imm32(32), character); + failures.append(branch32(NotEqual, character, Imm32(Unicode::toLower(ch)))); + } else { + ASSERT(!m_pattern.m_ignoreCase || (Unicode::toLower(ch) == Unicode::toUpper(ch))); + failures.append(jumpIfCharNotEquals(ch, state.inputOffset())); + } + + add32(Imm32(1), countRegister); + add32(Imm32(1), index); + if (term.quantityCount != 0xffffffff) { + branch32(NotEqual, countRegister, Imm32(term.quantityCount)).linkTo(loop, this); + failures.append(jump()); + } else + jump(loop); + + Label backtrackBegin(this); + loadFromFrame(term.frameLocation, countRegister); + state.jumpToBacktrack(branchTest32(Zero, countRegister), this); + sub32(Imm32(1), countRegister); + sub32(Imm32(1), index); + + failures.link(this); + + storeToFrame(countRegister, term.frameLocation); + + state.setBacktrackGenerated(backtrackBegin); + } + + void generatePatternCharacterNonGreedy(TermGenerationState& state) + { + const RegisterID character = regT0; + const RegisterID countRegister = regT1; + PatternTerm& term = state.term(); + UChar ch = term.patternCharacter; + + move(Imm32(0), countRegister); + + Jump firstTimeDoNothing = jump(); + + Label hardFail(this); + sub32(countRegister, index); + state.jumpToBacktrack(jump(), this); + + Label backtrackBegin(this); + loadFromFrame(term.frameLocation, countRegister); + + atEndOfInput().linkTo(hardFail, this); + if (term.quantityCount != 0xffffffff) + branch32(Equal, countRegister, Imm32(term.quantityCount), hardFail); + if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) { + readCharacter(state.inputOffset(), character); + or32(Imm32(32), character); + branch32(NotEqual, character, Imm32(Unicode::toLower(ch))).linkTo(hardFail, this); + } else { + ASSERT(!m_pattern.m_ignoreCase || (Unicode::toLower(ch) == Unicode::toUpper(ch))); + jumpIfCharNotEquals(ch, state.inputOffset()).linkTo(hardFail, this); + } + + add32(Imm32(1), countRegister); + add32(Imm32(1), index); + + firstTimeDoNothing.link(this); + storeToFrame(countRegister, term.frameLocation); + + state.setBacktrackGenerated(backtrackBegin); + } + + void generateCharacterClassSingle(TermGenerationState& state) + { + const RegisterID character = regT0; + PatternTerm& term = state.term(); + + JumpList matchDest; + readCharacter(state.inputOffset(), character); + matchCharacterClass(character, matchDest, term.characterClass); + + if (term.invertOrCapture) + state.jumpToBacktrack(matchDest, this); + else { + state.jumpToBacktrack(jump(), this); + matchDest.link(this); + } + } + + void generateCharacterClassFixed(TermGenerationState& state) + { + const RegisterID character = regT0; + const RegisterID countRegister = regT1; + PatternTerm& term = state.term(); + + move(index, countRegister); + sub32(Imm32(term.quantityCount), countRegister); + + Label loop(this); + JumpList matchDest; + load16(BaseIndex(input, countRegister, TimesTwo, (state.inputOffset() + term.quantityCount) * sizeof(UChar)), character); + matchCharacterClass(character, matchDest, term.characterClass); + + if (term.invertOrCapture) + state.jumpToBacktrack(matchDest, this); + else { + state.jumpToBacktrack(jump(), this); + matchDest.link(this); + } + + add32(Imm32(1), countRegister); + branch32(NotEqual, countRegister, index).linkTo(loop, this); + } + + void generateCharacterClassGreedy(TermGenerationState& state) + { + const RegisterID character = regT0; + const RegisterID countRegister = regT1; + PatternTerm& term = state.term(); + + move(Imm32(0), countRegister); + + JumpList failures; + Label loop(this); + failures.append(atEndOfInput()); + + if (term.invertOrCapture) { + readCharacter(state.inputOffset(), character); + matchCharacterClass(character, failures, term.characterClass); + } else { + JumpList matchDest; + readCharacter(state.inputOffset(), character); + matchCharacterClass(character, matchDest, term.characterClass); + failures.append(jump()); + matchDest.link(this); + } + + add32(Imm32(1), countRegister); + add32(Imm32(1), index); + if (term.quantityCount != 0xffffffff) { + branch32(NotEqual, countRegister, Imm32(term.quantityCount)).linkTo(loop, this); + failures.append(jump()); + } else + jump(loop); + + Label backtrackBegin(this); + loadFromFrame(term.frameLocation, countRegister); + state.jumpToBacktrack(branchTest32(Zero, countRegister), this); + sub32(Imm32(1), countRegister); + sub32(Imm32(1), index); + + failures.link(this); + + storeToFrame(countRegister, term.frameLocation); + + state.setBacktrackGenerated(backtrackBegin); + } + + void generateCharacterClassNonGreedy(TermGenerationState& state) + { + const RegisterID character = regT0; + const RegisterID countRegister = regT1; + PatternTerm& term = state.term(); + + move(Imm32(0), countRegister); + + Jump firstTimeDoNothing = jump(); + + Label hardFail(this); + sub32(countRegister, index); + state.jumpToBacktrack(jump(), this); + + Label backtrackBegin(this); + loadFromFrame(term.frameLocation, countRegister); + + atEndOfInput().linkTo(hardFail, this); + branch32(Equal, countRegister, Imm32(term.quantityCount), hardFail); + + JumpList matchDest; + readCharacter(state.inputOffset(), character); + matchCharacterClass(character, matchDest, term.characterClass); + + if (term.invertOrCapture) + matchDest.linkTo(hardFail, this); + else { + jump(hardFail); + matchDest.link(this); + } + + add32(Imm32(1), countRegister); + add32(Imm32(1), index); + + firstTimeDoNothing.link(this); + storeToFrame(countRegister, term.frameLocation); + + state.setBacktrackGenerated(backtrackBegin); + } + + void generateParenthesesDisjunction(PatternTerm& parenthesesTerm, TermGenerationState& state, unsigned alternativeFrameLocation) + { + ASSERT((parenthesesTerm.type == PatternTerm::TypeParenthesesSubpattern) || (parenthesesTerm.type == PatternTerm::TypeParentheticalAssertion)); + ASSERT(parenthesesTerm.quantityCount == 1); + + PatternDisjunction* disjunction = parenthesesTerm.parentheses.disjunction; + unsigned preCheckedCount = ((parenthesesTerm.quantityType == QuantifierFixedCount) && (parenthesesTerm.type != PatternTerm::TypeParentheticalAssertion)) ? disjunction->m_minimumSize : 0; + + if (disjunction->m_alternatives.length() == 1) { + state.resetAlternative(); + ASSERT(state.alternativeValid()); + PatternAlternative* alternative = state.alternative(); + optimizeAlternative(alternative); + + int countToCheck = alternative->m_minimumSize - preCheckedCount; + if (countToCheck) { + ASSERT((parenthesesTerm.type == PatternTerm::TypeParentheticalAssertion) || (parenthesesTerm.quantityType != QuantifierFixedCount)); + + // FIXME: This is quite horrible. The call to 'plantJumpToBacktrackIfExists' + // will be forced to always trampoline into here, just to decrement the index. + // Ick. + Jump skip = jump(); + + Label backtrackBegin(this); + sub32(Imm32(countToCheck), index); + state.addBacktrackJump(jump()); + + skip.link(this); + + state.setBacktrackGenerated(backtrackBegin); + + state.jumpToBacktrack(jumpIfNoAvailableInput(countToCheck), this); + state.checkedTotal += countToCheck; + } + + for (state.resetTerm(); state.termValid(); state.nextTerm()) + generateTerm(state); + + state.checkedTotal -= countToCheck; + } else { + JumpList successes; + + for (state.resetAlternative(); state.alternativeValid(); state.nextAlternative()) { + + PatternAlternative* alternative = state.alternative(); + optimizeAlternative(alternative); + + ASSERT(alternative->m_minimumSize >= preCheckedCount); + int countToCheck = alternative->m_minimumSize - preCheckedCount; + if (countToCheck) { + state.addBacktrackJump(jumpIfNoAvailableInput(countToCheck)); + state.checkedTotal += countToCheck; + } + + for (state.resetTerm(); state.termValid(); state.nextTerm()) + generateTerm(state); + + // Matched an alternative. + DataLabelPtr dataLabel = storeToFrameWithPatch(alternativeFrameLocation); + successes.append(jump()); + + // Alternative did not match. + Label backtrackLocation(this); + + // Can we backtrack the alternative? - if so, do so. If not, just fall through to the next one. + state.plantJumpToBacktrackIfExists(this); + + state.linkAlternativeBacktracks(this); + + if (countToCheck) { + sub32(Imm32(countToCheck), index); + state.checkedTotal -= countToCheck; + } + + m_backtrackRecords.append(AlternativeBacktrackRecord(dataLabel, backtrackLocation)); + } + // We fall through to here when the last alternative fails. + // Add a backtrack out of here for the parenthese handling code to link up. + state.addBacktrackJump(jump()); + + // Generate a trampoline for the parens code to backtrack to, to retry the + // next alternative. + state.setBacktrackGenerated(label()); + loadFromFrameAndJump(alternativeFrameLocation); + + // FIXME: both of the above hooks are a little inefficient, in that you + // may end up trampolining here, just to trampoline back out to the + // parentheses code, or vice versa. We can probably eliminate a jump + // by restructuring, but coding this way for now for simplicity during + // development. + + successes.link(this); + } + } + + void generateParenthesesSingle(TermGenerationState& state) + { + const RegisterID indexTemporary = regT0; + PatternTerm& term = state.term(); + PatternDisjunction* disjunction = term.parentheses.disjunction; + ASSERT(term.quantityCount == 1); + + if (term.parentheses.isCopy) { + m_shouldFallBack = true; + return; + } + + unsigned preCheckedCount = ((term.quantityCount == 1) && (term.quantityType == QuantifierFixedCount)) ? disjunction->m_minimumSize : 0; + + unsigned parenthesesFrameLocation = term.frameLocation; + unsigned alternativeFrameLocation = parenthesesFrameLocation; + if (term.quantityType != QuantifierFixedCount) + alternativeFrameLocation += RegexStackSpaceForBackTrackInfoParenthesesOnce; + + // optimized case - no capture & no quantifier can be handled in a light-weight manner. + if (!term.invertOrCapture && (term.quantityType == QuantifierFixedCount)) { + TermGenerationState parenthesesState(disjunction, state.checkedTotal); + generateParenthesesDisjunction(state.term(), parenthesesState, alternativeFrameLocation); + // this expects that any backtracks back out of the parentheses will be in the + // parenthesesState's backTrackJumps vector, and that if they need backtracking + // they will have set an entry point on the parenthesesState's backtrackLabel. + state.propagateBacktrackingFrom(parenthesesState, this); + } else { + Jump nonGreedySkipParentheses; + Label nonGreedyTryParentheses; + if (term.quantityType == QuantifierGreedy) + storeToFrame(Imm32(1), parenthesesFrameLocation); + else if (term.quantityType == QuantifierNonGreedy) { + storeToFrame(Imm32(0), parenthesesFrameLocation); + nonGreedySkipParentheses = jump(); + nonGreedyTryParentheses = label(); + storeToFrame(Imm32(1), parenthesesFrameLocation); + } + + // store the match start index + if (term.invertOrCapture) { + int inputOffset = state.inputOffset() - preCheckedCount; + if (inputOffset) { + move(index, indexTemporary); + add32(Imm32(inputOffset), indexTemporary); + store32(indexTemporary, Address(output, (term.parentheses.subpatternId << 1) * sizeof(int))); + } else + store32(index, Address(output, (term.parentheses.subpatternId << 1) * sizeof(int))); + } + + // generate the body of the parentheses + TermGenerationState parenthesesState(disjunction, state.checkedTotal); + generateParenthesesDisjunction(state.term(), parenthesesState, alternativeFrameLocation); + + // store the match end index + if (term.invertOrCapture) { + int inputOffset = state.inputOffset(); + if (inputOffset) { + move(index, indexTemporary); + add32(Imm32(state.inputOffset()), indexTemporary); + store32(indexTemporary, Address(output, ((term.parentheses.subpatternId << 1) + 1) * sizeof(int))); + } else + store32(index, Address(output, ((term.parentheses.subpatternId << 1) + 1) * sizeof(int))); + } + Jump success = jump(); + + // A failure AFTER the parens jumps here + Label backtrackFromAfterParens(this); + + if (term.quantityType == QuantifierGreedy) { + // If this is zero we have now tested with both with and without the parens. + loadFromFrame(parenthesesFrameLocation, indexTemporary); + state.jumpToBacktrack(branchTest32(Zero, indexTemporary), this); + } else if (term.quantityType == QuantifierNonGreedy) { + // If this is zero we have now tested with both with and without the parens. + loadFromFrame(parenthesesFrameLocation, indexTemporary); + branchTest32(Zero, indexTemporary).linkTo(nonGreedyTryParentheses, this); + } + + parenthesesState.plantJumpToBacktrackIfExists(this); + // A failure WITHIN the parens jumps here + parenthesesState.linkAlternativeBacktracks(this); + if (term.invertOrCapture) { + store32(Imm32(-1), Address(output, (term.parentheses.subpatternId << 1) * sizeof(int))); + store32(Imm32(-1), Address(output, ((term.parentheses.subpatternId << 1) + 1) * sizeof(int))); + } + + if (term.quantityType == QuantifierGreedy) + storeToFrame(Imm32(0), parenthesesFrameLocation); + else + state.jumpToBacktrack(jump(), this); + + state.setBacktrackGenerated(backtrackFromAfterParens); + if (term.quantityType == QuantifierNonGreedy) + nonGreedySkipParentheses.link(this); + success.link(this); + } + } + + void generateParenthesesGreedyNoBacktrack(TermGenerationState& state) + { + PatternTerm& parenthesesTerm = state.term(); + PatternDisjunction* disjunction = parenthesesTerm.parentheses.disjunction; + ASSERT(parenthesesTerm.type == PatternTerm::TypeParenthesesSubpattern); + ASSERT(parenthesesTerm.quantityCount != 1); // Handled by generateParenthesesSingle. + + // Capturing not yet implemented! + if (parenthesesTerm.invertOrCapture) { + m_shouldFallBack = true; + return; + } + + // Quantification limit not yet implemented! + if (parenthesesTerm.quantityCount != 0xffffffff) { + m_shouldFallBack = true; + return; + } + + // Need to reset nested subpatterns between iterations... + // for the minute this crude check rejects all patterns with any subpatterns! + if (m_pattern.m_numSubpatterns) { + m_shouldFallBack = true; + return; + } + + TermGenerationState parenthesesState(disjunction, state.checkedTotal); + + Label matchAgain(this); + + storeToFrame(index, parenthesesTerm.frameLocation); // Save the current index to check for zero len matches later. + + for (parenthesesState.resetAlternative(); parenthesesState.alternativeValid(); parenthesesState.nextAlternative()) { + + PatternAlternative* alternative = parenthesesState.alternative(); + optimizeAlternative(alternative); + + int countToCheck = alternative->m_minimumSize; + if (countToCheck) { + parenthesesState.addBacktrackJump(jumpIfNoAvailableInput(countToCheck)); + parenthesesState.checkedTotal += countToCheck; + } + + for (parenthesesState.resetTerm(); parenthesesState.termValid(); parenthesesState.nextTerm()) + generateTerm(parenthesesState); + + // If we get here, we matched! If the index advanced then try to match more since limit isn't supported yet. + branch32(GreaterThan, index, Address(stackPointerRegister, (parenthesesTerm.frameLocation * sizeof(void*))), matchAgain); + + parenthesesState.linkAlternativeBacktracks(this); + // We get here if the alternative fails to match - fall through to the next iteration, or out of the loop. + + if (countToCheck) { + sub32(Imm32(countToCheck), index); + parenthesesState.checkedTotal -= countToCheck; + } + } + + // If the last alternative falls through to here, we have a failed match... + // Which means that we match whatever we have matched up to this point (even if nothing). + } + + void generateParentheticalAssertion(TermGenerationState& state) + { + PatternTerm& term = state.term(); + PatternDisjunction* disjunction = term.parentheses.disjunction; + ASSERT(term.quantityCount == 1); + ASSERT(term.quantityType == QuantifierFixedCount); + + unsigned parenthesesFrameLocation = term.frameLocation; + unsigned alternativeFrameLocation = parenthesesFrameLocation + RegexStackSpaceForBackTrackInfoParentheticalAssertion; + + int countCheckedAfterAssertion = state.checkedTotal - term.inputPosition; + + if (term.invertOrCapture) { + // Inverted case + storeToFrame(index, parenthesesFrameLocation); + + state.checkedTotal -= countCheckedAfterAssertion; + if (countCheckedAfterAssertion) + sub32(Imm32(countCheckedAfterAssertion), index); + + TermGenerationState parenthesesState(disjunction, state.checkedTotal); + generateParenthesesDisjunction(state.term(), parenthesesState, alternativeFrameLocation); + // Success! - which means - Fail! + loadFromFrame(parenthesesFrameLocation, index); + state.jumpToBacktrack(jump(), this); + + // And fail means success. + parenthesesState.linkAlternativeBacktracks(this); + loadFromFrame(parenthesesFrameLocation, index); + + state.checkedTotal += countCheckedAfterAssertion; + } else { + // Normal case + storeToFrame(index, parenthesesFrameLocation); + + state.checkedTotal -= countCheckedAfterAssertion; + if (countCheckedAfterAssertion) + sub32(Imm32(countCheckedAfterAssertion), index); + + TermGenerationState parenthesesState(disjunction, state.checkedTotal); + generateParenthesesDisjunction(state.term(), parenthesesState, alternativeFrameLocation); + // Success! - which means - Success! + loadFromFrame(parenthesesFrameLocation, index); + Jump success = jump(); + + parenthesesState.linkAlternativeBacktracks(this); + loadFromFrame(parenthesesFrameLocation, index); + state.jumpToBacktrack(jump(), this); + + success.link(this); + + state.checkedTotal += countCheckedAfterAssertion; + } + } + + void generateTerm(TermGenerationState& state) + { + PatternTerm& term = state.term(); + + switch (term.type) { + case PatternTerm::TypeAssertionBOL: + generateAssertionBOL(state); + break; + + case PatternTerm::TypeAssertionEOL: + generateAssertionEOL(state); + break; + + case PatternTerm::TypeAssertionWordBoundary: + generateAssertionWordBoundary(state); + break; + + case PatternTerm::TypePatternCharacter: + switch (term.quantityType) { + case QuantifierFixedCount: + if (term.quantityCount == 1) { + if (state.isSinglePatternCharacterLookaheadTerm() && (state.lookaheadTerm().inputPosition == (term.inputPosition + 1))) { + generatePatternCharacterPair(state); + state.nextTerm(); + } else + generatePatternCharacterSingle(state); + } else + generatePatternCharacterFixed(state); + break; + case QuantifierGreedy: + generatePatternCharacterGreedy(state); + break; + case QuantifierNonGreedy: + generatePatternCharacterNonGreedy(state); + break; + } + break; + + case PatternTerm::TypeCharacterClass: + switch (term.quantityType) { + case QuantifierFixedCount: + if (term.quantityCount == 1) + generateCharacterClassSingle(state); + else + generateCharacterClassFixed(state); + break; + case QuantifierGreedy: + generateCharacterClassGreedy(state); + break; + case QuantifierNonGreedy: + generateCharacterClassNonGreedy(state); + break; + } + break; + + case PatternTerm::TypeBackReference: + m_shouldFallBack = true; + break; + + case PatternTerm::TypeForwardReference: + break; + + case PatternTerm::TypeParenthesesSubpattern: + if (term.quantityCount == 1) { + generateParenthesesSingle(state); + break; + } else if (state.isLastTerm() && state.isMainDisjunction()) { // Is this is the last term of the main disjunction? + // If this has a greedy quantifier, then it will never need to backtrack! + if (term.quantityType == QuantifierGreedy) { + generateParenthesesGreedyNoBacktrack(state); + break; + } + } + m_shouldFallBack = true; + break; + + case PatternTerm::TypeParentheticalAssertion: + generateParentheticalAssertion(state); + break; + } + } + + void generateDisjunction(PatternDisjunction* disjunction) + { + TermGenerationState state(disjunction, 0); + state.resetAlternative(); + + // Plant a check to see if there is sufficient input available to run the first alternative. + // Jumping back to the label 'firstAlternative' will get to this check, jumping to + // 'firstAlternativeInputChecked' will jump directly to matching the alternative having + // skipped this check. + + Label firstAlternative(this); + + // check availability for the next alternative + int countCheckedForCurrentAlternative = 0; + int countToCheckForFirstAlternative = 0; + bool hasShorterAlternatives = false; + JumpList notEnoughInputForPreviousAlternative; + + if (state.alternativeValid()) { + PatternAlternative* alternative = state.alternative(); + countToCheckForFirstAlternative = alternative->m_minimumSize; + state.checkedTotal += countToCheckForFirstAlternative; + if (countToCheckForFirstAlternative) + notEnoughInputForPreviousAlternative.append(jumpIfNoAvailableInput(countToCheckForFirstAlternative)); + countCheckedForCurrentAlternative = countToCheckForFirstAlternative; + } + + Label firstAlternativeInputChecked(this); + + while (state.alternativeValid()) { + // Track whether any alternatives are shorter than the first one. + hasShorterAlternatives = hasShorterAlternatives || (countCheckedForCurrentAlternative < countToCheckForFirstAlternative); + + PatternAlternative* alternative = state.alternative(); + optimizeAlternative(alternative); + + for (state.resetTerm(); state.termValid(); state.nextTerm()) + generateTerm(state); + + // If we get here, the alternative matched. + if (m_pattern.m_body->m_callFrameSize) + addPtr(Imm32(m_pattern.m_body->m_callFrameSize * sizeof(void*)), stackPointerRegister); + + ASSERT(index != returnRegister); + if (m_pattern.m_body->m_hasFixedSize) { + move(index, returnRegister); + if (alternative->m_minimumSize) + sub32(Imm32(alternative->m_minimumSize), returnRegister); + + store32(returnRegister, output); + } else + load32(Address(output), returnRegister); + + store32(index, Address(output, 4)); + + generateReturn(); + + state.nextAlternative(); + + // if there are any more alternatives, plant the check for input before looping. + if (state.alternativeValid()) { + PatternAlternative* nextAlternative = state.alternative(); + int countToCheckForNextAlternative = nextAlternative->m_minimumSize; + + if (countCheckedForCurrentAlternative > countToCheckForNextAlternative) { // CASE 1: current alternative was longer than the next one. + // If we get here, there the last input checked failed. + notEnoughInputForPreviousAlternative.link(this); + + // Check if sufficent input available to run the next alternative + notEnoughInputForPreviousAlternative.append(jumpIfNoAvailableInput(countToCheckForNextAlternative - countCheckedForCurrentAlternative)); + // We are now in the correct state to enter the next alternative; this add is only required + // to mirror and revert operation of the sub32, just below. + add32(Imm32(countCheckedForCurrentAlternative - countToCheckForNextAlternative), index); + + // If we get here, there the last input checked passed. + state.linkAlternativeBacktracks(this); + // No need to check if we can run the next alternative, since it is shorter - + // just update index. + sub32(Imm32(countCheckedForCurrentAlternative - countToCheckForNextAlternative), index); + } else if (countCheckedForCurrentAlternative < countToCheckForNextAlternative) { // CASE 2: next alternative is longer than the current one. + // If we get here, there the last input checked failed. + // If there is insufficient input to run the current alternative, and the next alternative is longer, + // then there is definitely not enough input to run it - don't even check. Just adjust index, as if + // we had checked. + notEnoughInputForPreviousAlternative.link(this); + add32(Imm32(countToCheckForNextAlternative - countCheckedForCurrentAlternative), index); + notEnoughInputForPreviousAlternative.append(jump()); + + // The next alternative is longer than the current one; check the difference. + state.linkAlternativeBacktracks(this); + notEnoughInputForPreviousAlternative.append(jumpIfNoAvailableInput(countToCheckForNextAlternative - countCheckedForCurrentAlternative)); + } else { // CASE 3: Both alternatives are the same length. + ASSERT(countCheckedForCurrentAlternative == countToCheckForNextAlternative); + + // If the next alterative is the same length as this one, then no need to check the input - + // if there was sufficent input to run the current alternative then there is sufficient + // input to run the next one; if not, there isn't. + state.linkAlternativeBacktracks(this); + } + + state.checkedTotal -= countCheckedForCurrentAlternative; + countCheckedForCurrentAlternative = countToCheckForNextAlternative; + state.checkedTotal += countCheckedForCurrentAlternative; + } + } + + // If we get here, all Alternatives failed... + + state.checkedTotal -= countCheckedForCurrentAlternative; + + // How much more input need there be to be able to retry from the first alternative? + // examples: + // /yarr_jit/ or /wrec|pcre/ + // In these examples we need check for one more input before looping. + // /yarr_jit|pcre/ + // In this case we need check for 5 more input to loop (+4 to allow for the first alterative + // being four longer than the last alternative checked, and another +1 to effectively move + // the start position along by one). + // /yarr|rules/ or /wrec|notsomuch/ + // In these examples, provided that there was sufficient input to have just been matching for + // the second alternative we can loop without checking for available input (since the second + // alternative is longer than the first). In the latter example we need to decrement index + // (by 4) so the start position is only progressed by 1 from the last iteration. + int incrementForNextIter = (countToCheckForFirstAlternative - countCheckedForCurrentAlternative) + 1; + + // First, deal with the cases where there was sufficient input to try the last alternative. + if (incrementForNextIter > 0) // We need to check for more input anyway, fall through to the checking below. + state.linkAlternativeBacktracks(this); + else if (m_pattern.m_body->m_hasFixedSize && !incrementForNextIter) // No need to update anything, link these backtracks straight to the to pof the loop! + state.linkAlternativeBacktracksTo(firstAlternativeInputChecked, this); + else { // no need to check the input, but we do have some bookkeeping to do first. + state.linkAlternativeBacktracks(this); + + // Where necessary update our preserved start position. + if (!m_pattern.m_body->m_hasFixedSize) { + move(index, regT0); + sub32(Imm32(countCheckedForCurrentAlternative - 1), regT0); + store32(regT0, Address(output)); + } + + // Update index if necessary, and loop (without checking). + if (incrementForNextIter) + add32(Imm32(incrementForNextIter), index); + jump().linkTo(firstAlternativeInputChecked, this); + } + + notEnoughInputForPreviousAlternative.link(this); + // Update our idea of the start position, if we're tracking this. + if (!m_pattern.m_body->m_hasFixedSize) { + if (countCheckedForCurrentAlternative - 1) { + move(index, regT0); + sub32(Imm32(countCheckedForCurrentAlternative - 1), regT0); + store32(regT0, Address(output)); + } else + store32(index, Address(output)); + } + // Check if there is sufficent input to run the first alternative again. + jumpIfAvailableInput(incrementForNextIter).linkTo(firstAlternativeInputChecked, this); + // No - insufficent input to run the first alteranative, are there any other alternatives we + // might need to check? If so, the last check will have left the index incremented by + // (countToCheckForFirstAlternative + 1), so we need test whether countToCheckForFirstAlternative + // LESS input is available, to have the effect of just progressing the start position by 1 + // from the last iteration. If this check passes we can just jump up to the check associated + // with the first alternative in the loop. This is a bit sad, since we'll end up trying the + // first alternative again, and this check will fail (otherwise the check planted just above + // here would have passed). This is a bit sad, however it saves trying to do something more + // complex here in compilation, and in the common case we should end up coallescing the checks. + // + // FIXME: a nice improvement here may be to stop trying to match sooner, based on the least + // of the minimum-alternative-lengths. E.g. if I have two alternatives of length 200 and 150, + // and a string of length 100, we'll end up looping index from 0 to 100, checking whether there + // is sufficient input to run either alternative (constantly failing). If there had been only + // one alternative, or if the shorter alternative had come first, we would have terminated + // immediately. :-/ + if (hasShorterAlternatives) + jumpIfAvailableInput(-countToCheckForFirstAlternative).linkTo(firstAlternative, this); + // index will now be a bit garbled (depending on whether 'hasShorterAlternatives' is true, + // it has either been incremented by 1 or by (countToCheckForFirstAlternative + 1) ... + // but since we're about to return a failure this doesn't really matter!) + + if (m_pattern.m_body->m_callFrameSize) + addPtr(Imm32(m_pattern.m_body->m_callFrameSize * sizeof(void*)), stackPointerRegister); + + move(Imm32(-1), returnRegister); + + generateReturn(); + } + + void generateEnter() + { +#if WTF_CPU_X86_64 + push(X86Registers::ebp); + move(stackPointerRegister, X86Registers::ebp); + push(X86Registers::ebx); +#elif WTF_CPU_X86 + push(X86Registers::ebp); + move(stackPointerRegister, X86Registers::ebp); + // TODO: do we need spill registers to fill the output pointer if there are no sub captures? + push(X86Registers::ebx); + push(X86Registers::edi); + push(X86Registers::esi); + // load output into edi (2 = saved ebp + return address). + #if WTF_COMPILER_MSVC + loadPtr(Address(X86Registers::ebp, 2 * sizeof(void*)), input); + loadPtr(Address(X86Registers::ebp, 3 * sizeof(void*)), index); + loadPtr(Address(X86Registers::ebp, 4 * sizeof(void*)), length); + loadPtr(Address(X86Registers::ebp, 5 * sizeof(void*)), output); + #else + loadPtr(Address(X86Registers::ebp, 2 * sizeof(void*)), output); + #endif +#elif WTF_CPU_ARM + push(ARMRegisters::r4); + push(ARMRegisters::r5); + push(ARMRegisters::r6); + move(ARMRegisters::r3, output); +#elif WTF_CPU_MIPS + // Do nothing. +#endif + } + + void generateReturn() + { +#if WTF_CPU_X86_64 + pop(X86Registers::ebx); + pop(X86Registers::ebp); +#elif WTF_CPU_X86 + pop(X86Registers::esi); + pop(X86Registers::edi); + pop(X86Registers::ebx); + pop(X86Registers::ebp); +#elif WTF_CPU_ARM + pop(ARMRegisters::r6); + pop(ARMRegisters::r5); + pop(ARMRegisters::r4); +#elif WTF_CPU_MIPS + // Do nothing +#endif + ret(); + } + +public: + RegexGenerator(RegexPattern& pattern) + : m_pattern(pattern) + , m_shouldFallBack(false) + { + } + + void generate() + { + generateEnter(); + + if (!m_pattern.m_body->m_hasFixedSize) + store32(index, Address(output)); + + if (m_pattern.m_body->m_callFrameSize) + subPtr(Imm32(m_pattern.m_body->m_callFrameSize * sizeof(void*)), stackPointerRegister); + + generateDisjunction(m_pattern.m_body); + } + + void compile(ExecutableAllocator& allocator, RegexCodeBlock& jitObject) + { + generate(); + + ExecutablePool *executablePool = allocator.poolForSize(size()); + if (!executablePool) { + m_shouldFallBack = true; + return; + } + + LinkBuffer patchBuffer(this, executablePool); + + for (unsigned i = 0; i < m_backtrackRecords.length(); ++i) + patchBuffer.patch(m_backtrackRecords[i].dataLabel, patchBuffer.locationOf(m_backtrackRecords[i].backtrackLocation)); + + jitObject.set(patchBuffer.finalizeCode()); + } + + bool shouldFallBack() + { + return m_shouldFallBack; + } + +private: + RegexPattern& m_pattern; + bool m_shouldFallBack; + js::Vector m_backtrackRecords; +}; + +void jitCompileRegex(ExecutableAllocator& allocator, RegexCodeBlock& jitObject, const UString&patternString, unsigned& numSubpatterns, int &error, bool &fellBack, bool ignoreCase, bool multiline) +{ + fellBack = false; + RegexPattern pattern(ignoreCase, multiline); + if ((error = compileRegex(patternString, pattern))) + return; + numSubpatterns = pattern.m_numSubpatterns; + + if (!pattern.m_containsBackreferences) { + RegexGenerator generator(pattern); + generator.compile(allocator, jitObject); + if (!generator.shouldFallBack()) + return; + } + + fellBack = true; + JSRegExpIgnoreCaseOption ignoreCaseOption = ignoreCase ? JSRegExpIgnoreCase : JSRegExpDoNotIgnoreCase; + JSRegExpMultilineOption multilineOption = multiline ? JSRegExpMultiline : JSRegExpSingleLine; + jitObject.setFallback(jsRegExpCompile(reinterpret_cast(const_cast(patternString).chars()), patternString.length(), ignoreCaseOption, multilineOption, &numSubpatterns, &error)); +} + +}} + +#endif diff --git a/js/src/yarr/yarr/RegexJIT.h b/js/src/yarr/yarr/RegexJIT.h new file mode 100644 index 000000000000..775d92d1d7da --- /dev/null +++ b/js/src/yarr/yarr/RegexJIT.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RegexJIT_h +#define RegexJIT_h + +#include "assembler/assembler/MacroAssembler.h" +#include "assembler/assembler/MacroAssemblerCodeRef.h" +#include "assembler/jit/ExecutableAllocator.h" +#include "RegexPattern.h" +#include "yarr/jswtfbridge.h" + +#include "yarr/pcre/pcre.h" +struct JSRegExp; // temporary, remove when fallback is removed. + +#if WTF_CPU_X86 && !WTF_COMPILER_MSVC +#define YARR_CALL __attribute__ ((regparm (3))) +#else +#define YARR_CALL +#endif + +struct JSContext; + +namespace JSC { + +namespace Yarr { + +class RegexCodeBlock { + typedef int (*RegexJITCode)(const UChar* input, unsigned start, unsigned length, int* output) YARR_CALL; + +public: + RegexCodeBlock() + : m_fallback(0) + { + } + + ~RegexCodeBlock() + { + if (m_fallback) + jsRegExpFree(m_fallback); + if (m_ref.m_size) + m_ref.m_executablePool->release(); + } + + JSRegExp* getFallback() { return m_fallback; } + void setFallback(JSRegExp* fallback) { m_fallback = fallback; } + + bool operator!() { return (!m_ref.m_code.executableAddress() && !m_fallback); } + void set(MacroAssembler::CodeRef ref) { m_ref = ref; } + + int execute(const UChar* input, unsigned start, unsigned length, int* output) + { + return JS_EXTENSION(reinterpret_cast(m_ref.m_code.executableAddress())(input, start, length, output)); + } + +private: + MacroAssembler::CodeRef m_ref; + JSRegExp* m_fallback; +}; + +void jitCompileRegex(ExecutableAllocator &allocator, RegexCodeBlock& jitObject, const UString& pattern, unsigned& numSubpatterns, int& error, bool &fellBack, bool ignoreCase = false, bool multiline = false); + +inline int executeRegex(JSContext *cx, RegexCodeBlock& jitObject, const UChar* input, unsigned start, unsigned length, int* output, int outputArraySize) +{ + if (JSRegExp* fallback = jitObject.getFallback()) + return (jsRegExpExecute(cx, fallback, input, length, start, output, outputArraySize) < 0 ? -1 : output[0]); + + return jitObject.execute(input, start, length, output); +} + +} } // namespace JSC::Yarr + +#endif // RegexJIT_h diff --git a/js/src/yarr/yarr/RegexParser.h b/js/src/yarr/yarr/RegexParser.h new file mode 100644 index 000000000000..7460a65548bd --- /dev/null +++ b/js/src/yarr/yarr/RegexParser.h @@ -0,0 +1,848 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RegexParser_h +#define RegexParser_h + +#include +#include +#include "yarr/jswtfbridge.h" + +namespace JSC { namespace Yarr { + +enum BuiltInCharacterClassID { + DigitClassID, + SpaceClassID, + WordClassID, + NewlineClassID +}; + +// The Parser class should not be used directly - only via the Yarr::parse() method. +template +class Parser { +private: + template + friend int parse(FriendDelegate& delegate, const UString& pattern, unsigned backReferenceLimit); + + enum ErrorCode { + NoError, + PatternTooLarge, + QuantifierOutOfOrder, + QuantifierWithoutAtom, + MissingParentheses, + ParenthesesUnmatched, + ParenthesesTypeInvalid, + CharacterClassUnmatched, + CharacterClassOutOfOrder, + EscapeUnterminated, + QuantifierTooLarge, + NumberOfErrorCodes + }; + + /* + * CharacterClassParserDelegate: + * + * The class CharacterClassParserDelegate is used in the parsing of character + * classes. This class handles detection of character ranges. This class + * implements enough of the delegate interface such that it can be passed to + * parseEscape() as an EscapeDelegate. This allows parseEscape() to be reused + * to perform the parsing of escape characters in character sets. + */ + class CharacterClassParserDelegate { + public: + CharacterClassParserDelegate(Delegate& delegate, ErrorCode& err) + : m_delegate(delegate) + , m_err(err) + , m_state(empty) + { + } + + /* + * begin(): + * + * Called at beginning of construction. + */ + void begin(bool invert) + { + m_delegate.atomCharacterClassBegin(invert); + } + + /* + * atomPatternCharacterUnescaped(): + * + * This method is called directly from parseCharacterClass(), to report a new + * pattern character token. This method differs from atomPatternCharacter(), + * which will be called from parseEscape(), since a hypen provided via this + * method may be indicating a character range, but a hyphen parsed by + * parseEscape() cannot be interpreted as doing so. + */ + void atomPatternCharacterUnescaped(UChar ch) + { + switch (m_state) { + case empty: + m_character = ch; + m_state = cachedCharacter; + break; + + case cachedCharacter: + if (ch == '-') + m_state = cachedCharacterHyphen; + else { + m_delegate.atomCharacterClassAtom(m_character); + m_character = ch; + } + break; + + case cachedCharacterHyphen: + if (ch >= m_character) + m_delegate.atomCharacterClassRange(m_character, ch); + else + m_err = CharacterClassOutOfOrder; + m_state = empty; + } + } + + /* + * atomPatternCharacter(): + * + * Adds a pattern character, called by parseEscape(), as such will not + * interpret a hyphen as indicating a character range. + */ + void atomPatternCharacter(UChar ch) + { + // Flush if a character is already pending to prevent the + // hyphen from begin interpreted as indicating a range. + if((ch == '-') && (m_state == cachedCharacter)) + flush(); + + atomPatternCharacterUnescaped(ch); + } + + /* + * atomBuiltInCharacterClass(): + * + * Adds a built-in character class, called by parseEscape(). + */ + void atomBuiltInCharacterClass(BuiltInCharacterClassID classID, bool invert) + { + flush(); + m_delegate.atomCharacterClassBuiltIn(classID, invert); + } + + /* + * end(): + * + * Called at end of construction. + */ + void end() + { + flush(); + m_delegate.atomCharacterClassEnd(); + } + + // parseEscape() should never call these delegate methods when + // invoked with inCharacterClass set. + void assertionWordBoundary(bool) { JS_NOT_REACHED("parseEscape() should never call this"); } + void atomBackReference(unsigned) { JS_NOT_REACHED("parseEscape() should never call this"); } + + private: + void flush() + { + if (m_state != empty) // either cachedCharacter or cachedCharacterHyphen + m_delegate.atomCharacterClassAtom(m_character); + if (m_state == cachedCharacterHyphen) + m_delegate.atomCharacterClassAtom('-'); + m_state = empty; + } + + Delegate& m_delegate; + ErrorCode& m_err; + enum CharacterClassConstructionState { + empty, + cachedCharacter, + cachedCharacterHyphen + } m_state; + UChar m_character; + }; + + Parser(Delegate& delegate, const UString& pattern, unsigned backReferenceLimit) + : m_delegate(delegate) + , m_backReferenceLimit(backReferenceLimit) + , m_err(NoError) + , m_data(const_cast(pattern).chars()) + , m_size(pattern.length()) + , m_index(0) + , m_parenthesesNestingDepth(0) + { + } + + /* + * parseEscape(): + * + * Helper for parseTokens() AND parseCharacterClass(). + * Unlike the other parser methods, this function does not report tokens + * directly to the member delegate (m_delegate), instead tokens are + * emitted to the delegate provided as an argument. In the case of atom + * escapes, parseTokens() will call parseEscape() passing m_delegate as + * an argument, and as such the escape will be reported to the delegate. + * + * However this method may also be used by parseCharacterClass(), in which + * case a CharacterClassParserDelegate will be passed as the delegate that + * tokens should be added to. A boolean flag is also provided to indicate + * whether that an escape in a CharacterClass is being parsed (some parsing + * rules change in this context). + * + * The boolean value returned by this method indicates whether the token + * parsed was an atom (outside of a characted class \b and \B will be + * interpreted as assertions). + */ + template + bool parseEscape(EscapeDelegate& delegate) + { + JS_ASSERT(!m_err); + JS_ASSERT(peek() == '\\'); + consume(); + + if (atEndOfPattern()) { + m_err = EscapeUnterminated; + return false; + } + + switch (peek()) { + // Assertions + case 'b': + consume(); + if (inCharacterClass) + delegate.atomPatternCharacter('\b'); + else { + delegate.assertionWordBoundary(false); + return false; + } + break; + case 'B': + consume(); + if (inCharacterClass) + delegate.atomPatternCharacter('B'); + else { + delegate.assertionWordBoundary(true); + return false; + } + break; + + // CharacterClassEscape + case 'd': + consume(); + delegate.atomBuiltInCharacterClass(DigitClassID, false); + break; + case 's': + consume(); + delegate.atomBuiltInCharacterClass(SpaceClassID, false); + break; + case 'w': + consume(); + delegate.atomBuiltInCharacterClass(WordClassID, false); + break; + case 'D': + consume(); + delegate.atomBuiltInCharacterClass(DigitClassID, true); + break; + case 'S': + consume(); + delegate.atomBuiltInCharacterClass(SpaceClassID, true); + break; + case 'W': + consume(); + delegate.atomBuiltInCharacterClass(WordClassID, true); + break; + + // DecimalEscape + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + // To match Firefox, we parse an invalid backreference in the range [1-7] as an octal escape. + // First, try to parse this as backreference. + if (!inCharacterClass) { + ParseState state = saveState(); + + unsigned backReference; + if (!consumeNumber(backReference)) + return false; + if (backReference <= m_backReferenceLimit) { + delegate.atomBackReference(backReference); + break; + } + + restoreState(state); + } + + // Not a backreference, and not octal. + if (peek() >= '8') { + delegate.atomPatternCharacter('\\'); + break; + } + + // Fall-through to handle this as an octal escape. + } + + // Octal escape + case '0': + delegate.atomPatternCharacter(consumeOctal()); + break; + + // ControlEscape + case 'f': + consume(); + delegate.atomPatternCharacter('\f'); + break; + case 'n': + consume(); + delegate.atomPatternCharacter('\n'); + break; + case 'r': + consume(); + delegate.atomPatternCharacter('\r'); + break; + case 't': + consume(); + delegate.atomPatternCharacter('\t'); + break; + case 'v': + consume(); + delegate.atomPatternCharacter('\v'); + break; + + // ControlLetter + case 'c': { + ParseState state = saveState(); + consume(); + if (!atEndOfPattern()) { + int control = consume(); + + // To match Firefox, inside a character class, we also accept numbers and '_' as control characters. + if (inCharacterClass ? WTF::isASCIIAlphanumeric(control) || (control == '_') : WTF::isASCIIAlpha(control)) { + delegate.atomPatternCharacter(control & 0x1f); + break; + } + } + restoreState(state); + delegate.atomPatternCharacter('\\'); + break; + } + + // HexEscape + case 'x': { + consume(); + int x = tryConsumeHex(2); + if (x == -1) + delegate.atomPatternCharacter('x'); + else + delegate.atomPatternCharacter(x); + break; + } + + // UnicodeEscape + case 'u': { + consume(); + int u = tryConsumeHex(4); + if (u == -1) + delegate.atomPatternCharacter('u'); + else + delegate.atomPatternCharacter(u); + break; + } + + // IdentityEscape + default: + delegate.atomPatternCharacter(consume()); + } + + return true; + } + + /* + * parseAtomEscape(), parseCharacterClassEscape(): + * + * These methods alias to parseEscape(). + */ + bool parseAtomEscape() + { + return parseEscape(m_delegate); + } + void parseCharacterClassEscape(CharacterClassParserDelegate& delegate) + { + parseEscape(delegate); + } + + /* + * parseCharacterClass(): + * + * Helper for parseTokens(); calls dirctly and indirectly (via parseCharacterClassEscape) + * to an instance of CharacterClassParserDelegate, to describe the character class to the + * delegate. + */ + void parseCharacterClass() + { + JS_ASSERT(!m_err); + JS_ASSERT(peek() == '['); + consume(); + + CharacterClassParserDelegate characterClassConstructor(m_delegate, m_err); + + characterClassConstructor.begin(tryConsume('^')); + + while (!atEndOfPattern()) { + switch (peek()) { + case ']': + consume(); + characterClassConstructor.end(); + return; + + case '\\': + parseCharacterClassEscape(characterClassConstructor); + break; + + default: + characterClassConstructor.atomPatternCharacterUnescaped(consume()); + } + + if (m_err) + return; + } + + m_err = CharacterClassUnmatched; + } + + /* + * parseParenthesesBegin(): + * + * Helper for parseTokens(); checks for parentheses types other than regular capturing subpatterns. + */ + void parseParenthesesBegin() + { + JS_ASSERT(!m_err); + JS_ASSERT(peek() == '('); + consume(); + + if (tryConsume('?')) { + if (atEndOfPattern()) { + m_err = ParenthesesTypeInvalid; + return; + } + + switch (consume()) { + case ':': + m_delegate.atomParenthesesSubpatternBegin(false); + break; + + case '=': + m_delegate.atomParentheticalAssertionBegin(); + break; + + case '!': + m_delegate.atomParentheticalAssertionBegin(true); + break; + + default: + m_err = ParenthesesTypeInvalid; + } + } else + m_delegate.atomParenthesesSubpatternBegin(); + + ++m_parenthesesNestingDepth; + } + + /* + * parseParenthesesEnd(): + * + * Helper for parseTokens(); checks for parse errors (due to unmatched parentheses). + */ + void parseParenthesesEnd() + { + JS_ASSERT(!m_err); + JS_ASSERT(peek() == ')'); + consume(); + + if (m_parenthesesNestingDepth > 0) + m_delegate.atomParenthesesEnd(); + else + m_err = ParenthesesUnmatched; + + --m_parenthesesNestingDepth; + } + + /* + * parseQuantifier(): + * + * Helper for parseTokens(); checks for parse errors and non-greedy quantifiers. + */ + void parseQuantifier(bool lastTokenWasAnAtom, unsigned min, unsigned max) + { + JS_ASSERT(!m_err); + JS_ASSERT(min <= max); + + if (lastTokenWasAnAtom) + m_delegate.quantifyAtom(min, max, !tryConsume('?')); + else + m_err = QuantifierWithoutAtom; + } + + /* + * parseTokens(): + * + * This method loops over the input pattern reporting tokens to the delegate. + * The method returns when a parse error is detected, or the end of the pattern + * is reached. One piece of state is tracked around the loop, which is whether + * the last token passed to the delegate was an atom (this is necessary to detect + * a parse error when a quantifier provided without an atom to quantify). + */ + void parseTokens() + { + bool lastTokenWasAnAtom = false; + + while (!atEndOfPattern()) { + switch (peek()) { + case '|': + consume(); + m_delegate.disjunction(); + lastTokenWasAnAtom = false; + break; + + case '(': + parseParenthesesBegin(); + lastTokenWasAnAtom = false; + break; + + case ')': + parseParenthesesEnd(); + lastTokenWasAnAtom = true; + break; + + case '^': + consume(); + m_delegate.assertionBOL(); + lastTokenWasAnAtom = false; + break; + + case '$': + consume(); + m_delegate.assertionEOL(); + lastTokenWasAnAtom = false; + break; + + case '.': + consume(); + m_delegate.atomBuiltInCharacterClass(NewlineClassID, true); + lastTokenWasAnAtom = true; + break; + + case '[': + parseCharacterClass(); + lastTokenWasAnAtom = true; + break; + + case '\\': + lastTokenWasAnAtom = parseAtomEscape(); + break; + + case '*': + consume(); + parseQuantifier(lastTokenWasAnAtom, 0, UINT_MAX); + lastTokenWasAnAtom = false; + break; + + case '+': + consume(); + parseQuantifier(lastTokenWasAnAtom, 1, UINT_MAX); + lastTokenWasAnAtom = false; + break; + + case '?': + consume(); + parseQuantifier(lastTokenWasAnAtom, 0, 1); + lastTokenWasAnAtom = false; + break; + + case '{': { + ParseState state = saveState(); + + consume(); + if (peekIsDigit()) { + unsigned min; + if (!consumeNumber(min)) + break; + unsigned max = min; + + if (tryConsume(',')) { + if (peekIsDigit()) { + if (!consumeNumber(max)) + break; + } else { + max = UINT_MAX; + } + } + + if (tryConsume('}')) { + if (min <= max) + parseQuantifier(lastTokenWasAnAtom, min, max); + else + m_err = QuantifierOutOfOrder; + lastTokenWasAnAtom = false; + break; + } + } + + restoreState(state); + } // if we did not find a complete quantifer, fall through to the default case. + + default: + m_delegate.atomPatternCharacter(consume()); + lastTokenWasAnAtom = true; + } + + if (m_err) + return; + } + + if (m_parenthesesNestingDepth > 0) + m_err = MissingParentheses; + } + + /* + * parse(): + * + * This method calls regexBegin(), calls parseTokens() to parse over the input + * patterns, calls regexEnd() or regexError() as appropriate, and converts any + * error code to a const char* for a result. + */ + int parse() + { + m_delegate.regexBegin(); + + if (m_size > MAX_PATTERN_SIZE) + m_err = PatternTooLarge; + else + parseTokens(); + JS_ASSERT(atEndOfPattern() || m_err); + + if (m_err) + m_delegate.regexError(); + else + m_delegate.regexEnd(); + + return static_cast(m_err); + } + + + // Misc helper functions: + + typedef unsigned ParseState; + + ParseState saveState() + { + return m_index; + } + + void restoreState(ParseState state) + { + m_index = state; + } + + bool atEndOfPattern() + { + JS_ASSERT(m_index <= m_size); + return m_index == m_size; + } + + int peek() + { + JS_ASSERT(m_index < m_size); + return m_data[m_index]; + } + + bool peekIsDigit() + { + return !atEndOfPattern() && WTF::isASCIIDigit(peek()); + } + + unsigned peekDigit() + { + JS_ASSERT(peekIsDigit()); + return peek() - '0'; + } + + int consume() + { + JS_ASSERT(m_index < m_size); + return m_data[m_index++]; + } + + unsigned consumeDigit() + { + JS_ASSERT(peekIsDigit()); + return consume() - '0'; + } + + bool consumeNumber(unsigned &accum) + { + accum = consumeDigit(); + while (peekIsDigit()) { + unsigned newValue = accum * 10 + peekDigit(); + if (newValue < accum) { /* Overflow check. */ + m_err = QuantifierTooLarge; + return false; + } + accum = newValue; + consume(); + } + return true; + } + + unsigned consumeOctal() + { + JS_ASSERT(WTF::isASCIIOctalDigit(peek())); + + unsigned n = consumeDigit(); + while (n < 32 && !atEndOfPattern() && WTF::isASCIIOctalDigit(peek())) + n = n * 8 + consumeDigit(); + return n; + } + + bool tryConsume(UChar ch) + { + if (atEndOfPattern() || (m_data[m_index] != ch)) + return false; + ++m_index; + return true; + } + + int tryConsumeHex(int count) + { + ParseState state = saveState(); + + int n = 0; + while (count--) { + if (atEndOfPattern() || !WTF::isASCIIHexDigit(peek())) { + restoreState(state); + return -1; + } + n = (n << 4) | WTF::toASCIIHexValue(consume()); + } + return n; + } + + Delegate& m_delegate; + unsigned m_backReferenceLimit; + ErrorCode m_err; + const UChar* m_data; + unsigned m_size; + unsigned m_index; + unsigned m_parenthesesNestingDepth; + + // Derived by empirical testing of compile time in PCRE and WREC. + static const unsigned MAX_PATTERN_SIZE = 1024 * 1024; +}; + +/* + * Yarr::parse(): + * + * The parse method is passed a pattern to be parsed and a delegate upon which + * callbacks will be made to record the parsed tokens forming the regex. + * Yarr::parse() returns null on success, or a const C string providing an error + * message where a parse error occurs. + * + * The Delegate must implement the following interface: + * + * void assertionBOL(); + * void assertionEOL(); + * void assertionWordBoundary(bool invert); + * + * void atomPatternCharacter(UChar ch); + * void atomBuiltInCharacterClass(BuiltInCharacterClassID classID, bool invert); + * void atomCharacterClassBegin(bool invert) + * void atomCharacterClassAtom(UChar ch) + * void atomCharacterClassRange(UChar begin, UChar end) + * void atomCharacterClassBuiltIn(BuiltInCharacterClassID classID, bool invert) + * void atomCharacterClassEnd() + * void atomParenthesesSubpatternBegin(bool capture = true); + * void atomParentheticalAssertionBegin(bool invert = false); + * void atomParenthesesEnd(); + * void atomBackReference(unsigned subpatternId); + * + * void quantifyAtom(unsigned min, unsigned max, bool greedy); + * + * void disjunction(); + * + * void regexBegin(); + * void regexEnd(); + * void regexError(); + * + * Before any call recording tokens are made, regexBegin() will be called on the + * delegate once. Once parsing is complete either regexEnd() or regexError() will + * be called, as appropriate. + * + * The regular expression is described by a sequence of assertion*() and atom*() + * callbacks to the delegate, describing the terms in the regular expression. + * Following an atom a quantifyAtom() call may occur to indicate that the previous + * atom should be quantified. In the case of atoms described across multiple + * calls (parentheses and character classes) the call to quantifyAtom() will come + * after the call to the atom*End() method, never after atom*Begin(). + * + * Character classes may either be described by a single call to + * atomBuiltInCharacterClass(), or by a sequence of atomCharacterClass*() calls. + * In the latter case, ...Begin() will be called, followed by a sequence of + * calls to ...Atom(), ...Range(), and ...BuiltIn(), followed by a call to ...End(). + * + * Sequences of atoms and assertions are broken into alternatives via calls to + * disjunction(). Assertions, atoms, and disjunctions emitted between calls to + * atomParenthesesBegin() and atomParenthesesEnd() form the body of a subpattern. + * atomParenthesesBegin() is passed a subpatternId. In the case of a regular + * capturing subpattern, this will be the subpatternId associated with these + * parentheses, and will also by definition be the lowest subpatternId of these + * parentheses and of any nested paretheses. The atomParenthesesEnd() method + * is passed the subpatternId of the last capturing subexpression nested within + * these paretheses. In the case of a capturing subpattern with no nested + * capturing subpatterns, the same subpatternId will be passed to the begin and + * end functions. In the case of non-capturing subpatterns the subpatternId + * passed to the begin method is also the first possible subpatternId that might + * be nested within these paretheses. If a set of non-capturing parentheses does + * not contain any capturing subpatterns, then the subpatternId passed to begin + * will be greater than the subpatternId passed to end. + */ + +template +int parse(Delegate& delegate, const UString& pattern, unsigned backReferenceLimit = UINT_MAX) +{ + return Parser(delegate, pattern, backReferenceLimit).parse(); +} + +} } // namespace JSC::Yarr + +#endif // RegexParser_h diff --git a/js/src/yarr/yarr/RegexPattern.h b/js/src/yarr/yarr/RegexPattern.h new file mode 100644 index 000000000000..ec9bfe7cb5a6 --- /dev/null +++ b/js/src/yarr/yarr/RegexPattern.h @@ -0,0 +1,399 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RegexPattern_h +#define RegexPattern_h + + +#include "jsvector.h" +#include "yarr/jswtfbridge.h" + + +namespace JSC { namespace Yarr { + +#define RegexStackSpaceForBackTrackInfoPatternCharacter 1 // Only for !fixed quantifiers. +#define RegexStackSpaceForBackTrackInfoCharacterClass 1 // Only for !fixed quantifiers. +#define RegexStackSpaceForBackTrackInfoBackReference 2 +#define RegexStackSpaceForBackTrackInfoAlternative 1 // One per alternative. +#define RegexStackSpaceForBackTrackInfoParentheticalAssertion 1 +#define RegexStackSpaceForBackTrackInfoParenthesesOnce 1 // Only for !fixed quantifiers. +#define RegexStackSpaceForBackTrackInfoParentheses 4 + +struct PatternDisjunction; + +struct CharacterRange { + UChar begin; + UChar end; + + CharacterRange(UChar begin, UChar end) + : begin(begin) + , end(end) + { + } +}; + +/* + * Wraps a table and indicates inversion. Can be efficiently borrowed + * between character classes, so it's refcounted. + */ +struct CharacterClassTable { + const char* m_table; + bool m_inverted; + jsrefcount m_refcount; + + /* Ownership transferred to caller. */ + static CharacterClassTable *create(const char* table, bool inverted) + { + return new CharacterClassTable(table, inverted); + } + + void incref() { JS_ATOMIC_INCREMENT(&m_refcount); } + void decref() { if (JS_ATOMIC_DECREMENT(&m_refcount) == 0) delete this; } + +private: + CharacterClassTable(const char* table, bool inverted) + : m_table(table) + , m_inverted(inverted) + , m_refcount(0) + { + } +}; + +struct CharacterClass { + // All CharacterClass instances have to have the full set of matches and ranges, + // they may have an optional table for faster lookups (which must match the + // specified matches and ranges) + CharacterClass(CharacterClassTable *table) + : m_table(table) + { + if (m_table) + m_table->incref(); + } + ~CharacterClass() + { + if (m_table) + m_table->decref(); + } + typedef js::Vector UChars; + typedef js::Vector CharacterRanges; + UChars m_matches; + CharacterRanges m_ranges; + UChars m_matchesUnicode; + CharacterRanges m_rangesUnicode; + CharacterClassTable *m_table; +}; + +enum QuantifierType { + QuantifierFixedCount, + QuantifierGreedy, + QuantifierNonGreedy +}; + +struct PatternTerm { + enum Type { + TypeAssertionBOL, + TypeAssertionEOL, + TypeAssertionWordBoundary, + TypePatternCharacter, + TypeCharacterClass, + TypeBackReference, + TypeForwardReference, + TypeParenthesesSubpattern, + TypeParentheticalAssertion + } type; + bool invertOrCapture; + union { + UChar patternCharacter; + CharacterClass* characterClass; + unsigned subpatternId; + struct { + PatternDisjunction* disjunction; + unsigned subpatternId; + unsigned lastSubpatternId; + bool isCopy; + } parentheses; + }; + QuantifierType quantityType; + unsigned quantityCount; + int inputPosition; + unsigned frameLocation; + + PatternTerm(UChar ch) + : type(PatternTerm::TypePatternCharacter) + { + patternCharacter = ch; + quantityType = QuantifierFixedCount; + quantityCount = 1; + } + + PatternTerm(CharacterClass* charClass, bool invert) + : type(PatternTerm::TypeCharacterClass) + , invertOrCapture(invert) + { + characterClass = charClass; + quantityType = QuantifierFixedCount; + quantityCount = 1; + } + + PatternTerm(Type type, unsigned subpatternId, PatternDisjunction* disjunction, bool invertOrCapture) + : type(type) + , invertOrCapture(invertOrCapture) + { + parentheses.disjunction = disjunction; + parentheses.subpatternId = subpatternId; + parentheses.isCopy = false; + quantityType = QuantifierFixedCount; + quantityCount = 1; + } + + PatternTerm(Type type, bool invert = false) + : type(type) + , invertOrCapture(invert) + { + quantityType = QuantifierFixedCount; + quantityCount = 1; + } + + PatternTerm(unsigned spatternId) + : type(TypeBackReference) + , invertOrCapture(false) + { + subpatternId = spatternId; + quantityType = QuantifierFixedCount; + quantityCount = 1; + } + + static PatternTerm ForwardReference() + { + return PatternTerm(TypeForwardReference); + } + + static PatternTerm BOL() + { + return PatternTerm(TypeAssertionBOL); + } + + static PatternTerm EOL() + { + return PatternTerm(TypeAssertionEOL); + } + + static PatternTerm WordBoundary(bool invert) + { + return PatternTerm(TypeAssertionWordBoundary, invert); + } + + bool invert() + { + return invertOrCapture; + } + + bool capture() + { + return invertOrCapture; + } + + void quantify(unsigned count, QuantifierType type) + { + quantityCount = count; + quantityType = type; + } +}; + +struct PatternAlternative { + PatternAlternative(PatternDisjunction* disjunction) + : m_parent(disjunction) + { + } + + PatternTerm& lastTerm() + { + JS_ASSERT(m_terms.length()); + return m_terms[m_terms.length() - 1]; + } + + void removeLastTerm() + { + JS_ASSERT(m_terms.length()); + m_terms.popBack(); + } + + js::Vector m_terms; + PatternDisjunction* m_parent; + unsigned m_minimumSize; + bool m_hasFixedSize; +}; + +struct PatternDisjunction { + PatternDisjunction(PatternAlternative* parent = 0) + : m_parent(parent) + { + } + + ~PatternDisjunction() + { + deleteAllValues(m_alternatives); + } + + PatternAlternative* addNewAlternative() + { + PatternAlternative* alternative = new PatternAlternative(this); + m_alternatives.append(alternative); + return alternative; + } + + js::Vector m_alternatives; + PatternAlternative* m_parent; + unsigned m_minimumSize; + unsigned m_callFrameSize; + bool m_hasFixedSize; +}; + +// You probably don't want to be calling these functions directly +// (please to be calling newlineCharacterClass() et al on your +// friendly neighborhood RegexPattern instance to get nicely +// cached copies). +CharacterClass* newlineCreate(); +CharacterClass* digitsCreate(); +CharacterClass* spacesCreate(); +CharacterClass* wordcharCreate(); +CharacterClass* nondigitsCreate(); +CharacterClass* nonspacesCreate(); +CharacterClass* nonwordcharCreate(); + +struct RegexPattern { + RegexPattern(bool ignoreCase, bool multiline) + : m_ignoreCase(ignoreCase) + , m_multiline(multiline) + , m_numSubpatterns(0) + , m_maxBackReference(0) + , m_containsBackreferences(false) + , newlineCached(0) + , digitsCached(0) + , spacesCached(0) + , wordcharCached(0) + , nondigitsCached(0) + , nonspacesCached(0) + , nonwordcharCached(0) + { + } + + ~RegexPattern() + { + deleteAllValues(m_disjunctions); + deleteAllValues(m_userCharacterClasses); + } + + void reset() + { + m_numSubpatterns = 0; + m_maxBackReference = 0; + + m_containsBackreferences = false; + + newlineCached = 0; + digitsCached = 0; + spacesCached = 0; + wordcharCached = 0; + nondigitsCached = 0; + nonspacesCached = 0; + nonwordcharCached = 0; + + deleteAllValues(m_disjunctions); + m_disjunctions.clear(); + deleteAllValues(m_userCharacterClasses); + m_userCharacterClasses.clear(); + } + + bool containsIllegalBackReference() + { + return m_maxBackReference > m_numSubpatterns; + } + + CharacterClass* newlineCharacterClass() + { + if (!newlineCached) + m_userCharacterClasses.append(newlineCached = newlineCreate()); + return newlineCached; + } + CharacterClass* digitsCharacterClass() + { + if (!digitsCached) + m_userCharacterClasses.append(digitsCached = digitsCreate()); + return digitsCached; + } + CharacterClass* spacesCharacterClass() + { + if (!spacesCached) + m_userCharacterClasses.append(spacesCached = spacesCreate()); + return spacesCached; + } + CharacterClass* wordcharCharacterClass() + { + if (!wordcharCached) + m_userCharacterClasses.append(wordcharCached = wordcharCreate()); + return wordcharCached; + } + CharacterClass* nondigitsCharacterClass() + { + if (!nondigitsCached) + m_userCharacterClasses.append(nondigitsCached = nondigitsCreate()); + return nondigitsCached; + } + CharacterClass* nonspacesCharacterClass() + { + if (!nonspacesCached) + m_userCharacterClasses.append(nonspacesCached = nonspacesCreate()); + return nonspacesCached; + } + CharacterClass* nonwordcharCharacterClass() + { + if (!nonwordcharCached) + m_userCharacterClasses.append(nonwordcharCached = nonwordcharCreate()); + return nonwordcharCached; + } + + bool m_ignoreCase; + bool m_multiline; + unsigned m_numSubpatterns; + unsigned m_maxBackReference; + bool m_containsBackreferences; + PatternDisjunction *m_body; + js::Vector m_disjunctions; + js::Vector m_userCharacterClasses; + +private: + CharacterClass* newlineCached; + CharacterClass* digitsCached; + CharacterClass* spacesCached; + CharacterClass* wordcharCached; + CharacterClass* nondigitsCached; + CharacterClass* nonspacesCached; + CharacterClass* nonwordcharCached; +}; + +} } // namespace JSC::Yarr + +#endif // RegexPattern_h diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp index e7aa916fd075..0a951ba93801 100644 --- a/layout/base/nsDocumentViewer.cpp +++ b/layout/base/nsDocumentViewer.cpp @@ -860,7 +860,9 @@ DocumentViewerImpl::InitInternal(nsIWidget* aParentWidget, // it in one place (Show()) and require that callers call init(), open(), // show() in that order or something. if (!mPresContext && - (aParentWidget || containerView || mDocument->GetDisplayDocument())) { + (aParentWidget || containerView || + (mDocument->GetDisplayDocument() && + mDocument->GetDisplayDocument()->GetShell()))) { // Create presentation context if (mIsPageMode) { //Presentation context already created in SetPageMode which is calling this method diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 3fb5c91a2152..cb8ab689fe29 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -109,6 +109,7 @@ using namespace mozilla::layers; using namespace mozilla::dom; +namespace css = mozilla::css; /** * A namespace class for static layout utilities. @@ -1752,6 +1753,15 @@ static nscoord AddPercents(nsLayoutUtils::IntrinsicWidthType aType, static PRBool GetAbsoluteCoord(const nsStyleCoord& aStyle, nscoord& aResult) { + if (aStyle.IsCalcUnit()) { + if (aStyle.CalcHasPercent()) { + return PR_FALSE; + } + // If it has no percents, we can pass 0 for the percentage basis. + aResult = nsRuleNode::ComputeComputedCalc(aStyle, 0); + return PR_TRUE; + } + if (eStyleUnit_Coord != aStyle.GetUnit()) return PR_FALSE; @@ -2036,12 +2046,21 @@ nsLayoutUtils::IntrinsicForContainer(nsIRenderingContext *aRenderingContext, PROP_WIDTH, w)) { result = AddPercents(aType, w + coordOutsideWidth, pctOutsideWidth); } - else if (aType == MIN_WIDTH && eStyleUnit_Percent == styleWidth.GetUnit() && + else if (aType == MIN_WIDTH && + // The only cases of coord-percent-calc() units that + // GetAbsoluteCoord didn't handle are percent and calc()s + // containing percent. + styleWidth.IsCoordPercentCalcUnit() && aFrame->IsFrameOfType(nsIFrame::eReplaced)) { // A percentage width on replaced elements means they can shrink to 0. result = 0; // let |min| handle padding/border/margin } else { + // NOTE: We could really do a lot better for percents and for some + // cases of calc() containing percent (certainly including any where + // the coefficient on the percent is positive and there are no max() + // expressions). However, doing better for percents wouldn't be + // backwards compatible. result = AddPercents(aType, result, pctTotal); } @@ -2138,15 +2157,12 @@ nsLayoutUtils::ComputeWidthValue( "width less than zero"); nscoord result; - if (eStyleUnit_Coord == aCoord.GetUnit()) { - result = aCoord.GetCoordValue(); - NS_ASSERTION(result >= 0, "width less than zero"); + if (aCoord.IsCoordPercentCalcUnit()) { + result = nsRuleNode::ComputeCoordPercentCalc(aCoord, aContainingBlockWidth); + // The result of a calc() expression might be less than 0; we + // should clamp at runtime (below). (Percentages and coords that + // are less than 0 have already been dropped by the parser.) result -= aContentEdgeToBoxSizing; - } else if (eStyleUnit_Percent == aCoord.GetUnit()) { - NS_ASSERTION(aCoord.GetPercentValue() >= 0.0f, "width less than zero"); - result = NSToCoordFloorClamped(aContainingBlockWidth * - aCoord.GetPercentValue()) - - aContentEdgeToBoxSizing; } else if (eStyleUnit_Enumerated == aCoord.GetUnit()) { PRInt32 val = aCoord.GetIntValue(); switch (val) { diff --git a/layout/generic/nsAbsoluteContainingBlock.cpp b/layout/generic/nsAbsoluteContainingBlock.cpp index f75ad0603769..db8391185085 100644 --- a/layout/generic/nsAbsoluteContainingBlock.cpp +++ b/layout/generic/nsAbsoluteContainingBlock.cpp @@ -226,29 +226,9 @@ static inline PRBool IsFixedPaddingSize(nsStyleUnit aUnit) { static inline PRBool IsFixedMarginSize(nsStyleUnit aUnit) { return aUnit == eStyleUnit_Coord; } -static inline PRBool IsFixedMaxSize(nsStyleUnit aUnit) { - return aUnit == eStyleUnit_None || aUnit == eStyleUnit_Coord; -} static inline PRBool IsFixedOffset(nsStyleUnit aUnit) { return aUnit == eStyleUnit_Coord; } -static inline PRBool IsFixedHeight(nsStyleUnit aUnit) { - return aUnit == eStyleUnit_Coord; -} - -static inline PRBool IsFixedWidth(const nsStyleCoord& aCoord) -{ - return aCoord.GetUnit() == eStyleUnit_Coord || - (aCoord.GetUnit() == eStyleUnit_Enumerated && - (aCoord.GetIntValue() == NS_STYLE_WIDTH_MAX_CONTENT || - aCoord.GetIntValue() == NS_STYLE_WIDTH_MIN_CONTENT)); -} - -static inline PRBool IsFixedMaxWidth(const nsStyleCoord& aCoord) -{ - return aCoord.GetUnit() == eStyleUnit_None || - IsFixedWidth(aCoord); -} PRBool nsAbsoluteContainingBlock::FrameDependsOnContainer(nsIFrame* f, @@ -286,9 +266,9 @@ nsAbsoluteContainingBlock::FrameDependsOnContainer(nsIFrame* f, // then our frame width does not depend on the parent width. // Note that borders never depend on the parent width // XXX All of the enumerated values except -moz-available are ok too. - if (!IsFixedWidth(pos->mWidth) || - !IsFixedWidth(pos->mMinWidth) || - !IsFixedMaxWidth(pos->mMaxWidth) || + if (pos->WidthDependsOnContainer() || + pos->MinWidthDependsOnContainer() || + pos->MaxWidthDependsOnContainer() || !IsFixedPaddingSize(padding->mPadding.GetLeftUnit()) || !IsFixedPaddingSize(padding->mPadding.GetRightUnit())) { return PR_TRUE; @@ -325,12 +305,12 @@ nsAbsoluteContainingBlock::FrameDependsOnContainer(nsIFrame* f, // and height is a length or height and bottom are auto and top is not auto, // then our frame height does not depend on the parent height. // Note that borders never depend on the parent height - if (!(IsFixedHeight(pos->mHeight.GetUnit()) || - (pos->mHeight.GetUnit() == eStyleUnit_Auto && + if ((pos->HeightDependsOnContainer() && + !(pos->mHeight.GetUnit() == eStyleUnit_Auto && pos->mOffset.GetBottomUnit() == eStyleUnit_Auto && pos->mOffset.GetTopUnit() != eStyleUnit_Auto)) || - !IsFixedHeight(pos->mMinHeight.GetUnit()) || - !IsFixedMaxSize(pos->mMaxHeight.GetUnit()) || + pos->MinHeightDependsOnContainer() || + pos->MaxHeightDependsOnContainer() || !IsFixedPaddingSize(padding->mPadding.GetTopUnit()) || !IsFixedPaddingSize(padding->mPadding.GetBottomUnit())) { return PR_TRUE; diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp index 23acbfcc3cd1..9f12e30f4cb7 100644 --- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -3486,8 +3486,7 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, this, aFloatAvailableSpace.mHasFloats); #endif - const nsMargin& borderPadding = aState.BorderPadding(); - nscoord x = aFloatAvailableSpace.mRect.x + borderPadding.left; + nscoord x = aFloatAvailableSpace.mRect.x; nscoord availWidth = aFloatAvailableSpace.mRect.width; nscoord availHeight; if (aState.GetFlag(BRS_UNCONSTRAINEDHEIGHT)) { @@ -5557,11 +5556,9 @@ nsBlockFrame::AdjustFloatAvailableSpace(nsBlockReflowState& aState, availWidth = aFloatAvailableSpace.width; } - // aState.mY is relative to the border-top, make it relative to the content-top - nscoord contentYOffset = aState.mY - aState.BorderPadding().top; nscoord availHeight = NS_UNCONSTRAINEDSIZE == aState.mContentArea.height ? NS_UNCONSTRAINEDSIZE - : NS_MAX(0, aState.mContentArea.height - contentYOffset); + : NS_MAX(0, aState.mContentArea.YMost() - aState.mY); #ifdef DISABLE_FLOAT_BREAKING_IN_COLUMNS if (availHeight != NS_UNCONSTRAINEDSIZE && @@ -5574,8 +5571,8 @@ nsBlockFrame::AdjustFloatAvailableSpace(nsBlockReflowState& aState, } #endif - return nsRect(aState.BorderPadding().left, - aState.BorderPadding().top, + return nsRect(aState.mContentArea.x, + aState.mContentArea.y, availWidth, availHeight); } @@ -6610,7 +6607,7 @@ nsBlockFrame::ReflowBullet(nsBlockReflowState& aState, // Reflow the bullet now nsSize availSize; // Make up a width since it doesn't really matter (XXX). - availSize.width = rs.ComputedWidth(); + availSize.width = aState.mContentArea.width; availSize.height = NS_UNCONSTRAINEDSIZE; // Get the reason right. @@ -6644,26 +6641,22 @@ nsBlockFrame::ReflowBullet(nsBlockReflowState& aState, // the block's border, and the bullet frame's margin. nscoord x; if (rs.mStyleVisibility->mDirection == NS_STYLE_DIRECTION_LTR) { - // Note: floatAvailSpace.x is relative to the content box and never - // less than zero. Converting to frame coordinates and subtracting - // the padding and border cancel each other out, and the NS_MAX() - // with 0 (or with the left border+padding) is even implied in the - // right place. - x = floatAvailSpace.x - reflowState.mComputedMargin.right - aMetrics.width; + // The floatAvailSpace.x gives us the content/float edge. Then we + // subtract out the left border/padding and the bullet's width and + // margin to offset the position. + x = floatAvailSpace.x - rs.mComputedBorderPadding.left + - reflowState.mComputedMargin.right - aMetrics.width; } else { - // The XMost() of the available space and the computed width both - // give us offsets from the left content edge. Then we add the left - // border/padding to get into frame coordinates, and the right - // border/padding and the bullet's margin to offset the position. - x = NS_MIN(rs.ComputedWidth(), floatAvailSpace.XMost()) - + rs.mComputedBorderPadding.LeftRight() + // The XMost() of the available space give us offsets from the left + // border edge. Then we add the right border/padding and the + // bullet's margin to offset the position. + x = floatAvailSpace.XMost() + rs.mComputedBorderPadding.right + reflowState.mComputedMargin.left; } // Approximate the bullets position; vertical alignment will provide // the final vertical location. - const nsMargin& bp = aState.BorderPadding(); - nscoord y = bp.top; + nscoord y = aState.mContentArea.y; mBullet->SetRect(nsRect(x, y, aMetrics.width, aMetrics.height)); mBullet->DidReflow(aState.mPresContext, &aState.mReflowState, NS_FRAME_REFLOW_FINISHED); } diff --git a/layout/generic/nsBlockReflowContext.cpp b/layout/generic/nsBlockReflowContext.cpp index f9d4637eb09d..0340dbf74ecf 100644 --- a/layout/generic/nsBlockReflowContext.cpp +++ b/layout/generic/nsBlockReflowContext.cpp @@ -278,24 +278,11 @@ nsBlockReflowContext::ReflowBlock(const nsRect& aSpace, // from 10.3.3 to determine what to apply. At this point in the // reflow auto left/right margins will have a zero value. - nscoord x = mSpace.x + aFrameRS.mComputedMargin.left; - nscoord y = mSpace.y + mTopMargin.get() + aClearance; + mX = tx = mSpace.x + aFrameRS.mComputedMargin.left; + mY = ty = mSpace.y + mTopMargin.get() + aClearance; if ((mFrame->GetStateBits() & NS_BLOCK_FLOAT_MGR) == 0) - aFrameRS.mBlockDelta = mOuterReflowState.mBlockDelta + y - aLine->mBounds.y; - - mX = x; - mY = y; - - // Compute the translation to be used for adjusting the spacemanagager - // coordinate system for the frame. The spacemanager coordinates are - // inside the callers border+padding, but the x/y coordinates - // are not (recall that frame coordinates are relative to the parents - // origin and that the parents border/padding is inside the - // parent frame. Therefore we have to subtract out the parents - // border+padding before translating. - tx = x - mOuterReflowState.mComputedBorderPadding.left; - ty = y - mOuterReflowState.mComputedBorderPadding.top; + aFrameRS.mBlockDelta = mOuterReflowState.mBlockDelta + ty - aLine->mBounds.y; } // Let frame know that we are reflowing it diff --git a/layout/generic/nsBlockReflowState.cpp b/layout/generic/nsBlockReflowState.cpp index 12d667c191b7..1071fc75f362 100644 --- a/layout/generic/nsBlockReflowState.cpp +++ b/layout/generic/nsBlockReflowState.cpp @@ -102,9 +102,7 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, NS_ASSERTION(mFloatManager, "FloatManager should be set in nsBlockReflowState" ); if (mFloatManager) { - // Translate into our content area and then save the - // coordinate system origin for later. - mFloatManager->Translate(borderPadding.left, borderPadding.top); + // Save the coordinate system origin for later. mFloatManager->GetTranslation(mFloatManagerX, mFloatManagerY); mFloatManager->PushState(&mFloatManagerStateBefore); // never popped } @@ -139,8 +137,8 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, SetFlag(BRS_UNCONSTRAINEDHEIGHT, PR_TRUE); mContentArea.height = mBottomEdge = NS_UNCONSTRAINEDSIZE; } - - mY = borderPadding.top; + mContentArea.x = borderPadding.left; + mY = mContentArea.y = borderPadding.top; mPrevChild = nsnull; mCurrentLine = aFrame->end_lines(); @@ -148,16 +146,6 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, mMinLineHeight = aReflowState.CalcLineHeight(); } -nsBlockReflowState::~nsBlockReflowState() -{ - // Restore the coordinate system, unless the float manager is null, - // which means it was just destroyed. - if (mFloatManager) { - const nsMargin& borderPadding = BorderPadding(); - mFloatManager->Translate(-borderPadding.left, -borderPadding.top); - } -} - nsLineBox* nsBlockReflowState::NewLineBox(nsIFrame* aFrame, PRInt32 aCount, @@ -187,9 +175,9 @@ nsBlockReflowState::ComputeReplacedBlockOffsetsForFloats(nsIFrame* aFrame, // only give it free space. An example is a table frame - the // tables do not flow around floats. // However, we can let its margins intersect floats. - NS_ASSERTION(aFloatAvailableSpace.x >= 0, "bad avail space rect x"); + NS_ASSERTION(aFloatAvailableSpace.x >= mContentArea.x, "bad avail space rect x"); NS_ASSERTION(aFloatAvailableSpace.width == 0 || - aFloatAvailableSpace.XMost() <= mContentArea.width, + aFloatAvailableSpace.XMost() <= mContentArea.XMost(), "bad avail space rect width"); nscoord leftOffset, rightOffset; @@ -210,13 +198,13 @@ nsBlockReflowState::ComputeReplacedBlockOffsetsForFloats(nsIFrame* aFrame, aReplacedWidth->marginRight == os.mComputedMargin.right), "unexpected aReplacedWidth"); - nscoord leftFloatXOffset = aFloatAvailableSpace.x; + nscoord leftFloatXOffset = aFloatAvailableSpace.x - mContentArea.x; leftOffset = NS_MAX(leftFloatXOffset, os.mComputedMargin.left) - (aReplacedWidth ? aReplacedWidth->marginLeft : os.mComputedMargin.left); leftOffset = NS_MAX(leftOffset, 0); // in case of negative margin nscoord rightFloatXOffset = - mContentArea.width - aFloatAvailableSpace.XMost(); + mContentArea.XMost() - aFloatAvailableSpace.XMost(); rightOffset = NS_MAX(rightFloatXOffset, os.mComputedMargin.right) - (aReplacedWidth ? aReplacedWidth->marginRight : os.mComputedMargin.right); @@ -248,8 +236,6 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame, // it off the page/column. Negative available height can confuse other code // and is nonsense in principle. - const nsMargin& borderPadding = BorderPadding(); - // XXX Do we really want this condition to be this restrictive (i.e., // more restrictive than it used to be)? The |else| here is allowed // by the CSS spec, but only out of desperation given implementations, @@ -275,14 +261,14 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame, case NS_STYLE_FLOAT_EDGE_CONTENT: // content and only content does runaround of floats // The child block will flow around the float. Therefore // give it all of the available space. - aResult.x = borderPadding.left; + aResult.x = mContentArea.x; aResult.width = mContentArea.width; break; case NS_STYLE_FLOAT_EDGE_MARGIN: { // The child block's margins should be placed adjacent to, // but not overlap the float. - aResult.x = aFloatAvailableSpace.mRect.x + borderPadding.left; + aResult.x = aFloatAvailableSpace.mRect.x; aResult.width = aFloatAvailableSpace.mRect.width; } break; @@ -292,7 +278,7 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame, // Since there are no floats present the float-edge property // doesn't matter therefore give the block element all of the // available space since it will flow around the float itself. - aResult.x = borderPadding.left; + aResult.x = mContentArea.x; aResult.width = mContentArea.width; } } @@ -310,7 +296,7 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame, ComputeReplacedBlockOffsetsForFloats(aFrame, aFloatAvailableSpace.mRect, leftOffset, rightOffset, replacedWidth); - aResult.x = borderPadding.left + leftOffset; + aResult.x = mContentArea.x + leftOffset; aResult.width = mContentArea.width - leftOffset - rightOffset; } @@ -332,12 +318,11 @@ nsBlockReflowState::GetFloatAvailableSpaceWithState( "bad coord system"); #endif - nscoord y = aY - BorderPadding().top; nscoord height = (mContentArea.height == nscoord_MAX) - ? nscoord_MAX : NS_MAX(mContentArea.height - y, 0); + ? nscoord_MAX : NS_MAX(mContentArea.YMost() - aY, 0); nsFlowAreaRect result = - mFloatManager->GetFlowArea(y, nsFloatManager::BAND_FROM_POINT, - height, mContentArea.width, aState); + mFloatManager->GetFlowArea(aY, nsFloatManager::BAND_FROM_POINT, + height, mContentArea, aState); // Keep the width >= 0 for compatibility with nsSpaceManager. if (result.mRect.width < 0) result.mRect.width = 0; @@ -367,9 +352,8 @@ nsBlockReflowState::GetFloatAvailableSpaceForHeight( #endif nsFlowAreaRect result = - mFloatManager->GetFlowArea(aY - BorderPadding().top, - nsFloatManager::WIDTH_WITHIN_HEIGHT, - aHeight, mContentArea.width, aState); + mFloatManager->GetFlowArea(aY, nsFloatManager::WIDTH_WITHIN_HEIGHT, + aHeight, mContentArea, aState); // Keep the width >= 0 for compatibility with nsSpaceManager. if (result.mRect.width < 0) result.mRect.width = 0; @@ -510,12 +494,6 @@ nsBlockReflowState::RecoverStateFrom(nsLineList::iterator aLine, // Place floats for this line into the float manager if (aLine->HasFloats() || aLine->IsBlock()) { - // Undo border/padding translation since the nsFloatCache's - // coordinates are relative to the frame not relative to the - // border/padding. - const nsMargin& bp = BorderPadding(); - mFloatManager->Translate(-bp.left, -bp.top); - RecoverFloats(aLine, aDeltaY); #ifdef DEBUG @@ -523,8 +501,6 @@ nsBlockReflowState::RecoverStateFrom(nsLineList::iterator aLine, mFloatManager->List(stdout); } #endif - // And then put the translation back again - mFloatManager->Translate(bp.left, bp.top); } } @@ -600,8 +576,7 @@ nsBlockReflowState::AddFloat(nsLineLayout* aLineLayout, if (placed) { // Pass on updated available space to the current inline reflow engine nsFlowAreaRect floatAvailSpace = GetFloatAvailableSpace(mY); - nsRect availSpace(nsPoint(floatAvailSpace.mRect.x + BorderPadding().left, - mY), + nsRect availSpace(nsPoint(floatAvailSpace.mRect.x, mY), floatAvailSpace.mRect.Size()); aLineLayout->UpdateBand(availSpace, aFloat); // Record this float in the current-line list @@ -681,7 +656,7 @@ nsBlockReflowState::FlowAndPlaceFloat(nsIFrame* aFloat) // Enforce CSS2 9.5.1 rule [2], i.e., make sure that a float isn't // ``above'' another float that preceded it in the flow. - mY = NS_MAX(mFloatManager->GetLowestFloatTop() + BorderPadding().top, mY); + mY = NS_MAX(mFloatManager->GetLowestFloatTop(), mY); // See if the float should clear any preceding floats... // XXX We need to mark this float somehow so that it gets reflowed @@ -791,10 +766,7 @@ nsBlockReflowState::FlowAndPlaceFloat(nsIFrame* aFloat) // We don't worry about the geometry of the prev in flow, let the continuation // place and size itself as required. - // Assign an x and y coordinate to the float. Note that the x,y - // coordinates are computed relative to the translation in the - // spacemanager which means that the impacted region will be - // inside the border/padding area. + // Assign an x and y coordinate to the float. nscoord floatX, floatY; if (NS_STYLE_FLOAT_LEFT == floatDisplay->mFloats) { floatX = floatAvailableSpace.mRect.x; @@ -810,16 +782,12 @@ nsBlockReflowState::FlowAndPlaceFloat(nsIFrame* aFloat) floatX = floatAvailableSpace.mRect.x; } } - const nsMargin& borderPadding = BorderPadding(); - floatY = mY - borderPadding.top; - if (floatY < 0) { - // CSS2 spec, 9.5.1 rule [4]: "A floating box's outer top may not - // be higher than the top of its containing block." (Since the - // containing block is the content edge of the block box, this - // means the margin edge of the float can't be higher than the - // content edge of the block that contains it.) - floatY = 0; - } + // CSS2 spec, 9.5.1 rule [4]: "A floating box's outer top may not + // be higher than the top of its containing block." (Since the + // containing block is the content edge of the block box, this + // means the margin edge of the float can't be higher than the + // content edge of the block that contains it.) + floatY = NS_MAX(mY, mContentArea.y); // Reflow the float after computing its vertical position so it knows // where to break. @@ -845,7 +813,7 @@ nsBlockReflowState::FlowAndPlaceFloat(nsIFrame* aFloat) (!mReflowState.mFlags.mIsTopOfPage || !IsAdjacentWithTop() || pushedDown) && aFloat->GetSize().height + floatMargin.TopBottom() > - mContentArea.height - floatY) || + mContentArea.YMost() - floatY) || NS_FRAME_IS_TRUNCATED(reflowStatus)) { PushFloatPastBreak(aFloat); @@ -853,11 +821,10 @@ nsBlockReflowState::FlowAndPlaceFloat(nsIFrame* aFloat) } // Calculate the actual origin of the float frame's border rect - // relative to the parent block; floatX/Y must be converted from space-manager - // coordinates to parent coordinates, and the margin must be added in + // relative to the parent block; the margin must be added in // to get the border rect - nsPoint origin(borderPadding.left + floatMargin.left + floatX, - borderPadding.top + floatMargin.top + floatY); + nsPoint origin(floatMargin.left + floatX, + floatMargin.top + floatY); // If float is relatively positioned, factor that in as well origin += aFloat->GetRelativeOffset(floatDisplay); @@ -884,9 +851,7 @@ nsBlockReflowState::FlowAndPlaceFloat(nsIFrame* aFloat) region.height = NS_MAX(region.height, mContentArea.height - floatY); } nsresult rv = - // spacemanager translation is inset by the border+padding. - mFloatManager->AddFloat(aFloat, - region - nsPoint(borderPadding.left, borderPadding.top)); + mFloatManager->AddFloat(aFloat, region); NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "bad float placement"); // store region rv = nsFloatManager::StoreRegionFor(aFloat, region); @@ -899,8 +864,8 @@ nsBlockReflowState::FlowAndPlaceFloat(nsIFrame* aFloat) // less damage; e.g., if only height has changed, then only note the // area into which the float has grown or from which the float has // shrunk. - nscoord top = NS_MIN(region.y, oldRegion.y) - borderPadding.top; - nscoord bottom = NS_MAX(region.YMost(), oldRegion.YMost()) - borderPadding.top; + nscoord top = NS_MIN(region.y, oldRegion.y); + nscoord bottom = NS_MAX(region.YMost(), oldRegion.YMost()); mFloatManager->IncludeInDamage(top, bottom); } @@ -993,8 +958,7 @@ nsBlockReflowState::ClearFloats(nscoord aY, PRUint8 aBreakType, #ifdef DEBUG if (nsBlockFrame::gNoisyReflow) { nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); - printf("clear floats: in: aY=%d(%d)\n", - aY, aY - BorderPadding().top); + printf("clear floats: in: aY=%d\n", aY); } #endif @@ -1004,12 +968,10 @@ nsBlockReflowState::ClearFloats(nscoord aY, PRUint8 aBreakType, mFloatManager->List(stdout); #endif - const nsMargin& bp = BorderPadding(); nscoord newY = aY; if (aBreakType != NS_STYLE_CLEAR_NONE) { - newY = bp.top + - mFloatManager->ClearFloats(newY - bp.top, aBreakType, aFlags); + newY = mFloatManager->ClearFloats(newY, aBreakType, aFlags); } if (aReplacedBlock) { @@ -1019,11 +981,10 @@ nsBlockReflowState::ClearFloats(nscoord aY, PRUint8 aBreakType, nsBlockFrame::WidthToClearPastFloats(*this, floatAvailableSpace.mRect, aReplacedBlock); if (!floatAvailableSpace.mHasFloats || - NS_MAX(floatAvailableSpace.mRect.x, replacedWidth.marginLeft) + + NS_MAX(floatAvailableSpace.mRect.x - mContentArea.x, + replacedWidth.marginLeft) + replacedWidth.borderBoxWidth + - NS_MAX(mContentArea.width - - NS_MIN(mContentArea.width, - floatAvailableSpace.mRect.XMost()), + NS_MAX(mContentArea.XMost() - floatAvailableSpace.mRect.XMost(), replacedWidth.marginRight) <= mContentArea.width) { break; @@ -1047,7 +1008,7 @@ nsBlockReflowState::ClearFloats(nscoord aY, PRUint8 aBreakType, #ifdef DEBUG if (nsBlockFrame::gNoisyReflow) { nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); - printf("clear floats: out: y=%d(%d)\n", newY, newY - bp.top); + printf("clear floats: out: y=%d\n", newY); } #endif diff --git a/layout/generic/nsBlockReflowState.h b/layout/generic/nsBlockReflowState.h index b284e514e8cb..c8705aec15f0 100644 --- a/layout/generic/nsBlockReflowState.h +++ b/layout/generic/nsBlockReflowState.h @@ -75,8 +75,6 @@ public: PRBool aTopMarginRoot, PRBool aBottomMarginRoot, PRBool aBlockNeedsFloatManager); - ~nsBlockReflowState(); - /** * Get the available reflow space (the area not occupied by floats) * for the current y coordinate. The available space is relative to @@ -220,12 +218,15 @@ public: nscoord mBottomEdge; - // The content area to reflow child frames within. The x/y - // coordinates are known to be mBorderPadding.left and - // mBorderPadding.top. The width/height may be NS_UNCONSTRAINEDSIZE - // if the container reflowing this frame has given the frame an - // unconstrained area. - nsSize mContentArea; + // The content area to reflow child frames within. This is within + // this frame's coordinate system, which means mContentArea.x == + // BorderPadding().left and mContentArea.y == BorderPadding().top. + // The height may be NS_UNCONSTRAINEDSIZE, which indicates that there + // is no page/column boundary below (the common case). + // mContentArea.YMost() should only be called after checking that + // mContentArea.height is not NS_UNCONSTRAINEDSIZE; otherwise + // coordinate overflow may occur. + nsRect mContentArea; // Continuation out-of-flow float frames that need to move to our // next in flow are placed here during reflow. It's a pointer to diff --git a/layout/generic/nsFloatManager.cpp b/layout/generic/nsFloatManager.cpp index 50aa41408658..1aa4da693ca9 100644 --- a/layout/generic/nsFloatManager.cpp +++ b/layout/generic/nsFloatManager.cpp @@ -144,11 +144,11 @@ void nsFloatManager::Shutdown() nsFlowAreaRect nsFloatManager::GetFlowArea(nscoord aYOffset, BandInfoType aInfoType, - nscoord aHeight, nscoord aContentAreaWidth, + nscoord aHeight, nsRect aContentArea, SavedState* aState) const { NS_ASSERTION(aHeight >= 0, "unexpected max height"); - NS_ASSERTION(aContentAreaWidth >= 0, "unexpected content area width"); + NS_ASSERTION(aContentArea.width >= 0, "unexpected content area width"); nscoord top = aYOffset + mY; if (top < nscoord_MIN) { @@ -172,7 +172,8 @@ nsFloatManager::GetFlowArea(nscoord aYOffset, BandInfoType aInfoType, if (floatCount == 0 || (mFloats[floatCount-1].mLeftYMost <= top && mFloats[floatCount-1].mRightYMost <= top)) { - return nsFlowAreaRect(0, aYOffset, aContentAreaWidth, aHeight, PR_FALSE); + return nsFlowAreaRect(aContentArea.x, aYOffset, aContentArea.width, + aHeight, PR_FALSE); } nscoord bottom; @@ -189,8 +190,8 @@ nsFloatManager::GetFlowArea(nscoord aYOffset, BandInfoType aInfoType, bottom = nscoord_MAX; } } - nscoord left = mX; - nscoord right = aContentAreaWidth + mX; + nscoord left = mX + aContentArea.x; + nscoord right = mX + aContentArea.XMost(); if (right < left) { NS_WARNING("bad value"); right = left; diff --git a/layout/generic/nsFloatManager.h b/layout/generic/nsFloatManager.h index 941419319cfa..343b3ce45225 100644 --- a/layout/generic/nsFloatManager.h +++ b/layout/generic/nsFloatManager.h @@ -156,14 +156,13 @@ public: * @param aY [in] vertical coordinate for top of available space * desired * @param aHeight [in] see above - * @param aContentAreaWidth [in] the width of the content area (whose left - * edge must be zero in the current translation) + * @param aContentArea [in] an nsRect representing the content area * @param aState [in] If null, use the current state, otherwise, do * computation based only on floats present in the given * saved state. * @return An nsFlowAreaRect whose: - * mRect is the resulting rectangle for line boxes. It will not go - * left of 0, nor right of aContentAreaWidth, but will be + * mRect is the resulting rectangle for line boxes. It will not + * extend beyond aContentArea's horizontal bounds, but may be * narrower when floats are present. * mBandHasFloats is whether there are floats at the sides of the * return value including those that do not reduce the line box @@ -173,7 +172,7 @@ public: */ enum BandInfoType { BAND_FROM_POINT, WIDTH_WITHIN_HEIGHT }; nsFlowAreaRect GetFlowArea(nscoord aY, BandInfoType aInfoType, - nscoord aHeight, nscoord aContentAreaWidth, + nscoord aHeight, nsRect aContentArea, SavedState* aState) const; /** diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index 73dfa901b6a6..20a170ff2bea 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -1157,29 +1157,6 @@ nsXULScrollFrame::GetMaxSize(nsBoxLayoutState& aState) return maxSize; } -#if 0 // XXXldb I don't think this is even needed -/* virtual */ nscoord -nsXULScrollFrame::GetMinWidth(nsIRenderingContext *aRenderingContext) -{ - nsStyleUnit widthUnit = GetStylePosition()->mWidth.GetUnit(); - if (widthUnit == eStyleUnit_Percent || widthUnit == eStyleUnit_Auto) { - nsMargin border = aReflowState.mComputedBorderPadding; - aDesiredSize.mMaxElementWidth = border.right + border.left; - mMaxElementWidth = aDesiredSize.mMaxElementWidth; - } else { - NS_NOTYETIMPLEMENTED("Use the info from the scrolled frame"); -#if 0 - // if not set then use the cached size. If set then set it. - if (aDesiredSize.mMaxElementWidth == -1) - aDesiredSize.mMaxElementWidth = mMaxElementWidth; - else - mMaxElementWidth = aDesiredSize.mMaxElementWidth; -#endif - } - return 0; -} -#endif - #ifdef NS_DEBUG NS_IMETHODIMP nsXULScrollFrame::GetFrameName(nsAString& aResult) const diff --git a/layout/generic/nsImageFrame.cpp b/layout/generic/nsImageFrame.cpp index 8a50a435afa9..53bd336fcdac 100644 --- a/layout/generic/nsImageFrame.cpp +++ b/layout/generic/nsImageFrame.cpp @@ -125,13 +125,8 @@ static PRBool HaveFixedSize(const nsStylePosition* aStylePosition) // check the width and height values in the reflow state's style struct // - if width and height are specified as either coord or percentage, then // the size of the image frame is constrained - nsStyleUnit widthUnit = aStylePosition->mWidth.GetUnit(); - nsStyleUnit heightUnit = aStylePosition->mHeight.GetUnit(); - - return ((widthUnit == eStyleUnit_Coord || - widthUnit == eStyleUnit_Percent) && - (heightUnit == eStyleUnit_Coord || - heightUnit == eStyleUnit_Percent)); + return aStylePosition->mWidth.IsCoordPercentCalcUnit() && + aStylePosition->mHeight.IsCoordPercentCalcUnit(); } // use the data in the reflow state to decide if the image has a constrained size // (i.e. width and height that are based on the containing block size and not the image size) @@ -146,11 +141,15 @@ inline PRBool HaveFixedSize(const nsHTMLReflowState& aReflowState) // during pass 1 reflow, ComputedWidth() is NS_UNCONSTRAINEDSIZE // in pass 2 reflow, ComputedWidth() is 0, it also needs to return PR_FALSE // see bug 156731 - nsStyleUnit heightUnit = (*(aReflowState.mStylePosition)).mHeight.GetUnit(); - nsStyleUnit widthUnit = (*(aReflowState.mStylePosition)).mWidth.GetUnit(); - return ((eStyleUnit_Percent == heightUnit && NS_UNCONSTRAINEDSIZE == aReflowState.ComputedHeight()) || - (eStyleUnit_Percent == widthUnit && (NS_UNCONSTRAINEDSIZE == aReflowState.ComputedWidth() || - 0 == aReflowState.ComputedWidth()))) + const nsStyleCoord &height = aReflowState.mStylePosition->mHeight; + const nsStyleCoord &width = aReflowState.mStylePosition->mWidth; + return (((eStyleUnit_Percent == height.GetUnit() || + (height.IsCalcUnit() && height.CalcHasPercent())) && + NS_UNCONSTRAINEDSIZE == aReflowState.ComputedHeight()) || + ((eStyleUnit_Percent == width.GetUnit() || + (width.IsCalcUnit() && width.CalcHasPercent())) && + (NS_UNCONSTRAINEDSIZE == aReflowState.ComputedWidth() || + 0 == aReflowState.ComputedWidth()))) ? PR_FALSE : HaveFixedSize(aReflowState.mStylePosition); } diff --git a/layout/generic/nsLineLayout.cpp b/layout/generic/nsLineLayout.cpp index d8e346abe46e..03a57eceb2f5 100644 --- a/layout/generic/nsLineLayout.cpp +++ b/layout/generic/nsLineLayout.cpp @@ -653,16 +653,6 @@ HasPercentageUnitSide(const nsStyleSides& aSides) return PR_FALSE; } -inline PRBool -WidthDependsOnContainer(const nsStyleCoord& aCoord) -{ - return aCoord.GetUnit() == eStyleUnit_Percent || - (aCoord.GetUnit() == eStyleUnit_Enumerated && - (aCoord.GetIntValue() == NS_STYLE_WIDTH_AVAILABLE || - aCoord.GetIntValue() == NS_STYLE_WIDTH_FIT_CONTENT)); - -} - static PRBool IsPercentageAware(const nsIFrame* aFrame) { @@ -694,9 +684,10 @@ IsPercentageAware(const nsIFrame* aFrame) const nsStylePosition* pos = aFrame->GetStylePosition(); - if (WidthDependsOnContainer(pos->mWidth) || - WidthDependsOnContainer(pos->mMaxWidth) || - WidthDependsOnContainer(pos->mMinWidth) || + if ((pos->WidthDependsOnContainer() && + pos->mWidth.GetUnit() != eStyleUnit_Auto) || + pos->MaxWidthDependsOnContainer() || + pos->MinWidthDependsOnContainer() || eStyleUnit_Percent == pos->mOffset.GetRightUnit() || eStyleUnit_Percent == pos->mOffset.GetLeftUnit()) { return PR_TRUE; @@ -822,25 +813,16 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame, // Let frame know that are reflowing it. Note that we don't bother // positioning the frame yet, because we're probably going to end up // moving it when we do the vertical alignment - nscoord x = pfd->mBounds.x; - nscoord y = pfd->mBounds.y; - aFrame->WillReflow(mPresContext); - // Adjust spacemanager coordinate system for the frame. The - // spacemanager coordinates are inside the current spans - // border+padding, but the x/y coordinates are not (recall that - // frame coordinates are relative to the parents origin and that the - // parents border/padding is inside the parent - // frame. Therefore we have to subtract out the parents - // border+padding before translating. + // Adjust spacemanager coordinate system for the frame. nsHTMLReflowMetrics metrics; #ifdef DEBUG metrics.width = nscoord(0xdeadbeef); metrics.height = nscoord(0xdeadbeef); #endif - nscoord tx = x - psd->mReflowState->mComputedBorderPadding.left; - nscoord ty = y - psd->mReflowState->mComputedBorderPadding.top; + nscoord tx = pfd->mBounds.x; + nscoord ty = pfd->mBounds.y; mFloatManager->Translate(tx, ty); nsIAtom* frameType = aFrame->GetType(); diff --git a/layout/generic/nsSpacerFrame.cpp b/layout/generic/nsSpacerFrame.cpp index 7921b8ddcaa1..a1d6ebcd8c13 100644 --- a/layout/generic/nsSpacerFrame.cpp +++ b/layout/generic/nsSpacerFrame.cpp @@ -172,6 +172,7 @@ SpacerFrame::GetDesiredSize(nsHTMLReflowMetrics& aMetrics, nsSize aPercentBase) float factor = position->mWidth.GetPercentValue(); aMetrics.width = NSToCoordRound(factor * aPercentBase.width); } + // else treat enumerated values and calc() like 'auto' // height unit = position->mHeight.GetUnit(); diff --git a/layout/reftests/css-calc/reftest.list b/layout/reftests/css-calc/reftest.list new file mode 100644 index 000000000000..86d8c941f334 --- /dev/null +++ b/layout/reftests/css-calc/reftest.list @@ -0,0 +1,4 @@ +== width-block-1.html width-block-1-ref.html +== width-block-intrinsic-1.html width-block-intrinsic-1-ref.html +== width-table-auto-1.html width-table-auto-1-ref.html +== width-table-fixed-1.html width-table-fixed-1-ref.html diff --git a/layout/reftests/css-calc/width-block-1-ref.html b/layout/reftests/css-calc/width-block-1-ref.html new file mode 100644 index 000000000000..dcadf353adab --- /dev/null +++ b/layout/reftests/css-calc/width-block-1-ref.html @@ -0,0 +1,31 @@ + +width: calc() on blocks + + +

50% - 3px

+

25% - 3px + 25%

+

25% - 3px + 12.5% * 2

+

25% - 3px + 12.5%*2

+

25% - 3px + 2*12.5%

+

25% - 3px + 2 * 12.5%

+

min(25%, 150px)

+

min(25%, 100px)

+

max(25%, 150px)

+

max(25%, 100px)

+

min(25%, 150px) + 5%

+

min(25%, 100px) + 5%

+

max(25%, 150px) + 5%

+

max(25%, 100px) + 5%

+

min(25%, 150px) - 2em

+

min(25%, 100px) - 2em

+

max(25%, 150px) - 2em

+

max(25%, 100px) - 2em

+

30% + 20%

+

30% + max(20%, 1px)

+

max(25%, 50%)

+

max(25%, 50%)

diff --git a/layout/reftests/css-calc/width-block-1.html b/layout/reftests/css-calc/width-block-1.html new file mode 100644 index 000000000000..7813dd5c2198 --- /dev/null +++ b/layout/reftests/css-calc/width-block-1.html @@ -0,0 +1,31 @@ + +width: calc() on blocks + + +

50% - 3px

+

25% - 3px + 25%

+

25% - 3px + 12.5% * 2

+

25% - 3px + 12.5%*2

+

25% - 3px + 2*12.5%

+

25% - 3px + 2 * 12.5%

+

min(25%, 150px)

+

min(25%, 100px)

+

max(25%, 150px)

+

max(25%, 100px)

+

min(25%, 150px) + 5%

+

min(25%, 100px) + 5%

+

max(25%, 150px) + 5%

+

max(25%, 100px) + 5%

+

min(25%, 150px) - 2em

+

min(25%, 100px) - 2em

+

max(25%, 150px) - 2em

+

max(25%, 100px) - 2em

+

30% + 20%

+

30% + max(20%, 1px)

+

max(25%, 50%)

+

max(25%, 50%)

diff --git a/layout/reftests/css-calc/width-block-intrinsic-1-ref.html b/layout/reftests/css-calc/width-block-intrinsic-1-ref.html new file mode 100644 index 000000000000..0ef6f6929281 --- /dev/null +++ b/layout/reftests/css-calc/width-block-intrinsic-1-ref.html @@ -0,0 +1,22 @@ + +intrinsic width of width: calc() on blocks + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/layout/reftests/css-calc/width-block-intrinsic-1.html b/layout/reftests/css-calc/width-block-intrinsic-1.html new file mode 100644 index 000000000000..613ac266cba4 --- /dev/null +++ b/layout/reftests/css-calc/width-block-intrinsic-1.html @@ -0,0 +1,25 @@ + +intrinsic width of width: calc() on blocks + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/layout/reftests/css-calc/width-table-auto-1-ref.html b/layout/reftests/css-calc/width-table-auto-1-ref.html new file mode 100644 index 000000000000..6b0a33fa13f2 --- /dev/null +++ b/layout/reftests/css-calc/width-table-auto-1-ref.html @@ -0,0 +1,27 @@ + +width: calc() on table-layout: auto tables + + + + +
xy
+ + + + +
xy
+ + + + +
xy
+ + + + +
xy
+ + + + +
xy
diff --git a/layout/reftests/css-calc/width-table-auto-1.html b/layout/reftests/css-calc/width-table-auto-1.html new file mode 100644 index 000000000000..1b30c54d227a --- /dev/null +++ b/layout/reftests/css-calc/width-table-auto-1.html @@ -0,0 +1,27 @@ + +width: calc() on table-layout: auto tables + + + + +
xy
+ + + + +
xy
+ + + + +
xy
+ + + + +
xy
+ + + + +
xy
diff --git a/layout/reftests/css-calc/width-table-fixed-1-ref.html b/layout/reftests/css-calc/width-table-fixed-1-ref.html new file mode 100644 index 000000000000..23bc8428ade5 --- /dev/null +++ b/layout/reftests/css-calc/width-table-fixed-1-ref.html @@ -0,0 +1,30 @@ + +width: calc() on table-layout: auto tables + + + + + +
xy
+ + + + +
xy
+ + + + +
xy
+ + + + +
xy
+ + + + +
xy
diff --git a/layout/reftests/css-calc/width-table-fixed-1.html b/layout/reftests/css-calc/width-table-fixed-1.html new file mode 100644 index 000000000000..9637418faaac --- /dev/null +++ b/layout/reftests/css-calc/width-table-fixed-1.html @@ -0,0 +1,30 @@ + +width: calc() on table-layout: auto tables + + + + + +
xy
+ + + + +
xy
+ + + + +
xy
+ + + + +
xy
+ + + + +
xy
diff --git a/layout/reftests/reftest.list b/layout/reftests/reftest.list index ce9dd91a9367..b01e6ad061fd 100644 --- a/layout/reftests/reftest.list +++ b/layout/reftests/reftest.list @@ -41,15 +41,18 @@ include bugs/reftest.list # canvas 2D include canvas/reftest.list +# css calc() tests +include css-calc/reftest.list + +# css character encoding tests +include css-charset/reftest.list + # css default pseudo class tests include css-default/reftest.list # css @import tests include css-import/reftest.list -# css character encoding tests -include css-charset/reftest.list - # css gradients include css-gradients/reftest.list diff --git a/layout/style/CSSCalc.h b/layout/style/CSSCalc.h index c6edabc20802..d9211d09cc3b 100644 --- a/layout/style/CSSCalc.h +++ b/layout/style/CSSCalc.h @@ -38,6 +38,7 @@ #define CSSCalc_h_ #include "nsCSSValue.h" +#include "nsStyleCoord.h" #include namespace mozilla { @@ -152,6 +153,38 @@ ComputeCalc(const typename CalcOps::input_type& aValue, CalcOps &aOps) } } +#define CHECK_UNIT(u_) \ + PR_STATIC_ASSERT(int(eCSSUnit_##u_) + 14 == int(eStyleUnit_##u_)); \ + PR_STATIC_ASSERT(eCSSUnit_##u_ >= eCSSUnit_Calc); \ + PR_STATIC_ASSERT(eCSSUnit_##u_ <= eCSSUnit_Calc_Maximum); + +CHECK_UNIT(Calc) +CHECK_UNIT(Calc_Plus) +CHECK_UNIT(Calc_Minus) +CHECK_UNIT(Calc_Times_L) +CHECK_UNIT(Calc_Times_R) +CHECK_UNIT(Calc_Divided) +CHECK_UNIT(Calc_Minimum) +CHECK_UNIT(Calc_Maximum) + +#undef CHECK_UNIT + +inline nsStyleUnit +ConvertCalcUnit(nsCSSUnit aUnit) +{ + NS_ABORT_IF_FALSE(eCSSUnit_Calc <= aUnit && + aUnit <= eCSSUnit_Calc_Maximum, "out of range"); + return nsStyleUnit(aUnit + 14); +} + +inline nsCSSUnit +ConvertCalcUnit(nsStyleUnit aUnit) +{ + NS_ABORT_IF_FALSE(eStyleUnit_Calc <= aUnit && + aUnit <= eStyleUnit_Calc_Maximum, "out of range"); + return nsCSSUnit(aUnit - 14); +} + /** * The input unit operation for input_type being nsCSSValue. */ @@ -167,6 +200,30 @@ struct CSSValueInputCalcOps }; +/** + * The input unit operation for input_type being nsStyleCoord + */ +struct StyleCoordInputCalcOps +{ + typedef nsStyleCoord input_type; + typedef nsStyleCoord::Array input_array_type; + + static nsCSSUnit GetUnit(const nsStyleCoord& aValue) + { + if (aValue.IsCalcUnit()) { + return css::ConvertCalcUnit(aValue.GetUnit()); + } + return eCSSUnit_Null; + } + + float ComputeNumber(const nsStyleCoord& aValue) + { + NS_ABORT_IF_FALSE(PR_FALSE, "SpecifiedToComputedCalcOps should not " + "leave numbers in structure"); + return 0.0f; + } +}; + /** * Basic*CalcOps provide a partial implementation of the CalcOps * template parameter to ComputeCalc, for those callers whose merging @@ -275,37 +332,6 @@ struct NumbersAlreadyNormalizedOps : public CSSValueInputCalcOps } }; -#define CHECK_UNIT(u_) \ - PR_STATIC_ASSERT(int(eCSSUnit_Calc_##u_) + 14 == int(eStyleUnit_Calc_##u_));\ - PR_STATIC_ASSERT(eCSSUnit_Calc_##u_ >= eCSSUnit_Calc_Plus); \ - PR_STATIC_ASSERT(eCSSUnit_Calc_##u_ <= eCSSUnit_Calc_Maximum); - -CHECK_UNIT(Plus) -CHECK_UNIT(Minus) -CHECK_UNIT(Times_L) -CHECK_UNIT(Times_R) -CHECK_UNIT(Divided) -CHECK_UNIT(Minimum) -CHECK_UNIT(Maximum) - -#undef CHECK_UNIT - -inline nsStyleUnit -ConvertCalcUnit(nsCSSUnit aUnit) -{ - NS_ABORT_IF_FALSE(eCSSUnit_Calc_Plus <= aUnit && - aUnit <= eCSSUnit_Calc_Maximum, "out of range"); - return nsStyleUnit(aUnit + 14); -} - -inline nsCSSUnit -ConvertCalcUnit(nsStyleUnit aUnit) -{ - NS_ABORT_IF_FALSE(eStyleUnit_Calc_Plus <= aUnit && - aUnit <= eStyleUnit_Calc_Maximum, "out of range"); - return nsCSSUnit(aUnit - 14); -} - /** * SerializeCalc appends the serialization of aValue to a string. * diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index 4151c85cc828..5cf0796b0be7 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -512,7 +512,9 @@ protected: PRBool ParseTransition(); PRBool ParseTransitionTimingFunction(); PRBool ParseTransitionTimingFunctionValues(nsCSSValue& aValue); - PRBool ParseTransitionTimingFunctionValueComponent(float& aComponent, char aStop); + PRBool ParseTransitionTimingFunctionValueComponent(float& aComponent, + char aStop, + PRBool aCheckRange); PRBool AppendValueToList(nsCSSValueList**& aListTail, const nsCSSValue& aValue); @@ -1307,6 +1309,7 @@ CSSParserImpl::GetURLInParens(nsString& aURL) NS_ASSERTION(!mHavePushBack, "mustn't have pushback at this point"); do { if (! mScanner.NextURL(mToken)) { + // EOF return PR_FALSE; } } while (eCSSToken_WhiteSpace == mToken.mType); @@ -3613,7 +3616,8 @@ CSSParserImpl::ParseColor(nsCSSValue& aValue) aValue.SetColorValue(NS_RGB(r,g,b)); return PR_TRUE; } - return PR_FALSE; // already pushed back + SkipUntil(')'); + return PR_FALSE; } else if (mToken.mIdent.LowerCaseEqualsLiteral("-moz-rgba") || mToken.mIdent.LowerCaseEqualsLiteral("rgba")) { @@ -3627,7 +3631,8 @@ CSSParserImpl::ParseColor(nsCSSValue& aValue) aValue.SetColorValue(NS_RGBA(r, g, b, a)); return PR_TRUE; } - return PR_FALSE; // already pushed back + SkipUntil(')'); + return PR_FALSE; } else if (mToken.mIdent.LowerCaseEqualsLiteral("hsl")) { // hsl ( hue , saturation , lightness ) @@ -3636,6 +3641,7 @@ CSSParserImpl::ParseColor(nsCSSValue& aValue) aValue.SetColorValue(rgba); return PR_TRUE; } + SkipUntil(')'); return PR_FALSE; } else if (mToken.mIdent.LowerCaseEqualsLiteral("-moz-hsla") || @@ -3650,6 +3656,7 @@ CSSParserImpl::ParseColor(nsCSSValue& aValue) NS_GET_B(rgba), a)); return PR_TRUE; } + SkipUntil(')'); return PR_FALSE; } break; @@ -4472,12 +4479,20 @@ CSSParserImpl::ParseVariant(nsCSSValue& aValue, if (((aVariantMask & VARIANT_ATTR) != 0) && (eCSSToken_Function == tk->mType) && tk->mIdent.LowerCaseEqualsLiteral("attr")) { - return ParseAttr(aValue); + if (!ParseAttr(aValue)) { + SkipUntil(')'); + return PR_FALSE; + } + return PR_TRUE; } if (((aVariantMask & VARIANT_CUBIC_BEZIER) != 0) && (eCSSToken_Function == tk->mType)) { if (tk->mIdent.LowerCaseEqualsLiteral("cubic-bezier")) { - return ParseTransitionTimingFunctionValues(aValue); + if (!ParseTransitionTimingFunctionValues(aValue)) { + SkipUntil(')'); + return PR_FALSE; + } + return PR_TRUE; } } if ((aVariantMask & VARIANT_CALC) && @@ -5729,7 +5744,7 @@ CSSParserImpl::ParseSingleValueProperty(nsCSSValue& aValue, case eCSSProperty_height: return ParseNonNegativeVariant(aValue, VARIANT_AHLP, nsnull); case eCSSProperty_width: - return ParseNonNegativeVariant(aValue, VARIANT_AHKLP, + return ParseNonNegativeVariant(aValue, VARIANT_AHKLP | VARIANT_CALC, nsCSSProps::kWidthKTable); case eCSSProperty_force_broken_image_icon: return ParseNonNegativeVariant(aValue, VARIANT_HI, nsnull); @@ -8859,10 +8874,10 @@ CSSParserImpl::ParseTransitionTimingFunctionValues(nsCSSValue& aValue) } float x1, x2, y1, y2; - if (!ParseTransitionTimingFunctionValueComponent(x1, ',') || - !ParseTransitionTimingFunctionValueComponent(y1, ',') || - !ParseTransitionTimingFunctionValueComponent(x2, ',') || - !ParseTransitionTimingFunctionValueComponent(y2, ')')) { + if (!ParseTransitionTimingFunctionValueComponent(x1, ',', PR_TRUE) || + !ParseTransitionTimingFunctionValueComponent(y1, ',', PR_FALSE) || + !ParseTransitionTimingFunctionValueComponent(x2, ',', PR_TRUE) || + !ParseTransitionTimingFunctionValueComponent(y2, ')', PR_FALSE)) { return PR_FALSE; } @@ -8878,14 +8893,19 @@ CSSParserImpl::ParseTransitionTimingFunctionValues(nsCSSValue& aValue) PRBool CSSParserImpl::ParseTransitionTimingFunctionValueComponent(float& aComponent, - char aStop) + char aStop, + PRBool aCheckRange) { if (!GetToken(PR_TRUE)) { return PR_FALSE; } nsCSSToken* tk = &mToken; if (tk->mType == eCSSToken_Number) { - aComponent = tk->mNumber; + float num = tk->mNumber; + if (aCheckRange && (num < 0.0 || num > 1.0)) { + return PR_FALSE; + } + aComponent = num; if (ExpectSymbol(aStop, PR_TRUE)) { return PR_TRUE; } diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index d83b3ad91864..e316b2413ff3 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -510,21 +510,19 @@ SpecifiedCalcToComputedCalc(const nsCSSValue& aValue, nsStyleCoord& aCoord, SpecifiedToComputedCalcOps ops(aStyleContext, aStyleContext->PresContext(), aCanStoreInRuleTree); aCoord = ComputeCalc(aValue, ops); + if (!aCoord.IsCalcUnit()) { + // Some callers distinguish between calc(50%) and 50%, or calc(50px) + // and 50px. + nsStyleCoord::Array *array = + nsStyleCoord::Array::Create(aStyleContext, aCanStoreInRuleTree, 1); + array->Item(0) = aCoord; + aCoord.SetArrayValue(array, eStyleUnit_Calc); + } } -struct ComputeComputedCalcCalcOps : public css::BasicCoordCalcOps +struct ComputeComputedCalcCalcOps : public css::StyleCoordInputCalcOps, + public css::BasicCoordCalcOps { - typedef nsStyleCoord input_type; - typedef nsStyleCoord::Array input_array_type; - - static nsCSSUnit GetUnit(const nsStyleCoord& aValue) - { - if (aValue.IsCalcUnit()) { - return css::ConvertCalcUnit(aValue.GetUnit()); - } - return eCSSUnit_Null; - } - const nscoord mPercentageBasis; ComputeComputedCalcCalcOps(nscoord aPercentageBasis) @@ -543,13 +541,6 @@ struct ComputeComputedCalcCalcOps : public css::BasicCoordCalcOps } return result; } - - float ComputeNumber(const nsStyleCoord& aValue) - { - NS_ABORT_IF_FALSE(PR_FALSE, "SpecifiedToComputedCalcOps should not " - "leave numbers in structure"); - return 0.0f; - } }; // This is our public API for handling calc() expressions that involve @@ -562,22 +553,6 @@ nsRuleNode::ComputeComputedCalc(const nsStyleCoord& aValue, return css::ComputeCalc(aValue, ops); } -/* static */ nscoord -nsRuleNode::ComputeCoordPercentCalc(const nsStyleCoord& aCoord, - nscoord aPercentageBasis) -{ - switch (aCoord.GetUnit()) { - case eStyleUnit_Coord: - return aCoord.GetCoordValue(); - case eStyleUnit_Percent: - return NSCoordSaturatingMultiply(aPercentageBasis, - aCoord.GetPercentValue()); - default: - NS_ABORT_IF_FALSE(aCoord.IsCalcUnit(), "unexpected unit"); - return nsRuleNode::ComputeComputedCalc(aCoord, aPercentageBasis); - } -} - #define SETCOORD_NORMAL 0x01 // N #define SETCOORD_AUTO 0x02 // A #define SETCOORD_INHERIT 0x04 // H @@ -5556,8 +5531,8 @@ nsRuleNode::ComputePositionData(void* aStartStruct, } SetCoord(posData.mWidth, pos->mWidth, parentPos->mWidth, - SETCOORD_LPAEH | SETCOORD_INITIAL_AUTO, aContext, - mPresContext, canStoreInRuleTree); + SETCOORD_LPAEH | SETCOORD_INITIAL_AUTO | SETCOORD_STORE_CALC, + aContext, mPresContext, canStoreInRuleTree); SetCoord(posData.mMinWidth, pos->mMinWidth, parentPos->mMinWidth, SETCOORD_LPEH | SETCOORD_INITIAL_ZERO, aContext, mPresContext, canStoreInRuleTree); diff --git a/layout/style/nsRuleNode.h b/layout/style/nsRuleNode.h index 5d9a0c92fb00..a31614dc1c74 100644 --- a/layout/style/nsRuleNode.h +++ b/layout/style/nsRuleNode.h @@ -767,7 +767,11 @@ public: // Compute the value of an nsStyleCoord that is either a coord, a // percent, or a calc expression. static nscoord ComputeCoordPercentCalc(const nsStyleCoord& aCoord, - nscoord aPercentageBasis); + nscoord aPercentageBasis) + { + // ComputeComputedCalc will handle coords and percents correctly + return ComputeComputedCalc(aCoord, aPercentageBasis); + } // Return whether the rule tree for which this node is the root has // cached data such that we need to do dynamic change handling for diff --git a/layout/style/nsStyleAnimation.h b/layout/style/nsStyleAnimation.h index 6b5ae25e182e..52804533b9bd 100644 --- a/layout/style/nsStyleAnimation.h +++ b/layout/style/nsStyleAnimation.h @@ -128,7 +128,6 @@ public: const Value& aEndValue, double aPortion, Value& aResultValue) { - NS_ABORT_IF_FALSE(0.0 <= aPortion && aPortion <= 1.0, "out of range"); return AddWeighted(aProperty, 1.0 - aPortion, aStartValue, aPortion, aEndValue, aResultValue); } diff --git a/layout/style/nsStyleCoord.cpp b/layout/style/nsStyleCoord.cpp index fa0f9092131c..234b2a0c1a80 100644 --- a/layout/style/nsStyleCoord.cpp +++ b/layout/style/nsStyleCoord.cpp @@ -211,6 +211,24 @@ nsStyleCoord::GetAngleValueInRadians() const } } +PRBool +nsStyleCoord::CalcHasPercent() const +{ + NS_ABORT_IF_FALSE(IsCalcUnit(), "caller should check IsCalcUnit()"); + nsStyleCoord::Array *a = GetArrayValue(); + for (size_t i = 0, i_end = a->Count(); i < i_end; ++i) { + const nsStyleCoord &v = a->Item(i); + if (v.GetUnit() == eStyleUnit_Percent) { + return PR_TRUE; + } + if (v.IsCalcUnit() && v.CalcHasPercent()) { + return PR_TRUE; + } + } + return PR_FALSE; +} + + inline void* nsStyleCoord::Array::operator new(size_t aSelfSize, nsStyleContext *aAllocationContext, diff --git a/layout/style/nsStyleCoord.h b/layout/style/nsStyleCoord.h index a79859f7dbc3..f86c6e041b0a 100644 --- a/layout/style/nsStyleCoord.h +++ b/layout/style/nsStyleCoord.h @@ -60,12 +60,12 @@ enum nsStyleUnit { eStyleUnit_Coord = 20, // (nscoord) value is twips eStyleUnit_Integer = 30, // (int) value is simple integer eStyleUnit_Enumerated = 32, // (int) value has enumerated meaning - // The following are all of the eCSSUnit_Calc_* types (but not - // eCSSUnit_Calc itself, since we don't need to distinguish - // calc(min()) from min() in compute dstyle). They are all weak + // The following are all of the eCSSUnit_Calc_* types. They are weak // pointers to a calc tree allocated by nsStyleContext::Alloc. // NOTE: They are in the same order as the eCSSUnit_Calc_* values so // that converting between the two sets is just addition/subtraction. + eStyleUnit_Calc = 39, // (Array*) calc() toplevel, to + // distinguish 50% from calc(50%), etc. eStyleUnit_Calc_Plus = 40, // (Array*) + node within calc() eStyleUnit_Calc_Minus = 41, // (Array*) - within calc eStyleUnit_Calc_Times_L = 42, // (Array*) num * val within calc @@ -121,9 +121,19 @@ public: } PRBool IsCalcUnit() const { - return eStyleUnit_Calc_Plus <= mUnit && mUnit <= eStyleUnit_Calc_Maximum; + return eStyleUnit_Calc <= mUnit && mUnit <= eStyleUnit_Calc_Maximum; } + PRBool IsCoordPercentCalcUnit() const { + return mUnit == eStyleUnit_Coord || + mUnit == eStyleUnit_Percent || + IsCalcUnit(); + } + + // Does this calc() expression have any percentages inside it? Can be + // called only when IsCalcUnit() is true. + PRBool CalcHasPercent() const; + PRBool IsArrayValue() const { return IsCalcUnit(); } diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index 572c58e92074..7af9e6c3031b 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -1146,6 +1146,17 @@ nsChangeHint nsStylePosition::MaxDifference() } #endif +/* static */ PRBool +nsStylePosition::WidthCoordDependsOnContainer(const nsStyleCoord &aCoord) +{ + return aCoord.GetUnit() == eStyleUnit_Auto || + aCoord.GetUnit() == eStyleUnit_Percent || + (aCoord.IsCalcUnit() && aCoord.CalcHasPercent()) || + (aCoord.GetUnit() == eStyleUnit_Enumerated && + (aCoord.GetIntValue() == NS_STYLE_WIDTH_FIT_CONTENT || + aCoord.GetIntValue() == NS_STYLE_WIDTH_AVAILABLE)); +} + // -------------------- // nsStyleTable // diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index b74933948501..92fe473f4ec8 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -1052,14 +1052,35 @@ struct nsStylePosition { static PRBool ForceCompare() { return PR_TRUE; } nsStyleSides mOffset; // [reset] coord, percent, auto - nsStyleCoord mWidth; // [reset] coord, percent, auto, enum + nsStyleCoord mWidth; // [reset] coord, percent, enum, auto nsStyleCoord mMinWidth; // [reset] coord, percent, enum - nsStyleCoord mMaxWidth; // [reset] coord, percent, null, enum + nsStyleCoord mMaxWidth; // [reset] coord, percent, enum, none nsStyleCoord mHeight; // [reset] coord, percent, auto nsStyleCoord mMinHeight; // [reset] coord, percent - nsStyleCoord mMaxHeight; // [reset] coord, percent, null + nsStyleCoord mMaxHeight; // [reset] coord, percent, none PRUint8 mBoxSizing; // [reset] see nsStyleConsts.h nsStyleCoord mZIndex; // [reset] integer, auto + + PRBool WidthDependsOnContainer() const + { return WidthCoordDependsOnContainer(mWidth); } + PRBool MinWidthDependsOnContainer() const + { return WidthCoordDependsOnContainer(mMinWidth); } + PRBool MaxWidthDependsOnContainer() const + { return WidthCoordDependsOnContainer(mMaxWidth); } + PRBool HeightDependsOnContainer() const + { return HeightCoordDependsOnContainer(mHeight); } + PRBool MinHeightDependsOnContainer() const + { return HeightCoordDependsOnContainer(mMinHeight); } + PRBool MaxHeightDependsOnContainer() const + { return HeightCoordDependsOnContainer(mMaxHeight); } + +private: + static PRBool WidthCoordDependsOnContainer(const nsStyleCoord &aCoord); + static PRBool HeightCoordDependsOnContainer(const nsStyleCoord &aCoord) + { + return aCoord.GetUnit() == eStyleUnit_Auto || // CSS 2.1, 10.6.4, item (5) + aCoord.GetUnit() == eStyleUnit_Percent; + } }; struct nsStyleTextReset { diff --git a/layout/style/test/Makefile.in b/layout/style/test/Makefile.in index 473cf3667f17..ff57722c8abd 100644 --- a/layout/style/test/Makefile.in +++ b/layout/style/test/Makefile.in @@ -175,6 +175,7 @@ _TEST_FILES = test_acid3_test46.html \ test_transitions_per_property.html \ test_transitions_dynamic_changes.html \ test_transitions_bug537151.html \ + test_unclosed_parentheses.html \ test_units_angle.html \ test_units_frequency.html \ test_units_length.html \ diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index dcdcae5560eb..2a4bb26f0fe3 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -1031,7 +1031,10 @@ var gCSSProperties = { "-moz-repeating-radial-gradient(top left 99deg, cover, red, blue)", "-moz-repeating-radial-gradient(15% 20% -1.2345rad, circle, red, blue)", "-moz-repeating-radial-gradient(45px 399grad, ellipse closest-corner, red, blue)", - "-moz-repeating-radial-gradient(45px 399grad, farthest-side circle, red, blue)" + "-moz-repeating-radial-gradient(45px 399grad, farthest-side circle, red, blue)", + "-moz-image-rect(url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==), 2, 10, 10, 2)", + "-moz-image-rect(url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==), 10%, 50%, 30%, 0%)", + "-moz-image-rect(url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==), 10, 50%, 30%, 0)", ], invalid_values: [ /* Old syntax */ @@ -2138,8 +2141,8 @@ var gCSSProperties = { domProp: "textShadow", inherited: true, type: CSS_TYPE_LONGHAND, - initial_values: [ "none" ], prerequisites: { "color": "blue" }, + initial_values: [ "none" ], other_values: [ "2px 2px", "2px 2px 1px", "2px 2px green", "2px 2px 1px green", "green 2px 2px", "green 2px 2px 1px", "green 2px 2px, blue 1px 3px 4px", "currentColor 3px 3px", "blue 2px 2px, currentColor 1px 2px", /* calc() values */ "2px 2px -moz-calc(-5px)", /* clamped */ @@ -2211,8 +2214,8 @@ var gCSSProperties = { inherited: false, type: CSS_TYPE_LONGHAND, initial_values: [ "ease", "cubic-bezier(0.25, 0.1, 0.25, 1.0)" ], - other_values: [ "linear", "ease-in", "ease-out", "ease-in-out", "linear, ease-in, cubic-bezier(0.1, 0.2, 0.8, 0.9)" ], - invalid_values: [ "none", "auto" ] + other_values: [ "linear", "ease-in", "ease-out", "ease-in-out", "linear, ease-in, cubic-bezier(0.1, 0.2, 0.8, 0.9)", "cubic-bezier(0.5, 0.5, 0.5, 0.5)", "cubic-bezier(0.25, 1.5, 0.75, -0.5)" ], + invalid_values: [ "none", "auto", "cubic-bezier(0.25, 0.1, 0.25)", "cubic-bezier(0.25, 0.1, 0.25, 0.25, 1.0)", "cubic-bezier(-0.5, 0.5, 0.5, 0.5)", "cubic-bezier(1.5, 0.5, 0.5, 0.5)", "cubic-bezier(0.5, 0.5, -0.5, 0.5)", "cubic-bezier(0.5, 0.5, 1.5, 0.5)" ] }, "unicode-bidi": { domProp: "unicodeBidi", @@ -2278,10 +2281,67 @@ var gCSSProperties = { domProp: "width", inherited: false, type: CSS_TYPE_LONGHAND, + /* computed value tests for width test more with display:block */ + prerequisites: { "display": "block" }, initial_values: [ " auto" ], /* XXX these have prerequisites */ - other_values: [ "15px", "3em", "15%", "-moz-max-content", "-moz-min-content", "-moz-fit-content", "-moz-available" ], - invalid_values: [ "none", "-2px" ] + other_values: [ "15px", "3em", "15%", "-moz-max-content", "-moz-min-content", "-moz-fit-content", "-moz-available", + /* valid calc() values */ + "-moz-calc(-2px)", + "-moz-calc(2px)", + "-moz-calc(50%)", + "-moz-calc(50% + 2px)", + "-moz-calc( 50% + 2px)", + "-moz-calc(50% + 2px )", + "-moz-calc( 50% + 2px )", + "-moz-calc(50% - -2px)", + "-moz-calc(2px - -50%)", + "-moz-calc(3*25px)", + "-moz-calc(3 *25px)", + "-moz-calc(3 * 25px)", + "-moz-calc(3* 25px)", + "-moz-calc(25px*3)", + "-moz-calc(25px *3)", + "-moz-calc(25px* 3)", + "-moz-calc(25px * 3)", + "-moz-calc(3*25px + 50%)", + "-moz-calc(50% - 3em + 2px)", + "-moz-calc(50% - (3em + 2px))", + "-moz-calc((50% - 3em) + 2px)", + "-moz-min(50%, 30em)", + "-moz-calc(min(50%, 30em))", + "-moz-max(30em, 2px + 50%)", + "-moz-calc(max(30em, 2px + 50%))", + "-moz-min(30%, 30em,200px, min(500px ,40em))", + "-moz-calc(min(30%, 30em,200px, min(500px ,40em)))", + "-moz-min(50%)", + "-moz-max(20px)", + "-moz-calc(min(2em))", + "-moz-calc(max(50%))", + "-moz-calc(50px/2)", + "-moz-calc(50px/(2 - 1))" + ], + invalid_values: [ "none", "-2px", + /* invalid calc() values */ + "-moz-calc(50%+ 2px)", + "-moz-calc(50% +2px)", + "-moz-calc(50%+2px)", + "-moz-min()", + "-moz-calc(min())", + "-moz-max()", + "-moz-calc(max())", + "-moz-calc(50px/(2 - 2))", + /* If we ever support division by values, which is + * complicated for the reasons described in + * http://lists.w3.org/Archives/Public/www-style/2010Jan/0007.html + * , we should support all 4 of these as described in + * http://lists.w3.org/Archives/Public/www-style/2009Dec/0296.html + */ + "-moz-calc((3em / 100%) * 3em)", + "-moz-calc(3em / 100% * 3em)", + "-moz-calc(3em * (3em / 100%))", + "-moz-calc(3em * 3em / 100%)" + ] }, "word-spacing": { domProp: "wordSpacing", diff --git a/layout/style/test/test_initial_computation.html b/layout/style/test/test_initial_computation.html index e8dad5836847..7e6f91cff4f1 100644 --- a/layout/style/test/test_initial_computation.html +++ b/layout/style/test/test_initial_computation.html @@ -8,6 +8,10 @@ + + + + + + + +Mozilla Bug 575672 +

+ +
+
+
+ + diff --git a/layout/style/test/test_value_computation.html b/layout/style/test/test_value_computation.html index cc60c9da2dfb..c35fde4614ea 100644 --- a/layout/style/test/test_value_computation.html +++ b/layout/style/test/test_value_computation.html @@ -8,6 +8,10 @@ +