diff --git a/Cargo.lock b/Cargo.lock index 57437b8dca4a..82848e441cf9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1211,7 +1211,7 @@ dependencies = [ "malloc_size_of 0.0.1", "nsstring 0.1.0", "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "selectors 0.21.0", "servo_arc 0.1.1", "smallvec 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1280,7 +1280,7 @@ dependencies = [ "rsdparsa_capi 0.1.0", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "storage 0.1.0", - "u2fhid 0.2.3", + "u2fhid 0.2.4", "webrender_bindings 0.1.0", "xpcom 0.1.0", "xulstore 0.1.0", @@ -1648,11 +1648,10 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.1.5" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1890,7 +1889,7 @@ dependencies = [ "nserror 0.1.0", "nsstring 0.1.0", "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", "xpcom 0.1.0", ] @@ -2119,20 +2118,25 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.6.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "lock_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "parking_lot_core" -version = "0.2.14" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (git+https://github.com/froydnj/winapi-rs?branch=aarch64)", ] @@ -2527,7 +2531,7 @@ dependencies = [ "serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.88 (git+https://github.com/servo/serde?branch=deserialize_from_enums10)", "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "uuid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2626,6 +2630,11 @@ name = "scopeguard" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "scopeguard" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "scroll" version = "0.9.2" @@ -2889,7 +2898,7 @@ dependencies = [ "num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "ordered-float 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3290,7 +3299,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "u2fhid" -version = "0.2.3" +version = "0.2.4" dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "boxfnonce 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3299,7 +3308,7 @@ dependencies = [ "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "libudev 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "runloop 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (git+https://github.com/froydnj/winapi-rs?branch=aarch64)", ] @@ -3379,8 +3388,11 @@ dependencies = [ [[package]] name = "uuid" -version = "0.7.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "vcpkg" @@ -3851,7 +3863,7 @@ dependencies = [ "checksum linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "70fb39025bc7cdd76305867c4eccf2f2dcf6e9a57f5b21a93e1c2d86cd03ec9e" "checksum lmdb-rkv 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1452294309db7977dc75e1e8135a8c654d9e52e04ff0c0bd06c880897a91defd" "checksum lmdb-rkv-sys 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1470e0168f1832e35afd6d0931ae60db625685332837b97aa156773ec9c5e393" -"checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" +"checksum lock_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed946d4529956a20f2d63ebe1b69996d5a2137c91913fe3ebbeff957f5bca7ff" "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" "checksum lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084" @@ -3891,8 +3903,8 @@ dependencies = [ "checksum ordermap 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063" "checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" "checksum packed_simd 0.3.3 (git+https://github.com/hsivonen/packed_simd?branch=rust_1_32)" = "" -"checksum parking_lot 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "69376b761943787ebd5cc85a5bc95958651a22609c5c1c2b65de21786baec72b" -"checksum parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4db1a8ccf734a7bce794cc19b3df06ed87ab2f3907036b693c68f56b4d4537fa" +"checksum parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa7767817701cce701d5585b9c4db3cdd02086398322c1d7e8bf5094a96a2ce7" +"checksum parking_lot_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb88cb1cb3790baa6776844f968fea3be44956cf184fa1be5a03341f5491278c" "checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" "checksum percent-encoding 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de154f638187706bde41d9b4738748933d64e6b37bdbffc0b47a97d16a6ae356" "checksum petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f" @@ -3947,6 +3959,7 @@ dependencies = [ "checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d" "checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" "checksum scopeguard 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c79eb2c3ac4bc2507cda80e7f3ac5b88bd8eae4c0914d5663e6a8933994be918" +"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" "checksum scroll 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f84d114ef17fd144153d608fba7c446b0145d038985e7a8cc5d08bb0ce20383" "checksum scroll_derive 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8f1aa96c45e7f5a91cb7fabe7b279f02fea7126239fc40b732316e8b6a2d0fcb" "checksum semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537" @@ -4012,7 +4025,7 @@ dependencies = [ "checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" "checksum uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e1436e58182935dcd9ce0add9ea0b558e8a87befe01c1a301e6020aeb0876363" -"checksum uuid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dab5c5526c5caa3d106653401a267fed923e7046f35895ffcb5ca42db64942e6" +"checksum uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90dbc611eb48397705a6b0f6e917da23ae517e4d127123d2cf7674206627d32a" "checksum vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9e0a7d8bed3178a8fb112199d466eeca9ed09a14ba8ad67718179b4fd5487d0b" "checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" diff --git a/browser/config/mozconfigs/win32/mingwclang b/browser/config/mozconfigs/win32/mingwclang index a0435b9c5e1c..7f02e756d966 100644 --- a/browser/config/mozconfigs/win32/mingwclang +++ b/browser/config/mozconfigs/win32/mingwclang @@ -56,11 +56,6 @@ RANLIB=llvm-ranlib # For Stylo BINDGEN_CFLAGS="-I$TOOLTOOL_DIR/clang/i686-w64-mingw32/include/c++/v1 -I$TOOLTOOL_DIR/clang/i686-w64-mingw32/include" -# Bug 1471698 - Work around binutils corrupting mingw clang binaries. -LDFLAGS="-Wl,-S" -STRIP=/bin/true -OBJCOPY=/bin/true - # We want to make sure we use binutils and other binaries in the tooltool # package. mk_add_options "export PATH=$TOOLTOOL_DIR/clang/bin:$TOOLTOOL_DIR/mingw32/bin:$TOOLTOOL_DIR/wine/bin:$TOOLTOOL_DIR/upx/bin:$TOOLTOOL_DIR/fxc2/bin:$PATH" diff --git a/browser/config/mozconfigs/win64/mingwclang b/browser/config/mozconfigs/win64/mingwclang index 1c5e73dbac62..34fd2685c5e0 100755 --- a/browser/config/mozconfigs/win64/mingwclang +++ b/browser/config/mozconfigs/win64/mingwclang @@ -56,11 +56,6 @@ RANLIB=llvm-ranlib # For Stylo BINDGEN_CFLAGS="-I$TOOLTOOL_DIR/clang/x86_64-w64-mingw32/include/c++/v1 -I$TOOLTOOL_DIR/clang/x86_64-w64-mingw32/include" -# Bug 1471698 - Work around binutils corrupting mingw clang binaries. -LDFLAGS="-Wl,-S" -STRIP=/bin/true -OBJCOPY=/bin/true - # We want to make sure we use binutils and other binaries in the tooltool # package. mk_add_options "export PATH=$TOOLTOOL_DIR/clang/bin:$TOOLTOOL_DIR/mingw32/bin:$TOOLTOOL_DIR/wine/bin:$TOOLTOOL_DIR/upx/bin:$TOOLTOOL_DIR/fxc2/bin:$PATH" diff --git a/browser/extensions/formautofill/OSKeyStore.jsm b/browser/extensions/formautofill/OSKeyStore.jsm index 4458e5ca59f2..8dabcdbce613 100644 --- a/browser/extensions/formautofill/OSKeyStore.jsm +++ b/browser/extensions/formautofill/OSKeyStore.jsm @@ -140,18 +140,6 @@ var OSKeyStore = { } }); - if (nativeOSKeyStore.isNSSKeyStore) { - // Workaround bug 1492305: NSS-implemented methods don't reject when user cancels. - unlockPromise = unlockPromise.then(() => { - log.debug("ensureLoggedIn: isNSSKeyStore: ", reauth, Services.logins.isLoggedIn); - // User has hit the cancel button on the master password prompt. - // We must reject the promise chain here. - if (!Services.logins.isLoggedIn) { - throw Components.Exception("User canceled OS unlock entry (Workaround)", Cr.NS_ERROR_FAILURE); - } - }); - } - unlockPromise = unlockPromise.then(() => { log.debug("ensureLoggedIn: Logged in"); this._pendingUnlockPromise = null; diff --git a/devtools/client/debugger/bin/module-manifest.json b/devtools/client/debugger/bin/module-manifest.json index 39a0b8fb65e2..15808b68c0e4 100644 --- a/devtools/client/debugger/bin/module-manifest.json +++ b/devtools/client/debugger/bin/module-manifest.json @@ -15,7 +15,7 @@ "byName": {}, "byBlocks": {}, "usedIds": { - "0": 0 + "1": 1 } } } @@ -36,7 +36,7 @@ "byName": {}, "byBlocks": {}, "usedIds": { - "0": 0 + "1": 1 } } } @@ -57,7 +57,7 @@ "byName": {}, "byBlocks": {}, "usedIds": { - "0": 0 + "1": 1 } } } @@ -78,7 +78,7 @@ "byName": {}, "byBlocks": {}, "usedIds": { - "0": 0 + "1": 1 } } } @@ -99,7 +99,7 @@ "byName": {}, "byBlocks": {}, "usedIds": { - "0": 0 + "1": 1 } } } @@ -120,7 +120,7 @@ "byName": {}, "byBlocks": {}, "usedIds": { - "0": 0 + "1": 1 } } } @@ -141,7 +141,7 @@ "byName": {}, "byBlocks": {}, "usedIds": { - "0": 0 + "1": 1 } } } @@ -639,7 +639,16 @@ "../../babel-loader/lib/index.js?ignore=src/lib!../../../packages/devtools-reps/src/object-inspector/utils/selection.js": 488, "../../css-loader/lib/css-base.js": 489, "external \"devtools/client/framework/menu\"": 490, - "external \"devtools/client/framework/menu-item\"": 491 + "external \"devtools/client/framework/menu-item\"": 491, + "../../whatwg-url/lib/url-state-machine.js": 492, + "../../whatwg-url/lib/urlencoded.js": 493, + "../../whatwg-url/lib/utils.js": 494, + "../../whatwg-url/lib/infra.js": 495, + "../../whatwg-url/lib/URLSearchParams.js": 496, + "../../whatwg-url/lib/public-api.js": 497, + "../../whatwg-url/lib/URL.js": 498, + "../../whatwg-url/lib/URL-impl.js": 499, + "../../whatwg-url/lib/URLSearchParams-impl.js": 500 }, "usedIds": { "0": 0, @@ -1133,7 +1142,16 @@ "488": 488, "489": 489, "490": 490, - "491": 491 + "491": 491, + "492": 492, + "493": 493, + "494": 494, + "495": 495, + "496": 496, + "497": 497, + "498": 498, + "499": 499, + "500": 500 } }, "chunks": { diff --git a/devtools/client/debugger/dist/vendors.js b/devtools/client/debugger/dist/vendors.js index fc96f55bb475..1c95e4004663 100644 --- a/devtools/client/debugger/dist/vendors.js +++ b/devtools/client/debugger/dist/vendors.js @@ -1,13 +1,13 @@ (function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') - module.exports = factory(require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/react"), require("Services"), require("devtools/shared/flags"), require("devtools/client/shared/vendor/react-dom"), require("devtools/client/shared/vendor/lodash"), require("devtools/client/framework/menu"), require("devtools/client/framework/menu-item")); + module.exports = factory(require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/react"), require("Services"), require("devtools/shared/flags"), require("devtools/client/shared/vendor/react-dom"), require("devtools/client/shared/vendor/lodash"), require("devtools/client/framework/menu")); else if(typeof define === 'function' && define.amd) - define(["devtools/client/shared/vendor/react-prop-types", "devtools/client/shared/vendor/react-dom-factories", "devtools/client/shared/vendor/react", "Services", "devtools/shared/flags", "devtools/client/shared/vendor/react-dom", "devtools/client/shared/vendor/lodash", "devtools/client/framework/menu", "devtools/client/framework/menu-item"], factory); + define(["devtools/client/shared/vendor/react-prop-types", "devtools/client/shared/vendor/react-dom-factories", "devtools/client/shared/vendor/react", "Services", "devtools/shared/flags", "devtools/client/shared/vendor/react-dom", "devtools/client/shared/vendor/lodash", "devtools/client/framework/menu"], factory); else { - var a = typeof exports === 'object' ? factory(require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/react"), require("Services"), require("devtools/shared/flags"), require("devtools/client/shared/vendor/react-dom"), require("devtools/client/shared/vendor/lodash"), require("devtools/client/framework/menu"), require("devtools/client/framework/menu-item")) : factory(root["devtools/client/shared/vendor/react-prop-types"], root["devtools/client/shared/vendor/react-dom-factories"], root["devtools/client/shared/vendor/react"], root["Services"], root["devtools/shared/flags"], root["devtools/client/shared/vendor/react-dom"], root["devtools/client/shared/vendor/lodash"], root["devtools/client/framework/menu"], root["devtools/client/framework/menu-item"]); + var a = typeof exports === 'object' ? factory(require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/react"), require("Services"), require("devtools/shared/flags"), require("devtools/client/shared/vendor/react-dom"), require("devtools/client/shared/vendor/lodash"), require("devtools/client/framework/menu")) : factory(root["devtools/client/shared/vendor/react-prop-types"], root["devtools/client/shared/vendor/react-dom-factories"], root["devtools/client/shared/vendor/react"], root["Services"], root["devtools/shared/flags"], root["devtools/client/shared/vendor/react-dom"], root["devtools/client/shared/vendor/lodash"], root["devtools/client/framework/menu"]); for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i]; } -})(typeof self !== 'undefined' ? self : this, function(__WEBPACK_EXTERNAL_MODULE_0__, __WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_6__, __WEBPACK_EXTERNAL_MODULE_37__, __WEBPACK_EXTERNAL_MODULE_103__, __WEBPACK_EXTERNAL_MODULE_112__, __WEBPACK_EXTERNAL_MODULE_417__, __WEBPACK_EXTERNAL_MODULE_490__, __WEBPACK_EXTERNAL_MODULE_491__) { +})(typeof self !== 'undefined' ? self : this, function(__WEBPACK_EXTERNAL_MODULE_0__, __WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_6__, __WEBPACK_EXTERNAL_MODULE_37__, __WEBPACK_EXTERNAL_MODULE_103__, __WEBPACK_EXTERNAL_MODULE_112__, __WEBPACK_EXTERNAL_MODULE_417__, __WEBPACK_EXTERNAL_MODULE_490__) { return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; @@ -87,6 +87,18 @@ module.exports = __WEBPACK_EXTERNAL_MODULE_0__; module.exports = __WEBPACK_EXTERNAL_MODULE_1__; +/***/ }), + +/***/ 100: +/***/ (function(module, exports) { + +var toString = {}.toString; + +module.exports = Array.isArray || function (arr) { + return toString.call(arr) == '[object Array]'; +}; + + /***/ }), /***/ 102: @@ -1464,6 +1476,346 @@ try { module.exports = g; +/***/ }), + +/***/ 173: +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +function _(message, opts) { + return `${opts && opts.context ? opts.context : "Value"} ${message}.`; +} + +function type(V) { + if (V === null) { + return "Null"; + } + switch (typeof V) { + case "undefined": + return "Undefined"; + case "boolean": + return "Boolean"; + case "number": + return "Number"; + case "string": + return "String"; + case "symbol": + return "Symbol"; + case "object": + // Falls through + case "function": + // Falls through + default: + // Per ES spec, typeof returns an implemention-defined value that is not any of the existing ones for + // uncallable non-standard exotic objects. Yet Type() which the Web IDL spec depends on returns Object for + // such cases. So treat the default case as an object. + return "Object"; + } +} + +// Round x to the nearest integer, choosing the even integer if it lies halfway between two. +function evenRound(x) { + // There are four cases for numbers with fractional part being .5: + // + // case | x | floor(x) | round(x) | expected | x <> 0 | x % 1 | x & 1 | example + // 1 | 2n + 0.5 | 2n | 2n + 1 | 2n | > | 0.5 | 0 | 0.5 -> 0 + // 2 | 2n + 1.5 | 2n + 1 | 2n + 2 | 2n + 2 | > | 0.5 | 1 | 1.5 -> 2 + // 3 | -2n - 0.5 | -2n - 1 | -2n | -2n | < | -0.5 | 0 | -0.5 -> 0 + // 4 | -2n - 1.5 | -2n - 2 | -2n - 1 | -2n - 2 | < | -0.5 | 1 | -1.5 -> -2 + // (where n is a non-negative integer) + // + // Branch here for cases 1 and 4 + if ((x > 0 && (x % 1) === +0.5 && (x & 1) === 0) || + (x < 0 && (x % 1) === -0.5 && (x & 1) === 1)) { + return censorNegativeZero(Math.floor(x)); + } + + return censorNegativeZero(Math.round(x)); +} + +function integerPart(n) { + return censorNegativeZero(Math.trunc(n)); +} + +function sign(x) { + return x < 0 ? -1 : 1; +} + +function modulo(x, y) { + // https://tc39.github.io/ecma262/#eqn-modulo + // Note that http://stackoverflow.com/a/4467559/3191 does NOT work for large modulos + const signMightNotMatch = x % y; + if (sign(y) !== sign(signMightNotMatch)) { + return signMightNotMatch + y; + } + return signMightNotMatch; +} + +function censorNegativeZero(x) { + return x === 0 ? 0 : x; +} + +function createIntegerConversion(bitLength, typeOpts) { + const isSigned = !typeOpts.unsigned; + + let lowerBound; + let upperBound; + if (bitLength === 64) { + upperBound = Math.pow(2, 53) - 1; + lowerBound = !isSigned ? 0 : -Math.pow(2, 53) + 1; + } else if (!isSigned) { + lowerBound = 0; + upperBound = Math.pow(2, bitLength) - 1; + } else { + lowerBound = -Math.pow(2, bitLength - 1); + upperBound = Math.pow(2, bitLength - 1) - 1; + } + + const twoToTheBitLength = Math.pow(2, bitLength); + const twoToOneLessThanTheBitLength = Math.pow(2, bitLength - 1); + + return (V, opts) => { + if (opts === undefined) { + opts = {}; + } + + let x = +V; + x = censorNegativeZero(x); // Spec discussion ongoing: https://github.com/heycam/webidl/issues/306 + + if (opts.enforceRange) { + if (!Number.isFinite(x)) { + throw new TypeError(_("is not a finite number", opts)); + } + + x = integerPart(x); + + if (x < lowerBound || x > upperBound) { + throw new TypeError(_( + `is outside the accepted range of ${lowerBound} to ${upperBound}, inclusive`, opts)); + } + + return x; + } + + if (!Number.isNaN(x) && opts.clamp) { + x = Math.min(Math.max(x, lowerBound), upperBound); + x = evenRound(x); + return x; + } + + if (!Number.isFinite(x) || x === 0) { + return 0; + } + x = integerPart(x); + + // Math.pow(2, 64) is not accurately representable in JavaScript, so try to avoid these per-spec operations if + // possible. Hopefully it's an optimization for the non-64-bitLength cases too. + if (x >= lowerBound && x <= upperBound) { + return x; + } + + // These will not work great for bitLength of 64, but oh well. See the README for more details. + x = modulo(x, twoToTheBitLength); + if (isSigned && x >= twoToOneLessThanTheBitLength) { + return x - twoToTheBitLength; + } + return x; + }; +} + +exports.any = V => { + return V; +}; + +exports.void = function () { + return undefined; +}; + +exports.boolean = function (val) { + return !!val; +}; + +exports.byte = createIntegerConversion(8, { unsigned: false }); +exports.octet = createIntegerConversion(8, { unsigned: true }); + +exports.short = createIntegerConversion(16, { unsigned: false }); +exports["unsigned short"] = createIntegerConversion(16, { unsigned: true }); + +exports.long = createIntegerConversion(32, { unsigned: false }); +exports["unsigned long"] = createIntegerConversion(32, { unsigned: true }); + +exports["long long"] = createIntegerConversion(64, { unsigned: false }); +exports["unsigned long long"] = createIntegerConversion(64, { unsigned: true }); + +exports.double = (V, opts) => { + const x = +V; + + if (!Number.isFinite(x)) { + throw new TypeError(_("is not a finite floating-point value", opts)); + } + + return x; +}; + +exports["unrestricted double"] = V => { + const x = +V; + + return x; +}; + +exports.float = (V, opts) => { + const x = +V; + + if (!Number.isFinite(x)) { + throw new TypeError(_("is not a finite floating-point value", opts)); + } + + if (Object.is(x, -0)) { + return x; + } + + const y = Math.fround(x); + + if (!Number.isFinite(y)) { + throw new TypeError(_("is outside the range of a single-precision floating-point value", opts)); + } + + return y; +}; + +exports["unrestricted float"] = V => { + const x = +V; + + if (isNaN(x)) { + return x; + } + + if (Object.is(x, -0)) { + return x; + } + + return Math.fround(x); +}; + +exports.DOMString = function (V, opts) { + if (opts === undefined) { + opts = {}; + } + + if (opts.treatNullAsEmptyString && V === null) { + return ""; + } + + if (typeof V === "symbol") { + throw new TypeError(_("is a symbol, which cannot be converted to a string", opts)); + } + + return String(V); +}; + +exports.ByteString = (V, opts) => { + const x = exports.DOMString(V, opts); + let c; + for (let i = 0; (c = x.codePointAt(i)) !== undefined; ++i) { + if (c > 255) { + throw new TypeError(_("is not a valid ByteString", opts)); + } + } + + return x; +}; + +exports.USVString = (V, opts) => { + const S = exports.DOMString(V, opts); + const n = S.length; + const U = []; + for (let i = 0; i < n; ++i) { + const c = S.charCodeAt(i); + if (c < 0xD800 || c > 0xDFFF) { + U.push(String.fromCodePoint(c)); + } else if (0xDC00 <= c && c <= 0xDFFF) { + U.push(String.fromCodePoint(0xFFFD)); + } else if (i === n - 1) { + U.push(String.fromCodePoint(0xFFFD)); + } else { + const d = S.charCodeAt(i + 1); + if (0xDC00 <= d && d <= 0xDFFF) { + const a = c & 0x3FF; + const b = d & 0x3FF; + U.push(String.fromCodePoint((2 << 15) + ((2 << 9) * a) + b)); + ++i; + } else { + U.push(String.fromCodePoint(0xFFFD)); + } + } + } + + return U.join(""); +}; + +exports.object = (V, opts) => { + if (type(V) !== "Object") { + throw new TypeError(_("is not an object", opts)); + } + + return V; +}; + +// Not exported, but used in Function and VoidFunction. + +// Neither Function nor VoidFunction is defined with [TreatNonObjectAsNull], so +// handling for that is omitted. +function convertCallbackFunction(V, opts) { + if (typeof V !== "function") { + throw new TypeError(_("is not a function", opts)); + } + return V; +} + +[ + Error, + ArrayBuffer, // The IsDetachedBuffer abstract operation is not exposed in JS + DataView, Int8Array, Int16Array, Int32Array, Uint8Array, + Uint16Array, Uint32Array, Uint8ClampedArray, Float32Array, Float64Array +].forEach(func => { + const name = func.name; + const article = /^[AEIOU]/.test(name) ? "an" : "a"; + exports[name] = (V, opts) => { + if (!(V instanceof func)) { + throw new TypeError(_(`is not ${article} ${name} object`, opts)); + } + + return V; + }; +}); + +// Common definitions + +exports.ArrayBufferView = (V, opts) => { + if (!ArrayBuffer.isView(V)) { + throw new TypeError(_("is not a view on an ArrayBuffer object", opts)); + } + + return V; +}; + +exports.BufferSource = (V, opts) => { + if (!(ArrayBuffer.isView(V) || V instanceof ArrayBuffer)) { + throw new TypeError(_("is not an ArrayBuffer object or a view on one", opts)); + } + + return V; +}; + +exports.DOMTimeStamp = exports["unsigned long long"]; + +exports.Function = convertCallbackFunction; + +exports.VoidFunction = convertCallbackFunction; + + /***/ }), /***/ 183: @@ -2083,6 +2435,2983 @@ module.exports = __WEBPACK_EXTERNAL_MODULE_37__; /***/ }), +/***/ 397: +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +const punycode = __webpack_require__(62); +const regexes = __webpack_require__(398); +const mappingTable = __webpack_require__(399); + +function containsNonASCII(str) { + return /[^\x00-\x7F]/.test(str); +} + +function findStatus(val, { useSTD3ASCIIRules }) { + let start = 0; + let end = mappingTable.length - 1; + + while (start <= end) { + const mid = Math.floor((start + end) / 2); + + const target = mappingTable[mid]; + if (target[0][0] <= val && target[0][1] >= val) { + if (target[1].startsWith("disallowed_STD3_")) { + const newStatus = useSTD3ASCIIRules ? "disallowed" : target[1].slice(16); + return [newStatus, ...target.slice(2)]; + } + return target.slice(1); + } else if (target[0][0] > val) { + end = mid - 1; + } else { + start = mid + 1; + } + } + + return null; +} + +function mapChars(domainName, { useSTD3ASCIIRules, processingOption }) { + let hasError = false; + let processed = ""; + + for (const ch of domainName) { + const [status, mapping] = findStatus(ch.codePointAt(0), { useSTD3ASCIIRules }); + + switch (status) { + case "disallowed": + hasError = true; + processed += ch; + break; + case "ignored": + break; + case "mapped": + processed += mapping; + break; + case "deviation": + if (processingOption === "transitional") { + processed += mapping; + } else { + processed += ch; + } + break; + case "valid": + processed += ch; + break; + } + } + + return { + string: processed, + error: hasError + }; +} + +function validateLabel(label, { checkHyphens, checkBidi, checkJoiners, processingOption, useSTD3ASCIIRules }) { + if (label.normalize("NFC") !== label) { + return false; + } + + const codePoints = Array.from(label); + + if (checkHyphens) { + if ((codePoints[2] === "-" && codePoints[3] === "-") || + (label.startsWith("-") || label.endsWith("-"))) { + return false; + } + } + + if (label.includes(".") || + (codePoints.length > 0 && regexes.combiningMarks.test(codePoints[0]))) { + return false; + } + + for (const ch of codePoints) { + const [status] = findStatus(ch.codePointAt(0), { useSTD3ASCIIRules }); + if ((processingOption === "transitional" && status !== "valid") || + (processingOption === "nontransitional" && + status !== "valid" && status !== "deviation")) { + return false; + } + } + + // https://tools.ietf.org/html/rfc5892#appendix-A + if (checkJoiners) { + let last = 0; + for (const [i, ch] of codePoints.entries()) { + if (ch === "\u200C" || ch === "\u200D") { + if (i > 0) { + if (regexes.combiningClassVirama.test(codePoints[i - 1])) { + continue; + } + if (ch === "\u200C") { + // TODO: make this more efficient + const next = codePoints.indexOf("\u200C", i + 1); + const test = next < 0 ? codePoints.slice(last) : codePoints.slice(last, next); + if (regexes.validZWNJ.test(test.join(""))) { + last = i + 1; + continue; + } + } + } + return false; + } + } + } + + // https://tools.ietf.org/html/rfc5893#section-2 + if (checkBidi) { + let rtl; + + // 1 + if (regexes.bidiS1LTR.test(codePoints[0])) { + rtl = false; + } else if (regexes.bidiS1RTL.test(codePoints[0])) { + rtl = true; + } else { + return false; + } + + if (rtl) { + // 2-4 + if (!regexes.bidiS2.test(label) || + !regexes.bidiS3.test(label) || + (regexes.bidiS4EN.test(label) && regexes.bidiS4AN.test(label))) { + return false; + } + } else if (!regexes.bidiS5.test(label) || + !regexes.bidiS6.test(label)) { // 5-6 + return false; + } + } + + return true; +} + +function isBidiDomain(labels) { + const domain = labels.map(label => { + if (label.startsWith("xn--")) { + try { + return punycode.decode(label.substring(4)); + } catch (err) { + return ""; + } + } + return label; + }).join("."); + return regexes.bidiDomain.test(domain); +} + +function processing(domainName, options) { + const { processingOption } = options; + + // 1. Map. + let { string, error } = mapChars(domainName, options); + + // 2. Normalize. + string = string.normalize("NFC"); + + // 3. Break. + const labels = string.split("."); + const isBidi = isBidiDomain(labels); + + // 4. Convert/Validate. + for (const [i, origLabel] of labels.entries()) { + let label = origLabel; + let curProcessing = processingOption; + if (label.startsWith("xn--")) { + try { + label = punycode.decode(label.substring(4)); + labels[i] = label; + } catch (err) { + error = true; + continue; + } + curProcessing = "nontransitional"; + } + + // No need to validate if we already know there is an error. + if (error) { + continue; + } + const validation = validateLabel(label, Object.assign({}, options, { + processingOption: curProcessing, + checkBidi: options.checkBidi && isBidi + })); + if (!validation) { + error = true; + } + } + + return { + string: labels.join("."), + error + }; +} + +function toASCII(domainName, { + checkHyphens = false, + checkBidi = false, + checkJoiners = false, + useSTD3ASCIIRules = false, + processingOption = "nontransitional", + verifyDNSLength = false +} = {}) { + if (processingOption !== "transitional" && processingOption !== "nontransitional") { + throw new RangeError("processingOption must be either transitional or nontransitional"); + } + + const result = processing(domainName, { + processingOption, + checkHyphens, + checkBidi, + checkJoiners, + useSTD3ASCIIRules + }); + let labels = result.string.split("."); + labels = labels.map(l => { + if (containsNonASCII(l)) { + try { + return "xn--" + punycode.encode(l); + } catch (e) { + result.error = true; + } + } + return l; + }); + + if (verifyDNSLength) { + const total = labels.join(".").length; + if (total > 253 || total === 0) { + result.error = true; + } + + for (let i = 0; i < labels.length; ++i) { + if (labels[i].length > 63 || labels[i].length === 0) { + result.error = true; + break; + } + } + } + + if (result.error) { + return null; + } + return labels.join("."); +} + +function toUnicode(domainName, { + checkHyphens = false, + checkBidi = false, + checkJoiners = false, + useSTD3ASCIIRules = false +} = {}) { + const result = processing(domainName, { + processingOption: "nontransitional", + checkHyphens, + checkBidi, + checkJoiners, + useSTD3ASCIIRules + }); + + return { + domain: result.string, + error: result.error + }; +} + +module.exports = { + toASCII, + toUnicode +}; + + +/***/ }), + +/***/ 398: +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +const combiningMarks = /[\u0300-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08D4-\u08E1\u08E3-\u0903\u093A-\u093C\u093E-\u094F\u0951-\u0957\u0962\u0963\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0AFA-\u0AFF\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B62\u0B63\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C00-\u0C03\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C81-\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0D00-\u0D03\u0D3B\u0D3C\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D82\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0F18\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102B-\u103E\u1056-\u1059\u105E-\u1060\u1062-\u1064\u1067-\u106D\u1071-\u1074\u1082-\u108D\u108F\u109A-\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4-\u17D3\u17DD\u180B-\u180D\u1885\u1886\u18A9\u1920-\u192B\u1930-\u193B\u1A17-\u1A1B\u1A55-\u1A5E\u1A60-\u1A7C\u1A7F\u1AB0-\u1ABE\u1B00-\u1B04\u1B34-\u1B44\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAD\u1BE6-\u1BF3\u1C24-\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE8\u1CED\u1CF2-\u1CF4\u1CF7-\u1CF9\u1DC0-\u1DF9\u1DFB-\u1DFF\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3099\u309A\uA66F-\uA672\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA823-\uA827\uA880\uA881\uA8B4-\uA8C5\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA953\uA980-\uA983\uA9B3-\uA9C0\uA9E5\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uAA7B-\uAA7D\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEB-\uAAEF\uAAF5\uAAF6\uABE3-\uABEA\uABEC\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F\u{101FD}\u{102E0}\u{10376}-\u{1037A}\u{10A01}-\u{10A03}\u{10A05}\u{10A06}\u{10A0C}-\u{10A0F}\u{10A38}-\u{10A3A}\u{10A3F}\u{10AE5}\u{10AE6}\u{11000}-\u{11002}\u{11038}-\u{11046}\u{1107F}-\u{11082}\u{110B0}-\u{110BA}\u{11100}-\u{11102}\u{11127}-\u{11134}\u{11173}\u{11180}-\u{11182}\u{111B3}-\u{111C0}\u{111CA}-\u{111CC}\u{1122C}-\u{11237}\u{1123E}\u{112DF}-\u{112EA}\u{11300}-\u{11303}\u{1133C}\u{1133E}-\u{11344}\u{11347}\u{11348}\u{1134B}-\u{1134D}\u{11357}\u{11362}\u{11363}\u{11366}-\u{1136C}\u{11370}-\u{11374}\u{11435}-\u{11446}\u{114B0}-\u{114C3}\u{115AF}-\u{115B5}\u{115B8}-\u{115C0}\u{115DC}\u{115DD}\u{11630}-\u{11640}\u{116AB}-\u{116B7}\u{1171D}-\u{1172B}\u{11A01}-\u{11A0A}\u{11A33}-\u{11A39}\u{11A3B}-\u{11A3E}\u{11A47}\u{11A51}-\u{11A5B}\u{11A8A}-\u{11A99}\u{11C2F}-\u{11C36}\u{11C38}-\u{11C3F}\u{11C92}-\u{11CA7}\u{11CA9}-\u{11CB6}\u{11D31}-\u{11D36}\u{11D3A}\u{11D3C}\u{11D3D}\u{11D3F}-\u{11D45}\u{11D47}\u{16AF0}-\u{16AF4}\u{16B30}-\u{16B36}\u{16F51}-\u{16F7E}\u{16F8F}-\u{16F92}\u{1BC9D}\u{1BC9E}\u{1D165}-\u{1D169}\u{1D16D}-\u{1D172}\u{1D17B}-\u{1D182}\u{1D185}-\u{1D18B}\u{1D1AA}-\u{1D1AD}\u{1D242}-\u{1D244}\u{1DA00}-\u{1DA36}\u{1DA3B}-\u{1DA6C}\u{1DA75}\u{1DA84}\u{1DA9B}-\u{1DA9F}\u{1DAA1}-\u{1DAAF}\u{1E000}-\u{1E006}\u{1E008}-\u{1E018}\u{1E01B}-\u{1E021}\u{1E023}\u{1E024}\u{1E026}-\u{1E02A}\u{1E8D0}-\u{1E8D6}\u{1E944}-\u{1E94A}\u{E0100}-\u{E01EF}]/u; +const combiningClassVirama = /[\u094D\u09CD\u0A4D\u0ACD\u0B4D\u0BCD\u0C4D\u0CCD\u0D3B\u0D3C\u0D4D\u0DCA\u0E3A\u0F84\u1039\u103A\u1714\u1734\u17D2\u1A60\u1B44\u1BAA\u1BAB\u1BF2\u1BF3\u2D7F\uA806\uA8C4\uA953\uA9C0\uAAF6\uABED\u{10A3F}\u{11046}\u{1107F}\u{110B9}\u{11133}\u{11134}\u{111C0}\u{11235}\u{112EA}\u{1134D}\u{11442}\u{114C2}\u{115BF}\u{1163F}\u{116B6}\u{1172B}\u{11A34}\u{11A47}\u{11A99}\u{11C3F}\u{11D44}\u{11D45}]/u; +const validZWNJ = /[\u0620\u0626\u0628\u062A-\u062E\u0633-\u063F\u0641-\u0647\u0649\u064A\u066E\u066F\u0678-\u0687\u069A-\u06BF\u06C1\u06C2\u06CC\u06CE\u06D0\u06D1\u06FA-\u06FC\u06FF\u0712-\u0714\u071A-\u071D\u071F-\u0727\u0729\u072B\u072D\u072E\u074E-\u0758\u075C-\u076A\u076D-\u0770\u0772\u0775-\u0777\u077A-\u077F\u07CA-\u07EA\u0841-\u0845\u0848\u084A-\u0853\u0855\u0860\u0862-\u0865\u0868\u08A0-\u08A9\u08AF\u08B0\u08B3\u08B4\u08B6-\u08B8\u08BA-\u08BD\u1807\u1820-\u1877\u1887-\u18A8\u18AA\uA840-\uA872\u{10AC0}-\u{10AC4}\u{10ACD}\u{10AD3}-\u{10ADC}\u{10ADE}-\u{10AE0}\u{10AEB}-\u{10AEE}\u{10B80}\u{10B82}\u{10B86}-\u{10B88}\u{10B8A}\u{10B8B}\u{10B8D}\u{10B90}\u{10BAD}\u{10BAE}\u{1E900}-\u{1E943}][\xAD\u0300-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u061C\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u070F\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08D4-\u08E1\u08E3-\u0902\u093A\u093C\u0941-\u0948\u094D\u0951-\u0957\u0962\u0963\u0981\u09BC\u09C1-\u09C4\u09CD\u09E2\u09E3\u0A01\u0A02\u0A3C\u0A41\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81\u0A82\u0ABC\u0AC1-\u0AC5\u0AC7\u0AC8\u0ACD\u0AE2\u0AE3\u0AFA-\u0AFF\u0B01\u0B3C\u0B3F\u0B41-\u0B44\u0B4D\u0B56\u0B62\u0B63\u0B82\u0BC0\u0BCD\u0C00\u0C3E-\u0C40\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C81\u0CBC\u0CBF\u0CC6\u0CCC\u0CCD\u0CE2\u0CE3\u0D00\u0D01\u0D3B\u0D3C\u0D41-\u0D44\u0D4D\u0D62\u0D63\u0DCA\u0DD2-\u0DD4\u0DD6\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0F18\u0F19\u0F35\u0F37\u0F39\u0F71-\u0F7E\u0F80-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102D-\u1030\u1032-\u1037\u1039\u103A\u103D\u103E\u1058\u1059\u105E-\u1060\u1071-\u1074\u1082\u1085\u1086\u108D\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4\u17B5\u17B7-\u17BD\u17C6\u17C9-\u17D3\u17DD\u180B-\u180D\u1885\u1886\u18A9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193B\u1A17\u1A18\u1A1B\u1A56\u1A58-\u1A5E\u1A60\u1A62\u1A65-\u1A6C\u1A73-\u1A7C\u1A7F\u1AB0-\u1ABE\u1B00-\u1B03\u1B34\u1B36-\u1B3A\u1B3C\u1B42\u1B6B-\u1B73\u1B80\u1B81\u1BA2-\u1BA5\u1BA8\u1BA9\u1BAB-\u1BAD\u1BE6\u1BE8\u1BE9\u1BED\u1BEF-\u1BF1\u1C2C-\u1C33\u1C36\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE0\u1CE2-\u1CE8\u1CED\u1CF4\u1CF8\u1CF9\u1DC0-\u1DF9\u1DFB-\u1DFF\u200B\u200E\u200F\u202A-\u202E\u2060-\u2064\u206A-\u206F\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302D\u3099\u309A\uA66F-\uA672\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA825\uA826\uA8C4\uA8C5\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA951\uA980-\uA982\uA9B3\uA9B6-\uA9B9\uA9BC\uA9E5\uAA29-\uAA2E\uAA31\uAA32\uAA35\uAA36\uAA43\uAA4C\uAA7C\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEC\uAAED\uAAF6\uABE5\uABE8\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F\uFEFF\uFFF9-\uFFFB\u{101FD}\u{102E0}\u{10376}-\u{1037A}\u{10A01}-\u{10A03}\u{10A05}\u{10A06}\u{10A0C}-\u{10A0F}\u{10A38}-\u{10A3A}\u{10A3F}\u{10AE5}\u{10AE6}\u{11001}\u{11038}-\u{11046}\u{1107F}-\u{11081}\u{110B3}-\u{110B6}\u{110B9}\u{110BA}\u{110BD}\u{11100}-\u{11102}\u{11127}-\u{1112B}\u{1112D}-\u{11134}\u{11173}\u{11180}\u{11181}\u{111B6}-\u{111BE}\u{111CA}-\u{111CC}\u{1122F}-\u{11231}\u{11234}\u{11236}\u{11237}\u{1123E}\u{112DF}\u{112E3}-\u{112EA}\u{11300}\u{11301}\u{1133C}\u{11340}\u{11366}-\u{1136C}\u{11370}-\u{11374}\u{11438}-\u{1143F}\u{11442}-\u{11444}\u{11446}\u{114B3}-\u{114B8}\u{114BA}\u{114BF}\u{114C0}\u{114C2}\u{114C3}\u{115B2}-\u{115B5}\u{115BC}\u{115BD}\u{115BF}\u{115C0}\u{115DC}\u{115DD}\u{11633}-\u{1163A}\u{1163D}\u{1163F}\u{11640}\u{116AB}\u{116AD}\u{116B0}-\u{116B5}\u{116B7}\u{1171D}-\u{1171F}\u{11722}-\u{11725}\u{11727}-\u{1172B}\u{11A01}-\u{11A06}\u{11A09}\u{11A0A}\u{11A33}-\u{11A38}\u{11A3B}-\u{11A3E}\u{11A47}\u{11A51}-\u{11A56}\u{11A59}-\u{11A5B}\u{11A8A}-\u{11A96}\u{11A98}\u{11A99}\u{11C30}-\u{11C36}\u{11C38}-\u{11C3D}\u{11C3F}\u{11C92}-\u{11CA7}\u{11CAA}-\u{11CB0}\u{11CB2}\u{11CB3}\u{11CB5}\u{11CB6}\u{11D31}-\u{11D36}\u{11D3A}\u{11D3C}\u{11D3D}\u{11D3F}-\u{11D45}\u{11D47}\u{16AF0}-\u{16AF4}\u{16B30}-\u{16B36}\u{16F8F}-\u{16F92}\u{1BC9D}\u{1BC9E}\u{1BCA0}-\u{1BCA3}\u{1D167}-\u{1D169}\u{1D173}-\u{1D182}\u{1D185}-\u{1D18B}\u{1D1AA}-\u{1D1AD}\u{1D242}-\u{1D244}\u{1DA00}-\u{1DA36}\u{1DA3B}-\u{1DA6C}\u{1DA75}\u{1DA84}\u{1DA9B}-\u{1DA9F}\u{1DAA1}-\u{1DAAF}\u{1E000}-\u{1E006}\u{1E008}-\u{1E018}\u{1E01B}-\u{1E021}\u{1E023}\u{1E024}\u{1E026}-\u{1E02A}\u{1E8D0}-\u{1E8D6}\u{1E944}-\u{1E94A}\u{E0001}\u{E0020}-\u{E007F}\u{E0100}-\u{E01EF}]*\u200C[\xAD\u0300-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u061C\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u070F\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08D4-\u08E1\u08E3-\u0902\u093A\u093C\u0941-\u0948\u094D\u0951-\u0957\u0962\u0963\u0981\u09BC\u09C1-\u09C4\u09CD\u09E2\u09E3\u0A01\u0A02\u0A3C\u0A41\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81\u0A82\u0ABC\u0AC1-\u0AC5\u0AC7\u0AC8\u0ACD\u0AE2\u0AE3\u0AFA-\u0AFF\u0B01\u0B3C\u0B3F\u0B41-\u0B44\u0B4D\u0B56\u0B62\u0B63\u0B82\u0BC0\u0BCD\u0C00\u0C3E-\u0C40\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C81\u0CBC\u0CBF\u0CC6\u0CCC\u0CCD\u0CE2\u0CE3\u0D00\u0D01\u0D3B\u0D3C\u0D41-\u0D44\u0D4D\u0D62\u0D63\u0DCA\u0DD2-\u0DD4\u0DD6\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0F18\u0F19\u0F35\u0F37\u0F39\u0F71-\u0F7E\u0F80-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102D-\u1030\u1032-\u1037\u1039\u103A\u103D\u103E\u1058\u1059\u105E-\u1060\u1071-\u1074\u1082\u1085\u1086\u108D\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4\u17B5\u17B7-\u17BD\u17C6\u17C9-\u17D3\u17DD\u180B-\u180D\u1885\u1886\u18A9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193B\u1A17\u1A18\u1A1B\u1A56\u1A58-\u1A5E\u1A60\u1A62\u1A65-\u1A6C\u1A73-\u1A7C\u1A7F\u1AB0-\u1ABE\u1B00-\u1B03\u1B34\u1B36-\u1B3A\u1B3C\u1B42\u1B6B-\u1B73\u1B80\u1B81\u1BA2-\u1BA5\u1BA8\u1BA9\u1BAB-\u1BAD\u1BE6\u1BE8\u1BE9\u1BED\u1BEF-\u1BF1\u1C2C-\u1C33\u1C36\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE0\u1CE2-\u1CE8\u1CED\u1CF4\u1CF8\u1CF9\u1DC0-\u1DF9\u1DFB-\u1DFF\u200B\u200E\u200F\u202A-\u202E\u2060-\u2064\u206A-\u206F\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302D\u3099\u309A\uA66F-\uA672\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA825\uA826\uA8C4\uA8C5\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA951\uA980-\uA982\uA9B3\uA9B6-\uA9B9\uA9BC\uA9E5\uAA29-\uAA2E\uAA31\uAA32\uAA35\uAA36\uAA43\uAA4C\uAA7C\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEC\uAAED\uAAF6\uABE5\uABE8\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F\uFEFF\uFFF9-\uFFFB\u{101FD}\u{102E0}\u{10376}-\u{1037A}\u{10A01}-\u{10A03}\u{10A05}\u{10A06}\u{10A0C}-\u{10A0F}\u{10A38}-\u{10A3A}\u{10A3F}\u{10AE5}\u{10AE6}\u{11001}\u{11038}-\u{11046}\u{1107F}-\u{11081}\u{110B3}-\u{110B6}\u{110B9}\u{110BA}\u{110BD}\u{11100}-\u{11102}\u{11127}-\u{1112B}\u{1112D}-\u{11134}\u{11173}\u{11180}\u{11181}\u{111B6}-\u{111BE}\u{111CA}-\u{111CC}\u{1122F}-\u{11231}\u{11234}\u{11236}\u{11237}\u{1123E}\u{112DF}\u{112E3}-\u{112EA}\u{11300}\u{11301}\u{1133C}\u{11340}\u{11366}-\u{1136C}\u{11370}-\u{11374}\u{11438}-\u{1143F}\u{11442}-\u{11444}\u{11446}\u{114B3}-\u{114B8}\u{114BA}\u{114BF}\u{114C0}\u{114C2}\u{114C3}\u{115B2}-\u{115B5}\u{115BC}\u{115BD}\u{115BF}\u{115C0}\u{115DC}\u{115DD}\u{11633}-\u{1163A}\u{1163D}\u{1163F}\u{11640}\u{116AB}\u{116AD}\u{116B0}-\u{116B5}\u{116B7}\u{1171D}-\u{1171F}\u{11722}-\u{11725}\u{11727}-\u{1172B}\u{11A01}-\u{11A06}\u{11A09}\u{11A0A}\u{11A33}-\u{11A38}\u{11A3B}-\u{11A3E}\u{11A47}\u{11A51}-\u{11A56}\u{11A59}-\u{11A5B}\u{11A8A}-\u{11A96}\u{11A98}\u{11A99}\u{11C30}-\u{11C36}\u{11C38}-\u{11C3D}\u{11C3F}\u{11C92}-\u{11CA7}\u{11CAA}-\u{11CB0}\u{11CB2}\u{11CB3}\u{11CB5}\u{11CB6}\u{11D31}-\u{11D36}\u{11D3A}\u{11D3C}\u{11D3D}\u{11D3F}-\u{11D45}\u{11D47}\u{16AF0}-\u{16AF4}\u{16B30}-\u{16B36}\u{16F8F}-\u{16F92}\u{1BC9D}\u{1BC9E}\u{1BCA0}-\u{1BCA3}\u{1D167}-\u{1D169}\u{1D173}-\u{1D182}\u{1D185}-\u{1D18B}\u{1D1AA}-\u{1D1AD}\u{1D242}-\u{1D244}\u{1DA00}-\u{1DA36}\u{1DA3B}-\u{1DA6C}\u{1DA75}\u{1DA84}\u{1DA9B}-\u{1DA9F}\u{1DAA1}-\u{1DAAF}\u{1E000}-\u{1E006}\u{1E008}-\u{1E018}\u{1E01B}-\u{1E021}\u{1E023}\u{1E024}\u{1E026}-\u{1E02A}\u{1E8D0}-\u{1E8D6}\u{1E944}-\u{1E94A}\u{E0001}\u{E0020}-\u{E007F}\u{E0100}-\u{E01EF}]*[\u0620\u0622-\u063F\u0641-\u064A\u066E\u066F\u0671-\u0673\u0675-\u06D3\u06D5\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u077F\u07CA-\u07EA\u0840-\u0855\u0860\u0862-\u0865\u0867-\u086A\u08A0-\u08AC\u08AE-\u08B4\u08B6-\u08BD\u1807\u1820-\u1877\u1887-\u18A8\u18AA\uA840-\uA871\u{10AC0}-\u{10AC5}\u{10AC7}\u{10AC9}\u{10ACA}\u{10ACE}-\u{10AD6}\u{10AD8}-\u{10AE1}\u{10AE4}\u{10AEB}-\u{10AEF}\u{10B80}-\u{10B91}\u{10BA9}-\u{10BAE}\u{1E900}-\u{1E943}]/u; +const bidiDomain = /[\u05BE\u05C0\u05C3\u05C6\u05D0-\u05EA\u05F0-\u05F4\u0600-\u0605\u0608\u060B\u060D\u061B\u061C\u061E-\u064A\u0660-\u0669\u066B-\u066F\u0671-\u06D5\u06DD\u06E5\u06E6\u06EE\u06EF\u06FA-\u070D\u070F\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07C0-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0830-\u083E\u0840-\u0858\u085E\u0860-\u086A\u08A0-\u08B4\u08B6-\u08BD\u08E2\u200F\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBC1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFC\uFE70-\uFE74\uFE76-\uFEFC\u{10800}-\u{10805}\u{10808}\u{1080A}-\u{10835}\u{10837}\u{10838}\u{1083C}\u{1083F}-\u{10855}\u{10857}-\u{1089E}\u{108A7}-\u{108AF}\u{108E0}-\u{108F2}\u{108F4}\u{108F5}\u{108FB}-\u{1091B}\u{10920}-\u{10939}\u{1093F}\u{10980}-\u{109B7}\u{109BC}-\u{109CF}\u{109D2}-\u{10A00}\u{10A10}-\u{10A13}\u{10A15}-\u{10A17}\u{10A19}-\u{10A33}\u{10A40}-\u{10A47}\u{10A50}-\u{10A58}\u{10A60}-\u{10A9F}\u{10AC0}-\u{10AE4}\u{10AEB}-\u{10AF6}\u{10B00}-\u{10B35}\u{10B40}-\u{10B55}\u{10B58}-\u{10B72}\u{10B78}-\u{10B91}\u{10B99}-\u{10B9C}\u{10BA9}-\u{10BAF}\u{10C00}-\u{10C48}\u{10C80}-\u{10CB2}\u{10CC0}-\u{10CF2}\u{10CFA}-\u{10CFF}\u{10E60}-\u{10E7E}\u{1E800}-\u{1E8C4}\u{1E8C7}-\u{1E8CF}\u{1E900}-\u{1E943}\u{1E950}-\u{1E959}\u{1E95E}\u{1E95F}\u{1EE00}-\u{1EE03}\u{1EE05}-\u{1EE1F}\u{1EE21}\u{1EE22}\u{1EE24}\u{1EE27}\u{1EE29}-\u{1EE32}\u{1EE34}-\u{1EE37}\u{1EE39}\u{1EE3B}\u{1EE42}\u{1EE47}\u{1EE49}\u{1EE4B}\u{1EE4D}-\u{1EE4F}\u{1EE51}\u{1EE52}\u{1EE54}\u{1EE57}\u{1EE59}\u{1EE5B}\u{1EE5D}\u{1EE5F}\u{1EE61}\u{1EE62}\u{1EE64}\u{1EE67}-\u{1EE6A}\u{1EE6C}-\u{1EE72}\u{1EE74}-\u{1EE77}\u{1EE79}-\u{1EE7C}\u{1EE7E}\u{1EE80}-\u{1EE89}\u{1EE8B}-\u{1EE9B}\u{1EEA1}-\u{1EEA3}\u{1EEA5}-\u{1EEA9}\u{1EEAB}-\u{1EEBB}]/u; +const bidiS1LTR = /[A-Za-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02B8\u02BB-\u02C1\u02D0\u02D1\u02E0-\u02E4\u02EE\u0370-\u0373\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0482\u048A-\u052F\u0531-\u0556\u0559-\u055F\u0561-\u0587\u0589\u0903-\u0939\u093B\u093D-\u0940\u0949-\u094C\u094E-\u0950\u0958-\u0961\u0964-\u0980\u0982\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD-\u09C0\u09C7\u09C8\u09CB\u09CC\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E1\u09E6-\u09F1\u09F4-\u09FA\u09FC\u09FD\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3E-\u0A40\u0A59-\u0A5C\u0A5E\u0A66-\u0A6F\u0A72-\u0A74\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD-\u0AC0\u0AC9\u0ACB\u0ACC\u0AD0\u0AE0\u0AE1\u0AE6-\u0AF0\u0AF9\u0B02\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B3E\u0B40\u0B47\u0B48\u0B4B\u0B4C\u0B57\u0B5C\u0B5D\u0B5F-\u0B61\u0B66-\u0B77\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE\u0BBF\u0BC1\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCC\u0BD0\u0BD7\u0BE6-\u0BF2\u0C01-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C41-\u0C44\u0C58-\u0C5A\u0C60\u0C61\u0C66-\u0C6F\u0C7F\u0C80\u0C82\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD-\u0CC4\u0CC6-\u0CC8\u0CCA\u0CCB\u0CD5\u0CD6\u0CDE\u0CE0\u0CE1\u0CE6-\u0CEF\u0CF1\u0CF2\u0D02\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D40\u0D46-\u0D48\u0D4A-\u0D4C\u0D4E\u0D4F\u0D54-\u0D61\u0D66-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCF-\u0DD1\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2-\u0DF4\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E4F-\u0E5B\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00-\u0F17\u0F1A-\u0F34\u0F36\u0F38\u0F3E-\u0F47\u0F49-\u0F6C\u0F7F\u0F85\u0F88-\u0F8C\u0FBE-\u0FC5\u0FC7-\u0FCC\u0FCE-\u0FDA\u1000-\u102C\u1031\u1038\u103B\u103C\u103F-\u1057\u105A-\u105D\u1061-\u1070\u1075-\u1081\u1083\u1084\u1087-\u108C\u108E-\u109C\u109E-\u10C5\u10C7\u10CD\u10D0-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1360-\u137C\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u167F\u1681-\u169A\u16A0-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1735\u1736\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17B6\u17BE-\u17C5\u17C7\u17C8\u17D4-\u17DA\u17DC\u17E0-\u17E9\u1810-\u1819\u1820-\u1877\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1923-\u1926\u1929-\u192B\u1930\u1931\u1933-\u1938\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u1A00-\u1A16\u1A19\u1A1A\u1A1E-\u1A55\u1A57\u1A61\u1A63\u1A64\u1A6D-\u1A72\u1A80-\u1A89\u1A90-\u1A99\u1AA0-\u1AAD\u1B04-\u1B33\u1B35\u1B3B\u1B3D-\u1B41\u1B43-\u1B4B\u1B50-\u1B6A\u1B74-\u1B7C\u1B82-\u1BA1\u1BA6\u1BA7\u1BAA\u1BAE-\u1BE5\u1BE7\u1BEA-\u1BEC\u1BEE\u1BF2\u1BF3\u1BFC-\u1C2B\u1C34\u1C35\u1C3B-\u1C49\u1C4D-\u1C88\u1CC0-\u1CC7\u1CD3\u1CE1\u1CE9-\u1CEC\u1CEE-\u1CF3\u1CF5-\u1CF7\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200E\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u214F\u2160-\u2188\u2336-\u237A\u2395\u249C-\u24E9\u26AC\u2800-\u28FF\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D70\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005-\u3007\u3021-\u3029\u302E\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312E\u3131-\u318E\u3190-\u31BA\u31F0-\u321C\u3220-\u324F\u3260-\u327B\u327F-\u32B0\u32C0-\u32CB\u32D0-\u32FE\u3300-\u3376\u337B-\u33DD\u33E0-\u33FE\u3400-\u4DB5\u4E00-\u9FEA\uA000-\uA48C\uA4D0-\uA60C\uA610-\uA62B\uA640-\uA66E\uA680-\uA69D\uA6A0-\uA6EF\uA6F2-\uA6F7\uA722-\uA787\uA789-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA824\uA827\uA830-\uA837\uA840-\uA873\uA880-\uA8C3\uA8CE-\uA8D9\uA8F2-\uA8FD\uA900-\uA925\uA92E-\uA946\uA952\uA953\uA95F-\uA97C\uA983-\uA9B2\uA9B4\uA9B5\uA9BA\uA9BB\uA9BD-\uA9CD\uA9CF-\uA9D9\uA9DE-\uA9E4\uA9E6-\uA9FE\uAA00-\uAA28\uAA2F\uAA30\uAA33\uAA34\uAA40-\uAA42\uAA44-\uAA4B\uAA4D\uAA50-\uAA59\uAA5C-\uAA7B\uAA7D-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAAEB\uAAEE-\uAAF5\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB65\uAB70-\uABE4\uABE6\uABE7\uABE9-\uABEC\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uD800-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC\u{10000}-\u{1000B}\u{1000D}-\u{10026}\u{10028}-\u{1003A}\u{1003C}\u{1003D}\u{1003F}-\u{1004D}\u{10050}-\u{1005D}\u{10080}-\u{100FA}\u{10100}\u{10102}\u{10107}-\u{10133}\u{10137}-\u{1013F}\u{1018D}\u{1018E}\u{101D0}-\u{101FC}\u{10280}-\u{1029C}\u{102A0}-\u{102D0}\u{10300}-\u{10323}\u{1032D}-\u{1034A}\u{10350}-\u{10375}\u{10380}-\u{1039D}\u{1039F}-\u{103C3}\u{103C8}-\u{103D5}\u{10400}-\u{1049D}\u{104A0}-\u{104A9}\u{104B0}-\u{104D3}\u{104D8}-\u{104FB}\u{10500}-\u{10527}\u{10530}-\u{10563}\u{1056F}\u{10600}-\u{10736}\u{10740}-\u{10755}\u{10760}-\u{10767}\u{11000}\u{11002}-\u{11037}\u{11047}-\u{1104D}\u{11066}-\u{1106F}\u{11082}-\u{110B2}\u{110B7}\u{110B8}\u{110BB}-\u{110C1}\u{110D0}-\u{110E8}\u{110F0}-\u{110F9}\u{11103}-\u{11126}\u{1112C}\u{11136}-\u{11143}\u{11150}-\u{11172}\u{11174}-\u{11176}\u{11182}-\u{111B5}\u{111BF}-\u{111C9}\u{111CD}\u{111D0}-\u{111DF}\u{111E1}-\u{111F4}\u{11200}-\u{11211}\u{11213}-\u{1122E}\u{11232}\u{11233}\u{11235}\u{11238}-\u{1123D}\u{11280}-\u{11286}\u{11288}\u{1128A}-\u{1128D}\u{1128F}-\u{1129D}\u{1129F}-\u{112A9}\u{112B0}-\u{112DE}\u{112E0}-\u{112E2}\u{112F0}-\u{112F9}\u{11302}\u{11303}\u{11305}-\u{1130C}\u{1130F}\u{11310}\u{11313}-\u{11328}\u{1132A}-\u{11330}\u{11332}\u{11333}\u{11335}-\u{11339}\u{1133D}-\u{1133F}\u{11341}-\u{11344}\u{11347}\u{11348}\u{1134B}-\u{1134D}\u{11350}\u{11357}\u{1135D}-\u{11363}\u{11400}-\u{11437}\u{11440}\u{11441}\u{11445}\u{11447}-\u{11459}\u{1145B}\u{1145D}\u{11480}-\u{114B2}\u{114B9}\u{114BB}-\u{114BE}\u{114C1}\u{114C4}-\u{114C7}\u{114D0}-\u{114D9}\u{11580}-\u{115B1}\u{115B8}-\u{115BB}\u{115BE}\u{115C1}-\u{115DB}\u{11600}-\u{11632}\u{1163B}\u{1163C}\u{1163E}\u{11641}-\u{11644}\u{11650}-\u{11659}\u{11680}-\u{116AA}\u{116AC}\u{116AE}\u{116AF}\u{116B6}\u{116C0}-\u{116C9}\u{11700}-\u{11719}\u{11720}\u{11721}\u{11726}\u{11730}-\u{1173F}\u{118A0}-\u{118F2}\u{118FF}\u{11A00}\u{11A07}\u{11A08}\u{11A0B}-\u{11A32}\u{11A39}\u{11A3A}\u{11A3F}-\u{11A46}\u{11A50}\u{11A57}\u{11A58}\u{11A5C}-\u{11A83}\u{11A86}-\u{11A89}\u{11A97}\u{11A9A}-\u{11A9C}\u{11A9E}-\u{11AA2}\u{11AC0}-\u{11AF8}\u{11C00}-\u{11C08}\u{11C0A}-\u{11C2F}\u{11C3E}-\u{11C45}\u{11C50}-\u{11C6C}\u{11C70}-\u{11C8F}\u{11CA9}\u{11CB1}\u{11CB4}\u{11D00}-\u{11D06}\u{11D08}\u{11D09}\u{11D0B}-\u{11D30}\u{11D46}\u{11D50}-\u{11D59}\u{12000}-\u{12399}\u{12400}-\u{1246E}\u{12470}-\u{12474}\u{12480}-\u{12543}\u{13000}-\u{1342E}\u{14400}-\u{14646}\u{16800}-\u{16A38}\u{16A40}-\u{16A5E}\u{16A60}-\u{16A69}\u{16A6E}\u{16A6F}\u{16AD0}-\u{16AED}\u{16AF5}\u{16B00}-\u{16B2F}\u{16B37}-\u{16B45}\u{16B50}-\u{16B59}\u{16B5B}-\u{16B61}\u{16B63}-\u{16B77}\u{16B7D}-\u{16B8F}\u{16F00}-\u{16F44}\u{16F50}-\u{16F7E}\u{16F93}-\u{16F9F}\u{16FE0}\u{16FE1}\u{17000}-\u{187EC}\u{18800}-\u{18AF2}\u{1B000}-\u{1B11E}\u{1B170}-\u{1B2FB}\u{1BC00}-\u{1BC6A}\u{1BC70}-\u{1BC7C}\u{1BC80}-\u{1BC88}\u{1BC90}-\u{1BC99}\u{1BC9C}\u{1BC9F}\u{1D000}-\u{1D0F5}\u{1D100}-\u{1D126}\u{1D129}-\u{1D166}\u{1D16A}-\u{1D172}\u{1D183}\u{1D184}\u{1D18C}-\u{1D1A9}\u{1D1AE}-\u{1D1E8}\u{1D360}-\u{1D371}\u{1D400}-\u{1D454}\u{1D456}-\u{1D49C}\u{1D49E}\u{1D49F}\u{1D4A2}\u{1D4A5}\u{1D4A6}\u{1D4A9}-\u{1D4AC}\u{1D4AE}-\u{1D4B9}\u{1D4BB}\u{1D4BD}-\u{1D4C3}\u{1D4C5}-\u{1D505}\u{1D507}-\u{1D50A}\u{1D50D}-\u{1D514}\u{1D516}-\u{1D51C}\u{1D51E}-\u{1D539}\u{1D53B}-\u{1D53E}\u{1D540}-\u{1D544}\u{1D546}\u{1D54A}-\u{1D550}\u{1D552}-\u{1D6A5}\u{1D6A8}-\u{1D6DA}\u{1D6DC}-\u{1D714}\u{1D716}-\u{1D74E}\u{1D750}-\u{1D788}\u{1D78A}-\u{1D7C2}\u{1D7C4}-\u{1D7CB}\u{1D800}-\u{1D9FF}\u{1DA37}-\u{1DA3A}\u{1DA6D}-\u{1DA74}\u{1DA76}-\u{1DA83}\u{1DA85}-\u{1DA8B}\u{1F110}-\u{1F12E}\u{1F130}-\u{1F169}\u{1F170}-\u{1F1AC}\u{1F1E6}-\u{1F202}\u{1F210}-\u{1F23B}\u{1F240}-\u{1F248}\u{1F250}\u{1F251}\u{20000}-\u{2A6D6}\u{2A700}-\u{2B734}\u{2B740}-\u{2B81D}\u{2B820}-\u{2CEA1}\u{2CEB0}-\u{2EBE0}\u{2F800}-\u{2FA1D}\u{F0000}-\u{FFFFD}\u{100000}-\u{10FFFD}]/u; +const bidiS1RTL = /[\u05BE\u05C0\u05C3\u05C6\u05D0-\u05EA\u05F0-\u05F4\u0608\u060B\u060D\u061B\u061C\u061E-\u064A\u066D-\u066F\u0671-\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u070D\u070F\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07C0-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0830-\u083E\u0840-\u0858\u085E\u0860-\u086A\u08A0-\u08B4\u08B6-\u08BD\u200F\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBC1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFC\uFE70-\uFE74\uFE76-\uFEFC\u{10800}-\u{10805}\u{10808}\u{1080A}-\u{10835}\u{10837}\u{10838}\u{1083C}\u{1083F}-\u{10855}\u{10857}-\u{1089E}\u{108A7}-\u{108AF}\u{108E0}-\u{108F2}\u{108F4}\u{108F5}\u{108FB}-\u{1091B}\u{10920}-\u{10939}\u{1093F}\u{10980}-\u{109B7}\u{109BC}-\u{109CF}\u{109D2}-\u{10A00}\u{10A10}-\u{10A13}\u{10A15}-\u{10A17}\u{10A19}-\u{10A33}\u{10A40}-\u{10A47}\u{10A50}-\u{10A58}\u{10A60}-\u{10A9F}\u{10AC0}-\u{10AE4}\u{10AEB}-\u{10AF6}\u{10B00}-\u{10B35}\u{10B40}-\u{10B55}\u{10B58}-\u{10B72}\u{10B78}-\u{10B91}\u{10B99}-\u{10B9C}\u{10BA9}-\u{10BAF}\u{10C00}-\u{10C48}\u{10C80}-\u{10CB2}\u{10CC0}-\u{10CF2}\u{10CFA}-\u{10CFF}\u{1E800}-\u{1E8C4}\u{1E8C7}-\u{1E8CF}\u{1E900}-\u{1E943}\u{1E950}-\u{1E959}\u{1E95E}\u{1E95F}\u{1EE00}-\u{1EE03}\u{1EE05}-\u{1EE1F}\u{1EE21}\u{1EE22}\u{1EE24}\u{1EE27}\u{1EE29}-\u{1EE32}\u{1EE34}-\u{1EE37}\u{1EE39}\u{1EE3B}\u{1EE42}\u{1EE47}\u{1EE49}\u{1EE4B}\u{1EE4D}-\u{1EE4F}\u{1EE51}\u{1EE52}\u{1EE54}\u{1EE57}\u{1EE59}\u{1EE5B}\u{1EE5D}\u{1EE5F}\u{1EE61}\u{1EE62}\u{1EE64}\u{1EE67}-\u{1EE6A}\u{1EE6C}-\u{1EE72}\u{1EE74}-\u{1EE77}\u{1EE79}-\u{1EE7C}\u{1EE7E}\u{1EE80}-\u{1EE89}\u{1EE8B}-\u{1EE9B}\u{1EEA1}-\u{1EEA3}\u{1EEA5}-\u{1EEA9}\u{1EEAB}-\u{1EEBB}]/u; +const bidiS2 = /^[\0-\x08\x0E-\x1B!-@\[-`\{-\x84\x86-\xA9\xAB-\xB4\xB6-\xB9\xBB-\xBF\xD7\xF7\u02B9\u02BA\u02C2-\u02CF\u02D2-\u02DF\u02E5-\u02ED\u02EF-\u036F\u0374\u0375\u037E\u0384\u0385\u0387\u03F6\u0483-\u0489\u058A\u058D-\u058F\u0591-\u05C7\u05D0-\u05EA\u05F0-\u05F4\u0600-\u061C\u061E-\u070D\u070F-\u074A\u074D-\u07B1\u07C0-\u07FA\u0800-\u082D\u0830-\u083E\u0840-\u085B\u085E\u0860-\u086A\u08A0-\u08B4\u08B6-\u08BD\u08D4-\u0902\u093A\u093C\u0941-\u0948\u094D\u0951-\u0957\u0962\u0963\u0981\u09BC\u09C1-\u09C4\u09CD\u09E2\u09E3\u09F2\u09F3\u09FB\u0A01\u0A02\u0A3C\u0A41\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81\u0A82\u0ABC\u0AC1-\u0AC5\u0AC7\u0AC8\u0ACD\u0AE2\u0AE3\u0AF1\u0AFA-\u0AFF\u0B01\u0B3C\u0B3F\u0B41-\u0B44\u0B4D\u0B56\u0B62\u0B63\u0B82\u0BC0\u0BCD\u0BF3-\u0BFA\u0C00\u0C3E-\u0C40\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C78-\u0C7E\u0C81\u0CBC\u0CCC\u0CCD\u0CE2\u0CE3\u0D00\u0D01\u0D3B\u0D3C\u0D41-\u0D44\u0D4D\u0D62\u0D63\u0DCA\u0DD2-\u0DD4\u0DD6\u0E31\u0E34-\u0E3A\u0E3F\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0F18\u0F19\u0F35\u0F37\u0F39-\u0F3D\u0F71-\u0F7E\u0F80-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102D-\u1030\u1032-\u1037\u1039\u103A\u103D\u103E\u1058\u1059\u105E-\u1060\u1071-\u1074\u1082\u1085\u1086\u108D\u109D\u135D-\u135F\u1390-\u1399\u1400\u169B\u169C\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4\u17B5\u17B7-\u17BD\u17C6\u17C9-\u17D3\u17DB\u17DD\u17F0-\u17F9\u1800-\u180E\u1885\u1886\u18A9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193B\u1940\u1944\u1945\u19DE-\u19FF\u1A17\u1A18\u1A1B\u1A56\u1A58-\u1A5E\u1A60\u1A62\u1A65-\u1A6C\u1A73-\u1A7C\u1A7F\u1AB0-\u1ABE\u1B00-\u1B03\u1B34\u1B36-\u1B3A\u1B3C\u1B42\u1B6B-\u1B73\u1B80\u1B81\u1BA2-\u1BA5\u1BA8\u1BA9\u1BAB-\u1BAD\u1BE6\u1BE8\u1BE9\u1BED\u1BEF-\u1BF1\u1C2C-\u1C33\u1C36\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE0\u1CE2-\u1CE8\u1CED\u1CF4\u1CF8\u1CF9\u1DC0-\u1DF9\u1DFB-\u1DFF\u1FBD\u1FBF-\u1FC1\u1FCD-\u1FCF\u1FDD-\u1FDF\u1FED-\u1FEF\u1FFD\u1FFE\u200B-\u200D\u200F-\u2027\u202F-\u205E\u2060-\u2064\u206A-\u2070\u2074-\u207E\u2080-\u208E\u20A0-\u20BF\u20D0-\u20F0\u2100\u2101\u2103-\u2106\u2108\u2109\u2114\u2116-\u2118\u211E-\u2123\u2125\u2127\u2129\u212E\u213A\u213B\u2140-\u2144\u214A-\u214D\u2150-\u215F\u2189-\u218B\u2190-\u2335\u237B-\u2394\u2396-\u2426\u2440-\u244A\u2460-\u249B\u24EA-\u26AB\u26AD-\u27FF\u2900-\u2B73\u2B76-\u2B95\u2B98-\u2BB9\u2BBD-\u2BC8\u2BCA-\u2BD2\u2BEC-\u2BEF\u2CE5-\u2CEA\u2CEF-\u2CF1\u2CF9-\u2CFF\u2D7F\u2DE0-\u2E49\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3001-\u3004\u3008-\u3020\u302A-\u302D\u3030\u3036\u3037\u303D-\u303F\u3099-\u309C\u30A0\u30FB\u31C0-\u31E3\u321D\u321E\u3250-\u325F\u327C-\u327E\u32B1-\u32BF\u32CC-\u32CF\u3377-\u337A\u33DE\u33DF\u33FF\u4DC0-\u4DFF\uA490-\uA4C6\uA60D-\uA60F\uA66F-\uA67F\uA69E\uA69F\uA6F0\uA6F1\uA700-\uA721\uA788\uA802\uA806\uA80B\uA825\uA826\uA828-\uA82B\uA838\uA839\uA874-\uA877\uA8C4\uA8C5\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA951\uA980-\uA982\uA9B3\uA9B6-\uA9B9\uA9BC\uA9E5\uAA29-\uAA2E\uAA31\uAA32\uAA35\uAA36\uAA43\uAA4C\uAA7C\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEC\uAAED\uAAF6\uABE5\uABE8\uABED\uFB1D-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBC1\uFBD3-\uFD3F\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFD\uFE00-\uFE19\uFE20-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFE70-\uFE74\uFE76-\uFEFC\uFEFF\uFF01-\uFF20\uFF3B-\uFF40\uFF5B-\uFF65\uFFE0-\uFFE6\uFFE8-\uFFEE\uFFF9-\uFFFD\u{10101}\u{10140}-\u{1018C}\u{10190}-\u{1019B}\u{101A0}\u{101FD}\u{102E0}-\u{102FB}\u{10376}-\u{1037A}\u{10800}-\u{10805}\u{10808}\u{1080A}-\u{10835}\u{10837}\u{10838}\u{1083C}\u{1083F}-\u{10855}\u{10857}-\u{1089E}\u{108A7}-\u{108AF}\u{108E0}-\u{108F2}\u{108F4}\u{108F5}\u{108FB}-\u{1091B}\u{1091F}-\u{10939}\u{1093F}\u{10980}-\u{109B7}\u{109BC}-\u{109CF}\u{109D2}-\u{10A03}\u{10A05}\u{10A06}\u{10A0C}-\u{10A13}\u{10A15}-\u{10A17}\u{10A19}-\u{10A33}\u{10A38}-\u{10A3A}\u{10A3F}-\u{10A47}\u{10A50}-\u{10A58}\u{10A60}-\u{10A9F}\u{10AC0}-\u{10AE6}\u{10AEB}-\u{10AF6}\u{10B00}-\u{10B35}\u{10B39}-\u{10B55}\u{10B58}-\u{10B72}\u{10B78}-\u{10B91}\u{10B99}-\u{10B9C}\u{10BA9}-\u{10BAF}\u{10C00}-\u{10C48}\u{10C80}-\u{10CB2}\u{10CC0}-\u{10CF2}\u{10CFA}-\u{10CFF}\u{10E60}-\u{10E7E}\u{11001}\u{11038}-\u{11046}\u{11052}-\u{11065}\u{1107F}-\u{11081}\u{110B3}-\u{110B6}\u{110B9}\u{110BA}\u{11100}-\u{11102}\u{11127}-\u{1112B}\u{1112D}-\u{11134}\u{11173}\u{11180}\u{11181}\u{111B6}-\u{111BE}\u{111CA}-\u{111CC}\u{1122F}-\u{11231}\u{11234}\u{11236}\u{11237}\u{1123E}\u{112DF}\u{112E3}-\u{112EA}\u{11300}\u{11301}\u{1133C}\u{11340}\u{11366}-\u{1136C}\u{11370}-\u{11374}\u{11438}-\u{1143F}\u{11442}-\u{11444}\u{11446}\u{114B3}-\u{114B8}\u{114BA}\u{114BF}\u{114C0}\u{114C2}\u{114C3}\u{115B2}-\u{115B5}\u{115BC}\u{115BD}\u{115BF}\u{115C0}\u{115DC}\u{115DD}\u{11633}-\u{1163A}\u{1163D}\u{1163F}\u{11640}\u{11660}-\u{1166C}\u{116AB}\u{116AD}\u{116B0}-\u{116B5}\u{116B7}\u{1171D}-\u{1171F}\u{11722}-\u{11725}\u{11727}-\u{1172B}\u{11A01}-\u{11A06}\u{11A09}\u{11A0A}\u{11A33}-\u{11A38}\u{11A3B}-\u{11A3E}\u{11A47}\u{11A51}-\u{11A56}\u{11A59}-\u{11A5B}\u{11A8A}-\u{11A96}\u{11A98}\u{11A99}\u{11C30}-\u{11C36}\u{11C38}-\u{11C3D}\u{11C92}-\u{11CA7}\u{11CAA}-\u{11CB0}\u{11CB2}\u{11CB3}\u{11CB5}\u{11CB6}\u{11D31}-\u{11D36}\u{11D3A}\u{11D3C}\u{11D3D}\u{11D3F}-\u{11D45}\u{11D47}\u{16AF0}-\u{16AF4}\u{16B30}-\u{16B36}\u{16F8F}-\u{16F92}\u{1BC9D}\u{1BC9E}\u{1BCA0}-\u{1BCA3}\u{1D167}-\u{1D169}\u{1D173}-\u{1D182}\u{1D185}-\u{1D18B}\u{1D1AA}-\u{1D1AD}\u{1D200}-\u{1D245}\u{1D300}-\u{1D356}\u{1D6DB}\u{1D715}\u{1D74F}\u{1D789}\u{1D7C3}\u{1D7CE}-\u{1D7FF}\u{1DA00}-\u{1DA36}\u{1DA3B}-\u{1DA6C}\u{1DA75}\u{1DA84}\u{1DA9B}-\u{1DA9F}\u{1DAA1}-\u{1DAAF}\u{1E000}-\u{1E006}\u{1E008}-\u{1E018}\u{1E01B}-\u{1E021}\u{1E023}\u{1E024}\u{1E026}-\u{1E02A}\u{1E800}-\u{1E8C4}\u{1E8C7}-\u{1E8D6}\u{1E900}-\u{1E94A}\u{1E950}-\u{1E959}\u{1E95E}\u{1E95F}\u{1EE00}-\u{1EE03}\u{1EE05}-\u{1EE1F}\u{1EE21}\u{1EE22}\u{1EE24}\u{1EE27}\u{1EE29}-\u{1EE32}\u{1EE34}-\u{1EE37}\u{1EE39}\u{1EE3B}\u{1EE42}\u{1EE47}\u{1EE49}\u{1EE4B}\u{1EE4D}-\u{1EE4F}\u{1EE51}\u{1EE52}\u{1EE54}\u{1EE57}\u{1EE59}\u{1EE5B}\u{1EE5D}\u{1EE5F}\u{1EE61}\u{1EE62}\u{1EE64}\u{1EE67}-\u{1EE6A}\u{1EE6C}-\u{1EE72}\u{1EE74}-\u{1EE77}\u{1EE79}-\u{1EE7C}\u{1EE7E}\u{1EE80}-\u{1EE89}\u{1EE8B}-\u{1EE9B}\u{1EEA1}-\u{1EEA3}\u{1EEA5}-\u{1EEA9}\u{1EEAB}-\u{1EEBB}\u{1EEF0}\u{1EEF1}\u{1F000}-\u{1F02B}\u{1F030}-\u{1F093}\u{1F0A0}-\u{1F0AE}\u{1F0B1}-\u{1F0BF}\u{1F0C1}-\u{1F0CF}\u{1F0D1}-\u{1F0F5}\u{1F100}-\u{1F10C}\u{1F16A}\u{1F16B}\u{1F260}-\u{1F265}\u{1F300}-\u{1F6D4}\u{1F6E0}-\u{1F6EC}\u{1F6F0}-\u{1F6F8}\u{1F700}-\u{1F773}\u{1F780}-\u{1F7D4}\u{1F800}-\u{1F80B}\u{1F810}-\u{1F847}\u{1F850}-\u{1F859}\u{1F860}-\u{1F887}\u{1F890}-\u{1F8AD}\u{1F900}-\u{1F90B}\u{1F910}-\u{1F93E}\u{1F940}-\u{1F94C}\u{1F950}-\u{1F96B}\u{1F980}-\u{1F997}\u{1F9C0}\u{1F9D0}-\u{1F9E6}\u{E0001}\u{E0020}-\u{E007F}\u{E0100}-\u{E01EF}]*$/u; +const bidiS3 = /[0-9\xB2\xB3\xB9\u05BE\u05C0\u05C3\u05C6\u05D0-\u05EA\u05F0-\u05F4\u0600-\u0605\u0608\u060B\u060D\u061B\u061C\u061E-\u064A\u0660-\u0669\u066B-\u066F\u0671-\u06D5\u06DD\u06E5\u06E6\u06EE-\u070D\u070F\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07C0-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0830-\u083E\u0840-\u0858\u085E\u0860-\u086A\u08A0-\u08B4\u08B6-\u08BD\u08E2\u200F\u2070\u2074-\u2079\u2080-\u2089\u2488-\u249B\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBC1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFC\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\u{102E1}-\u{102FB}\u{10800}-\u{10805}\u{10808}\u{1080A}-\u{10835}\u{10837}\u{10838}\u{1083C}\u{1083F}-\u{10855}\u{10857}-\u{1089E}\u{108A7}-\u{108AF}\u{108E0}-\u{108F2}\u{108F4}\u{108F5}\u{108FB}-\u{1091B}\u{10920}-\u{10939}\u{1093F}\u{10980}-\u{109B7}\u{109BC}-\u{109CF}\u{109D2}-\u{10A00}\u{10A10}-\u{10A13}\u{10A15}-\u{10A17}\u{10A19}-\u{10A33}\u{10A40}-\u{10A47}\u{10A50}-\u{10A58}\u{10A60}-\u{10A9F}\u{10AC0}-\u{10AE4}\u{10AEB}-\u{10AF6}\u{10B00}-\u{10B35}\u{10B40}-\u{10B55}\u{10B58}-\u{10B72}\u{10B78}-\u{10B91}\u{10B99}-\u{10B9C}\u{10BA9}-\u{10BAF}\u{10C00}-\u{10C48}\u{10C80}-\u{10CB2}\u{10CC0}-\u{10CF2}\u{10CFA}-\u{10CFF}\u{10E60}-\u{10E7E}\u{1D7CE}-\u{1D7FF}\u{1E800}-\u{1E8C4}\u{1E8C7}-\u{1E8CF}\u{1E900}-\u{1E943}\u{1E950}-\u{1E959}\u{1E95E}\u{1E95F}\u{1EE00}-\u{1EE03}\u{1EE05}-\u{1EE1F}\u{1EE21}\u{1EE22}\u{1EE24}\u{1EE27}\u{1EE29}-\u{1EE32}\u{1EE34}-\u{1EE37}\u{1EE39}\u{1EE3B}\u{1EE42}\u{1EE47}\u{1EE49}\u{1EE4B}\u{1EE4D}-\u{1EE4F}\u{1EE51}\u{1EE52}\u{1EE54}\u{1EE57}\u{1EE59}\u{1EE5B}\u{1EE5D}\u{1EE5F}\u{1EE61}\u{1EE62}\u{1EE64}\u{1EE67}-\u{1EE6A}\u{1EE6C}-\u{1EE72}\u{1EE74}-\u{1EE77}\u{1EE79}-\u{1EE7C}\u{1EE7E}\u{1EE80}-\u{1EE89}\u{1EE8B}-\u{1EE9B}\u{1EEA1}-\u{1EEA3}\u{1EEA5}-\u{1EEA9}\u{1EEAB}-\u{1EEBB}\u{1F100}-\u{1F10A}][\u0300-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08D4-\u08E1\u08E3-\u0902\u093A\u093C\u0941-\u0948\u094D\u0951-\u0957\u0962\u0963\u0981\u09BC\u09C1-\u09C4\u09CD\u09E2\u09E3\u0A01\u0A02\u0A3C\u0A41\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81\u0A82\u0ABC\u0AC1-\u0AC5\u0AC7\u0AC8\u0ACD\u0AE2\u0AE3\u0AFA-\u0AFF\u0B01\u0B3C\u0B3F\u0B41-\u0B44\u0B4D\u0B56\u0B62\u0B63\u0B82\u0BC0\u0BCD\u0C00\u0C3E-\u0C40\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C81\u0CBC\u0CCC\u0CCD\u0CE2\u0CE3\u0D00\u0D01\u0D3B\u0D3C\u0D41-\u0D44\u0D4D\u0D62\u0D63\u0DCA\u0DD2-\u0DD4\u0DD6\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0F18\u0F19\u0F35\u0F37\u0F39\u0F71-\u0F7E\u0F80-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102D-\u1030\u1032-\u1037\u1039\u103A\u103D\u103E\u1058\u1059\u105E-\u1060\u1071-\u1074\u1082\u1085\u1086\u108D\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4\u17B5\u17B7-\u17BD\u17C6\u17C9-\u17D3\u17DD\u180B-\u180D\u1885\u1886\u18A9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193B\u1A17\u1A18\u1A1B\u1A56\u1A58-\u1A5E\u1A60\u1A62\u1A65-\u1A6C\u1A73-\u1A7C\u1A7F\u1AB0-\u1ABE\u1B00-\u1B03\u1B34\u1B36-\u1B3A\u1B3C\u1B42\u1B6B-\u1B73\u1B80\u1B81\u1BA2-\u1BA5\u1BA8\u1BA9\u1BAB-\u1BAD\u1BE6\u1BE8\u1BE9\u1BED\u1BEF-\u1BF1\u1C2C-\u1C33\u1C36\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE0\u1CE2-\u1CE8\u1CED\u1CF4\u1CF8\u1CF9\u1DC0-\u1DF9\u1DFB-\u1DFF\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302D\u3099\u309A\uA66F-\uA672\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA825\uA826\uA8C4\uA8C5\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA951\uA980-\uA982\uA9B3\uA9B6-\uA9B9\uA9BC\uA9E5\uAA29-\uAA2E\uAA31\uAA32\uAA35\uAA36\uAA43\uAA4C\uAA7C\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEC\uAAED\uAAF6\uABE5\uABE8\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F\u{101FD}\u{102E0}\u{10376}-\u{1037A}\u{10A01}-\u{10A03}\u{10A05}\u{10A06}\u{10A0C}-\u{10A0F}\u{10A38}-\u{10A3A}\u{10A3F}\u{10AE5}\u{10AE6}\u{11001}\u{11038}-\u{11046}\u{1107F}-\u{11081}\u{110B3}-\u{110B6}\u{110B9}\u{110BA}\u{11100}-\u{11102}\u{11127}-\u{1112B}\u{1112D}-\u{11134}\u{11173}\u{11180}\u{11181}\u{111B6}-\u{111BE}\u{111CA}-\u{111CC}\u{1122F}-\u{11231}\u{11234}\u{11236}\u{11237}\u{1123E}\u{112DF}\u{112E3}-\u{112EA}\u{11300}\u{11301}\u{1133C}\u{11340}\u{11366}-\u{1136C}\u{11370}-\u{11374}\u{11438}-\u{1143F}\u{11442}-\u{11444}\u{11446}\u{114B3}-\u{114B8}\u{114BA}\u{114BF}\u{114C0}\u{114C2}\u{114C3}\u{115B2}-\u{115B5}\u{115BC}\u{115BD}\u{115BF}\u{115C0}\u{115DC}\u{115DD}\u{11633}-\u{1163A}\u{1163D}\u{1163F}\u{11640}\u{116AB}\u{116AD}\u{116B0}-\u{116B5}\u{116B7}\u{1171D}-\u{1171F}\u{11722}-\u{11725}\u{11727}-\u{1172B}\u{11A01}-\u{11A06}\u{11A09}\u{11A0A}\u{11A33}-\u{11A38}\u{11A3B}-\u{11A3E}\u{11A47}\u{11A51}-\u{11A56}\u{11A59}-\u{11A5B}\u{11A8A}-\u{11A96}\u{11A98}\u{11A99}\u{11C30}-\u{11C36}\u{11C38}-\u{11C3D}\u{11C92}-\u{11CA7}\u{11CAA}-\u{11CB0}\u{11CB2}\u{11CB3}\u{11CB5}\u{11CB6}\u{11D31}-\u{11D36}\u{11D3A}\u{11D3C}\u{11D3D}\u{11D3F}-\u{11D45}\u{11D47}\u{16AF0}-\u{16AF4}\u{16B30}-\u{16B36}\u{16F8F}-\u{16F92}\u{1BC9D}\u{1BC9E}\u{1D167}-\u{1D169}\u{1D17B}-\u{1D182}\u{1D185}-\u{1D18B}\u{1D1AA}-\u{1D1AD}\u{1D242}-\u{1D244}\u{1DA00}-\u{1DA36}\u{1DA3B}-\u{1DA6C}\u{1DA75}\u{1DA84}\u{1DA9B}-\u{1DA9F}\u{1DAA1}-\u{1DAAF}\u{1E000}-\u{1E006}\u{1E008}-\u{1E018}\u{1E01B}-\u{1E021}\u{1E023}\u{1E024}\u{1E026}-\u{1E02A}\u{1E8D0}-\u{1E8D6}\u{1E944}-\u{1E94A}\u{E0100}-\u{E01EF}]*$/u; +const bidiS4EN = /[0-9\xB2\xB3\xB9\u06F0-\u06F9\u2070\u2074-\u2079\u2080-\u2089\u2488-\u249B\uFF10-\uFF19\u{102E1}-\u{102FB}\u{1D7CE}-\u{1D7FF}\u{1F100}-\u{1F10A}]/u; +const bidiS4AN = /[\u0600-\u0605\u0660-\u0669\u066B\u066C\u06DD\u08E2\u{10E60}-\u{10E7E}]/u; +const bidiS5 = /^[\0-\x08\x0E-\x1B!-\x84\x86-\u0377\u037A-\u037F\u0384-\u038A\u038C\u038E-\u03A1\u03A3-\u052F\u0531-\u0556\u0559-\u055F\u0561-\u0587\u0589\u058A\u058D-\u058F\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0606\u0607\u0609\u060A\u060C\u060E-\u061A\u064B-\u065F\u066A\u0670\u06D6-\u06DC\u06DE-\u06E4\u06E7-\u06ED\u06F0-\u06F9\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u07F6-\u07F9\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08D4-\u08E1\u08E3-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09FD\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AF1\u0AF9-\u0AFF\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B77\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BFA\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58-\u0C5A\u0C60-\u0C63\u0C66-\u0C6F\u0C78-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D00-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D44\u0D46-\u0D48\u0D4A-\u0D4F\u0D54-\u0D63\u0D66-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2-\u0DF4\u0E01-\u0E3A\u0E3F-\u0E5B\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00-\u0F47\u0F49-\u0F6C\u0F71-\u0F97\u0F99-\u0FBC\u0FBE-\u0FCC\u0FCE-\u0FDA\u1000-\u10C5\u10C7\u10CD\u10D0-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u137C\u1380-\u1399\u13A0-\u13F5\u13F8-\u13FD\u1400-\u167F\u1681-\u169C\u16A0-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1736\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17DD\u17E0-\u17E9\u17F0-\u17F9\u1800-\u180E\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1940\u1944-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u19DE-\u1A1B\u1A1E-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA0-\u1AAD\u1AB0-\u1ABE\u1B00-\u1B4B\u1B50-\u1B7C\u1B80-\u1BF3\u1BFC-\u1C37\u1C3B-\u1C49\u1C4D-\u1C88\u1CC0-\u1CC7\u1CD0-\u1CF9\u1D00-\u1DF9\u1DFB-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FC4\u1FC6-\u1FD3\u1FD6-\u1FDB\u1FDD-\u1FEF\u1FF2-\u1FF4\u1FF6-\u1FFE\u200B-\u200E\u2010-\u2027\u202F-\u205E\u2060-\u2064\u206A-\u2071\u2074-\u208E\u2090-\u209C\u20A0-\u20BF\u20D0-\u20F0\u2100-\u218B\u2190-\u2426\u2440-\u244A\u2460-\u2B73\u2B76-\u2B95\u2B98-\u2BB9\u2BBD-\u2BC8\u2BCA-\u2BD2\u2BEC-\u2BEF\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CF3\u2CF9-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D70\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2E49\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3001-\u303F\u3041-\u3096\u3099-\u30FF\u3105-\u312E\u3131-\u318E\u3190-\u31BA\u31C0-\u31E3\u31F0-\u321E\u3220-\u32FE\u3300-\u4DB5\u4DC0-\u9FEA\uA000-\uA48C\uA490-\uA4C6\uA4D0-\uA62B\uA640-\uA6F7\uA700-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA82B\uA830-\uA839\uA840-\uA877\uA880-\uA8C5\uA8CE-\uA8D9\uA8E0-\uA8FD\uA900-\uA953\uA95F-\uA97C\uA980-\uA9CD\uA9CF-\uA9D9\uA9DE-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA5C-\uAAC2\uAADB-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB65\uAB70-\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uD800-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1E\uFB29\uFD3E\uFD3F\uFDFD\uFE00-\uFE19\uFE20-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFEFF\uFF01-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC\uFFE0-\uFFE6\uFFE8-\uFFEE\uFFF9-\uFFFD\u{10000}-\u{1000B}\u{1000D}-\u{10026}\u{10028}-\u{1003A}\u{1003C}\u{1003D}\u{1003F}-\u{1004D}\u{10050}-\u{1005D}\u{10080}-\u{100FA}\u{10100}-\u{10102}\u{10107}-\u{10133}\u{10137}-\u{1018E}\u{10190}-\u{1019B}\u{101A0}\u{101D0}-\u{101FD}\u{10280}-\u{1029C}\u{102A0}-\u{102D0}\u{102E0}-\u{102FB}\u{10300}-\u{10323}\u{1032D}-\u{1034A}\u{10350}-\u{1037A}\u{10380}-\u{1039D}\u{1039F}-\u{103C3}\u{103C8}-\u{103D5}\u{10400}-\u{1049D}\u{104A0}-\u{104A9}\u{104B0}-\u{104D3}\u{104D8}-\u{104FB}\u{10500}-\u{10527}\u{10530}-\u{10563}\u{1056F}\u{10600}-\u{10736}\u{10740}-\u{10755}\u{10760}-\u{10767}\u{1091F}\u{10A01}-\u{10A03}\u{10A05}\u{10A06}\u{10A0C}-\u{10A0F}\u{10A38}-\u{10A3A}\u{10A3F}\u{10AE5}\u{10AE6}\u{10B39}-\u{10B3F}\u{11000}-\u{1104D}\u{11052}-\u{1106F}\u{1107F}-\u{110C1}\u{110D0}-\u{110E8}\u{110F0}-\u{110F9}\u{11100}-\u{11134}\u{11136}-\u{11143}\u{11150}-\u{11176}\u{11180}-\u{111CD}\u{111D0}-\u{111DF}\u{111E1}-\u{111F4}\u{11200}-\u{11211}\u{11213}-\u{1123E}\u{11280}-\u{11286}\u{11288}\u{1128A}-\u{1128D}\u{1128F}-\u{1129D}\u{1129F}-\u{112A9}\u{112B0}-\u{112EA}\u{112F0}-\u{112F9}\u{11300}-\u{11303}\u{11305}-\u{1130C}\u{1130F}\u{11310}\u{11313}-\u{11328}\u{1132A}-\u{11330}\u{11332}\u{11333}\u{11335}-\u{11339}\u{1133C}-\u{11344}\u{11347}\u{11348}\u{1134B}-\u{1134D}\u{11350}\u{11357}\u{1135D}-\u{11363}\u{11366}-\u{1136C}\u{11370}-\u{11374}\u{11400}-\u{11459}\u{1145B}\u{1145D}\u{11480}-\u{114C7}\u{114D0}-\u{114D9}\u{11580}-\u{115B5}\u{115B8}-\u{115DD}\u{11600}-\u{11644}\u{11650}-\u{11659}\u{11660}-\u{1166C}\u{11680}-\u{116B7}\u{116C0}-\u{116C9}\u{11700}-\u{11719}\u{1171D}-\u{1172B}\u{11730}-\u{1173F}\u{118A0}-\u{118F2}\u{118FF}\u{11A00}-\u{11A47}\u{11A50}-\u{11A83}\u{11A86}-\u{11A9C}\u{11A9E}-\u{11AA2}\u{11AC0}-\u{11AF8}\u{11C00}-\u{11C08}\u{11C0A}-\u{11C36}\u{11C38}-\u{11C45}\u{11C50}-\u{11C6C}\u{11C70}-\u{11C8F}\u{11C92}-\u{11CA7}\u{11CA9}-\u{11CB6}\u{11D00}-\u{11D06}\u{11D08}\u{11D09}\u{11D0B}-\u{11D36}\u{11D3A}\u{11D3C}\u{11D3D}\u{11D3F}-\u{11D47}\u{11D50}-\u{11D59}\u{12000}-\u{12399}\u{12400}-\u{1246E}\u{12470}-\u{12474}\u{12480}-\u{12543}\u{13000}-\u{1342E}\u{14400}-\u{14646}\u{16800}-\u{16A38}\u{16A40}-\u{16A5E}\u{16A60}-\u{16A69}\u{16A6E}\u{16A6F}\u{16AD0}-\u{16AED}\u{16AF0}-\u{16AF5}\u{16B00}-\u{16B45}\u{16B50}-\u{16B59}\u{16B5B}-\u{16B61}\u{16B63}-\u{16B77}\u{16B7D}-\u{16B8F}\u{16F00}-\u{16F44}\u{16F50}-\u{16F7E}\u{16F8F}-\u{16F9F}\u{16FE0}\u{16FE1}\u{17000}-\u{187EC}\u{18800}-\u{18AF2}\u{1B000}-\u{1B11E}\u{1B170}-\u{1B2FB}\u{1BC00}-\u{1BC6A}\u{1BC70}-\u{1BC7C}\u{1BC80}-\u{1BC88}\u{1BC90}-\u{1BC99}\u{1BC9C}-\u{1BCA3}\u{1D000}-\u{1D0F5}\u{1D100}-\u{1D126}\u{1D129}-\u{1D1E8}\u{1D200}-\u{1D245}\u{1D300}-\u{1D356}\u{1D360}-\u{1D371}\u{1D400}-\u{1D454}\u{1D456}-\u{1D49C}\u{1D49E}\u{1D49F}\u{1D4A2}\u{1D4A5}\u{1D4A6}\u{1D4A9}-\u{1D4AC}\u{1D4AE}-\u{1D4B9}\u{1D4BB}\u{1D4BD}-\u{1D4C3}\u{1D4C5}-\u{1D505}\u{1D507}-\u{1D50A}\u{1D50D}-\u{1D514}\u{1D516}-\u{1D51C}\u{1D51E}-\u{1D539}\u{1D53B}-\u{1D53E}\u{1D540}-\u{1D544}\u{1D546}\u{1D54A}-\u{1D550}\u{1D552}-\u{1D6A5}\u{1D6A8}-\u{1D7CB}\u{1D7CE}-\u{1DA8B}\u{1DA9B}-\u{1DA9F}\u{1DAA1}-\u{1DAAF}\u{1E000}-\u{1E006}\u{1E008}-\u{1E018}\u{1E01B}-\u{1E021}\u{1E023}\u{1E024}\u{1E026}-\u{1E02A}\u{1E8D0}-\u{1E8D6}\u{1E944}-\u{1E94A}\u{1EEF0}\u{1EEF1}\u{1F000}-\u{1F02B}\u{1F030}-\u{1F093}\u{1F0A0}-\u{1F0AE}\u{1F0B1}-\u{1F0BF}\u{1F0C1}-\u{1F0CF}\u{1F0D1}-\u{1F0F5}\u{1F100}-\u{1F10C}\u{1F110}-\u{1F12E}\u{1F130}-\u{1F16B}\u{1F170}-\u{1F1AC}\u{1F1E6}-\u{1F202}\u{1F210}-\u{1F23B}\u{1F240}-\u{1F248}\u{1F250}\u{1F251}\u{1F260}-\u{1F265}\u{1F300}-\u{1F6D4}\u{1F6E0}-\u{1F6EC}\u{1F6F0}-\u{1F6F8}\u{1F700}-\u{1F773}\u{1F780}-\u{1F7D4}\u{1F800}-\u{1F80B}\u{1F810}-\u{1F847}\u{1F850}-\u{1F859}\u{1F860}-\u{1F887}\u{1F890}-\u{1F8AD}\u{1F900}-\u{1F90B}\u{1F910}-\u{1F93E}\u{1F940}-\u{1F94C}\u{1F950}-\u{1F96B}\u{1F980}-\u{1F997}\u{1F9C0}\u{1F9D0}-\u{1F9E6}\u{20000}-\u{2A6D6}\u{2A700}-\u{2B734}\u{2B740}-\u{2B81D}\u{2B820}-\u{2CEA1}\u{2CEB0}-\u{2EBE0}\u{2F800}-\u{2FA1D}\u{E0001}\u{E0020}-\u{E007F}\u{E0100}-\u{E01EF}\u{F0000}-\u{FFFFD}\u{100000}-\u{10FFFD}]*$/u; +const bidiS6 = /[0-9A-Za-z\xAA\xB2\xB3\xB5\xB9\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02B8\u02BB-\u02C1\u02D0\u02D1\u02E0-\u02E4\u02EE\u0370-\u0373\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0482\u048A-\u052F\u0531-\u0556\u0559-\u055F\u0561-\u0587\u0589\u06F0-\u06F9\u0903-\u0939\u093B\u093D-\u0940\u0949-\u094C\u094E-\u0950\u0958-\u0961\u0964-\u0980\u0982\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD-\u09C0\u09C7\u09C8\u09CB\u09CC\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E1\u09E6-\u09F1\u09F4-\u09FA\u09FC\u09FD\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3E-\u0A40\u0A59-\u0A5C\u0A5E\u0A66-\u0A6F\u0A72-\u0A74\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD-\u0AC0\u0AC9\u0ACB\u0ACC\u0AD0\u0AE0\u0AE1\u0AE6-\u0AF0\u0AF9\u0B02\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B3E\u0B40\u0B47\u0B48\u0B4B\u0B4C\u0B57\u0B5C\u0B5D\u0B5F-\u0B61\u0B66-\u0B77\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE\u0BBF\u0BC1\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCC\u0BD0\u0BD7\u0BE6-\u0BF2\u0C01-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C41-\u0C44\u0C58-\u0C5A\u0C60\u0C61\u0C66-\u0C6F\u0C7F\u0C80\u0C82\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD-\u0CC4\u0CC6-\u0CC8\u0CCA\u0CCB\u0CD5\u0CD6\u0CDE\u0CE0\u0CE1\u0CE6-\u0CEF\u0CF1\u0CF2\u0D02\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D40\u0D46-\u0D48\u0D4A-\u0D4C\u0D4E\u0D4F\u0D54-\u0D61\u0D66-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCF-\u0DD1\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2-\u0DF4\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E4F-\u0E5B\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00-\u0F17\u0F1A-\u0F34\u0F36\u0F38\u0F3E-\u0F47\u0F49-\u0F6C\u0F7F\u0F85\u0F88-\u0F8C\u0FBE-\u0FC5\u0FC7-\u0FCC\u0FCE-\u0FDA\u1000-\u102C\u1031\u1038\u103B\u103C\u103F-\u1057\u105A-\u105D\u1061-\u1070\u1075-\u1081\u1083\u1084\u1087-\u108C\u108E-\u109C\u109E-\u10C5\u10C7\u10CD\u10D0-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1360-\u137C\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u167F\u1681-\u169A\u16A0-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1735\u1736\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17B6\u17BE-\u17C5\u17C7\u17C8\u17D4-\u17DA\u17DC\u17E0-\u17E9\u1810-\u1819\u1820-\u1877\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1923-\u1926\u1929-\u192B\u1930\u1931\u1933-\u1938\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u1A00-\u1A16\u1A19\u1A1A\u1A1E-\u1A55\u1A57\u1A61\u1A63\u1A64\u1A6D-\u1A72\u1A80-\u1A89\u1A90-\u1A99\u1AA0-\u1AAD\u1B04-\u1B33\u1B35\u1B3B\u1B3D-\u1B41\u1B43-\u1B4B\u1B50-\u1B6A\u1B74-\u1B7C\u1B82-\u1BA1\u1BA6\u1BA7\u1BAA\u1BAE-\u1BE5\u1BE7\u1BEA-\u1BEC\u1BEE\u1BF2\u1BF3\u1BFC-\u1C2B\u1C34\u1C35\u1C3B-\u1C49\u1C4D-\u1C88\u1CC0-\u1CC7\u1CD3\u1CE1\u1CE9-\u1CEC\u1CEE-\u1CF3\u1CF5-\u1CF7\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200E\u2070\u2071\u2074-\u2079\u207F-\u2089\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u214F\u2160-\u2188\u2336-\u237A\u2395\u2488-\u24E9\u26AC\u2800-\u28FF\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D70\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005-\u3007\u3021-\u3029\u302E\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312E\u3131-\u318E\u3190-\u31BA\u31F0-\u321C\u3220-\u324F\u3260-\u327B\u327F-\u32B0\u32C0-\u32CB\u32D0-\u32FE\u3300-\u3376\u337B-\u33DD\u33E0-\u33FE\u3400-\u4DB5\u4E00-\u9FEA\uA000-\uA48C\uA4D0-\uA60C\uA610-\uA62B\uA640-\uA66E\uA680-\uA69D\uA6A0-\uA6EF\uA6F2-\uA6F7\uA722-\uA787\uA789-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA824\uA827\uA830-\uA837\uA840-\uA873\uA880-\uA8C3\uA8CE-\uA8D9\uA8F2-\uA8FD\uA900-\uA925\uA92E-\uA946\uA952\uA953\uA95F-\uA97C\uA983-\uA9B2\uA9B4\uA9B5\uA9BA\uA9BB\uA9BD-\uA9CD\uA9CF-\uA9D9\uA9DE-\uA9E4\uA9E6-\uA9FE\uAA00-\uAA28\uAA2F\uAA30\uAA33\uAA34\uAA40-\uAA42\uAA44-\uAA4B\uAA4D\uAA50-\uAA59\uAA5C-\uAA7B\uAA7D-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAAEB\uAAEE-\uAAF5\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB65\uAB70-\uABE4\uABE6\uABE7\uABE9-\uABEC\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uD800-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC\u{10000}-\u{1000B}\u{1000D}-\u{10026}\u{10028}-\u{1003A}\u{1003C}\u{1003D}\u{1003F}-\u{1004D}\u{10050}-\u{1005D}\u{10080}-\u{100FA}\u{10100}\u{10102}\u{10107}-\u{10133}\u{10137}-\u{1013F}\u{1018D}\u{1018E}\u{101D0}-\u{101FC}\u{10280}-\u{1029C}\u{102A0}-\u{102D0}\u{102E1}-\u{102FB}\u{10300}-\u{10323}\u{1032D}-\u{1034A}\u{10350}-\u{10375}\u{10380}-\u{1039D}\u{1039F}-\u{103C3}\u{103C8}-\u{103D5}\u{10400}-\u{1049D}\u{104A0}-\u{104A9}\u{104B0}-\u{104D3}\u{104D8}-\u{104FB}\u{10500}-\u{10527}\u{10530}-\u{10563}\u{1056F}\u{10600}-\u{10736}\u{10740}-\u{10755}\u{10760}-\u{10767}\u{11000}\u{11002}-\u{11037}\u{11047}-\u{1104D}\u{11066}-\u{1106F}\u{11082}-\u{110B2}\u{110B7}\u{110B8}\u{110BB}-\u{110C1}\u{110D0}-\u{110E8}\u{110F0}-\u{110F9}\u{11103}-\u{11126}\u{1112C}\u{11136}-\u{11143}\u{11150}-\u{11172}\u{11174}-\u{11176}\u{11182}-\u{111B5}\u{111BF}-\u{111C9}\u{111CD}\u{111D0}-\u{111DF}\u{111E1}-\u{111F4}\u{11200}-\u{11211}\u{11213}-\u{1122E}\u{11232}\u{11233}\u{11235}\u{11238}-\u{1123D}\u{11280}-\u{11286}\u{11288}\u{1128A}-\u{1128D}\u{1128F}-\u{1129D}\u{1129F}-\u{112A9}\u{112B0}-\u{112DE}\u{112E0}-\u{112E2}\u{112F0}-\u{112F9}\u{11302}\u{11303}\u{11305}-\u{1130C}\u{1130F}\u{11310}\u{11313}-\u{11328}\u{1132A}-\u{11330}\u{11332}\u{11333}\u{11335}-\u{11339}\u{1133D}-\u{1133F}\u{11341}-\u{11344}\u{11347}\u{11348}\u{1134B}-\u{1134D}\u{11350}\u{11357}\u{1135D}-\u{11363}\u{11400}-\u{11437}\u{11440}\u{11441}\u{11445}\u{11447}-\u{11459}\u{1145B}\u{1145D}\u{11480}-\u{114B2}\u{114B9}\u{114BB}-\u{114BE}\u{114C1}\u{114C4}-\u{114C7}\u{114D0}-\u{114D9}\u{11580}-\u{115B1}\u{115B8}-\u{115BB}\u{115BE}\u{115C1}-\u{115DB}\u{11600}-\u{11632}\u{1163B}\u{1163C}\u{1163E}\u{11641}-\u{11644}\u{11650}-\u{11659}\u{11680}-\u{116AA}\u{116AC}\u{116AE}\u{116AF}\u{116B6}\u{116C0}-\u{116C9}\u{11700}-\u{11719}\u{11720}\u{11721}\u{11726}\u{11730}-\u{1173F}\u{118A0}-\u{118F2}\u{118FF}\u{11A00}\u{11A07}\u{11A08}\u{11A0B}-\u{11A32}\u{11A39}\u{11A3A}\u{11A3F}-\u{11A46}\u{11A50}\u{11A57}\u{11A58}\u{11A5C}-\u{11A83}\u{11A86}-\u{11A89}\u{11A97}\u{11A9A}-\u{11A9C}\u{11A9E}-\u{11AA2}\u{11AC0}-\u{11AF8}\u{11C00}-\u{11C08}\u{11C0A}-\u{11C2F}\u{11C3E}-\u{11C45}\u{11C50}-\u{11C6C}\u{11C70}-\u{11C8F}\u{11CA9}\u{11CB1}\u{11CB4}\u{11D00}-\u{11D06}\u{11D08}\u{11D09}\u{11D0B}-\u{11D30}\u{11D46}\u{11D50}-\u{11D59}\u{12000}-\u{12399}\u{12400}-\u{1246E}\u{12470}-\u{12474}\u{12480}-\u{12543}\u{13000}-\u{1342E}\u{14400}-\u{14646}\u{16800}-\u{16A38}\u{16A40}-\u{16A5E}\u{16A60}-\u{16A69}\u{16A6E}\u{16A6F}\u{16AD0}-\u{16AED}\u{16AF5}\u{16B00}-\u{16B2F}\u{16B37}-\u{16B45}\u{16B50}-\u{16B59}\u{16B5B}-\u{16B61}\u{16B63}-\u{16B77}\u{16B7D}-\u{16B8F}\u{16F00}-\u{16F44}\u{16F50}-\u{16F7E}\u{16F93}-\u{16F9F}\u{16FE0}\u{16FE1}\u{17000}-\u{187EC}\u{18800}-\u{18AF2}\u{1B000}-\u{1B11E}\u{1B170}-\u{1B2FB}\u{1BC00}-\u{1BC6A}\u{1BC70}-\u{1BC7C}\u{1BC80}-\u{1BC88}\u{1BC90}-\u{1BC99}\u{1BC9C}\u{1BC9F}\u{1D000}-\u{1D0F5}\u{1D100}-\u{1D126}\u{1D129}-\u{1D166}\u{1D16A}-\u{1D172}\u{1D183}\u{1D184}\u{1D18C}-\u{1D1A9}\u{1D1AE}-\u{1D1E8}\u{1D360}-\u{1D371}\u{1D400}-\u{1D454}\u{1D456}-\u{1D49C}\u{1D49E}\u{1D49F}\u{1D4A2}\u{1D4A5}\u{1D4A6}\u{1D4A9}-\u{1D4AC}\u{1D4AE}-\u{1D4B9}\u{1D4BB}\u{1D4BD}-\u{1D4C3}\u{1D4C5}-\u{1D505}\u{1D507}-\u{1D50A}\u{1D50D}-\u{1D514}\u{1D516}-\u{1D51C}\u{1D51E}-\u{1D539}\u{1D53B}-\u{1D53E}\u{1D540}-\u{1D544}\u{1D546}\u{1D54A}-\u{1D550}\u{1D552}-\u{1D6A5}\u{1D6A8}-\u{1D6DA}\u{1D6DC}-\u{1D714}\u{1D716}-\u{1D74E}\u{1D750}-\u{1D788}\u{1D78A}-\u{1D7C2}\u{1D7C4}-\u{1D7CB}\u{1D7CE}-\u{1D9FF}\u{1DA37}-\u{1DA3A}\u{1DA6D}-\u{1DA74}\u{1DA76}-\u{1DA83}\u{1DA85}-\u{1DA8B}\u{1F100}-\u{1F10A}\u{1F110}-\u{1F12E}\u{1F130}-\u{1F169}\u{1F170}-\u{1F1AC}\u{1F1E6}-\u{1F202}\u{1F210}-\u{1F23B}\u{1F240}-\u{1F248}\u{1F250}\u{1F251}\u{20000}-\u{2A6D6}\u{2A700}-\u{2B734}\u{2B740}-\u{2B81D}\u{2B820}-\u{2CEA1}\u{2CEB0}-\u{2EBE0}\u{2F800}-\u{2FA1D}\u{F0000}-\u{FFFFD}\u{100000}-\u{10FFFD}][\u0300-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08D4-\u08E1\u08E3-\u0902\u093A\u093C\u0941-\u0948\u094D\u0951-\u0957\u0962\u0963\u0981\u09BC\u09C1-\u09C4\u09CD\u09E2\u09E3\u0A01\u0A02\u0A3C\u0A41\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81\u0A82\u0ABC\u0AC1-\u0AC5\u0AC7\u0AC8\u0ACD\u0AE2\u0AE3\u0AFA-\u0AFF\u0B01\u0B3C\u0B3F\u0B41-\u0B44\u0B4D\u0B56\u0B62\u0B63\u0B82\u0BC0\u0BCD\u0C00\u0C3E-\u0C40\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C81\u0CBC\u0CCC\u0CCD\u0CE2\u0CE3\u0D00\u0D01\u0D3B\u0D3C\u0D41-\u0D44\u0D4D\u0D62\u0D63\u0DCA\u0DD2-\u0DD4\u0DD6\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0F18\u0F19\u0F35\u0F37\u0F39\u0F71-\u0F7E\u0F80-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102D-\u1030\u1032-\u1037\u1039\u103A\u103D\u103E\u1058\u1059\u105E-\u1060\u1071-\u1074\u1082\u1085\u1086\u108D\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4\u17B5\u17B7-\u17BD\u17C6\u17C9-\u17D3\u17DD\u180B-\u180D\u1885\u1886\u18A9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193B\u1A17\u1A18\u1A1B\u1A56\u1A58-\u1A5E\u1A60\u1A62\u1A65-\u1A6C\u1A73-\u1A7C\u1A7F\u1AB0-\u1ABE\u1B00-\u1B03\u1B34\u1B36-\u1B3A\u1B3C\u1B42\u1B6B-\u1B73\u1B80\u1B81\u1BA2-\u1BA5\u1BA8\u1BA9\u1BAB-\u1BAD\u1BE6\u1BE8\u1BE9\u1BED\u1BEF-\u1BF1\u1C2C-\u1C33\u1C36\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE0\u1CE2-\u1CE8\u1CED\u1CF4\u1CF8\u1CF9\u1DC0-\u1DF9\u1DFB-\u1DFF\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302D\u3099\u309A\uA66F-\uA672\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA825\uA826\uA8C4\uA8C5\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA951\uA980-\uA982\uA9B3\uA9B6-\uA9B9\uA9BC\uA9E5\uAA29-\uAA2E\uAA31\uAA32\uAA35\uAA36\uAA43\uAA4C\uAA7C\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEC\uAAED\uAAF6\uABE5\uABE8\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F\u{101FD}\u{102E0}\u{10376}-\u{1037A}\u{10A01}-\u{10A03}\u{10A05}\u{10A06}\u{10A0C}-\u{10A0F}\u{10A38}-\u{10A3A}\u{10A3F}\u{10AE5}\u{10AE6}\u{11001}\u{11038}-\u{11046}\u{1107F}-\u{11081}\u{110B3}-\u{110B6}\u{110B9}\u{110BA}\u{11100}-\u{11102}\u{11127}-\u{1112B}\u{1112D}-\u{11134}\u{11173}\u{11180}\u{11181}\u{111B6}-\u{111BE}\u{111CA}-\u{111CC}\u{1122F}-\u{11231}\u{11234}\u{11236}\u{11237}\u{1123E}\u{112DF}\u{112E3}-\u{112EA}\u{11300}\u{11301}\u{1133C}\u{11340}\u{11366}-\u{1136C}\u{11370}-\u{11374}\u{11438}-\u{1143F}\u{11442}-\u{11444}\u{11446}\u{114B3}-\u{114B8}\u{114BA}\u{114BF}\u{114C0}\u{114C2}\u{114C3}\u{115B2}-\u{115B5}\u{115BC}\u{115BD}\u{115BF}\u{115C0}\u{115DC}\u{115DD}\u{11633}-\u{1163A}\u{1163D}\u{1163F}\u{11640}\u{116AB}\u{116AD}\u{116B0}-\u{116B5}\u{116B7}\u{1171D}-\u{1171F}\u{11722}-\u{11725}\u{11727}-\u{1172B}\u{11A01}-\u{11A06}\u{11A09}\u{11A0A}\u{11A33}-\u{11A38}\u{11A3B}-\u{11A3E}\u{11A47}\u{11A51}-\u{11A56}\u{11A59}-\u{11A5B}\u{11A8A}-\u{11A96}\u{11A98}\u{11A99}\u{11C30}-\u{11C36}\u{11C38}-\u{11C3D}\u{11C92}-\u{11CA7}\u{11CAA}-\u{11CB0}\u{11CB2}\u{11CB3}\u{11CB5}\u{11CB6}\u{11D31}-\u{11D36}\u{11D3A}\u{11D3C}\u{11D3D}\u{11D3F}-\u{11D45}\u{11D47}\u{16AF0}-\u{16AF4}\u{16B30}-\u{16B36}\u{16F8F}-\u{16F92}\u{1BC9D}\u{1BC9E}\u{1D167}-\u{1D169}\u{1D17B}-\u{1D182}\u{1D185}-\u{1D18B}\u{1D1AA}-\u{1D1AD}\u{1D242}-\u{1D244}\u{1DA00}-\u{1DA36}\u{1DA3B}-\u{1DA6C}\u{1DA75}\u{1DA84}\u{1DA9B}-\u{1DA9F}\u{1DAA1}-\u{1DAAF}\u{1E000}-\u{1E006}\u{1E008}-\u{1E018}\u{1E01B}-\u{1E021}\u{1E023}\u{1E024}\u{1E026}-\u{1E02A}\u{1E8D0}-\u{1E8D6}\u{1E944}-\u{1E94A}\u{E0100}-\u{E01EF}]*$/u; + +module.exports = { + combiningMarks, + combiningClassVirama, + validZWNJ, + bidiDomain, + bidiS1LTR, + bidiS1RTL, + bidiS2, + bidiS3, + bidiS4EN, + bidiS4AN, + bidiS5, + bidiS6 +}; + + +/***/ }), + +/***/ 399: +/***/ (function(module, exports) { + +module.exports = [[[0,44],"disallowed_STD3_valid"],[[45,46],"valid"],[[47,47],"disallowed_STD3_valid"],[[48,57],"valid"],[[58,64],"disallowed_STD3_valid"],[[65,65],"mapped","a"],[[66,66],"mapped","b"],[[67,67],"mapped","c"],[[68,68],"mapped","d"],[[69,69],"mapped","e"],[[70,70],"mapped","f"],[[71,71],"mapped","g"],[[72,72],"mapped","h"],[[73,73],"mapped","i"],[[74,74],"mapped","j"],[[75,75],"mapped","k"],[[76,76],"mapped","l"],[[77,77],"mapped","m"],[[78,78],"mapped","n"],[[79,79],"mapped","o"],[[80,80],"mapped","p"],[[81,81],"mapped","q"],[[82,82],"mapped","r"],[[83,83],"mapped","s"],[[84,84],"mapped","t"],[[85,85],"mapped","u"],[[86,86],"mapped","v"],[[87,87],"mapped","w"],[[88,88],"mapped","x"],[[89,89],"mapped","y"],[[90,90],"mapped","z"],[[91,96],"disallowed_STD3_valid"],[[97,122],"valid"],[[123,127],"disallowed_STD3_valid"],[[128,159],"disallowed"],[[160,160],"disallowed_STD3_mapped"," "],[[161,167],"valid","","NV8"],[[168,168],"disallowed_STD3_mapped"," ̈"],[[169,169],"valid","","NV8"],[[170,170],"mapped","a"],[[171,172],"valid","","NV8"],[[173,173],"ignored"],[[174,174],"valid","","NV8"],[[175,175],"disallowed_STD3_mapped"," ̄"],[[176,177],"valid","","NV8"],[[178,178],"mapped","2"],[[179,179],"mapped","3"],[[180,180],"disallowed_STD3_mapped"," ́"],[[181,181],"mapped","μ"],[[182,182],"valid","","NV8"],[[183,183],"valid"],[[184,184],"disallowed_STD3_mapped"," ̧"],[[185,185],"mapped","1"],[[186,186],"mapped","o"],[[187,187],"valid","","NV8"],[[188,188],"mapped","1⁄4"],[[189,189],"mapped","1⁄2"],[[190,190],"mapped","3⁄4"],[[191,191],"valid","","NV8"],[[192,192],"mapped","à"],[[193,193],"mapped","á"],[[194,194],"mapped","â"],[[195,195],"mapped","ã"],[[196,196],"mapped","ä"],[[197,197],"mapped","å"],[[198,198],"mapped","æ"],[[199,199],"mapped","ç"],[[200,200],"mapped","è"],[[201,201],"mapped","é"],[[202,202],"mapped","ê"],[[203,203],"mapped","ë"],[[204,204],"mapped","ì"],[[205,205],"mapped","í"],[[206,206],"mapped","î"],[[207,207],"mapped","ï"],[[208,208],"mapped","ð"],[[209,209],"mapped","ñ"],[[210,210],"mapped","ò"],[[211,211],"mapped","ó"],[[212,212],"mapped","ô"],[[213,213],"mapped","õ"],[[214,214],"mapped","ö"],[[215,215],"valid","","NV8"],[[216,216],"mapped","ø"],[[217,217],"mapped","ù"],[[218,218],"mapped","ú"],[[219,219],"mapped","û"],[[220,220],"mapped","ü"],[[221,221],"mapped","ý"],[[222,222],"mapped","þ"],[[223,223],"deviation","ss"],[[224,246],"valid"],[[247,247],"valid","","NV8"],[[248,255],"valid"],[[256,256],"mapped","ā"],[[257,257],"valid"],[[258,258],"mapped","ă"],[[259,259],"valid"],[[260,260],"mapped","ą"],[[261,261],"valid"],[[262,262],"mapped","ć"],[[263,263],"valid"],[[264,264],"mapped","ĉ"],[[265,265],"valid"],[[266,266],"mapped","ċ"],[[267,267],"valid"],[[268,268],"mapped","č"],[[269,269],"valid"],[[270,270],"mapped","ď"],[[271,271],"valid"],[[272,272],"mapped","đ"],[[273,273],"valid"],[[274,274],"mapped","ē"],[[275,275],"valid"],[[276,276],"mapped","ĕ"],[[277,277],"valid"],[[278,278],"mapped","ė"],[[279,279],"valid"],[[280,280],"mapped","ę"],[[281,281],"valid"],[[282,282],"mapped","ě"],[[283,283],"valid"],[[284,284],"mapped","ĝ"],[[285,285],"valid"],[[286,286],"mapped","ğ"],[[287,287],"valid"],[[288,288],"mapped","ġ"],[[289,289],"valid"],[[290,290],"mapped","ģ"],[[291,291],"valid"],[[292,292],"mapped","ĥ"],[[293,293],"valid"],[[294,294],"mapped","ħ"],[[295,295],"valid"],[[296,296],"mapped","ĩ"],[[297,297],"valid"],[[298,298],"mapped","ī"],[[299,299],"valid"],[[300,300],"mapped","ĭ"],[[301,301],"valid"],[[302,302],"mapped","į"],[[303,303],"valid"],[[304,304],"mapped","i̇"],[[305,305],"valid"],[[306,307],"mapped","ij"],[[308,308],"mapped","ĵ"],[[309,309],"valid"],[[310,310],"mapped","ķ"],[[311,312],"valid"],[[313,313],"mapped","ĺ"],[[314,314],"valid"],[[315,315],"mapped","ļ"],[[316,316],"valid"],[[317,317],"mapped","ľ"],[[318,318],"valid"],[[319,320],"mapped","l·"],[[321,321],"mapped","ł"],[[322,322],"valid"],[[323,323],"mapped","ń"],[[324,324],"valid"],[[325,325],"mapped","ņ"],[[326,326],"valid"],[[327,327],"mapped","ň"],[[328,328],"valid"],[[329,329],"mapped","ʼn"],[[330,330],"mapped","ŋ"],[[331,331],"valid"],[[332,332],"mapped","ō"],[[333,333],"valid"],[[334,334],"mapped","ŏ"],[[335,335],"valid"],[[336,336],"mapped","ő"],[[337,337],"valid"],[[338,338],"mapped","œ"],[[339,339],"valid"],[[340,340],"mapped","ŕ"],[[341,341],"valid"],[[342,342],"mapped","ŗ"],[[343,343],"valid"],[[344,344],"mapped","ř"],[[345,345],"valid"],[[346,346],"mapped","ś"],[[347,347],"valid"],[[348,348],"mapped","ŝ"],[[349,349],"valid"],[[350,350],"mapped","ş"],[[351,351],"valid"],[[352,352],"mapped","š"],[[353,353],"valid"],[[354,354],"mapped","ţ"],[[355,355],"valid"],[[356,356],"mapped","ť"],[[357,357],"valid"],[[358,358],"mapped","ŧ"],[[359,359],"valid"],[[360,360],"mapped","ũ"],[[361,361],"valid"],[[362,362],"mapped","ū"],[[363,363],"valid"],[[364,364],"mapped","ŭ"],[[365,365],"valid"],[[366,366],"mapped","ů"],[[367,367],"valid"],[[368,368],"mapped","ű"],[[369,369],"valid"],[[370,370],"mapped","ų"],[[371,371],"valid"],[[372,372],"mapped","ŵ"],[[373,373],"valid"],[[374,374],"mapped","ŷ"],[[375,375],"valid"],[[376,376],"mapped","ÿ"],[[377,377],"mapped","ź"],[[378,378],"valid"],[[379,379],"mapped","ż"],[[380,380],"valid"],[[381,381],"mapped","ž"],[[382,382],"valid"],[[383,383],"mapped","s"],[[384,384],"valid"],[[385,385],"mapped","ɓ"],[[386,386],"mapped","ƃ"],[[387,387],"valid"],[[388,388],"mapped","ƅ"],[[389,389],"valid"],[[390,390],"mapped","ɔ"],[[391,391],"mapped","ƈ"],[[392,392],"valid"],[[393,393],"mapped","ɖ"],[[394,394],"mapped","ɗ"],[[395,395],"mapped","ƌ"],[[396,397],"valid"],[[398,398],"mapped","ǝ"],[[399,399],"mapped","ə"],[[400,400],"mapped","ɛ"],[[401,401],"mapped","ƒ"],[[402,402],"valid"],[[403,403],"mapped","ɠ"],[[404,404],"mapped","ɣ"],[[405,405],"valid"],[[406,406],"mapped","ɩ"],[[407,407],"mapped","ɨ"],[[408,408],"mapped","ƙ"],[[409,411],"valid"],[[412,412],"mapped","ɯ"],[[413,413],"mapped","ɲ"],[[414,414],"valid"],[[415,415],"mapped","ɵ"],[[416,416],"mapped","ơ"],[[417,417],"valid"],[[418,418],"mapped","ƣ"],[[419,419],"valid"],[[420,420],"mapped","ƥ"],[[421,421],"valid"],[[422,422],"mapped","ʀ"],[[423,423],"mapped","ƨ"],[[424,424],"valid"],[[425,425],"mapped","ʃ"],[[426,427],"valid"],[[428,428],"mapped","ƭ"],[[429,429],"valid"],[[430,430],"mapped","ʈ"],[[431,431],"mapped","ư"],[[432,432],"valid"],[[433,433],"mapped","ʊ"],[[434,434],"mapped","ʋ"],[[435,435],"mapped","ƴ"],[[436,436],"valid"],[[437,437],"mapped","ƶ"],[[438,438],"valid"],[[439,439],"mapped","ʒ"],[[440,440],"mapped","ƹ"],[[441,443],"valid"],[[444,444],"mapped","ƽ"],[[445,451],"valid"],[[452,454],"mapped","dž"],[[455,457],"mapped","lj"],[[458,460],"mapped","nj"],[[461,461],"mapped","ǎ"],[[462,462],"valid"],[[463,463],"mapped","ǐ"],[[464,464],"valid"],[[465,465],"mapped","ǒ"],[[466,466],"valid"],[[467,467],"mapped","ǔ"],[[468,468],"valid"],[[469,469],"mapped","ǖ"],[[470,470],"valid"],[[471,471],"mapped","ǘ"],[[472,472],"valid"],[[473,473],"mapped","ǚ"],[[474,474],"valid"],[[475,475],"mapped","ǜ"],[[476,477],"valid"],[[478,478],"mapped","ǟ"],[[479,479],"valid"],[[480,480],"mapped","ǡ"],[[481,481],"valid"],[[482,482],"mapped","ǣ"],[[483,483],"valid"],[[484,484],"mapped","ǥ"],[[485,485],"valid"],[[486,486],"mapped","ǧ"],[[487,487],"valid"],[[488,488],"mapped","ǩ"],[[489,489],"valid"],[[490,490],"mapped","ǫ"],[[491,491],"valid"],[[492,492],"mapped","ǭ"],[[493,493],"valid"],[[494,494],"mapped","ǯ"],[[495,496],"valid"],[[497,499],"mapped","dz"],[[500,500],"mapped","ǵ"],[[501,501],"valid"],[[502,502],"mapped","ƕ"],[[503,503],"mapped","ƿ"],[[504,504],"mapped","ǹ"],[[505,505],"valid"],[[506,506],"mapped","ǻ"],[[507,507],"valid"],[[508,508],"mapped","ǽ"],[[509,509],"valid"],[[510,510],"mapped","ǿ"],[[511,511],"valid"],[[512,512],"mapped","ȁ"],[[513,513],"valid"],[[514,514],"mapped","ȃ"],[[515,515],"valid"],[[516,516],"mapped","ȅ"],[[517,517],"valid"],[[518,518],"mapped","ȇ"],[[519,519],"valid"],[[520,520],"mapped","ȉ"],[[521,521],"valid"],[[522,522],"mapped","ȋ"],[[523,523],"valid"],[[524,524],"mapped","ȍ"],[[525,525],"valid"],[[526,526],"mapped","ȏ"],[[527,527],"valid"],[[528,528],"mapped","ȑ"],[[529,529],"valid"],[[530,530],"mapped","ȓ"],[[531,531],"valid"],[[532,532],"mapped","ȕ"],[[533,533],"valid"],[[534,534],"mapped","ȗ"],[[535,535],"valid"],[[536,536],"mapped","ș"],[[537,537],"valid"],[[538,538],"mapped","ț"],[[539,539],"valid"],[[540,540],"mapped","ȝ"],[[541,541],"valid"],[[542,542],"mapped","ȟ"],[[543,543],"valid"],[[544,544],"mapped","ƞ"],[[545,545],"valid"],[[546,546],"mapped","ȣ"],[[547,547],"valid"],[[548,548],"mapped","ȥ"],[[549,549],"valid"],[[550,550],"mapped","ȧ"],[[551,551],"valid"],[[552,552],"mapped","ȩ"],[[553,553],"valid"],[[554,554],"mapped","ȫ"],[[555,555],"valid"],[[556,556],"mapped","ȭ"],[[557,557],"valid"],[[558,558],"mapped","ȯ"],[[559,559],"valid"],[[560,560],"mapped","ȱ"],[[561,561],"valid"],[[562,562],"mapped","ȳ"],[[563,563],"valid"],[[564,566],"valid"],[[567,569],"valid"],[[570,570],"mapped","ⱥ"],[[571,571],"mapped","ȼ"],[[572,572],"valid"],[[573,573],"mapped","ƚ"],[[574,574],"mapped","ⱦ"],[[575,576],"valid"],[[577,577],"mapped","ɂ"],[[578,578],"valid"],[[579,579],"mapped","ƀ"],[[580,580],"mapped","ʉ"],[[581,581],"mapped","ʌ"],[[582,582],"mapped","ɇ"],[[583,583],"valid"],[[584,584],"mapped","ɉ"],[[585,585],"valid"],[[586,586],"mapped","ɋ"],[[587,587],"valid"],[[588,588],"mapped","ɍ"],[[589,589],"valid"],[[590,590],"mapped","ɏ"],[[591,591],"valid"],[[592,680],"valid"],[[681,685],"valid"],[[686,687],"valid"],[[688,688],"mapped","h"],[[689,689],"mapped","ɦ"],[[690,690],"mapped","j"],[[691,691],"mapped","r"],[[692,692],"mapped","ɹ"],[[693,693],"mapped","ɻ"],[[694,694],"mapped","ʁ"],[[695,695],"mapped","w"],[[696,696],"mapped","y"],[[697,705],"valid"],[[706,709],"valid","","NV8"],[[710,721],"valid"],[[722,727],"valid","","NV8"],[[728,728],"disallowed_STD3_mapped"," ̆"],[[729,729],"disallowed_STD3_mapped"," ̇"],[[730,730],"disallowed_STD3_mapped"," ̊"],[[731,731],"disallowed_STD3_mapped"," ̨"],[[732,732],"disallowed_STD3_mapped"," ̃"],[[733,733],"disallowed_STD3_mapped"," ̋"],[[734,734],"valid","","NV8"],[[735,735],"valid","","NV8"],[[736,736],"mapped","ɣ"],[[737,737],"mapped","l"],[[738,738],"mapped","s"],[[739,739],"mapped","x"],[[740,740],"mapped","ʕ"],[[741,745],"valid","","NV8"],[[746,747],"valid","","NV8"],[[748,748],"valid"],[[749,749],"valid","","NV8"],[[750,750],"valid"],[[751,767],"valid","","NV8"],[[768,831],"valid"],[[832,832],"mapped","̀"],[[833,833],"mapped","́"],[[834,834],"valid"],[[835,835],"mapped","̓"],[[836,836],"mapped","̈́"],[[837,837],"mapped","ι"],[[838,846],"valid"],[[847,847],"ignored"],[[848,855],"valid"],[[856,860],"valid"],[[861,863],"valid"],[[864,865],"valid"],[[866,866],"valid"],[[867,879],"valid"],[[880,880],"mapped","ͱ"],[[881,881],"valid"],[[882,882],"mapped","ͳ"],[[883,883],"valid"],[[884,884],"mapped","ʹ"],[[885,885],"valid"],[[886,886],"mapped","ͷ"],[[887,887],"valid"],[[888,889],"disallowed"],[[890,890],"disallowed_STD3_mapped"," ι"],[[891,893],"valid"],[[894,894],"disallowed_STD3_mapped",";"],[[895,895],"mapped","ϳ"],[[896,899],"disallowed"],[[900,900],"disallowed_STD3_mapped"," ́"],[[901,901],"disallowed_STD3_mapped"," ̈́"],[[902,902],"mapped","ά"],[[903,903],"mapped","·"],[[904,904],"mapped","έ"],[[905,905],"mapped","ή"],[[906,906],"mapped","ί"],[[907,907],"disallowed"],[[908,908],"mapped","ό"],[[909,909],"disallowed"],[[910,910],"mapped","ύ"],[[911,911],"mapped","ώ"],[[912,912],"valid"],[[913,913],"mapped","α"],[[914,914],"mapped","β"],[[915,915],"mapped","γ"],[[916,916],"mapped","δ"],[[917,917],"mapped","ε"],[[918,918],"mapped","ζ"],[[919,919],"mapped","η"],[[920,920],"mapped","θ"],[[921,921],"mapped","ι"],[[922,922],"mapped","κ"],[[923,923],"mapped","λ"],[[924,924],"mapped","μ"],[[925,925],"mapped","ν"],[[926,926],"mapped","ξ"],[[927,927],"mapped","ο"],[[928,928],"mapped","π"],[[929,929],"mapped","ρ"],[[930,930],"disallowed"],[[931,931],"mapped","σ"],[[932,932],"mapped","τ"],[[933,933],"mapped","υ"],[[934,934],"mapped","φ"],[[935,935],"mapped","χ"],[[936,936],"mapped","ψ"],[[937,937],"mapped","ω"],[[938,938],"mapped","ϊ"],[[939,939],"mapped","ϋ"],[[940,961],"valid"],[[962,962],"deviation","σ"],[[963,974],"valid"],[[975,975],"mapped","ϗ"],[[976,976],"mapped","β"],[[977,977],"mapped","θ"],[[978,978],"mapped","υ"],[[979,979],"mapped","ύ"],[[980,980],"mapped","ϋ"],[[981,981],"mapped","φ"],[[982,982],"mapped","π"],[[983,983],"valid"],[[984,984],"mapped","ϙ"],[[985,985],"valid"],[[986,986],"mapped","ϛ"],[[987,987],"valid"],[[988,988],"mapped","ϝ"],[[989,989],"valid"],[[990,990],"mapped","ϟ"],[[991,991],"valid"],[[992,992],"mapped","ϡ"],[[993,993],"valid"],[[994,994],"mapped","ϣ"],[[995,995],"valid"],[[996,996],"mapped","ϥ"],[[997,997],"valid"],[[998,998],"mapped","ϧ"],[[999,999],"valid"],[[1000,1000],"mapped","ϩ"],[[1001,1001],"valid"],[[1002,1002],"mapped","ϫ"],[[1003,1003],"valid"],[[1004,1004],"mapped","ϭ"],[[1005,1005],"valid"],[[1006,1006],"mapped","ϯ"],[[1007,1007],"valid"],[[1008,1008],"mapped","κ"],[[1009,1009],"mapped","ρ"],[[1010,1010],"mapped","σ"],[[1011,1011],"valid"],[[1012,1012],"mapped","θ"],[[1013,1013],"mapped","ε"],[[1014,1014],"valid","","NV8"],[[1015,1015],"mapped","ϸ"],[[1016,1016],"valid"],[[1017,1017],"mapped","σ"],[[1018,1018],"mapped","ϻ"],[[1019,1019],"valid"],[[1020,1020],"valid"],[[1021,1021],"mapped","ͻ"],[[1022,1022],"mapped","ͼ"],[[1023,1023],"mapped","ͽ"],[[1024,1024],"mapped","ѐ"],[[1025,1025],"mapped","ё"],[[1026,1026],"mapped","ђ"],[[1027,1027],"mapped","ѓ"],[[1028,1028],"mapped","є"],[[1029,1029],"mapped","ѕ"],[[1030,1030],"mapped","і"],[[1031,1031],"mapped","ї"],[[1032,1032],"mapped","ј"],[[1033,1033],"mapped","љ"],[[1034,1034],"mapped","њ"],[[1035,1035],"mapped","ћ"],[[1036,1036],"mapped","ќ"],[[1037,1037],"mapped","ѝ"],[[1038,1038],"mapped","ў"],[[1039,1039],"mapped","џ"],[[1040,1040],"mapped","а"],[[1041,1041],"mapped","б"],[[1042,1042],"mapped","в"],[[1043,1043],"mapped","г"],[[1044,1044],"mapped","д"],[[1045,1045],"mapped","е"],[[1046,1046],"mapped","ж"],[[1047,1047],"mapped","з"],[[1048,1048],"mapped","и"],[[1049,1049],"mapped","й"],[[1050,1050],"mapped","к"],[[1051,1051],"mapped","л"],[[1052,1052],"mapped","м"],[[1053,1053],"mapped","н"],[[1054,1054],"mapped","о"],[[1055,1055],"mapped","п"],[[1056,1056],"mapped","р"],[[1057,1057],"mapped","с"],[[1058,1058],"mapped","т"],[[1059,1059],"mapped","у"],[[1060,1060],"mapped","ф"],[[1061,1061],"mapped","х"],[[1062,1062],"mapped","ц"],[[1063,1063],"mapped","ч"],[[1064,1064],"mapped","ш"],[[1065,1065],"mapped","щ"],[[1066,1066],"mapped","ъ"],[[1067,1067],"mapped","ы"],[[1068,1068],"mapped","ь"],[[1069,1069],"mapped","э"],[[1070,1070],"mapped","ю"],[[1071,1071],"mapped","я"],[[1072,1103],"valid"],[[1104,1104],"valid"],[[1105,1116],"valid"],[[1117,1117],"valid"],[[1118,1119],"valid"],[[1120,1120],"mapped","ѡ"],[[1121,1121],"valid"],[[1122,1122],"mapped","ѣ"],[[1123,1123],"valid"],[[1124,1124],"mapped","ѥ"],[[1125,1125],"valid"],[[1126,1126],"mapped","ѧ"],[[1127,1127],"valid"],[[1128,1128],"mapped","ѩ"],[[1129,1129],"valid"],[[1130,1130],"mapped","ѫ"],[[1131,1131],"valid"],[[1132,1132],"mapped","ѭ"],[[1133,1133],"valid"],[[1134,1134],"mapped","ѯ"],[[1135,1135],"valid"],[[1136,1136],"mapped","ѱ"],[[1137,1137],"valid"],[[1138,1138],"mapped","ѳ"],[[1139,1139],"valid"],[[1140,1140],"mapped","ѵ"],[[1141,1141],"valid"],[[1142,1142],"mapped","ѷ"],[[1143,1143],"valid"],[[1144,1144],"mapped","ѹ"],[[1145,1145],"valid"],[[1146,1146],"mapped","ѻ"],[[1147,1147],"valid"],[[1148,1148],"mapped","ѽ"],[[1149,1149],"valid"],[[1150,1150],"mapped","ѿ"],[[1151,1151],"valid"],[[1152,1152],"mapped","ҁ"],[[1153,1153],"valid"],[[1154,1154],"valid","","NV8"],[[1155,1158],"valid"],[[1159,1159],"valid"],[[1160,1161],"valid","","NV8"],[[1162,1162],"mapped","ҋ"],[[1163,1163],"valid"],[[1164,1164],"mapped","ҍ"],[[1165,1165],"valid"],[[1166,1166],"mapped","ҏ"],[[1167,1167],"valid"],[[1168,1168],"mapped","ґ"],[[1169,1169],"valid"],[[1170,1170],"mapped","ғ"],[[1171,1171],"valid"],[[1172,1172],"mapped","ҕ"],[[1173,1173],"valid"],[[1174,1174],"mapped","җ"],[[1175,1175],"valid"],[[1176,1176],"mapped","ҙ"],[[1177,1177],"valid"],[[1178,1178],"mapped","қ"],[[1179,1179],"valid"],[[1180,1180],"mapped","ҝ"],[[1181,1181],"valid"],[[1182,1182],"mapped","ҟ"],[[1183,1183],"valid"],[[1184,1184],"mapped","ҡ"],[[1185,1185],"valid"],[[1186,1186],"mapped","ң"],[[1187,1187],"valid"],[[1188,1188],"mapped","ҥ"],[[1189,1189],"valid"],[[1190,1190],"mapped","ҧ"],[[1191,1191],"valid"],[[1192,1192],"mapped","ҩ"],[[1193,1193],"valid"],[[1194,1194],"mapped","ҫ"],[[1195,1195],"valid"],[[1196,1196],"mapped","ҭ"],[[1197,1197],"valid"],[[1198,1198],"mapped","ү"],[[1199,1199],"valid"],[[1200,1200],"mapped","ұ"],[[1201,1201],"valid"],[[1202,1202],"mapped","ҳ"],[[1203,1203],"valid"],[[1204,1204],"mapped","ҵ"],[[1205,1205],"valid"],[[1206,1206],"mapped","ҷ"],[[1207,1207],"valid"],[[1208,1208],"mapped","ҹ"],[[1209,1209],"valid"],[[1210,1210],"mapped","һ"],[[1211,1211],"valid"],[[1212,1212],"mapped","ҽ"],[[1213,1213],"valid"],[[1214,1214],"mapped","ҿ"],[[1215,1215],"valid"],[[1216,1216],"disallowed"],[[1217,1217],"mapped","ӂ"],[[1218,1218],"valid"],[[1219,1219],"mapped","ӄ"],[[1220,1220],"valid"],[[1221,1221],"mapped","ӆ"],[[1222,1222],"valid"],[[1223,1223],"mapped","ӈ"],[[1224,1224],"valid"],[[1225,1225],"mapped","ӊ"],[[1226,1226],"valid"],[[1227,1227],"mapped","ӌ"],[[1228,1228],"valid"],[[1229,1229],"mapped","ӎ"],[[1230,1230],"valid"],[[1231,1231],"valid"],[[1232,1232],"mapped","ӑ"],[[1233,1233],"valid"],[[1234,1234],"mapped","ӓ"],[[1235,1235],"valid"],[[1236,1236],"mapped","ӕ"],[[1237,1237],"valid"],[[1238,1238],"mapped","ӗ"],[[1239,1239],"valid"],[[1240,1240],"mapped","ә"],[[1241,1241],"valid"],[[1242,1242],"mapped","ӛ"],[[1243,1243],"valid"],[[1244,1244],"mapped","ӝ"],[[1245,1245],"valid"],[[1246,1246],"mapped","ӟ"],[[1247,1247],"valid"],[[1248,1248],"mapped","ӡ"],[[1249,1249],"valid"],[[1250,1250],"mapped","ӣ"],[[1251,1251],"valid"],[[1252,1252],"mapped","ӥ"],[[1253,1253],"valid"],[[1254,1254],"mapped","ӧ"],[[1255,1255],"valid"],[[1256,1256],"mapped","ө"],[[1257,1257],"valid"],[[1258,1258],"mapped","ӫ"],[[1259,1259],"valid"],[[1260,1260],"mapped","ӭ"],[[1261,1261],"valid"],[[1262,1262],"mapped","ӯ"],[[1263,1263],"valid"],[[1264,1264],"mapped","ӱ"],[[1265,1265],"valid"],[[1266,1266],"mapped","ӳ"],[[1267,1267],"valid"],[[1268,1268],"mapped","ӵ"],[[1269,1269],"valid"],[[1270,1270],"mapped","ӷ"],[[1271,1271],"valid"],[[1272,1272],"mapped","ӹ"],[[1273,1273],"valid"],[[1274,1274],"mapped","ӻ"],[[1275,1275],"valid"],[[1276,1276],"mapped","ӽ"],[[1277,1277],"valid"],[[1278,1278],"mapped","ӿ"],[[1279,1279],"valid"],[[1280,1280],"mapped","ԁ"],[[1281,1281],"valid"],[[1282,1282],"mapped","ԃ"],[[1283,1283],"valid"],[[1284,1284],"mapped","ԅ"],[[1285,1285],"valid"],[[1286,1286],"mapped","ԇ"],[[1287,1287],"valid"],[[1288,1288],"mapped","ԉ"],[[1289,1289],"valid"],[[1290,1290],"mapped","ԋ"],[[1291,1291],"valid"],[[1292,1292],"mapped","ԍ"],[[1293,1293],"valid"],[[1294,1294],"mapped","ԏ"],[[1295,1295],"valid"],[[1296,1296],"mapped","ԑ"],[[1297,1297],"valid"],[[1298,1298],"mapped","ԓ"],[[1299,1299],"valid"],[[1300,1300],"mapped","ԕ"],[[1301,1301],"valid"],[[1302,1302],"mapped","ԗ"],[[1303,1303],"valid"],[[1304,1304],"mapped","ԙ"],[[1305,1305],"valid"],[[1306,1306],"mapped","ԛ"],[[1307,1307],"valid"],[[1308,1308],"mapped","ԝ"],[[1309,1309],"valid"],[[1310,1310],"mapped","ԟ"],[[1311,1311],"valid"],[[1312,1312],"mapped","ԡ"],[[1313,1313],"valid"],[[1314,1314],"mapped","ԣ"],[[1315,1315],"valid"],[[1316,1316],"mapped","ԥ"],[[1317,1317],"valid"],[[1318,1318],"mapped","ԧ"],[[1319,1319],"valid"],[[1320,1320],"mapped","ԩ"],[[1321,1321],"valid"],[[1322,1322],"mapped","ԫ"],[[1323,1323],"valid"],[[1324,1324],"mapped","ԭ"],[[1325,1325],"valid"],[[1326,1326],"mapped","ԯ"],[[1327,1327],"valid"],[[1328,1328],"disallowed"],[[1329,1329],"mapped","ա"],[[1330,1330],"mapped","բ"],[[1331,1331],"mapped","գ"],[[1332,1332],"mapped","դ"],[[1333,1333],"mapped","ե"],[[1334,1334],"mapped","զ"],[[1335,1335],"mapped","է"],[[1336,1336],"mapped","ը"],[[1337,1337],"mapped","թ"],[[1338,1338],"mapped","ժ"],[[1339,1339],"mapped","ի"],[[1340,1340],"mapped","լ"],[[1341,1341],"mapped","խ"],[[1342,1342],"mapped","ծ"],[[1343,1343],"mapped","կ"],[[1344,1344],"mapped","հ"],[[1345,1345],"mapped","ձ"],[[1346,1346],"mapped","ղ"],[[1347,1347],"mapped","ճ"],[[1348,1348],"mapped","մ"],[[1349,1349],"mapped","յ"],[[1350,1350],"mapped","ն"],[[1351,1351],"mapped","շ"],[[1352,1352],"mapped","ո"],[[1353,1353],"mapped","չ"],[[1354,1354],"mapped","պ"],[[1355,1355],"mapped","ջ"],[[1356,1356],"mapped","ռ"],[[1357,1357],"mapped","ս"],[[1358,1358],"mapped","վ"],[[1359,1359],"mapped","տ"],[[1360,1360],"mapped","ր"],[[1361,1361],"mapped","ց"],[[1362,1362],"mapped","ւ"],[[1363,1363],"mapped","փ"],[[1364,1364],"mapped","ք"],[[1365,1365],"mapped","օ"],[[1366,1366],"mapped","ֆ"],[[1367,1368],"disallowed"],[[1369,1369],"valid"],[[1370,1375],"valid","","NV8"],[[1376,1376],"disallowed"],[[1377,1414],"valid"],[[1415,1415],"mapped","եւ"],[[1416,1416],"disallowed"],[[1417,1417],"valid","","NV8"],[[1418,1418],"valid","","NV8"],[[1419,1420],"disallowed"],[[1421,1422],"valid","","NV8"],[[1423,1423],"valid","","NV8"],[[1424,1424],"disallowed"],[[1425,1441],"valid"],[[1442,1442],"valid"],[[1443,1455],"valid"],[[1456,1465],"valid"],[[1466,1466],"valid"],[[1467,1469],"valid"],[[1470,1470],"valid","","NV8"],[[1471,1471],"valid"],[[1472,1472],"valid","","NV8"],[[1473,1474],"valid"],[[1475,1475],"valid","","NV8"],[[1476,1476],"valid"],[[1477,1477],"valid"],[[1478,1478],"valid","","NV8"],[[1479,1479],"valid"],[[1480,1487],"disallowed"],[[1488,1514],"valid"],[[1515,1519],"disallowed"],[[1520,1524],"valid"],[[1525,1535],"disallowed"],[[1536,1539],"disallowed"],[[1540,1540],"disallowed"],[[1541,1541],"disallowed"],[[1542,1546],"valid","","NV8"],[[1547,1547],"valid","","NV8"],[[1548,1548],"valid","","NV8"],[[1549,1551],"valid","","NV8"],[[1552,1557],"valid"],[[1558,1562],"valid"],[[1563,1563],"valid","","NV8"],[[1564,1564],"disallowed"],[[1565,1565],"disallowed"],[[1566,1566],"valid","","NV8"],[[1567,1567],"valid","","NV8"],[[1568,1568],"valid"],[[1569,1594],"valid"],[[1595,1599],"valid"],[[1600,1600],"valid","","NV8"],[[1601,1618],"valid"],[[1619,1621],"valid"],[[1622,1624],"valid"],[[1625,1630],"valid"],[[1631,1631],"valid"],[[1632,1641],"valid"],[[1642,1645],"valid","","NV8"],[[1646,1647],"valid"],[[1648,1652],"valid"],[[1653,1653],"mapped","اٴ"],[[1654,1654],"mapped","وٴ"],[[1655,1655],"mapped","ۇٴ"],[[1656,1656],"mapped","يٴ"],[[1657,1719],"valid"],[[1720,1721],"valid"],[[1722,1726],"valid"],[[1727,1727],"valid"],[[1728,1742],"valid"],[[1743,1743],"valid"],[[1744,1747],"valid"],[[1748,1748],"valid","","NV8"],[[1749,1756],"valid"],[[1757,1757],"disallowed"],[[1758,1758],"valid","","NV8"],[[1759,1768],"valid"],[[1769,1769],"valid","","NV8"],[[1770,1773],"valid"],[[1774,1775],"valid"],[[1776,1785],"valid"],[[1786,1790],"valid"],[[1791,1791],"valid"],[[1792,1805],"valid","","NV8"],[[1806,1806],"disallowed"],[[1807,1807],"disallowed"],[[1808,1836],"valid"],[[1837,1839],"valid"],[[1840,1866],"valid"],[[1867,1868],"disallowed"],[[1869,1871],"valid"],[[1872,1901],"valid"],[[1902,1919],"valid"],[[1920,1968],"valid"],[[1969,1969],"valid"],[[1970,1983],"disallowed"],[[1984,2037],"valid"],[[2038,2042],"valid","","NV8"],[[2043,2047],"disallowed"],[[2048,2093],"valid"],[[2094,2095],"disallowed"],[[2096,2110],"valid","","NV8"],[[2111,2111],"disallowed"],[[2112,2139],"valid"],[[2140,2141],"disallowed"],[[2142,2142],"valid","","NV8"],[[2143,2143],"disallowed"],[[2144,2154],"valid"],[[2155,2207],"disallowed"],[[2208,2208],"valid"],[[2209,2209],"valid"],[[2210,2220],"valid"],[[2221,2226],"valid"],[[2227,2228],"valid"],[[2229,2229],"disallowed"],[[2230,2237],"valid"],[[2238,2259],"disallowed"],[[2260,2273],"valid"],[[2274,2274],"disallowed"],[[2275,2275],"valid"],[[2276,2302],"valid"],[[2303,2303],"valid"],[[2304,2304],"valid"],[[2305,2307],"valid"],[[2308,2308],"valid"],[[2309,2361],"valid"],[[2362,2363],"valid"],[[2364,2381],"valid"],[[2382,2382],"valid"],[[2383,2383],"valid"],[[2384,2388],"valid"],[[2389,2389],"valid"],[[2390,2391],"valid"],[[2392,2392],"mapped","क़"],[[2393,2393],"mapped","ख़"],[[2394,2394],"mapped","ग़"],[[2395,2395],"mapped","ज़"],[[2396,2396],"mapped","ड़"],[[2397,2397],"mapped","ढ़"],[[2398,2398],"mapped","फ़"],[[2399,2399],"mapped","य़"],[[2400,2403],"valid"],[[2404,2405],"valid","","NV8"],[[2406,2415],"valid"],[[2416,2416],"valid","","NV8"],[[2417,2418],"valid"],[[2419,2423],"valid"],[[2424,2424],"valid"],[[2425,2426],"valid"],[[2427,2428],"valid"],[[2429,2429],"valid"],[[2430,2431],"valid"],[[2432,2432],"valid"],[[2433,2435],"valid"],[[2436,2436],"disallowed"],[[2437,2444],"valid"],[[2445,2446],"disallowed"],[[2447,2448],"valid"],[[2449,2450],"disallowed"],[[2451,2472],"valid"],[[2473,2473],"disallowed"],[[2474,2480],"valid"],[[2481,2481],"disallowed"],[[2482,2482],"valid"],[[2483,2485],"disallowed"],[[2486,2489],"valid"],[[2490,2491],"disallowed"],[[2492,2492],"valid"],[[2493,2493],"valid"],[[2494,2500],"valid"],[[2501,2502],"disallowed"],[[2503,2504],"valid"],[[2505,2506],"disallowed"],[[2507,2509],"valid"],[[2510,2510],"valid"],[[2511,2518],"disallowed"],[[2519,2519],"valid"],[[2520,2523],"disallowed"],[[2524,2524],"mapped","ড়"],[[2525,2525],"mapped","ঢ়"],[[2526,2526],"disallowed"],[[2527,2527],"mapped","য়"],[[2528,2531],"valid"],[[2532,2533],"disallowed"],[[2534,2545],"valid"],[[2546,2554],"valid","","NV8"],[[2555,2555],"valid","","NV8"],[[2556,2556],"valid"],[[2557,2557],"valid","","NV8"],[[2558,2560],"disallowed"],[[2561,2561],"valid"],[[2562,2562],"valid"],[[2563,2563],"valid"],[[2564,2564],"disallowed"],[[2565,2570],"valid"],[[2571,2574],"disallowed"],[[2575,2576],"valid"],[[2577,2578],"disallowed"],[[2579,2600],"valid"],[[2601,2601],"disallowed"],[[2602,2608],"valid"],[[2609,2609],"disallowed"],[[2610,2610],"valid"],[[2611,2611],"mapped","ਲ਼"],[[2612,2612],"disallowed"],[[2613,2613],"valid"],[[2614,2614],"mapped","ਸ਼"],[[2615,2615],"disallowed"],[[2616,2617],"valid"],[[2618,2619],"disallowed"],[[2620,2620],"valid"],[[2621,2621],"disallowed"],[[2622,2626],"valid"],[[2627,2630],"disallowed"],[[2631,2632],"valid"],[[2633,2634],"disallowed"],[[2635,2637],"valid"],[[2638,2640],"disallowed"],[[2641,2641],"valid"],[[2642,2648],"disallowed"],[[2649,2649],"mapped","ਖ਼"],[[2650,2650],"mapped","ਗ਼"],[[2651,2651],"mapped","ਜ਼"],[[2652,2652],"valid"],[[2653,2653],"disallowed"],[[2654,2654],"mapped","ਫ਼"],[[2655,2661],"disallowed"],[[2662,2676],"valid"],[[2677,2677],"valid"],[[2678,2688],"disallowed"],[[2689,2691],"valid"],[[2692,2692],"disallowed"],[[2693,2699],"valid"],[[2700,2700],"valid"],[[2701,2701],"valid"],[[2702,2702],"disallowed"],[[2703,2705],"valid"],[[2706,2706],"disallowed"],[[2707,2728],"valid"],[[2729,2729],"disallowed"],[[2730,2736],"valid"],[[2737,2737],"disallowed"],[[2738,2739],"valid"],[[2740,2740],"disallowed"],[[2741,2745],"valid"],[[2746,2747],"disallowed"],[[2748,2757],"valid"],[[2758,2758],"disallowed"],[[2759,2761],"valid"],[[2762,2762],"disallowed"],[[2763,2765],"valid"],[[2766,2767],"disallowed"],[[2768,2768],"valid"],[[2769,2783],"disallowed"],[[2784,2784],"valid"],[[2785,2787],"valid"],[[2788,2789],"disallowed"],[[2790,2799],"valid"],[[2800,2800],"valid","","NV8"],[[2801,2801],"valid","","NV8"],[[2802,2808],"disallowed"],[[2809,2809],"valid"],[[2810,2815],"valid"],[[2816,2816],"disallowed"],[[2817,2819],"valid"],[[2820,2820],"disallowed"],[[2821,2828],"valid"],[[2829,2830],"disallowed"],[[2831,2832],"valid"],[[2833,2834],"disallowed"],[[2835,2856],"valid"],[[2857,2857],"disallowed"],[[2858,2864],"valid"],[[2865,2865],"disallowed"],[[2866,2867],"valid"],[[2868,2868],"disallowed"],[[2869,2869],"valid"],[[2870,2873],"valid"],[[2874,2875],"disallowed"],[[2876,2883],"valid"],[[2884,2884],"valid"],[[2885,2886],"disallowed"],[[2887,2888],"valid"],[[2889,2890],"disallowed"],[[2891,2893],"valid"],[[2894,2901],"disallowed"],[[2902,2903],"valid"],[[2904,2907],"disallowed"],[[2908,2908],"mapped","ଡ଼"],[[2909,2909],"mapped","ଢ଼"],[[2910,2910],"disallowed"],[[2911,2913],"valid"],[[2914,2915],"valid"],[[2916,2917],"disallowed"],[[2918,2927],"valid"],[[2928,2928],"valid","","NV8"],[[2929,2929],"valid"],[[2930,2935],"valid","","NV8"],[[2936,2945],"disallowed"],[[2946,2947],"valid"],[[2948,2948],"disallowed"],[[2949,2954],"valid"],[[2955,2957],"disallowed"],[[2958,2960],"valid"],[[2961,2961],"disallowed"],[[2962,2965],"valid"],[[2966,2968],"disallowed"],[[2969,2970],"valid"],[[2971,2971],"disallowed"],[[2972,2972],"valid"],[[2973,2973],"disallowed"],[[2974,2975],"valid"],[[2976,2978],"disallowed"],[[2979,2980],"valid"],[[2981,2983],"disallowed"],[[2984,2986],"valid"],[[2987,2989],"disallowed"],[[2990,2997],"valid"],[[2998,2998],"valid"],[[2999,3001],"valid"],[[3002,3005],"disallowed"],[[3006,3010],"valid"],[[3011,3013],"disallowed"],[[3014,3016],"valid"],[[3017,3017],"disallowed"],[[3018,3021],"valid"],[[3022,3023],"disallowed"],[[3024,3024],"valid"],[[3025,3030],"disallowed"],[[3031,3031],"valid"],[[3032,3045],"disallowed"],[[3046,3046],"valid"],[[3047,3055],"valid"],[[3056,3058],"valid","","NV8"],[[3059,3066],"valid","","NV8"],[[3067,3071],"disallowed"],[[3072,3072],"valid"],[[3073,3075],"valid"],[[3076,3076],"disallowed"],[[3077,3084],"valid"],[[3085,3085],"disallowed"],[[3086,3088],"valid"],[[3089,3089],"disallowed"],[[3090,3112],"valid"],[[3113,3113],"disallowed"],[[3114,3123],"valid"],[[3124,3124],"valid"],[[3125,3129],"valid"],[[3130,3132],"disallowed"],[[3133,3133],"valid"],[[3134,3140],"valid"],[[3141,3141],"disallowed"],[[3142,3144],"valid"],[[3145,3145],"disallowed"],[[3146,3149],"valid"],[[3150,3156],"disallowed"],[[3157,3158],"valid"],[[3159,3159],"disallowed"],[[3160,3161],"valid"],[[3162,3162],"valid"],[[3163,3167],"disallowed"],[[3168,3169],"valid"],[[3170,3171],"valid"],[[3172,3173],"disallowed"],[[3174,3183],"valid"],[[3184,3191],"disallowed"],[[3192,3199],"valid","","NV8"],[[3200,3200],"valid"],[[3201,3201],"valid"],[[3202,3203],"valid"],[[3204,3204],"disallowed"],[[3205,3212],"valid"],[[3213,3213],"disallowed"],[[3214,3216],"valid"],[[3217,3217],"disallowed"],[[3218,3240],"valid"],[[3241,3241],"disallowed"],[[3242,3251],"valid"],[[3252,3252],"disallowed"],[[3253,3257],"valid"],[[3258,3259],"disallowed"],[[3260,3261],"valid"],[[3262,3268],"valid"],[[3269,3269],"disallowed"],[[3270,3272],"valid"],[[3273,3273],"disallowed"],[[3274,3277],"valid"],[[3278,3284],"disallowed"],[[3285,3286],"valid"],[[3287,3293],"disallowed"],[[3294,3294],"valid"],[[3295,3295],"disallowed"],[[3296,3297],"valid"],[[3298,3299],"valid"],[[3300,3301],"disallowed"],[[3302,3311],"valid"],[[3312,3312],"disallowed"],[[3313,3314],"valid"],[[3315,3327],"disallowed"],[[3328,3328],"valid"],[[3329,3329],"valid"],[[3330,3331],"valid"],[[3332,3332],"disallowed"],[[3333,3340],"valid"],[[3341,3341],"disallowed"],[[3342,3344],"valid"],[[3345,3345],"disallowed"],[[3346,3368],"valid"],[[3369,3369],"valid"],[[3370,3385],"valid"],[[3386,3386],"valid"],[[3387,3388],"valid"],[[3389,3389],"valid"],[[3390,3395],"valid"],[[3396,3396],"valid"],[[3397,3397],"disallowed"],[[3398,3400],"valid"],[[3401,3401],"disallowed"],[[3402,3405],"valid"],[[3406,3406],"valid"],[[3407,3407],"valid","","NV8"],[[3408,3411],"disallowed"],[[3412,3414],"valid"],[[3415,3415],"valid"],[[3416,3422],"valid","","NV8"],[[3423,3423],"valid"],[[3424,3425],"valid"],[[3426,3427],"valid"],[[3428,3429],"disallowed"],[[3430,3439],"valid"],[[3440,3445],"valid","","NV8"],[[3446,3448],"valid","","NV8"],[[3449,3449],"valid","","NV8"],[[3450,3455],"valid"],[[3456,3457],"disallowed"],[[3458,3459],"valid"],[[3460,3460],"disallowed"],[[3461,3478],"valid"],[[3479,3481],"disallowed"],[[3482,3505],"valid"],[[3506,3506],"disallowed"],[[3507,3515],"valid"],[[3516,3516],"disallowed"],[[3517,3517],"valid"],[[3518,3519],"disallowed"],[[3520,3526],"valid"],[[3527,3529],"disallowed"],[[3530,3530],"valid"],[[3531,3534],"disallowed"],[[3535,3540],"valid"],[[3541,3541],"disallowed"],[[3542,3542],"valid"],[[3543,3543],"disallowed"],[[3544,3551],"valid"],[[3552,3557],"disallowed"],[[3558,3567],"valid"],[[3568,3569],"disallowed"],[[3570,3571],"valid"],[[3572,3572],"valid","","NV8"],[[3573,3584],"disallowed"],[[3585,3634],"valid"],[[3635,3635],"mapped","ํา"],[[3636,3642],"valid"],[[3643,3646],"disallowed"],[[3647,3647],"valid","","NV8"],[[3648,3662],"valid"],[[3663,3663],"valid","","NV8"],[[3664,3673],"valid"],[[3674,3675],"valid","","NV8"],[[3676,3712],"disallowed"],[[3713,3714],"valid"],[[3715,3715],"disallowed"],[[3716,3716],"valid"],[[3717,3718],"disallowed"],[[3719,3720],"valid"],[[3721,3721],"disallowed"],[[3722,3722],"valid"],[[3723,3724],"disallowed"],[[3725,3725],"valid"],[[3726,3731],"disallowed"],[[3732,3735],"valid"],[[3736,3736],"disallowed"],[[3737,3743],"valid"],[[3744,3744],"disallowed"],[[3745,3747],"valid"],[[3748,3748],"disallowed"],[[3749,3749],"valid"],[[3750,3750],"disallowed"],[[3751,3751],"valid"],[[3752,3753],"disallowed"],[[3754,3755],"valid"],[[3756,3756],"disallowed"],[[3757,3762],"valid"],[[3763,3763],"mapped","ໍາ"],[[3764,3769],"valid"],[[3770,3770],"disallowed"],[[3771,3773],"valid"],[[3774,3775],"disallowed"],[[3776,3780],"valid"],[[3781,3781],"disallowed"],[[3782,3782],"valid"],[[3783,3783],"disallowed"],[[3784,3789],"valid"],[[3790,3791],"disallowed"],[[3792,3801],"valid"],[[3802,3803],"disallowed"],[[3804,3804],"mapped","ຫນ"],[[3805,3805],"mapped","ຫມ"],[[3806,3807],"valid"],[[3808,3839],"disallowed"],[[3840,3840],"valid"],[[3841,3850],"valid","","NV8"],[[3851,3851],"valid"],[[3852,3852],"mapped","་"],[[3853,3863],"valid","","NV8"],[[3864,3865],"valid"],[[3866,3871],"valid","","NV8"],[[3872,3881],"valid"],[[3882,3892],"valid","","NV8"],[[3893,3893],"valid"],[[3894,3894],"valid","","NV8"],[[3895,3895],"valid"],[[3896,3896],"valid","","NV8"],[[3897,3897],"valid"],[[3898,3901],"valid","","NV8"],[[3902,3906],"valid"],[[3907,3907],"mapped","གྷ"],[[3908,3911],"valid"],[[3912,3912],"disallowed"],[[3913,3916],"valid"],[[3917,3917],"mapped","ཌྷ"],[[3918,3921],"valid"],[[3922,3922],"mapped","དྷ"],[[3923,3926],"valid"],[[3927,3927],"mapped","བྷ"],[[3928,3931],"valid"],[[3932,3932],"mapped","ཛྷ"],[[3933,3944],"valid"],[[3945,3945],"mapped","ཀྵ"],[[3946,3946],"valid"],[[3947,3948],"valid"],[[3949,3952],"disallowed"],[[3953,3954],"valid"],[[3955,3955],"mapped","ཱི"],[[3956,3956],"valid"],[[3957,3957],"mapped","ཱུ"],[[3958,3958],"mapped","ྲྀ"],[[3959,3959],"mapped","ྲཱྀ"],[[3960,3960],"mapped","ླྀ"],[[3961,3961],"mapped","ླཱྀ"],[[3962,3968],"valid"],[[3969,3969],"mapped","ཱྀ"],[[3970,3972],"valid"],[[3973,3973],"valid","","NV8"],[[3974,3979],"valid"],[[3980,3983],"valid"],[[3984,3986],"valid"],[[3987,3987],"mapped","ྒྷ"],[[3988,3989],"valid"],[[3990,3990],"valid"],[[3991,3991],"valid"],[[3992,3992],"disallowed"],[[3993,3996],"valid"],[[3997,3997],"mapped","ྜྷ"],[[3998,4001],"valid"],[[4002,4002],"mapped","ྡྷ"],[[4003,4006],"valid"],[[4007,4007],"mapped","ྦྷ"],[[4008,4011],"valid"],[[4012,4012],"mapped","ྫྷ"],[[4013,4013],"valid"],[[4014,4016],"valid"],[[4017,4023],"valid"],[[4024,4024],"valid"],[[4025,4025],"mapped","ྐྵ"],[[4026,4028],"valid"],[[4029,4029],"disallowed"],[[4030,4037],"valid","","NV8"],[[4038,4038],"valid"],[[4039,4044],"valid","","NV8"],[[4045,4045],"disallowed"],[[4046,4046],"valid","","NV8"],[[4047,4047],"valid","","NV8"],[[4048,4049],"valid","","NV8"],[[4050,4052],"valid","","NV8"],[[4053,4056],"valid","","NV8"],[[4057,4058],"valid","","NV8"],[[4059,4095],"disallowed"],[[4096,4129],"valid"],[[4130,4130],"valid"],[[4131,4135],"valid"],[[4136,4136],"valid"],[[4137,4138],"valid"],[[4139,4139],"valid"],[[4140,4146],"valid"],[[4147,4149],"valid"],[[4150,4153],"valid"],[[4154,4159],"valid"],[[4160,4169],"valid"],[[4170,4175],"valid","","NV8"],[[4176,4185],"valid"],[[4186,4249],"valid"],[[4250,4253],"valid"],[[4254,4255],"valid","","NV8"],[[4256,4293],"disallowed"],[[4294,4294],"disallowed"],[[4295,4295],"mapped","ⴧ"],[[4296,4300],"disallowed"],[[4301,4301],"mapped","ⴭ"],[[4302,4303],"disallowed"],[[4304,4342],"valid"],[[4343,4344],"valid"],[[4345,4346],"valid"],[[4347,4347],"valid","","NV8"],[[4348,4348],"mapped","ნ"],[[4349,4351],"valid"],[[4352,4441],"valid","","NV8"],[[4442,4446],"valid","","NV8"],[[4447,4448],"disallowed"],[[4449,4514],"valid","","NV8"],[[4515,4519],"valid","","NV8"],[[4520,4601],"valid","","NV8"],[[4602,4607],"valid","","NV8"],[[4608,4614],"valid"],[[4615,4615],"valid"],[[4616,4678],"valid"],[[4679,4679],"valid"],[[4680,4680],"valid"],[[4681,4681],"disallowed"],[[4682,4685],"valid"],[[4686,4687],"disallowed"],[[4688,4694],"valid"],[[4695,4695],"disallowed"],[[4696,4696],"valid"],[[4697,4697],"disallowed"],[[4698,4701],"valid"],[[4702,4703],"disallowed"],[[4704,4742],"valid"],[[4743,4743],"valid"],[[4744,4744],"valid"],[[4745,4745],"disallowed"],[[4746,4749],"valid"],[[4750,4751],"disallowed"],[[4752,4782],"valid"],[[4783,4783],"valid"],[[4784,4784],"valid"],[[4785,4785],"disallowed"],[[4786,4789],"valid"],[[4790,4791],"disallowed"],[[4792,4798],"valid"],[[4799,4799],"disallowed"],[[4800,4800],"valid"],[[4801,4801],"disallowed"],[[4802,4805],"valid"],[[4806,4807],"disallowed"],[[4808,4814],"valid"],[[4815,4815],"valid"],[[4816,4822],"valid"],[[4823,4823],"disallowed"],[[4824,4846],"valid"],[[4847,4847],"valid"],[[4848,4878],"valid"],[[4879,4879],"valid"],[[4880,4880],"valid"],[[4881,4881],"disallowed"],[[4882,4885],"valid"],[[4886,4887],"disallowed"],[[4888,4894],"valid"],[[4895,4895],"valid"],[[4896,4934],"valid"],[[4935,4935],"valid"],[[4936,4954],"valid"],[[4955,4956],"disallowed"],[[4957,4958],"valid"],[[4959,4959],"valid"],[[4960,4960],"valid","","NV8"],[[4961,4988],"valid","","NV8"],[[4989,4991],"disallowed"],[[4992,5007],"valid"],[[5008,5017],"valid","","NV8"],[[5018,5023],"disallowed"],[[5024,5108],"valid"],[[5109,5109],"valid"],[[5110,5111],"disallowed"],[[5112,5112],"mapped","Ᏸ"],[[5113,5113],"mapped","Ᏹ"],[[5114,5114],"mapped","Ᏺ"],[[5115,5115],"mapped","Ᏻ"],[[5116,5116],"mapped","Ᏼ"],[[5117,5117],"mapped","Ᏽ"],[[5118,5119],"disallowed"],[[5120,5120],"valid","","NV8"],[[5121,5740],"valid"],[[5741,5742],"valid","","NV8"],[[5743,5750],"valid"],[[5751,5759],"valid"],[[5760,5760],"disallowed"],[[5761,5786],"valid"],[[5787,5788],"valid","","NV8"],[[5789,5791],"disallowed"],[[5792,5866],"valid"],[[5867,5872],"valid","","NV8"],[[5873,5880],"valid"],[[5881,5887],"disallowed"],[[5888,5900],"valid"],[[5901,5901],"disallowed"],[[5902,5908],"valid"],[[5909,5919],"disallowed"],[[5920,5940],"valid"],[[5941,5942],"valid","","NV8"],[[5943,5951],"disallowed"],[[5952,5971],"valid"],[[5972,5983],"disallowed"],[[5984,5996],"valid"],[[5997,5997],"disallowed"],[[5998,6000],"valid"],[[6001,6001],"disallowed"],[[6002,6003],"valid"],[[6004,6015],"disallowed"],[[6016,6067],"valid"],[[6068,6069],"disallowed"],[[6070,6099],"valid"],[[6100,6102],"valid","","NV8"],[[6103,6103],"valid"],[[6104,6107],"valid","","NV8"],[[6108,6108],"valid"],[[6109,6109],"valid"],[[6110,6111],"disallowed"],[[6112,6121],"valid"],[[6122,6127],"disallowed"],[[6128,6137],"valid","","NV8"],[[6138,6143],"disallowed"],[[6144,6149],"valid","","NV8"],[[6150,6150],"disallowed"],[[6151,6154],"valid","","NV8"],[[6155,6157],"ignored"],[[6158,6158],"disallowed"],[[6159,6159],"disallowed"],[[6160,6169],"valid"],[[6170,6175],"disallowed"],[[6176,6263],"valid"],[[6264,6271],"disallowed"],[[6272,6313],"valid"],[[6314,6314],"valid"],[[6315,6319],"disallowed"],[[6320,6389],"valid"],[[6390,6399],"disallowed"],[[6400,6428],"valid"],[[6429,6430],"valid"],[[6431,6431],"disallowed"],[[6432,6443],"valid"],[[6444,6447],"disallowed"],[[6448,6459],"valid"],[[6460,6463],"disallowed"],[[6464,6464],"valid","","NV8"],[[6465,6467],"disallowed"],[[6468,6469],"valid","","NV8"],[[6470,6509],"valid"],[[6510,6511],"disallowed"],[[6512,6516],"valid"],[[6517,6527],"disallowed"],[[6528,6569],"valid"],[[6570,6571],"valid"],[[6572,6575],"disallowed"],[[6576,6601],"valid"],[[6602,6607],"disallowed"],[[6608,6617],"valid"],[[6618,6618],"valid","","XV8"],[[6619,6621],"disallowed"],[[6622,6623],"valid","","NV8"],[[6624,6655],"valid","","NV8"],[[6656,6683],"valid"],[[6684,6685],"disallowed"],[[6686,6687],"valid","","NV8"],[[6688,6750],"valid"],[[6751,6751],"disallowed"],[[6752,6780],"valid"],[[6781,6782],"disallowed"],[[6783,6793],"valid"],[[6794,6799],"disallowed"],[[6800,6809],"valid"],[[6810,6815],"disallowed"],[[6816,6822],"valid","","NV8"],[[6823,6823],"valid"],[[6824,6829],"valid","","NV8"],[[6830,6831],"disallowed"],[[6832,6845],"valid"],[[6846,6846],"valid","","NV8"],[[6847,6911],"disallowed"],[[6912,6987],"valid"],[[6988,6991],"disallowed"],[[6992,7001],"valid"],[[7002,7018],"valid","","NV8"],[[7019,7027],"valid"],[[7028,7036],"valid","","NV8"],[[7037,7039],"disallowed"],[[7040,7082],"valid"],[[7083,7085],"valid"],[[7086,7097],"valid"],[[7098,7103],"valid"],[[7104,7155],"valid"],[[7156,7163],"disallowed"],[[7164,7167],"valid","","NV8"],[[7168,7223],"valid"],[[7224,7226],"disallowed"],[[7227,7231],"valid","","NV8"],[[7232,7241],"valid"],[[7242,7244],"disallowed"],[[7245,7293],"valid"],[[7294,7295],"valid","","NV8"],[[7296,7296],"mapped","в"],[[7297,7297],"mapped","д"],[[7298,7298],"mapped","о"],[[7299,7299],"mapped","с"],[[7300,7301],"mapped","т"],[[7302,7302],"mapped","ъ"],[[7303,7303],"mapped","ѣ"],[[7304,7304],"mapped","ꙋ"],[[7305,7359],"disallowed"],[[7360,7367],"valid","","NV8"],[[7368,7375],"disallowed"],[[7376,7378],"valid"],[[7379,7379],"valid","","NV8"],[[7380,7410],"valid"],[[7411,7414],"valid"],[[7415,7415],"valid"],[[7416,7417],"valid"],[[7418,7423],"disallowed"],[[7424,7467],"valid"],[[7468,7468],"mapped","a"],[[7469,7469],"mapped","æ"],[[7470,7470],"mapped","b"],[[7471,7471],"valid"],[[7472,7472],"mapped","d"],[[7473,7473],"mapped","e"],[[7474,7474],"mapped","ǝ"],[[7475,7475],"mapped","g"],[[7476,7476],"mapped","h"],[[7477,7477],"mapped","i"],[[7478,7478],"mapped","j"],[[7479,7479],"mapped","k"],[[7480,7480],"mapped","l"],[[7481,7481],"mapped","m"],[[7482,7482],"mapped","n"],[[7483,7483],"valid"],[[7484,7484],"mapped","o"],[[7485,7485],"mapped","ȣ"],[[7486,7486],"mapped","p"],[[7487,7487],"mapped","r"],[[7488,7488],"mapped","t"],[[7489,7489],"mapped","u"],[[7490,7490],"mapped","w"],[[7491,7491],"mapped","a"],[[7492,7492],"mapped","ɐ"],[[7493,7493],"mapped","ɑ"],[[7494,7494],"mapped","ᴂ"],[[7495,7495],"mapped","b"],[[7496,7496],"mapped","d"],[[7497,7497],"mapped","e"],[[7498,7498],"mapped","ə"],[[7499,7499],"mapped","ɛ"],[[7500,7500],"mapped","ɜ"],[[7501,7501],"mapped","g"],[[7502,7502],"valid"],[[7503,7503],"mapped","k"],[[7504,7504],"mapped","m"],[[7505,7505],"mapped","ŋ"],[[7506,7506],"mapped","o"],[[7507,7507],"mapped","ɔ"],[[7508,7508],"mapped","ᴖ"],[[7509,7509],"mapped","ᴗ"],[[7510,7510],"mapped","p"],[[7511,7511],"mapped","t"],[[7512,7512],"mapped","u"],[[7513,7513],"mapped","ᴝ"],[[7514,7514],"mapped","ɯ"],[[7515,7515],"mapped","v"],[[7516,7516],"mapped","ᴥ"],[[7517,7517],"mapped","β"],[[7518,7518],"mapped","γ"],[[7519,7519],"mapped","δ"],[[7520,7520],"mapped","φ"],[[7521,7521],"mapped","χ"],[[7522,7522],"mapped","i"],[[7523,7523],"mapped","r"],[[7524,7524],"mapped","u"],[[7525,7525],"mapped","v"],[[7526,7526],"mapped","β"],[[7527,7527],"mapped","γ"],[[7528,7528],"mapped","ρ"],[[7529,7529],"mapped","φ"],[[7530,7530],"mapped","χ"],[[7531,7531],"valid"],[[7532,7543],"valid"],[[7544,7544],"mapped","н"],[[7545,7578],"valid"],[[7579,7579],"mapped","ɒ"],[[7580,7580],"mapped","c"],[[7581,7581],"mapped","ɕ"],[[7582,7582],"mapped","ð"],[[7583,7583],"mapped","ɜ"],[[7584,7584],"mapped","f"],[[7585,7585],"mapped","ɟ"],[[7586,7586],"mapped","ɡ"],[[7587,7587],"mapped","ɥ"],[[7588,7588],"mapped","ɨ"],[[7589,7589],"mapped","ɩ"],[[7590,7590],"mapped","ɪ"],[[7591,7591],"mapped","ᵻ"],[[7592,7592],"mapped","ʝ"],[[7593,7593],"mapped","ɭ"],[[7594,7594],"mapped","ᶅ"],[[7595,7595],"mapped","ʟ"],[[7596,7596],"mapped","ɱ"],[[7597,7597],"mapped","ɰ"],[[7598,7598],"mapped","ɲ"],[[7599,7599],"mapped","ɳ"],[[7600,7600],"mapped","ɴ"],[[7601,7601],"mapped","ɵ"],[[7602,7602],"mapped","ɸ"],[[7603,7603],"mapped","ʂ"],[[7604,7604],"mapped","ʃ"],[[7605,7605],"mapped","ƫ"],[[7606,7606],"mapped","ʉ"],[[7607,7607],"mapped","ʊ"],[[7608,7608],"mapped","ᴜ"],[[7609,7609],"mapped","ʋ"],[[7610,7610],"mapped","ʌ"],[[7611,7611],"mapped","z"],[[7612,7612],"mapped","ʐ"],[[7613,7613],"mapped","ʑ"],[[7614,7614],"mapped","ʒ"],[[7615,7615],"mapped","θ"],[[7616,7619],"valid"],[[7620,7626],"valid"],[[7627,7654],"valid"],[[7655,7669],"valid"],[[7670,7673],"valid"],[[7674,7674],"disallowed"],[[7675,7675],"valid"],[[7676,7676],"valid"],[[7677,7677],"valid"],[[7678,7679],"valid"],[[7680,7680],"mapped","ḁ"],[[7681,7681],"valid"],[[7682,7682],"mapped","ḃ"],[[7683,7683],"valid"],[[7684,7684],"mapped","ḅ"],[[7685,7685],"valid"],[[7686,7686],"mapped","ḇ"],[[7687,7687],"valid"],[[7688,7688],"mapped","ḉ"],[[7689,7689],"valid"],[[7690,7690],"mapped","ḋ"],[[7691,7691],"valid"],[[7692,7692],"mapped","ḍ"],[[7693,7693],"valid"],[[7694,7694],"mapped","ḏ"],[[7695,7695],"valid"],[[7696,7696],"mapped","ḑ"],[[7697,7697],"valid"],[[7698,7698],"mapped","ḓ"],[[7699,7699],"valid"],[[7700,7700],"mapped","ḕ"],[[7701,7701],"valid"],[[7702,7702],"mapped","ḗ"],[[7703,7703],"valid"],[[7704,7704],"mapped","ḙ"],[[7705,7705],"valid"],[[7706,7706],"mapped","ḛ"],[[7707,7707],"valid"],[[7708,7708],"mapped","ḝ"],[[7709,7709],"valid"],[[7710,7710],"mapped","ḟ"],[[7711,7711],"valid"],[[7712,7712],"mapped","ḡ"],[[7713,7713],"valid"],[[7714,7714],"mapped","ḣ"],[[7715,7715],"valid"],[[7716,7716],"mapped","ḥ"],[[7717,7717],"valid"],[[7718,7718],"mapped","ḧ"],[[7719,7719],"valid"],[[7720,7720],"mapped","ḩ"],[[7721,7721],"valid"],[[7722,7722],"mapped","ḫ"],[[7723,7723],"valid"],[[7724,7724],"mapped","ḭ"],[[7725,7725],"valid"],[[7726,7726],"mapped","ḯ"],[[7727,7727],"valid"],[[7728,7728],"mapped","ḱ"],[[7729,7729],"valid"],[[7730,7730],"mapped","ḳ"],[[7731,7731],"valid"],[[7732,7732],"mapped","ḵ"],[[7733,7733],"valid"],[[7734,7734],"mapped","ḷ"],[[7735,7735],"valid"],[[7736,7736],"mapped","ḹ"],[[7737,7737],"valid"],[[7738,7738],"mapped","ḻ"],[[7739,7739],"valid"],[[7740,7740],"mapped","ḽ"],[[7741,7741],"valid"],[[7742,7742],"mapped","ḿ"],[[7743,7743],"valid"],[[7744,7744],"mapped","ṁ"],[[7745,7745],"valid"],[[7746,7746],"mapped","ṃ"],[[7747,7747],"valid"],[[7748,7748],"mapped","ṅ"],[[7749,7749],"valid"],[[7750,7750],"mapped","ṇ"],[[7751,7751],"valid"],[[7752,7752],"mapped","ṉ"],[[7753,7753],"valid"],[[7754,7754],"mapped","ṋ"],[[7755,7755],"valid"],[[7756,7756],"mapped","ṍ"],[[7757,7757],"valid"],[[7758,7758],"mapped","ṏ"],[[7759,7759],"valid"],[[7760,7760],"mapped","ṑ"],[[7761,7761],"valid"],[[7762,7762],"mapped","ṓ"],[[7763,7763],"valid"],[[7764,7764],"mapped","ṕ"],[[7765,7765],"valid"],[[7766,7766],"mapped","ṗ"],[[7767,7767],"valid"],[[7768,7768],"mapped","ṙ"],[[7769,7769],"valid"],[[7770,7770],"mapped","ṛ"],[[7771,7771],"valid"],[[7772,7772],"mapped","ṝ"],[[7773,7773],"valid"],[[7774,7774],"mapped","ṟ"],[[7775,7775],"valid"],[[7776,7776],"mapped","ṡ"],[[7777,7777],"valid"],[[7778,7778],"mapped","ṣ"],[[7779,7779],"valid"],[[7780,7780],"mapped","ṥ"],[[7781,7781],"valid"],[[7782,7782],"mapped","ṧ"],[[7783,7783],"valid"],[[7784,7784],"mapped","ṩ"],[[7785,7785],"valid"],[[7786,7786],"mapped","ṫ"],[[7787,7787],"valid"],[[7788,7788],"mapped","ṭ"],[[7789,7789],"valid"],[[7790,7790],"mapped","ṯ"],[[7791,7791],"valid"],[[7792,7792],"mapped","ṱ"],[[7793,7793],"valid"],[[7794,7794],"mapped","ṳ"],[[7795,7795],"valid"],[[7796,7796],"mapped","ṵ"],[[7797,7797],"valid"],[[7798,7798],"mapped","ṷ"],[[7799,7799],"valid"],[[7800,7800],"mapped","ṹ"],[[7801,7801],"valid"],[[7802,7802],"mapped","ṻ"],[[7803,7803],"valid"],[[7804,7804],"mapped","ṽ"],[[7805,7805],"valid"],[[7806,7806],"mapped","ṿ"],[[7807,7807],"valid"],[[7808,7808],"mapped","ẁ"],[[7809,7809],"valid"],[[7810,7810],"mapped","ẃ"],[[7811,7811],"valid"],[[7812,7812],"mapped","ẅ"],[[7813,7813],"valid"],[[7814,7814],"mapped","ẇ"],[[7815,7815],"valid"],[[7816,7816],"mapped","ẉ"],[[7817,7817],"valid"],[[7818,7818],"mapped","ẋ"],[[7819,7819],"valid"],[[7820,7820],"mapped","ẍ"],[[7821,7821],"valid"],[[7822,7822],"mapped","ẏ"],[[7823,7823],"valid"],[[7824,7824],"mapped","ẑ"],[[7825,7825],"valid"],[[7826,7826],"mapped","ẓ"],[[7827,7827],"valid"],[[7828,7828],"mapped","ẕ"],[[7829,7833],"valid"],[[7834,7834],"mapped","aʾ"],[[7835,7835],"mapped","ṡ"],[[7836,7837],"valid"],[[7838,7838],"mapped","ss"],[[7839,7839],"valid"],[[7840,7840],"mapped","ạ"],[[7841,7841],"valid"],[[7842,7842],"mapped","ả"],[[7843,7843],"valid"],[[7844,7844],"mapped","ấ"],[[7845,7845],"valid"],[[7846,7846],"mapped","ầ"],[[7847,7847],"valid"],[[7848,7848],"mapped","ẩ"],[[7849,7849],"valid"],[[7850,7850],"mapped","ẫ"],[[7851,7851],"valid"],[[7852,7852],"mapped","ậ"],[[7853,7853],"valid"],[[7854,7854],"mapped","ắ"],[[7855,7855],"valid"],[[7856,7856],"mapped","ằ"],[[7857,7857],"valid"],[[7858,7858],"mapped","ẳ"],[[7859,7859],"valid"],[[7860,7860],"mapped","ẵ"],[[7861,7861],"valid"],[[7862,7862],"mapped","ặ"],[[7863,7863],"valid"],[[7864,7864],"mapped","ẹ"],[[7865,7865],"valid"],[[7866,7866],"mapped","ẻ"],[[7867,7867],"valid"],[[7868,7868],"mapped","ẽ"],[[7869,7869],"valid"],[[7870,7870],"mapped","ế"],[[7871,7871],"valid"],[[7872,7872],"mapped","ề"],[[7873,7873],"valid"],[[7874,7874],"mapped","ể"],[[7875,7875],"valid"],[[7876,7876],"mapped","ễ"],[[7877,7877],"valid"],[[7878,7878],"mapped","ệ"],[[7879,7879],"valid"],[[7880,7880],"mapped","ỉ"],[[7881,7881],"valid"],[[7882,7882],"mapped","ị"],[[7883,7883],"valid"],[[7884,7884],"mapped","ọ"],[[7885,7885],"valid"],[[7886,7886],"mapped","ỏ"],[[7887,7887],"valid"],[[7888,7888],"mapped","ố"],[[7889,7889],"valid"],[[7890,7890],"mapped","ồ"],[[7891,7891],"valid"],[[7892,7892],"mapped","ổ"],[[7893,7893],"valid"],[[7894,7894],"mapped","ỗ"],[[7895,7895],"valid"],[[7896,7896],"mapped","ộ"],[[7897,7897],"valid"],[[7898,7898],"mapped","ớ"],[[7899,7899],"valid"],[[7900,7900],"mapped","ờ"],[[7901,7901],"valid"],[[7902,7902],"mapped","ở"],[[7903,7903],"valid"],[[7904,7904],"mapped","ỡ"],[[7905,7905],"valid"],[[7906,7906],"mapped","ợ"],[[7907,7907],"valid"],[[7908,7908],"mapped","ụ"],[[7909,7909],"valid"],[[7910,7910],"mapped","ủ"],[[7911,7911],"valid"],[[7912,7912],"mapped","ứ"],[[7913,7913],"valid"],[[7914,7914],"mapped","ừ"],[[7915,7915],"valid"],[[7916,7916],"mapped","ử"],[[7917,7917],"valid"],[[7918,7918],"mapped","ữ"],[[7919,7919],"valid"],[[7920,7920],"mapped","ự"],[[7921,7921],"valid"],[[7922,7922],"mapped","ỳ"],[[7923,7923],"valid"],[[7924,7924],"mapped","ỵ"],[[7925,7925],"valid"],[[7926,7926],"mapped","ỷ"],[[7927,7927],"valid"],[[7928,7928],"mapped","ỹ"],[[7929,7929],"valid"],[[7930,7930],"mapped","ỻ"],[[7931,7931],"valid"],[[7932,7932],"mapped","ỽ"],[[7933,7933],"valid"],[[7934,7934],"mapped","ỿ"],[[7935,7935],"valid"],[[7936,7943],"valid"],[[7944,7944],"mapped","ἀ"],[[7945,7945],"mapped","ἁ"],[[7946,7946],"mapped","ἂ"],[[7947,7947],"mapped","ἃ"],[[7948,7948],"mapped","ἄ"],[[7949,7949],"mapped","ἅ"],[[7950,7950],"mapped","ἆ"],[[7951,7951],"mapped","ἇ"],[[7952,7957],"valid"],[[7958,7959],"disallowed"],[[7960,7960],"mapped","ἐ"],[[7961,7961],"mapped","ἑ"],[[7962,7962],"mapped","ἒ"],[[7963,7963],"mapped","ἓ"],[[7964,7964],"mapped","ἔ"],[[7965,7965],"mapped","ἕ"],[[7966,7967],"disallowed"],[[7968,7975],"valid"],[[7976,7976],"mapped","ἠ"],[[7977,7977],"mapped","ἡ"],[[7978,7978],"mapped","ἢ"],[[7979,7979],"mapped","ἣ"],[[7980,7980],"mapped","ἤ"],[[7981,7981],"mapped","ἥ"],[[7982,7982],"mapped","ἦ"],[[7983,7983],"mapped","ἧ"],[[7984,7991],"valid"],[[7992,7992],"mapped","ἰ"],[[7993,7993],"mapped","ἱ"],[[7994,7994],"mapped","ἲ"],[[7995,7995],"mapped","ἳ"],[[7996,7996],"mapped","ἴ"],[[7997,7997],"mapped","ἵ"],[[7998,7998],"mapped","ἶ"],[[7999,7999],"mapped","ἷ"],[[8000,8005],"valid"],[[8006,8007],"disallowed"],[[8008,8008],"mapped","ὀ"],[[8009,8009],"mapped","ὁ"],[[8010,8010],"mapped","ὂ"],[[8011,8011],"mapped","ὃ"],[[8012,8012],"mapped","ὄ"],[[8013,8013],"mapped","ὅ"],[[8014,8015],"disallowed"],[[8016,8023],"valid"],[[8024,8024],"disallowed"],[[8025,8025],"mapped","ὑ"],[[8026,8026],"disallowed"],[[8027,8027],"mapped","ὓ"],[[8028,8028],"disallowed"],[[8029,8029],"mapped","ὕ"],[[8030,8030],"disallowed"],[[8031,8031],"mapped","ὗ"],[[8032,8039],"valid"],[[8040,8040],"mapped","ὠ"],[[8041,8041],"mapped","ὡ"],[[8042,8042],"mapped","ὢ"],[[8043,8043],"mapped","ὣ"],[[8044,8044],"mapped","ὤ"],[[8045,8045],"mapped","ὥ"],[[8046,8046],"mapped","ὦ"],[[8047,8047],"mapped","ὧ"],[[8048,8048],"valid"],[[8049,8049],"mapped","ά"],[[8050,8050],"valid"],[[8051,8051],"mapped","έ"],[[8052,8052],"valid"],[[8053,8053],"mapped","ή"],[[8054,8054],"valid"],[[8055,8055],"mapped","ί"],[[8056,8056],"valid"],[[8057,8057],"mapped","ό"],[[8058,8058],"valid"],[[8059,8059],"mapped","ύ"],[[8060,8060],"valid"],[[8061,8061],"mapped","ώ"],[[8062,8063],"disallowed"],[[8064,8064],"mapped","ἀι"],[[8065,8065],"mapped","ἁι"],[[8066,8066],"mapped","ἂι"],[[8067,8067],"mapped","ἃι"],[[8068,8068],"mapped","ἄι"],[[8069,8069],"mapped","ἅι"],[[8070,8070],"mapped","ἆι"],[[8071,8071],"mapped","ἇι"],[[8072,8072],"mapped","ἀι"],[[8073,8073],"mapped","ἁι"],[[8074,8074],"mapped","ἂι"],[[8075,8075],"mapped","ἃι"],[[8076,8076],"mapped","ἄι"],[[8077,8077],"mapped","ἅι"],[[8078,8078],"mapped","ἆι"],[[8079,8079],"mapped","ἇι"],[[8080,8080],"mapped","ἠι"],[[8081,8081],"mapped","ἡι"],[[8082,8082],"mapped","ἢι"],[[8083,8083],"mapped","ἣι"],[[8084,8084],"mapped","ἤι"],[[8085,8085],"mapped","ἥι"],[[8086,8086],"mapped","ἦι"],[[8087,8087],"mapped","ἧι"],[[8088,8088],"mapped","ἠι"],[[8089,8089],"mapped","ἡι"],[[8090,8090],"mapped","ἢι"],[[8091,8091],"mapped","ἣι"],[[8092,8092],"mapped","ἤι"],[[8093,8093],"mapped","ἥι"],[[8094,8094],"mapped","ἦι"],[[8095,8095],"mapped","ἧι"],[[8096,8096],"mapped","ὠι"],[[8097,8097],"mapped","ὡι"],[[8098,8098],"mapped","ὢι"],[[8099,8099],"mapped","ὣι"],[[8100,8100],"mapped","ὤι"],[[8101,8101],"mapped","ὥι"],[[8102,8102],"mapped","ὦι"],[[8103,8103],"mapped","ὧι"],[[8104,8104],"mapped","ὠι"],[[8105,8105],"mapped","ὡι"],[[8106,8106],"mapped","ὢι"],[[8107,8107],"mapped","ὣι"],[[8108,8108],"mapped","ὤι"],[[8109,8109],"mapped","ὥι"],[[8110,8110],"mapped","ὦι"],[[8111,8111],"mapped","ὧι"],[[8112,8113],"valid"],[[8114,8114],"mapped","ὰι"],[[8115,8115],"mapped","αι"],[[8116,8116],"mapped","άι"],[[8117,8117],"disallowed"],[[8118,8118],"valid"],[[8119,8119],"mapped","ᾶι"],[[8120,8120],"mapped","ᾰ"],[[8121,8121],"mapped","ᾱ"],[[8122,8122],"mapped","ὰ"],[[8123,8123],"mapped","ά"],[[8124,8124],"mapped","αι"],[[8125,8125],"disallowed_STD3_mapped"," ̓"],[[8126,8126],"mapped","ι"],[[8127,8127],"disallowed_STD3_mapped"," ̓"],[[8128,8128],"disallowed_STD3_mapped"," ͂"],[[8129,8129],"disallowed_STD3_mapped"," ̈͂"],[[8130,8130],"mapped","ὴι"],[[8131,8131],"mapped","ηι"],[[8132,8132],"mapped","ήι"],[[8133,8133],"disallowed"],[[8134,8134],"valid"],[[8135,8135],"mapped","ῆι"],[[8136,8136],"mapped","ὲ"],[[8137,8137],"mapped","έ"],[[8138,8138],"mapped","ὴ"],[[8139,8139],"mapped","ή"],[[8140,8140],"mapped","ηι"],[[8141,8141],"disallowed_STD3_mapped"," ̓̀"],[[8142,8142],"disallowed_STD3_mapped"," ̓́"],[[8143,8143],"disallowed_STD3_mapped"," ̓͂"],[[8144,8146],"valid"],[[8147,8147],"mapped","ΐ"],[[8148,8149],"disallowed"],[[8150,8151],"valid"],[[8152,8152],"mapped","ῐ"],[[8153,8153],"mapped","ῑ"],[[8154,8154],"mapped","ὶ"],[[8155,8155],"mapped","ί"],[[8156,8156],"disallowed"],[[8157,8157],"disallowed_STD3_mapped"," ̔̀"],[[8158,8158],"disallowed_STD3_mapped"," ̔́"],[[8159,8159],"disallowed_STD3_mapped"," ̔͂"],[[8160,8162],"valid"],[[8163,8163],"mapped","ΰ"],[[8164,8167],"valid"],[[8168,8168],"mapped","ῠ"],[[8169,8169],"mapped","ῡ"],[[8170,8170],"mapped","ὺ"],[[8171,8171],"mapped","ύ"],[[8172,8172],"mapped","ῥ"],[[8173,8173],"disallowed_STD3_mapped"," ̈̀"],[[8174,8174],"disallowed_STD3_mapped"," ̈́"],[[8175,8175],"disallowed_STD3_mapped","`"],[[8176,8177],"disallowed"],[[8178,8178],"mapped","ὼι"],[[8179,8179],"mapped","ωι"],[[8180,8180],"mapped","ώι"],[[8181,8181],"disallowed"],[[8182,8182],"valid"],[[8183,8183],"mapped","ῶι"],[[8184,8184],"mapped","ὸ"],[[8185,8185],"mapped","ό"],[[8186,8186],"mapped","ὼ"],[[8187,8187],"mapped","ώ"],[[8188,8188],"mapped","ωι"],[[8189,8189],"disallowed_STD3_mapped"," ́"],[[8190,8190],"disallowed_STD3_mapped"," ̔"],[[8191,8191],"disallowed"],[[8192,8202],"disallowed_STD3_mapped"," "],[[8203,8203],"ignored"],[[8204,8205],"deviation",""],[[8206,8207],"disallowed"],[[8208,8208],"valid","","NV8"],[[8209,8209],"mapped","‐"],[[8210,8214],"valid","","NV8"],[[8215,8215],"disallowed_STD3_mapped"," ̳"],[[8216,8227],"valid","","NV8"],[[8228,8230],"disallowed"],[[8231,8231],"valid","","NV8"],[[8232,8238],"disallowed"],[[8239,8239],"disallowed_STD3_mapped"," "],[[8240,8242],"valid","","NV8"],[[8243,8243],"mapped","′′"],[[8244,8244],"mapped","′′′"],[[8245,8245],"valid","","NV8"],[[8246,8246],"mapped","‵‵"],[[8247,8247],"mapped","‵‵‵"],[[8248,8251],"valid","","NV8"],[[8252,8252],"disallowed_STD3_mapped","!!"],[[8253,8253],"valid","","NV8"],[[8254,8254],"disallowed_STD3_mapped"," ̅"],[[8255,8262],"valid","","NV8"],[[8263,8263],"disallowed_STD3_mapped","??"],[[8264,8264],"disallowed_STD3_mapped","?!"],[[8265,8265],"disallowed_STD3_mapped","!?"],[[8266,8269],"valid","","NV8"],[[8270,8274],"valid","","NV8"],[[8275,8276],"valid","","NV8"],[[8277,8278],"valid","","NV8"],[[8279,8279],"mapped","′′′′"],[[8280,8286],"valid","","NV8"],[[8287,8287],"disallowed_STD3_mapped"," "],[[8288,8288],"ignored"],[[8289,8291],"disallowed"],[[8292,8292],"ignored"],[[8293,8293],"disallowed"],[[8294,8297],"disallowed"],[[8298,8303],"disallowed"],[[8304,8304],"mapped","0"],[[8305,8305],"mapped","i"],[[8306,8307],"disallowed"],[[8308,8308],"mapped","4"],[[8309,8309],"mapped","5"],[[8310,8310],"mapped","6"],[[8311,8311],"mapped","7"],[[8312,8312],"mapped","8"],[[8313,8313],"mapped","9"],[[8314,8314],"disallowed_STD3_mapped","+"],[[8315,8315],"mapped","−"],[[8316,8316],"disallowed_STD3_mapped","="],[[8317,8317],"disallowed_STD3_mapped","("],[[8318,8318],"disallowed_STD3_mapped",")"],[[8319,8319],"mapped","n"],[[8320,8320],"mapped","0"],[[8321,8321],"mapped","1"],[[8322,8322],"mapped","2"],[[8323,8323],"mapped","3"],[[8324,8324],"mapped","4"],[[8325,8325],"mapped","5"],[[8326,8326],"mapped","6"],[[8327,8327],"mapped","7"],[[8328,8328],"mapped","8"],[[8329,8329],"mapped","9"],[[8330,8330],"disallowed_STD3_mapped","+"],[[8331,8331],"mapped","−"],[[8332,8332],"disallowed_STD3_mapped","="],[[8333,8333],"disallowed_STD3_mapped","("],[[8334,8334],"disallowed_STD3_mapped",")"],[[8335,8335],"disallowed"],[[8336,8336],"mapped","a"],[[8337,8337],"mapped","e"],[[8338,8338],"mapped","o"],[[8339,8339],"mapped","x"],[[8340,8340],"mapped","ə"],[[8341,8341],"mapped","h"],[[8342,8342],"mapped","k"],[[8343,8343],"mapped","l"],[[8344,8344],"mapped","m"],[[8345,8345],"mapped","n"],[[8346,8346],"mapped","p"],[[8347,8347],"mapped","s"],[[8348,8348],"mapped","t"],[[8349,8351],"disallowed"],[[8352,8359],"valid","","NV8"],[[8360,8360],"mapped","rs"],[[8361,8362],"valid","","NV8"],[[8363,8363],"valid","","NV8"],[[8364,8364],"valid","","NV8"],[[8365,8367],"valid","","NV8"],[[8368,8369],"valid","","NV8"],[[8370,8373],"valid","","NV8"],[[8374,8376],"valid","","NV8"],[[8377,8377],"valid","","NV8"],[[8378,8378],"valid","","NV8"],[[8379,8381],"valid","","NV8"],[[8382,8382],"valid","","NV8"],[[8383,8383],"valid","","NV8"],[[8384,8399],"disallowed"],[[8400,8417],"valid","","NV8"],[[8418,8419],"valid","","NV8"],[[8420,8426],"valid","","NV8"],[[8427,8427],"valid","","NV8"],[[8428,8431],"valid","","NV8"],[[8432,8432],"valid","","NV8"],[[8433,8447],"disallowed"],[[8448,8448],"disallowed_STD3_mapped","a/c"],[[8449,8449],"disallowed_STD3_mapped","a/s"],[[8450,8450],"mapped","c"],[[8451,8451],"mapped","°c"],[[8452,8452],"valid","","NV8"],[[8453,8453],"disallowed_STD3_mapped","c/o"],[[8454,8454],"disallowed_STD3_mapped","c/u"],[[8455,8455],"mapped","ɛ"],[[8456,8456],"valid","","NV8"],[[8457,8457],"mapped","°f"],[[8458,8458],"mapped","g"],[[8459,8462],"mapped","h"],[[8463,8463],"mapped","ħ"],[[8464,8465],"mapped","i"],[[8466,8467],"mapped","l"],[[8468,8468],"valid","","NV8"],[[8469,8469],"mapped","n"],[[8470,8470],"mapped","no"],[[8471,8472],"valid","","NV8"],[[8473,8473],"mapped","p"],[[8474,8474],"mapped","q"],[[8475,8477],"mapped","r"],[[8478,8479],"valid","","NV8"],[[8480,8480],"mapped","sm"],[[8481,8481],"mapped","tel"],[[8482,8482],"mapped","tm"],[[8483,8483],"valid","","NV8"],[[8484,8484],"mapped","z"],[[8485,8485],"valid","","NV8"],[[8486,8486],"mapped","ω"],[[8487,8487],"valid","","NV8"],[[8488,8488],"mapped","z"],[[8489,8489],"valid","","NV8"],[[8490,8490],"mapped","k"],[[8491,8491],"mapped","å"],[[8492,8492],"mapped","b"],[[8493,8493],"mapped","c"],[[8494,8494],"valid","","NV8"],[[8495,8496],"mapped","e"],[[8497,8497],"mapped","f"],[[8498,8498],"disallowed"],[[8499,8499],"mapped","m"],[[8500,8500],"mapped","o"],[[8501,8501],"mapped","א"],[[8502,8502],"mapped","ב"],[[8503,8503],"mapped","ג"],[[8504,8504],"mapped","ד"],[[8505,8505],"mapped","i"],[[8506,8506],"valid","","NV8"],[[8507,8507],"mapped","fax"],[[8508,8508],"mapped","π"],[[8509,8510],"mapped","γ"],[[8511,8511],"mapped","π"],[[8512,8512],"mapped","∑"],[[8513,8516],"valid","","NV8"],[[8517,8518],"mapped","d"],[[8519,8519],"mapped","e"],[[8520,8520],"mapped","i"],[[8521,8521],"mapped","j"],[[8522,8523],"valid","","NV8"],[[8524,8524],"valid","","NV8"],[[8525,8525],"valid","","NV8"],[[8526,8526],"valid"],[[8527,8527],"valid","","NV8"],[[8528,8528],"mapped","1⁄7"],[[8529,8529],"mapped","1⁄9"],[[8530,8530],"mapped","1⁄10"],[[8531,8531],"mapped","1⁄3"],[[8532,8532],"mapped","2⁄3"],[[8533,8533],"mapped","1⁄5"],[[8534,8534],"mapped","2⁄5"],[[8535,8535],"mapped","3⁄5"],[[8536,8536],"mapped","4⁄5"],[[8537,8537],"mapped","1⁄6"],[[8538,8538],"mapped","5⁄6"],[[8539,8539],"mapped","1⁄8"],[[8540,8540],"mapped","3⁄8"],[[8541,8541],"mapped","5⁄8"],[[8542,8542],"mapped","7⁄8"],[[8543,8543],"mapped","1⁄"],[[8544,8544],"mapped","i"],[[8545,8545],"mapped","ii"],[[8546,8546],"mapped","iii"],[[8547,8547],"mapped","iv"],[[8548,8548],"mapped","v"],[[8549,8549],"mapped","vi"],[[8550,8550],"mapped","vii"],[[8551,8551],"mapped","viii"],[[8552,8552],"mapped","ix"],[[8553,8553],"mapped","x"],[[8554,8554],"mapped","xi"],[[8555,8555],"mapped","xii"],[[8556,8556],"mapped","l"],[[8557,8557],"mapped","c"],[[8558,8558],"mapped","d"],[[8559,8559],"mapped","m"],[[8560,8560],"mapped","i"],[[8561,8561],"mapped","ii"],[[8562,8562],"mapped","iii"],[[8563,8563],"mapped","iv"],[[8564,8564],"mapped","v"],[[8565,8565],"mapped","vi"],[[8566,8566],"mapped","vii"],[[8567,8567],"mapped","viii"],[[8568,8568],"mapped","ix"],[[8569,8569],"mapped","x"],[[8570,8570],"mapped","xi"],[[8571,8571],"mapped","xii"],[[8572,8572],"mapped","l"],[[8573,8573],"mapped","c"],[[8574,8574],"mapped","d"],[[8575,8575],"mapped","m"],[[8576,8578],"valid","","NV8"],[[8579,8579],"disallowed"],[[8580,8580],"valid"],[[8581,8584],"valid","","NV8"],[[8585,8585],"mapped","0⁄3"],[[8586,8587],"valid","","NV8"],[[8588,8591],"disallowed"],[[8592,8682],"valid","","NV8"],[[8683,8691],"valid","","NV8"],[[8692,8703],"valid","","NV8"],[[8704,8747],"valid","","NV8"],[[8748,8748],"mapped","∫∫"],[[8749,8749],"mapped","∫∫∫"],[[8750,8750],"valid","","NV8"],[[8751,8751],"mapped","∮∮"],[[8752,8752],"mapped","∮∮∮"],[[8753,8799],"valid","","NV8"],[[8800,8800],"disallowed_STD3_valid"],[[8801,8813],"valid","","NV8"],[[8814,8815],"disallowed_STD3_valid"],[[8816,8945],"valid","","NV8"],[[8946,8959],"valid","","NV8"],[[8960,8960],"valid","","NV8"],[[8961,8961],"valid","","NV8"],[[8962,9000],"valid","","NV8"],[[9001,9001],"mapped","〈"],[[9002,9002],"mapped","〉"],[[9003,9082],"valid","","NV8"],[[9083,9083],"valid","","NV8"],[[9084,9084],"valid","","NV8"],[[9085,9114],"valid","","NV8"],[[9115,9166],"valid","","NV8"],[[9167,9168],"valid","","NV8"],[[9169,9179],"valid","","NV8"],[[9180,9191],"valid","","NV8"],[[9192,9192],"valid","","NV8"],[[9193,9203],"valid","","NV8"],[[9204,9210],"valid","","NV8"],[[9211,9214],"valid","","NV8"],[[9215,9215],"valid","","NV8"],[[9216,9252],"valid","","NV8"],[[9253,9254],"valid","","NV8"],[[9255,9279],"disallowed"],[[9280,9290],"valid","","NV8"],[[9291,9311],"disallowed"],[[9312,9312],"mapped","1"],[[9313,9313],"mapped","2"],[[9314,9314],"mapped","3"],[[9315,9315],"mapped","4"],[[9316,9316],"mapped","5"],[[9317,9317],"mapped","6"],[[9318,9318],"mapped","7"],[[9319,9319],"mapped","8"],[[9320,9320],"mapped","9"],[[9321,9321],"mapped","10"],[[9322,9322],"mapped","11"],[[9323,9323],"mapped","12"],[[9324,9324],"mapped","13"],[[9325,9325],"mapped","14"],[[9326,9326],"mapped","15"],[[9327,9327],"mapped","16"],[[9328,9328],"mapped","17"],[[9329,9329],"mapped","18"],[[9330,9330],"mapped","19"],[[9331,9331],"mapped","20"],[[9332,9332],"disallowed_STD3_mapped","(1)"],[[9333,9333],"disallowed_STD3_mapped","(2)"],[[9334,9334],"disallowed_STD3_mapped","(3)"],[[9335,9335],"disallowed_STD3_mapped","(4)"],[[9336,9336],"disallowed_STD3_mapped","(5)"],[[9337,9337],"disallowed_STD3_mapped","(6)"],[[9338,9338],"disallowed_STD3_mapped","(7)"],[[9339,9339],"disallowed_STD3_mapped","(8)"],[[9340,9340],"disallowed_STD3_mapped","(9)"],[[9341,9341],"disallowed_STD3_mapped","(10)"],[[9342,9342],"disallowed_STD3_mapped","(11)"],[[9343,9343],"disallowed_STD3_mapped","(12)"],[[9344,9344],"disallowed_STD3_mapped","(13)"],[[9345,9345],"disallowed_STD3_mapped","(14)"],[[9346,9346],"disallowed_STD3_mapped","(15)"],[[9347,9347],"disallowed_STD3_mapped","(16)"],[[9348,9348],"disallowed_STD3_mapped","(17)"],[[9349,9349],"disallowed_STD3_mapped","(18)"],[[9350,9350],"disallowed_STD3_mapped","(19)"],[[9351,9351],"disallowed_STD3_mapped","(20)"],[[9352,9371],"disallowed"],[[9372,9372],"disallowed_STD3_mapped","(a)"],[[9373,9373],"disallowed_STD3_mapped","(b)"],[[9374,9374],"disallowed_STD3_mapped","(c)"],[[9375,9375],"disallowed_STD3_mapped","(d)"],[[9376,9376],"disallowed_STD3_mapped","(e)"],[[9377,9377],"disallowed_STD3_mapped","(f)"],[[9378,9378],"disallowed_STD3_mapped","(g)"],[[9379,9379],"disallowed_STD3_mapped","(h)"],[[9380,9380],"disallowed_STD3_mapped","(i)"],[[9381,9381],"disallowed_STD3_mapped","(j)"],[[9382,9382],"disallowed_STD3_mapped","(k)"],[[9383,9383],"disallowed_STD3_mapped","(l)"],[[9384,9384],"disallowed_STD3_mapped","(m)"],[[9385,9385],"disallowed_STD3_mapped","(n)"],[[9386,9386],"disallowed_STD3_mapped","(o)"],[[9387,9387],"disallowed_STD3_mapped","(p)"],[[9388,9388],"disallowed_STD3_mapped","(q)"],[[9389,9389],"disallowed_STD3_mapped","(r)"],[[9390,9390],"disallowed_STD3_mapped","(s)"],[[9391,9391],"disallowed_STD3_mapped","(t)"],[[9392,9392],"disallowed_STD3_mapped","(u)"],[[9393,9393],"disallowed_STD3_mapped","(v)"],[[9394,9394],"disallowed_STD3_mapped","(w)"],[[9395,9395],"disallowed_STD3_mapped","(x)"],[[9396,9396],"disallowed_STD3_mapped","(y)"],[[9397,9397],"disallowed_STD3_mapped","(z)"],[[9398,9398],"mapped","a"],[[9399,9399],"mapped","b"],[[9400,9400],"mapped","c"],[[9401,9401],"mapped","d"],[[9402,9402],"mapped","e"],[[9403,9403],"mapped","f"],[[9404,9404],"mapped","g"],[[9405,9405],"mapped","h"],[[9406,9406],"mapped","i"],[[9407,9407],"mapped","j"],[[9408,9408],"mapped","k"],[[9409,9409],"mapped","l"],[[9410,9410],"mapped","m"],[[9411,9411],"mapped","n"],[[9412,9412],"mapped","o"],[[9413,9413],"mapped","p"],[[9414,9414],"mapped","q"],[[9415,9415],"mapped","r"],[[9416,9416],"mapped","s"],[[9417,9417],"mapped","t"],[[9418,9418],"mapped","u"],[[9419,9419],"mapped","v"],[[9420,9420],"mapped","w"],[[9421,9421],"mapped","x"],[[9422,9422],"mapped","y"],[[9423,9423],"mapped","z"],[[9424,9424],"mapped","a"],[[9425,9425],"mapped","b"],[[9426,9426],"mapped","c"],[[9427,9427],"mapped","d"],[[9428,9428],"mapped","e"],[[9429,9429],"mapped","f"],[[9430,9430],"mapped","g"],[[9431,9431],"mapped","h"],[[9432,9432],"mapped","i"],[[9433,9433],"mapped","j"],[[9434,9434],"mapped","k"],[[9435,9435],"mapped","l"],[[9436,9436],"mapped","m"],[[9437,9437],"mapped","n"],[[9438,9438],"mapped","o"],[[9439,9439],"mapped","p"],[[9440,9440],"mapped","q"],[[9441,9441],"mapped","r"],[[9442,9442],"mapped","s"],[[9443,9443],"mapped","t"],[[9444,9444],"mapped","u"],[[9445,9445],"mapped","v"],[[9446,9446],"mapped","w"],[[9447,9447],"mapped","x"],[[9448,9448],"mapped","y"],[[9449,9449],"mapped","z"],[[9450,9450],"mapped","0"],[[9451,9470],"valid","","NV8"],[[9471,9471],"valid","","NV8"],[[9472,9621],"valid","","NV8"],[[9622,9631],"valid","","NV8"],[[9632,9711],"valid","","NV8"],[[9712,9719],"valid","","NV8"],[[9720,9727],"valid","","NV8"],[[9728,9747],"valid","","NV8"],[[9748,9749],"valid","","NV8"],[[9750,9751],"valid","","NV8"],[[9752,9752],"valid","","NV8"],[[9753,9753],"valid","","NV8"],[[9754,9839],"valid","","NV8"],[[9840,9841],"valid","","NV8"],[[9842,9853],"valid","","NV8"],[[9854,9855],"valid","","NV8"],[[9856,9865],"valid","","NV8"],[[9866,9873],"valid","","NV8"],[[9874,9884],"valid","","NV8"],[[9885,9885],"valid","","NV8"],[[9886,9887],"valid","","NV8"],[[9888,9889],"valid","","NV8"],[[9890,9905],"valid","","NV8"],[[9906,9906],"valid","","NV8"],[[9907,9916],"valid","","NV8"],[[9917,9919],"valid","","NV8"],[[9920,9923],"valid","","NV8"],[[9924,9933],"valid","","NV8"],[[9934,9934],"valid","","NV8"],[[9935,9953],"valid","","NV8"],[[9954,9954],"valid","","NV8"],[[9955,9955],"valid","","NV8"],[[9956,9959],"valid","","NV8"],[[9960,9983],"valid","","NV8"],[[9984,9984],"valid","","NV8"],[[9985,9988],"valid","","NV8"],[[9989,9989],"valid","","NV8"],[[9990,9993],"valid","","NV8"],[[9994,9995],"valid","","NV8"],[[9996,10023],"valid","","NV8"],[[10024,10024],"valid","","NV8"],[[10025,10059],"valid","","NV8"],[[10060,10060],"valid","","NV8"],[[10061,10061],"valid","","NV8"],[[10062,10062],"valid","","NV8"],[[10063,10066],"valid","","NV8"],[[10067,10069],"valid","","NV8"],[[10070,10070],"valid","","NV8"],[[10071,10071],"valid","","NV8"],[[10072,10078],"valid","","NV8"],[[10079,10080],"valid","","NV8"],[[10081,10087],"valid","","NV8"],[[10088,10101],"valid","","NV8"],[[10102,10132],"valid","","NV8"],[[10133,10135],"valid","","NV8"],[[10136,10159],"valid","","NV8"],[[10160,10160],"valid","","NV8"],[[10161,10174],"valid","","NV8"],[[10175,10175],"valid","","NV8"],[[10176,10182],"valid","","NV8"],[[10183,10186],"valid","","NV8"],[[10187,10187],"valid","","NV8"],[[10188,10188],"valid","","NV8"],[[10189,10189],"valid","","NV8"],[[10190,10191],"valid","","NV8"],[[10192,10219],"valid","","NV8"],[[10220,10223],"valid","","NV8"],[[10224,10239],"valid","","NV8"],[[10240,10495],"valid","","NV8"],[[10496,10763],"valid","","NV8"],[[10764,10764],"mapped","∫∫∫∫"],[[10765,10867],"valid","","NV8"],[[10868,10868],"disallowed_STD3_mapped","::="],[[10869,10869],"disallowed_STD3_mapped","=="],[[10870,10870],"disallowed_STD3_mapped","==="],[[10871,10971],"valid","","NV8"],[[10972,10972],"mapped","⫝̸"],[[10973,11007],"valid","","NV8"],[[11008,11021],"valid","","NV8"],[[11022,11027],"valid","","NV8"],[[11028,11034],"valid","","NV8"],[[11035,11039],"valid","","NV8"],[[11040,11043],"valid","","NV8"],[[11044,11084],"valid","","NV8"],[[11085,11087],"valid","","NV8"],[[11088,11092],"valid","","NV8"],[[11093,11097],"valid","","NV8"],[[11098,11123],"valid","","NV8"],[[11124,11125],"disallowed"],[[11126,11157],"valid","","NV8"],[[11158,11159],"disallowed"],[[11160,11193],"valid","","NV8"],[[11194,11196],"disallowed"],[[11197,11208],"valid","","NV8"],[[11209,11209],"disallowed"],[[11210,11217],"valid","","NV8"],[[11218,11218],"valid","","NV8"],[[11219,11243],"disallowed"],[[11244,11247],"valid","","NV8"],[[11248,11263],"disallowed"],[[11264,11264],"mapped","ⰰ"],[[11265,11265],"mapped","ⰱ"],[[11266,11266],"mapped","ⰲ"],[[11267,11267],"mapped","ⰳ"],[[11268,11268],"mapped","ⰴ"],[[11269,11269],"mapped","ⰵ"],[[11270,11270],"mapped","ⰶ"],[[11271,11271],"mapped","ⰷ"],[[11272,11272],"mapped","ⰸ"],[[11273,11273],"mapped","ⰹ"],[[11274,11274],"mapped","ⰺ"],[[11275,11275],"mapped","ⰻ"],[[11276,11276],"mapped","ⰼ"],[[11277,11277],"mapped","ⰽ"],[[11278,11278],"mapped","ⰾ"],[[11279,11279],"mapped","ⰿ"],[[11280,11280],"mapped","ⱀ"],[[11281,11281],"mapped","ⱁ"],[[11282,11282],"mapped","ⱂ"],[[11283,11283],"mapped","ⱃ"],[[11284,11284],"mapped","ⱄ"],[[11285,11285],"mapped","ⱅ"],[[11286,11286],"mapped","ⱆ"],[[11287,11287],"mapped","ⱇ"],[[11288,11288],"mapped","ⱈ"],[[11289,11289],"mapped","ⱉ"],[[11290,11290],"mapped","ⱊ"],[[11291,11291],"mapped","ⱋ"],[[11292,11292],"mapped","ⱌ"],[[11293,11293],"mapped","ⱍ"],[[11294,11294],"mapped","ⱎ"],[[11295,11295],"mapped","ⱏ"],[[11296,11296],"mapped","ⱐ"],[[11297,11297],"mapped","ⱑ"],[[11298,11298],"mapped","ⱒ"],[[11299,11299],"mapped","ⱓ"],[[11300,11300],"mapped","ⱔ"],[[11301,11301],"mapped","ⱕ"],[[11302,11302],"mapped","ⱖ"],[[11303,11303],"mapped","ⱗ"],[[11304,11304],"mapped","ⱘ"],[[11305,11305],"mapped","ⱙ"],[[11306,11306],"mapped","ⱚ"],[[11307,11307],"mapped","ⱛ"],[[11308,11308],"mapped","ⱜ"],[[11309,11309],"mapped","ⱝ"],[[11310,11310],"mapped","ⱞ"],[[11311,11311],"disallowed"],[[11312,11358],"valid"],[[11359,11359],"disallowed"],[[11360,11360],"mapped","ⱡ"],[[11361,11361],"valid"],[[11362,11362],"mapped","ɫ"],[[11363,11363],"mapped","ᵽ"],[[11364,11364],"mapped","ɽ"],[[11365,11366],"valid"],[[11367,11367],"mapped","ⱨ"],[[11368,11368],"valid"],[[11369,11369],"mapped","ⱪ"],[[11370,11370],"valid"],[[11371,11371],"mapped","ⱬ"],[[11372,11372],"valid"],[[11373,11373],"mapped","ɑ"],[[11374,11374],"mapped","ɱ"],[[11375,11375],"mapped","ɐ"],[[11376,11376],"mapped","ɒ"],[[11377,11377],"valid"],[[11378,11378],"mapped","ⱳ"],[[11379,11379],"valid"],[[11380,11380],"valid"],[[11381,11381],"mapped","ⱶ"],[[11382,11383],"valid"],[[11384,11387],"valid"],[[11388,11388],"mapped","j"],[[11389,11389],"mapped","v"],[[11390,11390],"mapped","ȿ"],[[11391,11391],"mapped","ɀ"],[[11392,11392],"mapped","ⲁ"],[[11393,11393],"valid"],[[11394,11394],"mapped","ⲃ"],[[11395,11395],"valid"],[[11396,11396],"mapped","ⲅ"],[[11397,11397],"valid"],[[11398,11398],"mapped","ⲇ"],[[11399,11399],"valid"],[[11400,11400],"mapped","ⲉ"],[[11401,11401],"valid"],[[11402,11402],"mapped","ⲋ"],[[11403,11403],"valid"],[[11404,11404],"mapped","ⲍ"],[[11405,11405],"valid"],[[11406,11406],"mapped","ⲏ"],[[11407,11407],"valid"],[[11408,11408],"mapped","ⲑ"],[[11409,11409],"valid"],[[11410,11410],"mapped","ⲓ"],[[11411,11411],"valid"],[[11412,11412],"mapped","ⲕ"],[[11413,11413],"valid"],[[11414,11414],"mapped","ⲗ"],[[11415,11415],"valid"],[[11416,11416],"mapped","ⲙ"],[[11417,11417],"valid"],[[11418,11418],"mapped","ⲛ"],[[11419,11419],"valid"],[[11420,11420],"mapped","ⲝ"],[[11421,11421],"valid"],[[11422,11422],"mapped","ⲟ"],[[11423,11423],"valid"],[[11424,11424],"mapped","ⲡ"],[[11425,11425],"valid"],[[11426,11426],"mapped","ⲣ"],[[11427,11427],"valid"],[[11428,11428],"mapped","ⲥ"],[[11429,11429],"valid"],[[11430,11430],"mapped","ⲧ"],[[11431,11431],"valid"],[[11432,11432],"mapped","ⲩ"],[[11433,11433],"valid"],[[11434,11434],"mapped","ⲫ"],[[11435,11435],"valid"],[[11436,11436],"mapped","ⲭ"],[[11437,11437],"valid"],[[11438,11438],"mapped","ⲯ"],[[11439,11439],"valid"],[[11440,11440],"mapped","ⲱ"],[[11441,11441],"valid"],[[11442,11442],"mapped","ⲳ"],[[11443,11443],"valid"],[[11444,11444],"mapped","ⲵ"],[[11445,11445],"valid"],[[11446,11446],"mapped","ⲷ"],[[11447,11447],"valid"],[[11448,11448],"mapped","ⲹ"],[[11449,11449],"valid"],[[11450,11450],"mapped","ⲻ"],[[11451,11451],"valid"],[[11452,11452],"mapped","ⲽ"],[[11453,11453],"valid"],[[11454,11454],"mapped","ⲿ"],[[11455,11455],"valid"],[[11456,11456],"mapped","ⳁ"],[[11457,11457],"valid"],[[11458,11458],"mapped","ⳃ"],[[11459,11459],"valid"],[[11460,11460],"mapped","ⳅ"],[[11461,11461],"valid"],[[11462,11462],"mapped","ⳇ"],[[11463,11463],"valid"],[[11464,11464],"mapped","ⳉ"],[[11465,11465],"valid"],[[11466,11466],"mapped","ⳋ"],[[11467,11467],"valid"],[[11468,11468],"mapped","ⳍ"],[[11469,11469],"valid"],[[11470,11470],"mapped","ⳏ"],[[11471,11471],"valid"],[[11472,11472],"mapped","ⳑ"],[[11473,11473],"valid"],[[11474,11474],"mapped","ⳓ"],[[11475,11475],"valid"],[[11476,11476],"mapped","ⳕ"],[[11477,11477],"valid"],[[11478,11478],"mapped","ⳗ"],[[11479,11479],"valid"],[[11480,11480],"mapped","ⳙ"],[[11481,11481],"valid"],[[11482,11482],"mapped","ⳛ"],[[11483,11483],"valid"],[[11484,11484],"mapped","ⳝ"],[[11485,11485],"valid"],[[11486,11486],"mapped","ⳟ"],[[11487,11487],"valid"],[[11488,11488],"mapped","ⳡ"],[[11489,11489],"valid"],[[11490,11490],"mapped","ⳣ"],[[11491,11492],"valid"],[[11493,11498],"valid","","NV8"],[[11499,11499],"mapped","ⳬ"],[[11500,11500],"valid"],[[11501,11501],"mapped","ⳮ"],[[11502,11505],"valid"],[[11506,11506],"mapped","ⳳ"],[[11507,11507],"valid"],[[11508,11512],"disallowed"],[[11513,11519],"valid","","NV8"],[[11520,11557],"valid"],[[11558,11558],"disallowed"],[[11559,11559],"valid"],[[11560,11564],"disallowed"],[[11565,11565],"valid"],[[11566,11567],"disallowed"],[[11568,11621],"valid"],[[11622,11623],"valid"],[[11624,11630],"disallowed"],[[11631,11631],"mapped","ⵡ"],[[11632,11632],"valid","","NV8"],[[11633,11646],"disallowed"],[[11647,11647],"valid"],[[11648,11670],"valid"],[[11671,11679],"disallowed"],[[11680,11686],"valid"],[[11687,11687],"disallowed"],[[11688,11694],"valid"],[[11695,11695],"disallowed"],[[11696,11702],"valid"],[[11703,11703],"disallowed"],[[11704,11710],"valid"],[[11711,11711],"disallowed"],[[11712,11718],"valid"],[[11719,11719],"disallowed"],[[11720,11726],"valid"],[[11727,11727],"disallowed"],[[11728,11734],"valid"],[[11735,11735],"disallowed"],[[11736,11742],"valid"],[[11743,11743],"disallowed"],[[11744,11775],"valid"],[[11776,11799],"valid","","NV8"],[[11800,11803],"valid","","NV8"],[[11804,11805],"valid","","NV8"],[[11806,11822],"valid","","NV8"],[[11823,11823],"valid"],[[11824,11824],"valid","","NV8"],[[11825,11825],"valid","","NV8"],[[11826,11835],"valid","","NV8"],[[11836,11842],"valid","","NV8"],[[11843,11844],"valid","","NV8"],[[11845,11849],"valid","","NV8"],[[11850,11903],"disallowed"],[[11904,11929],"valid","","NV8"],[[11930,11930],"disallowed"],[[11931,11934],"valid","","NV8"],[[11935,11935],"mapped","母"],[[11936,12018],"valid","","NV8"],[[12019,12019],"mapped","龟"],[[12020,12031],"disallowed"],[[12032,12032],"mapped","一"],[[12033,12033],"mapped","丨"],[[12034,12034],"mapped","丶"],[[12035,12035],"mapped","丿"],[[12036,12036],"mapped","乙"],[[12037,12037],"mapped","亅"],[[12038,12038],"mapped","二"],[[12039,12039],"mapped","亠"],[[12040,12040],"mapped","人"],[[12041,12041],"mapped","儿"],[[12042,12042],"mapped","入"],[[12043,12043],"mapped","八"],[[12044,12044],"mapped","冂"],[[12045,12045],"mapped","冖"],[[12046,12046],"mapped","冫"],[[12047,12047],"mapped","几"],[[12048,12048],"mapped","凵"],[[12049,12049],"mapped","刀"],[[12050,12050],"mapped","力"],[[12051,12051],"mapped","勹"],[[12052,12052],"mapped","匕"],[[12053,12053],"mapped","匚"],[[12054,12054],"mapped","匸"],[[12055,12055],"mapped","十"],[[12056,12056],"mapped","卜"],[[12057,12057],"mapped","卩"],[[12058,12058],"mapped","厂"],[[12059,12059],"mapped","厶"],[[12060,12060],"mapped","又"],[[12061,12061],"mapped","口"],[[12062,12062],"mapped","囗"],[[12063,12063],"mapped","土"],[[12064,12064],"mapped","士"],[[12065,12065],"mapped","夂"],[[12066,12066],"mapped","夊"],[[12067,12067],"mapped","夕"],[[12068,12068],"mapped","大"],[[12069,12069],"mapped","女"],[[12070,12070],"mapped","子"],[[12071,12071],"mapped","宀"],[[12072,12072],"mapped","寸"],[[12073,12073],"mapped","小"],[[12074,12074],"mapped","尢"],[[12075,12075],"mapped","尸"],[[12076,12076],"mapped","屮"],[[12077,12077],"mapped","山"],[[12078,12078],"mapped","巛"],[[12079,12079],"mapped","工"],[[12080,12080],"mapped","己"],[[12081,12081],"mapped","巾"],[[12082,12082],"mapped","干"],[[12083,12083],"mapped","幺"],[[12084,12084],"mapped","广"],[[12085,12085],"mapped","廴"],[[12086,12086],"mapped","廾"],[[12087,12087],"mapped","弋"],[[12088,12088],"mapped","弓"],[[12089,12089],"mapped","彐"],[[12090,12090],"mapped","彡"],[[12091,12091],"mapped","彳"],[[12092,12092],"mapped","心"],[[12093,12093],"mapped","戈"],[[12094,12094],"mapped","戶"],[[12095,12095],"mapped","手"],[[12096,12096],"mapped","支"],[[12097,12097],"mapped","攴"],[[12098,12098],"mapped","文"],[[12099,12099],"mapped","斗"],[[12100,12100],"mapped","斤"],[[12101,12101],"mapped","方"],[[12102,12102],"mapped","无"],[[12103,12103],"mapped","日"],[[12104,12104],"mapped","曰"],[[12105,12105],"mapped","月"],[[12106,12106],"mapped","木"],[[12107,12107],"mapped","欠"],[[12108,12108],"mapped","止"],[[12109,12109],"mapped","歹"],[[12110,12110],"mapped","殳"],[[12111,12111],"mapped","毋"],[[12112,12112],"mapped","比"],[[12113,12113],"mapped","毛"],[[12114,12114],"mapped","氏"],[[12115,12115],"mapped","气"],[[12116,12116],"mapped","水"],[[12117,12117],"mapped","火"],[[12118,12118],"mapped","爪"],[[12119,12119],"mapped","父"],[[12120,12120],"mapped","爻"],[[12121,12121],"mapped","爿"],[[12122,12122],"mapped","片"],[[12123,12123],"mapped","牙"],[[12124,12124],"mapped","牛"],[[12125,12125],"mapped","犬"],[[12126,12126],"mapped","玄"],[[12127,12127],"mapped","玉"],[[12128,12128],"mapped","瓜"],[[12129,12129],"mapped","瓦"],[[12130,12130],"mapped","甘"],[[12131,12131],"mapped","生"],[[12132,12132],"mapped","用"],[[12133,12133],"mapped","田"],[[12134,12134],"mapped","疋"],[[12135,12135],"mapped","疒"],[[12136,12136],"mapped","癶"],[[12137,12137],"mapped","白"],[[12138,12138],"mapped","皮"],[[12139,12139],"mapped","皿"],[[12140,12140],"mapped","目"],[[12141,12141],"mapped","矛"],[[12142,12142],"mapped","矢"],[[12143,12143],"mapped","石"],[[12144,12144],"mapped","示"],[[12145,12145],"mapped","禸"],[[12146,12146],"mapped","禾"],[[12147,12147],"mapped","穴"],[[12148,12148],"mapped","立"],[[12149,12149],"mapped","竹"],[[12150,12150],"mapped","米"],[[12151,12151],"mapped","糸"],[[12152,12152],"mapped","缶"],[[12153,12153],"mapped","网"],[[12154,12154],"mapped","羊"],[[12155,12155],"mapped","羽"],[[12156,12156],"mapped","老"],[[12157,12157],"mapped","而"],[[12158,12158],"mapped","耒"],[[12159,12159],"mapped","耳"],[[12160,12160],"mapped","聿"],[[12161,12161],"mapped","肉"],[[12162,12162],"mapped","臣"],[[12163,12163],"mapped","自"],[[12164,12164],"mapped","至"],[[12165,12165],"mapped","臼"],[[12166,12166],"mapped","舌"],[[12167,12167],"mapped","舛"],[[12168,12168],"mapped","舟"],[[12169,12169],"mapped","艮"],[[12170,12170],"mapped","色"],[[12171,12171],"mapped","艸"],[[12172,12172],"mapped","虍"],[[12173,12173],"mapped","虫"],[[12174,12174],"mapped","血"],[[12175,12175],"mapped","行"],[[12176,12176],"mapped","衣"],[[12177,12177],"mapped","襾"],[[12178,12178],"mapped","見"],[[12179,12179],"mapped","角"],[[12180,12180],"mapped","言"],[[12181,12181],"mapped","谷"],[[12182,12182],"mapped","豆"],[[12183,12183],"mapped","豕"],[[12184,12184],"mapped","豸"],[[12185,12185],"mapped","貝"],[[12186,12186],"mapped","赤"],[[12187,12187],"mapped","走"],[[12188,12188],"mapped","足"],[[12189,12189],"mapped","身"],[[12190,12190],"mapped","車"],[[12191,12191],"mapped","辛"],[[12192,12192],"mapped","辰"],[[12193,12193],"mapped","辵"],[[12194,12194],"mapped","邑"],[[12195,12195],"mapped","酉"],[[12196,12196],"mapped","釆"],[[12197,12197],"mapped","里"],[[12198,12198],"mapped","金"],[[12199,12199],"mapped","長"],[[12200,12200],"mapped","門"],[[12201,12201],"mapped","阜"],[[12202,12202],"mapped","隶"],[[12203,12203],"mapped","隹"],[[12204,12204],"mapped","雨"],[[12205,12205],"mapped","靑"],[[12206,12206],"mapped","非"],[[12207,12207],"mapped","面"],[[12208,12208],"mapped","革"],[[12209,12209],"mapped","韋"],[[12210,12210],"mapped","韭"],[[12211,12211],"mapped","音"],[[12212,12212],"mapped","頁"],[[12213,12213],"mapped","風"],[[12214,12214],"mapped","飛"],[[12215,12215],"mapped","食"],[[12216,12216],"mapped","首"],[[12217,12217],"mapped","香"],[[12218,12218],"mapped","馬"],[[12219,12219],"mapped","骨"],[[12220,12220],"mapped","高"],[[12221,12221],"mapped","髟"],[[12222,12222],"mapped","鬥"],[[12223,12223],"mapped","鬯"],[[12224,12224],"mapped","鬲"],[[12225,12225],"mapped","鬼"],[[12226,12226],"mapped","魚"],[[12227,12227],"mapped","鳥"],[[12228,12228],"mapped","鹵"],[[12229,12229],"mapped","鹿"],[[12230,12230],"mapped","麥"],[[12231,12231],"mapped","麻"],[[12232,12232],"mapped","黃"],[[12233,12233],"mapped","黍"],[[12234,12234],"mapped","黑"],[[12235,12235],"mapped","黹"],[[12236,12236],"mapped","黽"],[[12237,12237],"mapped","鼎"],[[12238,12238],"mapped","鼓"],[[12239,12239],"mapped","鼠"],[[12240,12240],"mapped","鼻"],[[12241,12241],"mapped","齊"],[[12242,12242],"mapped","齒"],[[12243,12243],"mapped","龍"],[[12244,12244],"mapped","龜"],[[12245,12245],"mapped","龠"],[[12246,12271],"disallowed"],[[12272,12283],"disallowed"],[[12284,12287],"disallowed"],[[12288,12288],"disallowed_STD3_mapped"," "],[[12289,12289],"valid","","NV8"],[[12290,12290],"mapped","."],[[12291,12292],"valid","","NV8"],[[12293,12295],"valid"],[[12296,12329],"valid","","NV8"],[[12330,12333],"valid"],[[12334,12341],"valid","","NV8"],[[12342,12342],"mapped","〒"],[[12343,12343],"valid","","NV8"],[[12344,12344],"mapped","十"],[[12345,12345],"mapped","卄"],[[12346,12346],"mapped","卅"],[[12347,12347],"valid","","NV8"],[[12348,12348],"valid"],[[12349,12349],"valid","","NV8"],[[12350,12350],"valid","","NV8"],[[12351,12351],"valid","","NV8"],[[12352,12352],"disallowed"],[[12353,12436],"valid"],[[12437,12438],"valid"],[[12439,12440],"disallowed"],[[12441,12442],"valid"],[[12443,12443],"disallowed_STD3_mapped"," ゙"],[[12444,12444],"disallowed_STD3_mapped"," ゚"],[[12445,12446],"valid"],[[12447,12447],"mapped","より"],[[12448,12448],"valid","","NV8"],[[12449,12542],"valid"],[[12543,12543],"mapped","コト"],[[12544,12548],"disallowed"],[[12549,12588],"valid"],[[12589,12589],"valid"],[[12590,12590],"valid"],[[12591,12592],"disallowed"],[[12593,12593],"mapped","ᄀ"],[[12594,12594],"mapped","ᄁ"],[[12595,12595],"mapped","ᆪ"],[[12596,12596],"mapped","ᄂ"],[[12597,12597],"mapped","ᆬ"],[[12598,12598],"mapped","ᆭ"],[[12599,12599],"mapped","ᄃ"],[[12600,12600],"mapped","ᄄ"],[[12601,12601],"mapped","ᄅ"],[[12602,12602],"mapped","ᆰ"],[[12603,12603],"mapped","ᆱ"],[[12604,12604],"mapped","ᆲ"],[[12605,12605],"mapped","ᆳ"],[[12606,12606],"mapped","ᆴ"],[[12607,12607],"mapped","ᆵ"],[[12608,12608],"mapped","ᄚ"],[[12609,12609],"mapped","ᄆ"],[[12610,12610],"mapped","ᄇ"],[[12611,12611],"mapped","ᄈ"],[[12612,12612],"mapped","ᄡ"],[[12613,12613],"mapped","ᄉ"],[[12614,12614],"mapped","ᄊ"],[[12615,12615],"mapped","ᄋ"],[[12616,12616],"mapped","ᄌ"],[[12617,12617],"mapped","ᄍ"],[[12618,12618],"mapped","ᄎ"],[[12619,12619],"mapped","ᄏ"],[[12620,12620],"mapped","ᄐ"],[[12621,12621],"mapped","ᄑ"],[[12622,12622],"mapped","ᄒ"],[[12623,12623],"mapped","ᅡ"],[[12624,12624],"mapped","ᅢ"],[[12625,12625],"mapped","ᅣ"],[[12626,12626],"mapped","ᅤ"],[[12627,12627],"mapped","ᅥ"],[[12628,12628],"mapped","ᅦ"],[[12629,12629],"mapped","ᅧ"],[[12630,12630],"mapped","ᅨ"],[[12631,12631],"mapped","ᅩ"],[[12632,12632],"mapped","ᅪ"],[[12633,12633],"mapped","ᅫ"],[[12634,12634],"mapped","ᅬ"],[[12635,12635],"mapped","ᅭ"],[[12636,12636],"mapped","ᅮ"],[[12637,12637],"mapped","ᅯ"],[[12638,12638],"mapped","ᅰ"],[[12639,12639],"mapped","ᅱ"],[[12640,12640],"mapped","ᅲ"],[[12641,12641],"mapped","ᅳ"],[[12642,12642],"mapped","ᅴ"],[[12643,12643],"mapped","ᅵ"],[[12644,12644],"disallowed"],[[12645,12645],"mapped","ᄔ"],[[12646,12646],"mapped","ᄕ"],[[12647,12647],"mapped","ᇇ"],[[12648,12648],"mapped","ᇈ"],[[12649,12649],"mapped","ᇌ"],[[12650,12650],"mapped","ᇎ"],[[12651,12651],"mapped","ᇓ"],[[12652,12652],"mapped","ᇗ"],[[12653,12653],"mapped","ᇙ"],[[12654,12654],"mapped","ᄜ"],[[12655,12655],"mapped","ᇝ"],[[12656,12656],"mapped","ᇟ"],[[12657,12657],"mapped","ᄝ"],[[12658,12658],"mapped","ᄞ"],[[12659,12659],"mapped","ᄠ"],[[12660,12660],"mapped","ᄢ"],[[12661,12661],"mapped","ᄣ"],[[12662,12662],"mapped","ᄧ"],[[12663,12663],"mapped","ᄩ"],[[12664,12664],"mapped","ᄫ"],[[12665,12665],"mapped","ᄬ"],[[12666,12666],"mapped","ᄭ"],[[12667,12667],"mapped","ᄮ"],[[12668,12668],"mapped","ᄯ"],[[12669,12669],"mapped","ᄲ"],[[12670,12670],"mapped","ᄶ"],[[12671,12671],"mapped","ᅀ"],[[12672,12672],"mapped","ᅇ"],[[12673,12673],"mapped","ᅌ"],[[12674,12674],"mapped","ᇱ"],[[12675,12675],"mapped","ᇲ"],[[12676,12676],"mapped","ᅗ"],[[12677,12677],"mapped","ᅘ"],[[12678,12678],"mapped","ᅙ"],[[12679,12679],"mapped","ᆄ"],[[12680,12680],"mapped","ᆅ"],[[12681,12681],"mapped","ᆈ"],[[12682,12682],"mapped","ᆑ"],[[12683,12683],"mapped","ᆒ"],[[12684,12684],"mapped","ᆔ"],[[12685,12685],"mapped","ᆞ"],[[12686,12686],"mapped","ᆡ"],[[12687,12687],"disallowed"],[[12688,12689],"valid","","NV8"],[[12690,12690],"mapped","一"],[[12691,12691],"mapped","二"],[[12692,12692],"mapped","三"],[[12693,12693],"mapped","四"],[[12694,12694],"mapped","上"],[[12695,12695],"mapped","中"],[[12696,12696],"mapped","下"],[[12697,12697],"mapped","甲"],[[12698,12698],"mapped","乙"],[[12699,12699],"mapped","丙"],[[12700,12700],"mapped","丁"],[[12701,12701],"mapped","天"],[[12702,12702],"mapped","地"],[[12703,12703],"mapped","人"],[[12704,12727],"valid"],[[12728,12730],"valid"],[[12731,12735],"disallowed"],[[12736,12751],"valid","","NV8"],[[12752,12771],"valid","","NV8"],[[12772,12783],"disallowed"],[[12784,12799],"valid"],[[12800,12800],"disallowed_STD3_mapped","(ᄀ)"],[[12801,12801],"disallowed_STD3_mapped","(ᄂ)"],[[12802,12802],"disallowed_STD3_mapped","(ᄃ)"],[[12803,12803],"disallowed_STD3_mapped","(ᄅ)"],[[12804,12804],"disallowed_STD3_mapped","(ᄆ)"],[[12805,12805],"disallowed_STD3_mapped","(ᄇ)"],[[12806,12806],"disallowed_STD3_mapped","(ᄉ)"],[[12807,12807],"disallowed_STD3_mapped","(ᄋ)"],[[12808,12808],"disallowed_STD3_mapped","(ᄌ)"],[[12809,12809],"disallowed_STD3_mapped","(ᄎ)"],[[12810,12810],"disallowed_STD3_mapped","(ᄏ)"],[[12811,12811],"disallowed_STD3_mapped","(ᄐ)"],[[12812,12812],"disallowed_STD3_mapped","(ᄑ)"],[[12813,12813],"disallowed_STD3_mapped","(ᄒ)"],[[12814,12814],"disallowed_STD3_mapped","(가)"],[[12815,12815],"disallowed_STD3_mapped","(나)"],[[12816,12816],"disallowed_STD3_mapped","(다)"],[[12817,12817],"disallowed_STD3_mapped","(라)"],[[12818,12818],"disallowed_STD3_mapped","(마)"],[[12819,12819],"disallowed_STD3_mapped","(바)"],[[12820,12820],"disallowed_STD3_mapped","(사)"],[[12821,12821],"disallowed_STD3_mapped","(아)"],[[12822,12822],"disallowed_STD3_mapped","(자)"],[[12823,12823],"disallowed_STD3_mapped","(차)"],[[12824,12824],"disallowed_STD3_mapped","(카)"],[[12825,12825],"disallowed_STD3_mapped","(타)"],[[12826,12826],"disallowed_STD3_mapped","(파)"],[[12827,12827],"disallowed_STD3_mapped","(하)"],[[12828,12828],"disallowed_STD3_mapped","(주)"],[[12829,12829],"disallowed_STD3_mapped","(오전)"],[[12830,12830],"disallowed_STD3_mapped","(오후)"],[[12831,12831],"disallowed"],[[12832,12832],"disallowed_STD3_mapped","(一)"],[[12833,12833],"disallowed_STD3_mapped","(二)"],[[12834,12834],"disallowed_STD3_mapped","(三)"],[[12835,12835],"disallowed_STD3_mapped","(四)"],[[12836,12836],"disallowed_STD3_mapped","(五)"],[[12837,12837],"disallowed_STD3_mapped","(六)"],[[12838,12838],"disallowed_STD3_mapped","(七)"],[[12839,12839],"disallowed_STD3_mapped","(八)"],[[12840,12840],"disallowed_STD3_mapped","(九)"],[[12841,12841],"disallowed_STD3_mapped","(十)"],[[12842,12842],"disallowed_STD3_mapped","(月)"],[[12843,12843],"disallowed_STD3_mapped","(火)"],[[12844,12844],"disallowed_STD3_mapped","(水)"],[[12845,12845],"disallowed_STD3_mapped","(木)"],[[12846,12846],"disallowed_STD3_mapped","(金)"],[[12847,12847],"disallowed_STD3_mapped","(土)"],[[12848,12848],"disallowed_STD3_mapped","(日)"],[[12849,12849],"disallowed_STD3_mapped","(株)"],[[12850,12850],"disallowed_STD3_mapped","(有)"],[[12851,12851],"disallowed_STD3_mapped","(社)"],[[12852,12852],"disallowed_STD3_mapped","(名)"],[[12853,12853],"disallowed_STD3_mapped","(特)"],[[12854,12854],"disallowed_STD3_mapped","(財)"],[[12855,12855],"disallowed_STD3_mapped","(祝)"],[[12856,12856],"disallowed_STD3_mapped","(労)"],[[12857,12857],"disallowed_STD3_mapped","(代)"],[[12858,12858],"disallowed_STD3_mapped","(呼)"],[[12859,12859],"disallowed_STD3_mapped","(学)"],[[12860,12860],"disallowed_STD3_mapped","(監)"],[[12861,12861],"disallowed_STD3_mapped","(企)"],[[12862,12862],"disallowed_STD3_mapped","(資)"],[[12863,12863],"disallowed_STD3_mapped","(協)"],[[12864,12864],"disallowed_STD3_mapped","(祭)"],[[12865,12865],"disallowed_STD3_mapped","(休)"],[[12866,12866],"disallowed_STD3_mapped","(自)"],[[12867,12867],"disallowed_STD3_mapped","(至)"],[[12868,12868],"mapped","問"],[[12869,12869],"mapped","幼"],[[12870,12870],"mapped","文"],[[12871,12871],"mapped","箏"],[[12872,12879],"valid","","NV8"],[[12880,12880],"mapped","pte"],[[12881,12881],"mapped","21"],[[12882,12882],"mapped","22"],[[12883,12883],"mapped","23"],[[12884,12884],"mapped","24"],[[12885,12885],"mapped","25"],[[12886,12886],"mapped","26"],[[12887,12887],"mapped","27"],[[12888,12888],"mapped","28"],[[12889,12889],"mapped","29"],[[12890,12890],"mapped","30"],[[12891,12891],"mapped","31"],[[12892,12892],"mapped","32"],[[12893,12893],"mapped","33"],[[12894,12894],"mapped","34"],[[12895,12895],"mapped","35"],[[12896,12896],"mapped","ᄀ"],[[12897,12897],"mapped","ᄂ"],[[12898,12898],"mapped","ᄃ"],[[12899,12899],"mapped","ᄅ"],[[12900,12900],"mapped","ᄆ"],[[12901,12901],"mapped","ᄇ"],[[12902,12902],"mapped","ᄉ"],[[12903,12903],"mapped","ᄋ"],[[12904,12904],"mapped","ᄌ"],[[12905,12905],"mapped","ᄎ"],[[12906,12906],"mapped","ᄏ"],[[12907,12907],"mapped","ᄐ"],[[12908,12908],"mapped","ᄑ"],[[12909,12909],"mapped","ᄒ"],[[12910,12910],"mapped","가"],[[12911,12911],"mapped","나"],[[12912,12912],"mapped","다"],[[12913,12913],"mapped","라"],[[12914,12914],"mapped","마"],[[12915,12915],"mapped","바"],[[12916,12916],"mapped","사"],[[12917,12917],"mapped","아"],[[12918,12918],"mapped","자"],[[12919,12919],"mapped","차"],[[12920,12920],"mapped","카"],[[12921,12921],"mapped","타"],[[12922,12922],"mapped","파"],[[12923,12923],"mapped","하"],[[12924,12924],"mapped","참고"],[[12925,12925],"mapped","주의"],[[12926,12926],"mapped","우"],[[12927,12927],"valid","","NV8"],[[12928,12928],"mapped","一"],[[12929,12929],"mapped","二"],[[12930,12930],"mapped","三"],[[12931,12931],"mapped","四"],[[12932,12932],"mapped","五"],[[12933,12933],"mapped","六"],[[12934,12934],"mapped","七"],[[12935,12935],"mapped","八"],[[12936,12936],"mapped","九"],[[12937,12937],"mapped","十"],[[12938,12938],"mapped","月"],[[12939,12939],"mapped","火"],[[12940,12940],"mapped","水"],[[12941,12941],"mapped","木"],[[12942,12942],"mapped","金"],[[12943,12943],"mapped","土"],[[12944,12944],"mapped","日"],[[12945,12945],"mapped","株"],[[12946,12946],"mapped","有"],[[12947,12947],"mapped","社"],[[12948,12948],"mapped","名"],[[12949,12949],"mapped","特"],[[12950,12950],"mapped","財"],[[12951,12951],"mapped","祝"],[[12952,12952],"mapped","労"],[[12953,12953],"mapped","秘"],[[12954,12954],"mapped","男"],[[12955,12955],"mapped","女"],[[12956,12956],"mapped","適"],[[12957,12957],"mapped","優"],[[12958,12958],"mapped","印"],[[12959,12959],"mapped","注"],[[12960,12960],"mapped","項"],[[12961,12961],"mapped","休"],[[12962,12962],"mapped","写"],[[12963,12963],"mapped","正"],[[12964,12964],"mapped","上"],[[12965,12965],"mapped","中"],[[12966,12966],"mapped","下"],[[12967,12967],"mapped","左"],[[12968,12968],"mapped","右"],[[12969,12969],"mapped","医"],[[12970,12970],"mapped","宗"],[[12971,12971],"mapped","学"],[[12972,12972],"mapped","監"],[[12973,12973],"mapped","企"],[[12974,12974],"mapped","資"],[[12975,12975],"mapped","協"],[[12976,12976],"mapped","夜"],[[12977,12977],"mapped","36"],[[12978,12978],"mapped","37"],[[12979,12979],"mapped","38"],[[12980,12980],"mapped","39"],[[12981,12981],"mapped","40"],[[12982,12982],"mapped","41"],[[12983,12983],"mapped","42"],[[12984,12984],"mapped","43"],[[12985,12985],"mapped","44"],[[12986,12986],"mapped","45"],[[12987,12987],"mapped","46"],[[12988,12988],"mapped","47"],[[12989,12989],"mapped","48"],[[12990,12990],"mapped","49"],[[12991,12991],"mapped","50"],[[12992,12992],"mapped","1月"],[[12993,12993],"mapped","2月"],[[12994,12994],"mapped","3月"],[[12995,12995],"mapped","4月"],[[12996,12996],"mapped","5月"],[[12997,12997],"mapped","6月"],[[12998,12998],"mapped","7月"],[[12999,12999],"mapped","8月"],[[13000,13000],"mapped","9月"],[[13001,13001],"mapped","10月"],[[13002,13002],"mapped","11月"],[[13003,13003],"mapped","12月"],[[13004,13004],"mapped","hg"],[[13005,13005],"mapped","erg"],[[13006,13006],"mapped","ev"],[[13007,13007],"mapped","ltd"],[[13008,13008],"mapped","ア"],[[13009,13009],"mapped","イ"],[[13010,13010],"mapped","ウ"],[[13011,13011],"mapped","エ"],[[13012,13012],"mapped","オ"],[[13013,13013],"mapped","カ"],[[13014,13014],"mapped","キ"],[[13015,13015],"mapped","ク"],[[13016,13016],"mapped","ケ"],[[13017,13017],"mapped","コ"],[[13018,13018],"mapped","サ"],[[13019,13019],"mapped","シ"],[[13020,13020],"mapped","ス"],[[13021,13021],"mapped","セ"],[[13022,13022],"mapped","ソ"],[[13023,13023],"mapped","タ"],[[13024,13024],"mapped","チ"],[[13025,13025],"mapped","ツ"],[[13026,13026],"mapped","テ"],[[13027,13027],"mapped","ト"],[[13028,13028],"mapped","ナ"],[[13029,13029],"mapped","ニ"],[[13030,13030],"mapped","ヌ"],[[13031,13031],"mapped","ネ"],[[13032,13032],"mapped","ノ"],[[13033,13033],"mapped","ハ"],[[13034,13034],"mapped","ヒ"],[[13035,13035],"mapped","フ"],[[13036,13036],"mapped","ヘ"],[[13037,13037],"mapped","ホ"],[[13038,13038],"mapped","マ"],[[13039,13039],"mapped","ミ"],[[13040,13040],"mapped","ム"],[[13041,13041],"mapped","メ"],[[13042,13042],"mapped","モ"],[[13043,13043],"mapped","ヤ"],[[13044,13044],"mapped","ユ"],[[13045,13045],"mapped","ヨ"],[[13046,13046],"mapped","ラ"],[[13047,13047],"mapped","リ"],[[13048,13048],"mapped","ル"],[[13049,13049],"mapped","レ"],[[13050,13050],"mapped","ロ"],[[13051,13051],"mapped","ワ"],[[13052,13052],"mapped","ヰ"],[[13053,13053],"mapped","ヱ"],[[13054,13054],"mapped","ヲ"],[[13055,13055],"disallowed"],[[13056,13056],"mapped","アパート"],[[13057,13057],"mapped","アルファ"],[[13058,13058],"mapped","アンペア"],[[13059,13059],"mapped","アール"],[[13060,13060],"mapped","イニング"],[[13061,13061],"mapped","インチ"],[[13062,13062],"mapped","ウォン"],[[13063,13063],"mapped","エスクード"],[[13064,13064],"mapped","エーカー"],[[13065,13065],"mapped","オンス"],[[13066,13066],"mapped","オーム"],[[13067,13067],"mapped","カイリ"],[[13068,13068],"mapped","カラット"],[[13069,13069],"mapped","カロリー"],[[13070,13070],"mapped","ガロン"],[[13071,13071],"mapped","ガンマ"],[[13072,13072],"mapped","ギガ"],[[13073,13073],"mapped","ギニー"],[[13074,13074],"mapped","キュリー"],[[13075,13075],"mapped","ギルダー"],[[13076,13076],"mapped","キロ"],[[13077,13077],"mapped","キログラム"],[[13078,13078],"mapped","キロメートル"],[[13079,13079],"mapped","キロワット"],[[13080,13080],"mapped","グラム"],[[13081,13081],"mapped","グラムトン"],[[13082,13082],"mapped","クルゼイロ"],[[13083,13083],"mapped","クローネ"],[[13084,13084],"mapped","ケース"],[[13085,13085],"mapped","コルナ"],[[13086,13086],"mapped","コーポ"],[[13087,13087],"mapped","サイクル"],[[13088,13088],"mapped","サンチーム"],[[13089,13089],"mapped","シリング"],[[13090,13090],"mapped","センチ"],[[13091,13091],"mapped","セント"],[[13092,13092],"mapped","ダース"],[[13093,13093],"mapped","デシ"],[[13094,13094],"mapped","ドル"],[[13095,13095],"mapped","トン"],[[13096,13096],"mapped","ナノ"],[[13097,13097],"mapped","ノット"],[[13098,13098],"mapped","ハイツ"],[[13099,13099],"mapped","パーセント"],[[13100,13100],"mapped","パーツ"],[[13101,13101],"mapped","バーレル"],[[13102,13102],"mapped","ピアストル"],[[13103,13103],"mapped","ピクル"],[[13104,13104],"mapped","ピコ"],[[13105,13105],"mapped","ビル"],[[13106,13106],"mapped","ファラッド"],[[13107,13107],"mapped","フィート"],[[13108,13108],"mapped","ブッシェル"],[[13109,13109],"mapped","フラン"],[[13110,13110],"mapped","ヘクタール"],[[13111,13111],"mapped","ペソ"],[[13112,13112],"mapped","ペニヒ"],[[13113,13113],"mapped","ヘルツ"],[[13114,13114],"mapped","ペンス"],[[13115,13115],"mapped","ページ"],[[13116,13116],"mapped","ベータ"],[[13117,13117],"mapped","ポイント"],[[13118,13118],"mapped","ボルト"],[[13119,13119],"mapped","ホン"],[[13120,13120],"mapped","ポンド"],[[13121,13121],"mapped","ホール"],[[13122,13122],"mapped","ホーン"],[[13123,13123],"mapped","マイクロ"],[[13124,13124],"mapped","マイル"],[[13125,13125],"mapped","マッハ"],[[13126,13126],"mapped","マルク"],[[13127,13127],"mapped","マンション"],[[13128,13128],"mapped","ミクロン"],[[13129,13129],"mapped","ミリ"],[[13130,13130],"mapped","ミリバール"],[[13131,13131],"mapped","メガ"],[[13132,13132],"mapped","メガトン"],[[13133,13133],"mapped","メートル"],[[13134,13134],"mapped","ヤード"],[[13135,13135],"mapped","ヤール"],[[13136,13136],"mapped","ユアン"],[[13137,13137],"mapped","リットル"],[[13138,13138],"mapped","リラ"],[[13139,13139],"mapped","ルピー"],[[13140,13140],"mapped","ルーブル"],[[13141,13141],"mapped","レム"],[[13142,13142],"mapped","レントゲン"],[[13143,13143],"mapped","ワット"],[[13144,13144],"mapped","0点"],[[13145,13145],"mapped","1点"],[[13146,13146],"mapped","2点"],[[13147,13147],"mapped","3点"],[[13148,13148],"mapped","4点"],[[13149,13149],"mapped","5点"],[[13150,13150],"mapped","6点"],[[13151,13151],"mapped","7点"],[[13152,13152],"mapped","8点"],[[13153,13153],"mapped","9点"],[[13154,13154],"mapped","10点"],[[13155,13155],"mapped","11点"],[[13156,13156],"mapped","12点"],[[13157,13157],"mapped","13点"],[[13158,13158],"mapped","14点"],[[13159,13159],"mapped","15点"],[[13160,13160],"mapped","16点"],[[13161,13161],"mapped","17点"],[[13162,13162],"mapped","18点"],[[13163,13163],"mapped","19点"],[[13164,13164],"mapped","20点"],[[13165,13165],"mapped","21点"],[[13166,13166],"mapped","22点"],[[13167,13167],"mapped","23点"],[[13168,13168],"mapped","24点"],[[13169,13169],"mapped","hpa"],[[13170,13170],"mapped","da"],[[13171,13171],"mapped","au"],[[13172,13172],"mapped","bar"],[[13173,13173],"mapped","ov"],[[13174,13174],"mapped","pc"],[[13175,13175],"mapped","dm"],[[13176,13176],"mapped","dm2"],[[13177,13177],"mapped","dm3"],[[13178,13178],"mapped","iu"],[[13179,13179],"mapped","平成"],[[13180,13180],"mapped","昭和"],[[13181,13181],"mapped","大正"],[[13182,13182],"mapped","明治"],[[13183,13183],"mapped","株式会社"],[[13184,13184],"mapped","pa"],[[13185,13185],"mapped","na"],[[13186,13186],"mapped","μa"],[[13187,13187],"mapped","ma"],[[13188,13188],"mapped","ka"],[[13189,13189],"mapped","kb"],[[13190,13190],"mapped","mb"],[[13191,13191],"mapped","gb"],[[13192,13192],"mapped","cal"],[[13193,13193],"mapped","kcal"],[[13194,13194],"mapped","pf"],[[13195,13195],"mapped","nf"],[[13196,13196],"mapped","μf"],[[13197,13197],"mapped","μg"],[[13198,13198],"mapped","mg"],[[13199,13199],"mapped","kg"],[[13200,13200],"mapped","hz"],[[13201,13201],"mapped","khz"],[[13202,13202],"mapped","mhz"],[[13203,13203],"mapped","ghz"],[[13204,13204],"mapped","thz"],[[13205,13205],"mapped","μl"],[[13206,13206],"mapped","ml"],[[13207,13207],"mapped","dl"],[[13208,13208],"mapped","kl"],[[13209,13209],"mapped","fm"],[[13210,13210],"mapped","nm"],[[13211,13211],"mapped","μm"],[[13212,13212],"mapped","mm"],[[13213,13213],"mapped","cm"],[[13214,13214],"mapped","km"],[[13215,13215],"mapped","mm2"],[[13216,13216],"mapped","cm2"],[[13217,13217],"mapped","m2"],[[13218,13218],"mapped","km2"],[[13219,13219],"mapped","mm3"],[[13220,13220],"mapped","cm3"],[[13221,13221],"mapped","m3"],[[13222,13222],"mapped","km3"],[[13223,13223],"mapped","m∕s"],[[13224,13224],"mapped","m∕s2"],[[13225,13225],"mapped","pa"],[[13226,13226],"mapped","kpa"],[[13227,13227],"mapped","mpa"],[[13228,13228],"mapped","gpa"],[[13229,13229],"mapped","rad"],[[13230,13230],"mapped","rad∕s"],[[13231,13231],"mapped","rad∕s2"],[[13232,13232],"mapped","ps"],[[13233,13233],"mapped","ns"],[[13234,13234],"mapped","μs"],[[13235,13235],"mapped","ms"],[[13236,13236],"mapped","pv"],[[13237,13237],"mapped","nv"],[[13238,13238],"mapped","μv"],[[13239,13239],"mapped","mv"],[[13240,13240],"mapped","kv"],[[13241,13241],"mapped","mv"],[[13242,13242],"mapped","pw"],[[13243,13243],"mapped","nw"],[[13244,13244],"mapped","μw"],[[13245,13245],"mapped","mw"],[[13246,13246],"mapped","kw"],[[13247,13247],"mapped","mw"],[[13248,13248],"mapped","kω"],[[13249,13249],"mapped","mω"],[[13250,13250],"disallowed"],[[13251,13251],"mapped","bq"],[[13252,13252],"mapped","cc"],[[13253,13253],"mapped","cd"],[[13254,13254],"mapped","c∕kg"],[[13255,13255],"disallowed"],[[13256,13256],"mapped","db"],[[13257,13257],"mapped","gy"],[[13258,13258],"mapped","ha"],[[13259,13259],"mapped","hp"],[[13260,13260],"mapped","in"],[[13261,13261],"mapped","kk"],[[13262,13262],"mapped","km"],[[13263,13263],"mapped","kt"],[[13264,13264],"mapped","lm"],[[13265,13265],"mapped","ln"],[[13266,13266],"mapped","log"],[[13267,13267],"mapped","lx"],[[13268,13268],"mapped","mb"],[[13269,13269],"mapped","mil"],[[13270,13270],"mapped","mol"],[[13271,13271],"mapped","ph"],[[13272,13272],"disallowed"],[[13273,13273],"mapped","ppm"],[[13274,13274],"mapped","pr"],[[13275,13275],"mapped","sr"],[[13276,13276],"mapped","sv"],[[13277,13277],"mapped","wb"],[[13278,13278],"mapped","v∕m"],[[13279,13279],"mapped","a∕m"],[[13280,13280],"mapped","1日"],[[13281,13281],"mapped","2日"],[[13282,13282],"mapped","3日"],[[13283,13283],"mapped","4日"],[[13284,13284],"mapped","5日"],[[13285,13285],"mapped","6日"],[[13286,13286],"mapped","7日"],[[13287,13287],"mapped","8日"],[[13288,13288],"mapped","9日"],[[13289,13289],"mapped","10日"],[[13290,13290],"mapped","11日"],[[13291,13291],"mapped","12日"],[[13292,13292],"mapped","13日"],[[13293,13293],"mapped","14日"],[[13294,13294],"mapped","15日"],[[13295,13295],"mapped","16日"],[[13296,13296],"mapped","17日"],[[13297,13297],"mapped","18日"],[[13298,13298],"mapped","19日"],[[13299,13299],"mapped","20日"],[[13300,13300],"mapped","21日"],[[13301,13301],"mapped","22日"],[[13302,13302],"mapped","23日"],[[13303,13303],"mapped","24日"],[[13304,13304],"mapped","25日"],[[13305,13305],"mapped","26日"],[[13306,13306],"mapped","27日"],[[13307,13307],"mapped","28日"],[[13308,13308],"mapped","29日"],[[13309,13309],"mapped","30日"],[[13310,13310],"mapped","31日"],[[13311,13311],"mapped","gal"],[[13312,19893],"valid"],[[19894,19903],"disallowed"],[[19904,19967],"valid","","NV8"],[[19968,40869],"valid"],[[40870,40891],"valid"],[[40892,40899],"valid"],[[40900,40907],"valid"],[[40908,40908],"valid"],[[40909,40917],"valid"],[[40918,40938],"valid"],[[40939,40959],"disallowed"],[[40960,42124],"valid"],[[42125,42127],"disallowed"],[[42128,42145],"valid","","NV8"],[[42146,42147],"valid","","NV8"],[[42148,42163],"valid","","NV8"],[[42164,42164],"valid","","NV8"],[[42165,42176],"valid","","NV8"],[[42177,42177],"valid","","NV8"],[[42178,42180],"valid","","NV8"],[[42181,42181],"valid","","NV8"],[[42182,42182],"valid","","NV8"],[[42183,42191],"disallowed"],[[42192,42237],"valid"],[[42238,42239],"valid","","NV8"],[[42240,42508],"valid"],[[42509,42511],"valid","","NV8"],[[42512,42539],"valid"],[[42540,42559],"disallowed"],[[42560,42560],"mapped","ꙁ"],[[42561,42561],"valid"],[[42562,42562],"mapped","ꙃ"],[[42563,42563],"valid"],[[42564,42564],"mapped","ꙅ"],[[42565,42565],"valid"],[[42566,42566],"mapped","ꙇ"],[[42567,42567],"valid"],[[42568,42568],"mapped","ꙉ"],[[42569,42569],"valid"],[[42570,42570],"mapped","ꙋ"],[[42571,42571],"valid"],[[42572,42572],"mapped","ꙍ"],[[42573,42573],"valid"],[[42574,42574],"mapped","ꙏ"],[[42575,42575],"valid"],[[42576,42576],"mapped","ꙑ"],[[42577,42577],"valid"],[[42578,42578],"mapped","ꙓ"],[[42579,42579],"valid"],[[42580,42580],"mapped","ꙕ"],[[42581,42581],"valid"],[[42582,42582],"mapped","ꙗ"],[[42583,42583],"valid"],[[42584,42584],"mapped","ꙙ"],[[42585,42585],"valid"],[[42586,42586],"mapped","ꙛ"],[[42587,42587],"valid"],[[42588,42588],"mapped","ꙝ"],[[42589,42589],"valid"],[[42590,42590],"mapped","ꙟ"],[[42591,42591],"valid"],[[42592,42592],"mapped","ꙡ"],[[42593,42593],"valid"],[[42594,42594],"mapped","ꙣ"],[[42595,42595],"valid"],[[42596,42596],"mapped","ꙥ"],[[42597,42597],"valid"],[[42598,42598],"mapped","ꙧ"],[[42599,42599],"valid"],[[42600,42600],"mapped","ꙩ"],[[42601,42601],"valid"],[[42602,42602],"mapped","ꙫ"],[[42603,42603],"valid"],[[42604,42604],"mapped","ꙭ"],[[42605,42607],"valid"],[[42608,42611],"valid","","NV8"],[[42612,42619],"valid"],[[42620,42621],"valid"],[[42622,42622],"valid","","NV8"],[[42623,42623],"valid"],[[42624,42624],"mapped","ꚁ"],[[42625,42625],"valid"],[[42626,42626],"mapped","ꚃ"],[[42627,42627],"valid"],[[42628,42628],"mapped","ꚅ"],[[42629,42629],"valid"],[[42630,42630],"mapped","ꚇ"],[[42631,42631],"valid"],[[42632,42632],"mapped","ꚉ"],[[42633,42633],"valid"],[[42634,42634],"mapped","ꚋ"],[[42635,42635],"valid"],[[42636,42636],"mapped","ꚍ"],[[42637,42637],"valid"],[[42638,42638],"mapped","ꚏ"],[[42639,42639],"valid"],[[42640,42640],"mapped","ꚑ"],[[42641,42641],"valid"],[[42642,42642],"mapped","ꚓ"],[[42643,42643],"valid"],[[42644,42644],"mapped","ꚕ"],[[42645,42645],"valid"],[[42646,42646],"mapped","ꚗ"],[[42647,42647],"valid"],[[42648,42648],"mapped","ꚙ"],[[42649,42649],"valid"],[[42650,42650],"mapped","ꚛ"],[[42651,42651],"valid"],[[42652,42652],"mapped","ъ"],[[42653,42653],"mapped","ь"],[[42654,42654],"valid"],[[42655,42655],"valid"],[[42656,42725],"valid"],[[42726,42735],"valid","","NV8"],[[42736,42737],"valid"],[[42738,42743],"valid","","NV8"],[[42744,42751],"disallowed"],[[42752,42774],"valid","","NV8"],[[42775,42778],"valid"],[[42779,42783],"valid"],[[42784,42785],"valid","","NV8"],[[42786,42786],"mapped","ꜣ"],[[42787,42787],"valid"],[[42788,42788],"mapped","ꜥ"],[[42789,42789],"valid"],[[42790,42790],"mapped","ꜧ"],[[42791,42791],"valid"],[[42792,42792],"mapped","ꜩ"],[[42793,42793],"valid"],[[42794,42794],"mapped","ꜫ"],[[42795,42795],"valid"],[[42796,42796],"mapped","ꜭ"],[[42797,42797],"valid"],[[42798,42798],"mapped","ꜯ"],[[42799,42801],"valid"],[[42802,42802],"mapped","ꜳ"],[[42803,42803],"valid"],[[42804,42804],"mapped","ꜵ"],[[42805,42805],"valid"],[[42806,42806],"mapped","ꜷ"],[[42807,42807],"valid"],[[42808,42808],"mapped","ꜹ"],[[42809,42809],"valid"],[[42810,42810],"mapped","ꜻ"],[[42811,42811],"valid"],[[42812,42812],"mapped","ꜽ"],[[42813,42813],"valid"],[[42814,42814],"mapped","ꜿ"],[[42815,42815],"valid"],[[42816,42816],"mapped","ꝁ"],[[42817,42817],"valid"],[[42818,42818],"mapped","ꝃ"],[[42819,42819],"valid"],[[42820,42820],"mapped","ꝅ"],[[42821,42821],"valid"],[[42822,42822],"mapped","ꝇ"],[[42823,42823],"valid"],[[42824,42824],"mapped","ꝉ"],[[42825,42825],"valid"],[[42826,42826],"mapped","ꝋ"],[[42827,42827],"valid"],[[42828,42828],"mapped","ꝍ"],[[42829,42829],"valid"],[[42830,42830],"mapped","ꝏ"],[[42831,42831],"valid"],[[42832,42832],"mapped","ꝑ"],[[42833,42833],"valid"],[[42834,42834],"mapped","ꝓ"],[[42835,42835],"valid"],[[42836,42836],"mapped","ꝕ"],[[42837,42837],"valid"],[[42838,42838],"mapped","ꝗ"],[[42839,42839],"valid"],[[42840,42840],"mapped","ꝙ"],[[42841,42841],"valid"],[[42842,42842],"mapped","ꝛ"],[[42843,42843],"valid"],[[42844,42844],"mapped","ꝝ"],[[42845,42845],"valid"],[[42846,42846],"mapped","ꝟ"],[[42847,42847],"valid"],[[42848,42848],"mapped","ꝡ"],[[42849,42849],"valid"],[[42850,42850],"mapped","ꝣ"],[[42851,42851],"valid"],[[42852,42852],"mapped","ꝥ"],[[42853,42853],"valid"],[[42854,42854],"mapped","ꝧ"],[[42855,42855],"valid"],[[42856,42856],"mapped","ꝩ"],[[42857,42857],"valid"],[[42858,42858],"mapped","ꝫ"],[[42859,42859],"valid"],[[42860,42860],"mapped","ꝭ"],[[42861,42861],"valid"],[[42862,42862],"mapped","ꝯ"],[[42863,42863],"valid"],[[42864,42864],"mapped","ꝯ"],[[42865,42872],"valid"],[[42873,42873],"mapped","ꝺ"],[[42874,42874],"valid"],[[42875,42875],"mapped","ꝼ"],[[42876,42876],"valid"],[[42877,42877],"mapped","ᵹ"],[[42878,42878],"mapped","ꝿ"],[[42879,42879],"valid"],[[42880,42880],"mapped","ꞁ"],[[42881,42881],"valid"],[[42882,42882],"mapped","ꞃ"],[[42883,42883],"valid"],[[42884,42884],"mapped","ꞅ"],[[42885,42885],"valid"],[[42886,42886],"mapped","ꞇ"],[[42887,42888],"valid"],[[42889,42890],"valid","","NV8"],[[42891,42891],"mapped","ꞌ"],[[42892,42892],"valid"],[[42893,42893],"mapped","ɥ"],[[42894,42894],"valid"],[[42895,42895],"valid"],[[42896,42896],"mapped","ꞑ"],[[42897,42897],"valid"],[[42898,42898],"mapped","ꞓ"],[[42899,42899],"valid"],[[42900,42901],"valid"],[[42902,42902],"mapped","ꞗ"],[[42903,42903],"valid"],[[42904,42904],"mapped","ꞙ"],[[42905,42905],"valid"],[[42906,42906],"mapped","ꞛ"],[[42907,42907],"valid"],[[42908,42908],"mapped","ꞝ"],[[42909,42909],"valid"],[[42910,42910],"mapped","ꞟ"],[[42911,42911],"valid"],[[42912,42912],"mapped","ꞡ"],[[42913,42913],"valid"],[[42914,42914],"mapped","ꞣ"],[[42915,42915],"valid"],[[42916,42916],"mapped","ꞥ"],[[42917,42917],"valid"],[[42918,42918],"mapped","ꞧ"],[[42919,42919],"valid"],[[42920,42920],"mapped","ꞩ"],[[42921,42921],"valid"],[[42922,42922],"mapped","ɦ"],[[42923,42923],"mapped","ɜ"],[[42924,42924],"mapped","ɡ"],[[42925,42925],"mapped","ɬ"],[[42926,42926],"mapped","ɪ"],[[42927,42927],"disallowed"],[[42928,42928],"mapped","ʞ"],[[42929,42929],"mapped","ʇ"],[[42930,42930],"mapped","ʝ"],[[42931,42931],"mapped","ꭓ"],[[42932,42932],"mapped","ꞵ"],[[42933,42933],"valid"],[[42934,42934],"mapped","ꞷ"],[[42935,42935],"valid"],[[42936,42998],"disallowed"],[[42999,42999],"valid"],[[43000,43000],"mapped","ħ"],[[43001,43001],"mapped","œ"],[[43002,43002],"valid"],[[43003,43007],"valid"],[[43008,43047],"valid"],[[43048,43051],"valid","","NV8"],[[43052,43055],"disallowed"],[[43056,43065],"valid","","NV8"],[[43066,43071],"disallowed"],[[43072,43123],"valid"],[[43124,43127],"valid","","NV8"],[[43128,43135],"disallowed"],[[43136,43204],"valid"],[[43205,43205],"valid"],[[43206,43213],"disallowed"],[[43214,43215],"valid","","NV8"],[[43216,43225],"valid"],[[43226,43231],"disallowed"],[[43232,43255],"valid"],[[43256,43258],"valid","","NV8"],[[43259,43259],"valid"],[[43260,43260],"valid","","NV8"],[[43261,43261],"valid"],[[43262,43263],"disallowed"],[[43264,43309],"valid"],[[43310,43311],"valid","","NV8"],[[43312,43347],"valid"],[[43348,43358],"disallowed"],[[43359,43359],"valid","","NV8"],[[43360,43388],"valid","","NV8"],[[43389,43391],"disallowed"],[[43392,43456],"valid"],[[43457,43469],"valid","","NV8"],[[43470,43470],"disallowed"],[[43471,43481],"valid"],[[43482,43485],"disallowed"],[[43486,43487],"valid","","NV8"],[[43488,43518],"valid"],[[43519,43519],"disallowed"],[[43520,43574],"valid"],[[43575,43583],"disallowed"],[[43584,43597],"valid"],[[43598,43599],"disallowed"],[[43600,43609],"valid"],[[43610,43611],"disallowed"],[[43612,43615],"valid","","NV8"],[[43616,43638],"valid"],[[43639,43641],"valid","","NV8"],[[43642,43643],"valid"],[[43644,43647],"valid"],[[43648,43714],"valid"],[[43715,43738],"disallowed"],[[43739,43741],"valid"],[[43742,43743],"valid","","NV8"],[[43744,43759],"valid"],[[43760,43761],"valid","","NV8"],[[43762,43766],"valid"],[[43767,43776],"disallowed"],[[43777,43782],"valid"],[[43783,43784],"disallowed"],[[43785,43790],"valid"],[[43791,43792],"disallowed"],[[43793,43798],"valid"],[[43799,43807],"disallowed"],[[43808,43814],"valid"],[[43815,43815],"disallowed"],[[43816,43822],"valid"],[[43823,43823],"disallowed"],[[43824,43866],"valid"],[[43867,43867],"valid","","NV8"],[[43868,43868],"mapped","ꜧ"],[[43869,43869],"mapped","ꬷ"],[[43870,43870],"mapped","ɫ"],[[43871,43871],"mapped","ꭒ"],[[43872,43875],"valid"],[[43876,43877],"valid"],[[43878,43887],"disallowed"],[[43888,43888],"mapped","Ꭰ"],[[43889,43889],"mapped","Ꭱ"],[[43890,43890],"mapped","Ꭲ"],[[43891,43891],"mapped","Ꭳ"],[[43892,43892],"mapped","Ꭴ"],[[43893,43893],"mapped","Ꭵ"],[[43894,43894],"mapped","Ꭶ"],[[43895,43895],"mapped","Ꭷ"],[[43896,43896],"mapped","Ꭸ"],[[43897,43897],"mapped","Ꭹ"],[[43898,43898],"mapped","Ꭺ"],[[43899,43899],"mapped","Ꭻ"],[[43900,43900],"mapped","Ꭼ"],[[43901,43901],"mapped","Ꭽ"],[[43902,43902],"mapped","Ꭾ"],[[43903,43903],"mapped","Ꭿ"],[[43904,43904],"mapped","Ꮀ"],[[43905,43905],"mapped","Ꮁ"],[[43906,43906],"mapped","Ꮂ"],[[43907,43907],"mapped","Ꮃ"],[[43908,43908],"mapped","Ꮄ"],[[43909,43909],"mapped","Ꮅ"],[[43910,43910],"mapped","Ꮆ"],[[43911,43911],"mapped","Ꮇ"],[[43912,43912],"mapped","Ꮈ"],[[43913,43913],"mapped","Ꮉ"],[[43914,43914],"mapped","Ꮊ"],[[43915,43915],"mapped","Ꮋ"],[[43916,43916],"mapped","Ꮌ"],[[43917,43917],"mapped","Ꮍ"],[[43918,43918],"mapped","Ꮎ"],[[43919,43919],"mapped","Ꮏ"],[[43920,43920],"mapped","Ꮐ"],[[43921,43921],"mapped","Ꮑ"],[[43922,43922],"mapped","Ꮒ"],[[43923,43923],"mapped","Ꮓ"],[[43924,43924],"mapped","Ꮔ"],[[43925,43925],"mapped","Ꮕ"],[[43926,43926],"mapped","Ꮖ"],[[43927,43927],"mapped","Ꮗ"],[[43928,43928],"mapped","Ꮘ"],[[43929,43929],"mapped","Ꮙ"],[[43930,43930],"mapped","Ꮚ"],[[43931,43931],"mapped","Ꮛ"],[[43932,43932],"mapped","Ꮜ"],[[43933,43933],"mapped","Ꮝ"],[[43934,43934],"mapped","Ꮞ"],[[43935,43935],"mapped","Ꮟ"],[[43936,43936],"mapped","Ꮠ"],[[43937,43937],"mapped","Ꮡ"],[[43938,43938],"mapped","Ꮢ"],[[43939,43939],"mapped","Ꮣ"],[[43940,43940],"mapped","Ꮤ"],[[43941,43941],"mapped","Ꮥ"],[[43942,43942],"mapped","Ꮦ"],[[43943,43943],"mapped","Ꮧ"],[[43944,43944],"mapped","Ꮨ"],[[43945,43945],"mapped","Ꮩ"],[[43946,43946],"mapped","Ꮪ"],[[43947,43947],"mapped","Ꮫ"],[[43948,43948],"mapped","Ꮬ"],[[43949,43949],"mapped","Ꮭ"],[[43950,43950],"mapped","Ꮮ"],[[43951,43951],"mapped","Ꮯ"],[[43952,43952],"mapped","Ꮰ"],[[43953,43953],"mapped","Ꮱ"],[[43954,43954],"mapped","Ꮲ"],[[43955,43955],"mapped","Ꮳ"],[[43956,43956],"mapped","Ꮴ"],[[43957,43957],"mapped","Ꮵ"],[[43958,43958],"mapped","Ꮶ"],[[43959,43959],"mapped","Ꮷ"],[[43960,43960],"mapped","Ꮸ"],[[43961,43961],"mapped","Ꮹ"],[[43962,43962],"mapped","Ꮺ"],[[43963,43963],"mapped","Ꮻ"],[[43964,43964],"mapped","Ꮼ"],[[43965,43965],"mapped","Ꮽ"],[[43966,43966],"mapped","Ꮾ"],[[43967,43967],"mapped","Ꮿ"],[[43968,44010],"valid"],[[44011,44011],"valid","","NV8"],[[44012,44013],"valid"],[[44014,44015],"disallowed"],[[44016,44025],"valid"],[[44026,44031],"disallowed"],[[44032,55203],"valid"],[[55204,55215],"disallowed"],[[55216,55238],"valid","","NV8"],[[55239,55242],"disallowed"],[[55243,55291],"valid","","NV8"],[[55292,55295],"disallowed"],[[55296,57343],"disallowed"],[[57344,63743],"disallowed"],[[63744,63744],"mapped","豈"],[[63745,63745],"mapped","更"],[[63746,63746],"mapped","車"],[[63747,63747],"mapped","賈"],[[63748,63748],"mapped","滑"],[[63749,63749],"mapped","串"],[[63750,63750],"mapped","句"],[[63751,63752],"mapped","龜"],[[63753,63753],"mapped","契"],[[63754,63754],"mapped","金"],[[63755,63755],"mapped","喇"],[[63756,63756],"mapped","奈"],[[63757,63757],"mapped","懶"],[[63758,63758],"mapped","癩"],[[63759,63759],"mapped","羅"],[[63760,63760],"mapped","蘿"],[[63761,63761],"mapped","螺"],[[63762,63762],"mapped","裸"],[[63763,63763],"mapped","邏"],[[63764,63764],"mapped","樂"],[[63765,63765],"mapped","洛"],[[63766,63766],"mapped","烙"],[[63767,63767],"mapped","珞"],[[63768,63768],"mapped","落"],[[63769,63769],"mapped","酪"],[[63770,63770],"mapped","駱"],[[63771,63771],"mapped","亂"],[[63772,63772],"mapped","卵"],[[63773,63773],"mapped","欄"],[[63774,63774],"mapped","爛"],[[63775,63775],"mapped","蘭"],[[63776,63776],"mapped","鸞"],[[63777,63777],"mapped","嵐"],[[63778,63778],"mapped","濫"],[[63779,63779],"mapped","藍"],[[63780,63780],"mapped","襤"],[[63781,63781],"mapped","拉"],[[63782,63782],"mapped","臘"],[[63783,63783],"mapped","蠟"],[[63784,63784],"mapped","廊"],[[63785,63785],"mapped","朗"],[[63786,63786],"mapped","浪"],[[63787,63787],"mapped","狼"],[[63788,63788],"mapped","郎"],[[63789,63789],"mapped","來"],[[63790,63790],"mapped","冷"],[[63791,63791],"mapped","勞"],[[63792,63792],"mapped","擄"],[[63793,63793],"mapped","櫓"],[[63794,63794],"mapped","爐"],[[63795,63795],"mapped","盧"],[[63796,63796],"mapped","老"],[[63797,63797],"mapped","蘆"],[[63798,63798],"mapped","虜"],[[63799,63799],"mapped","路"],[[63800,63800],"mapped","露"],[[63801,63801],"mapped","魯"],[[63802,63802],"mapped","鷺"],[[63803,63803],"mapped","碌"],[[63804,63804],"mapped","祿"],[[63805,63805],"mapped","綠"],[[63806,63806],"mapped","菉"],[[63807,63807],"mapped","錄"],[[63808,63808],"mapped","鹿"],[[63809,63809],"mapped","論"],[[63810,63810],"mapped","壟"],[[63811,63811],"mapped","弄"],[[63812,63812],"mapped","籠"],[[63813,63813],"mapped","聾"],[[63814,63814],"mapped","牢"],[[63815,63815],"mapped","磊"],[[63816,63816],"mapped","賂"],[[63817,63817],"mapped","雷"],[[63818,63818],"mapped","壘"],[[63819,63819],"mapped","屢"],[[63820,63820],"mapped","樓"],[[63821,63821],"mapped","淚"],[[63822,63822],"mapped","漏"],[[63823,63823],"mapped","累"],[[63824,63824],"mapped","縷"],[[63825,63825],"mapped","陋"],[[63826,63826],"mapped","勒"],[[63827,63827],"mapped","肋"],[[63828,63828],"mapped","凜"],[[63829,63829],"mapped","凌"],[[63830,63830],"mapped","稜"],[[63831,63831],"mapped","綾"],[[63832,63832],"mapped","菱"],[[63833,63833],"mapped","陵"],[[63834,63834],"mapped","讀"],[[63835,63835],"mapped","拏"],[[63836,63836],"mapped","樂"],[[63837,63837],"mapped","諾"],[[63838,63838],"mapped","丹"],[[63839,63839],"mapped","寧"],[[63840,63840],"mapped","怒"],[[63841,63841],"mapped","率"],[[63842,63842],"mapped","異"],[[63843,63843],"mapped","北"],[[63844,63844],"mapped","磻"],[[63845,63845],"mapped","便"],[[63846,63846],"mapped","復"],[[63847,63847],"mapped","不"],[[63848,63848],"mapped","泌"],[[63849,63849],"mapped","數"],[[63850,63850],"mapped","索"],[[63851,63851],"mapped","參"],[[63852,63852],"mapped","塞"],[[63853,63853],"mapped","省"],[[63854,63854],"mapped","葉"],[[63855,63855],"mapped","說"],[[63856,63856],"mapped","殺"],[[63857,63857],"mapped","辰"],[[63858,63858],"mapped","沈"],[[63859,63859],"mapped","拾"],[[63860,63860],"mapped","若"],[[63861,63861],"mapped","掠"],[[63862,63862],"mapped","略"],[[63863,63863],"mapped","亮"],[[63864,63864],"mapped","兩"],[[63865,63865],"mapped","凉"],[[63866,63866],"mapped","梁"],[[63867,63867],"mapped","糧"],[[63868,63868],"mapped","良"],[[63869,63869],"mapped","諒"],[[63870,63870],"mapped","量"],[[63871,63871],"mapped","勵"],[[63872,63872],"mapped","呂"],[[63873,63873],"mapped","女"],[[63874,63874],"mapped","廬"],[[63875,63875],"mapped","旅"],[[63876,63876],"mapped","濾"],[[63877,63877],"mapped","礪"],[[63878,63878],"mapped","閭"],[[63879,63879],"mapped","驪"],[[63880,63880],"mapped","麗"],[[63881,63881],"mapped","黎"],[[63882,63882],"mapped","力"],[[63883,63883],"mapped","曆"],[[63884,63884],"mapped","歷"],[[63885,63885],"mapped","轢"],[[63886,63886],"mapped","年"],[[63887,63887],"mapped","憐"],[[63888,63888],"mapped","戀"],[[63889,63889],"mapped","撚"],[[63890,63890],"mapped","漣"],[[63891,63891],"mapped","煉"],[[63892,63892],"mapped","璉"],[[63893,63893],"mapped","秊"],[[63894,63894],"mapped","練"],[[63895,63895],"mapped","聯"],[[63896,63896],"mapped","輦"],[[63897,63897],"mapped","蓮"],[[63898,63898],"mapped","連"],[[63899,63899],"mapped","鍊"],[[63900,63900],"mapped","列"],[[63901,63901],"mapped","劣"],[[63902,63902],"mapped","咽"],[[63903,63903],"mapped","烈"],[[63904,63904],"mapped","裂"],[[63905,63905],"mapped","說"],[[63906,63906],"mapped","廉"],[[63907,63907],"mapped","念"],[[63908,63908],"mapped","捻"],[[63909,63909],"mapped","殮"],[[63910,63910],"mapped","簾"],[[63911,63911],"mapped","獵"],[[63912,63912],"mapped","令"],[[63913,63913],"mapped","囹"],[[63914,63914],"mapped","寧"],[[63915,63915],"mapped","嶺"],[[63916,63916],"mapped","怜"],[[63917,63917],"mapped","玲"],[[63918,63918],"mapped","瑩"],[[63919,63919],"mapped","羚"],[[63920,63920],"mapped","聆"],[[63921,63921],"mapped","鈴"],[[63922,63922],"mapped","零"],[[63923,63923],"mapped","靈"],[[63924,63924],"mapped","領"],[[63925,63925],"mapped","例"],[[63926,63926],"mapped","禮"],[[63927,63927],"mapped","醴"],[[63928,63928],"mapped","隸"],[[63929,63929],"mapped","惡"],[[63930,63930],"mapped","了"],[[63931,63931],"mapped","僚"],[[63932,63932],"mapped","寮"],[[63933,63933],"mapped","尿"],[[63934,63934],"mapped","料"],[[63935,63935],"mapped","樂"],[[63936,63936],"mapped","燎"],[[63937,63937],"mapped","療"],[[63938,63938],"mapped","蓼"],[[63939,63939],"mapped","遼"],[[63940,63940],"mapped","龍"],[[63941,63941],"mapped","暈"],[[63942,63942],"mapped","阮"],[[63943,63943],"mapped","劉"],[[63944,63944],"mapped","杻"],[[63945,63945],"mapped","柳"],[[63946,63946],"mapped","流"],[[63947,63947],"mapped","溜"],[[63948,63948],"mapped","琉"],[[63949,63949],"mapped","留"],[[63950,63950],"mapped","硫"],[[63951,63951],"mapped","紐"],[[63952,63952],"mapped","類"],[[63953,63953],"mapped","六"],[[63954,63954],"mapped","戮"],[[63955,63955],"mapped","陸"],[[63956,63956],"mapped","倫"],[[63957,63957],"mapped","崙"],[[63958,63958],"mapped","淪"],[[63959,63959],"mapped","輪"],[[63960,63960],"mapped","律"],[[63961,63961],"mapped","慄"],[[63962,63962],"mapped","栗"],[[63963,63963],"mapped","率"],[[63964,63964],"mapped","隆"],[[63965,63965],"mapped","利"],[[63966,63966],"mapped","吏"],[[63967,63967],"mapped","履"],[[63968,63968],"mapped","易"],[[63969,63969],"mapped","李"],[[63970,63970],"mapped","梨"],[[63971,63971],"mapped","泥"],[[63972,63972],"mapped","理"],[[63973,63973],"mapped","痢"],[[63974,63974],"mapped","罹"],[[63975,63975],"mapped","裏"],[[63976,63976],"mapped","裡"],[[63977,63977],"mapped","里"],[[63978,63978],"mapped","離"],[[63979,63979],"mapped","匿"],[[63980,63980],"mapped","溺"],[[63981,63981],"mapped","吝"],[[63982,63982],"mapped","燐"],[[63983,63983],"mapped","璘"],[[63984,63984],"mapped","藺"],[[63985,63985],"mapped","隣"],[[63986,63986],"mapped","鱗"],[[63987,63987],"mapped","麟"],[[63988,63988],"mapped","林"],[[63989,63989],"mapped","淋"],[[63990,63990],"mapped","臨"],[[63991,63991],"mapped","立"],[[63992,63992],"mapped","笠"],[[63993,63993],"mapped","粒"],[[63994,63994],"mapped","狀"],[[63995,63995],"mapped","炙"],[[63996,63996],"mapped","識"],[[63997,63997],"mapped","什"],[[63998,63998],"mapped","茶"],[[63999,63999],"mapped","刺"],[[64000,64000],"mapped","切"],[[64001,64001],"mapped","度"],[[64002,64002],"mapped","拓"],[[64003,64003],"mapped","糖"],[[64004,64004],"mapped","宅"],[[64005,64005],"mapped","洞"],[[64006,64006],"mapped","暴"],[[64007,64007],"mapped","輻"],[[64008,64008],"mapped","行"],[[64009,64009],"mapped","降"],[[64010,64010],"mapped","見"],[[64011,64011],"mapped","廓"],[[64012,64012],"mapped","兀"],[[64013,64013],"mapped","嗀"],[[64014,64015],"valid"],[[64016,64016],"mapped","塚"],[[64017,64017],"valid"],[[64018,64018],"mapped","晴"],[[64019,64020],"valid"],[[64021,64021],"mapped","凞"],[[64022,64022],"mapped","猪"],[[64023,64023],"mapped","益"],[[64024,64024],"mapped","礼"],[[64025,64025],"mapped","神"],[[64026,64026],"mapped","祥"],[[64027,64027],"mapped","福"],[[64028,64028],"mapped","靖"],[[64029,64029],"mapped","精"],[[64030,64030],"mapped","羽"],[[64031,64031],"valid"],[[64032,64032],"mapped","蘒"],[[64033,64033],"valid"],[[64034,64034],"mapped","諸"],[[64035,64036],"valid"],[[64037,64037],"mapped","逸"],[[64038,64038],"mapped","都"],[[64039,64041],"valid"],[[64042,64042],"mapped","飯"],[[64043,64043],"mapped","飼"],[[64044,64044],"mapped","館"],[[64045,64045],"mapped","鶴"],[[64046,64046],"mapped","郞"],[[64047,64047],"mapped","隷"],[[64048,64048],"mapped","侮"],[[64049,64049],"mapped","僧"],[[64050,64050],"mapped","免"],[[64051,64051],"mapped","勉"],[[64052,64052],"mapped","勤"],[[64053,64053],"mapped","卑"],[[64054,64054],"mapped","喝"],[[64055,64055],"mapped","嘆"],[[64056,64056],"mapped","器"],[[64057,64057],"mapped","塀"],[[64058,64058],"mapped","墨"],[[64059,64059],"mapped","層"],[[64060,64060],"mapped","屮"],[[64061,64061],"mapped","悔"],[[64062,64062],"mapped","慨"],[[64063,64063],"mapped","憎"],[[64064,64064],"mapped","懲"],[[64065,64065],"mapped","敏"],[[64066,64066],"mapped","既"],[[64067,64067],"mapped","暑"],[[64068,64068],"mapped","梅"],[[64069,64069],"mapped","海"],[[64070,64070],"mapped","渚"],[[64071,64071],"mapped","漢"],[[64072,64072],"mapped","煮"],[[64073,64073],"mapped","爫"],[[64074,64074],"mapped","琢"],[[64075,64075],"mapped","碑"],[[64076,64076],"mapped","社"],[[64077,64077],"mapped","祉"],[[64078,64078],"mapped","祈"],[[64079,64079],"mapped","祐"],[[64080,64080],"mapped","祖"],[[64081,64081],"mapped","祝"],[[64082,64082],"mapped","禍"],[[64083,64083],"mapped","禎"],[[64084,64084],"mapped","穀"],[[64085,64085],"mapped","突"],[[64086,64086],"mapped","節"],[[64087,64087],"mapped","練"],[[64088,64088],"mapped","縉"],[[64089,64089],"mapped","繁"],[[64090,64090],"mapped","署"],[[64091,64091],"mapped","者"],[[64092,64092],"mapped","臭"],[[64093,64094],"mapped","艹"],[[64095,64095],"mapped","著"],[[64096,64096],"mapped","褐"],[[64097,64097],"mapped","視"],[[64098,64098],"mapped","謁"],[[64099,64099],"mapped","謹"],[[64100,64100],"mapped","賓"],[[64101,64101],"mapped","贈"],[[64102,64102],"mapped","辶"],[[64103,64103],"mapped","逸"],[[64104,64104],"mapped","難"],[[64105,64105],"mapped","響"],[[64106,64106],"mapped","頻"],[[64107,64107],"mapped","恵"],[[64108,64108],"mapped","𤋮"],[[64109,64109],"mapped","舘"],[[64110,64111],"disallowed"],[[64112,64112],"mapped","並"],[[64113,64113],"mapped","况"],[[64114,64114],"mapped","全"],[[64115,64115],"mapped","侀"],[[64116,64116],"mapped","充"],[[64117,64117],"mapped","冀"],[[64118,64118],"mapped","勇"],[[64119,64119],"mapped","勺"],[[64120,64120],"mapped","喝"],[[64121,64121],"mapped","啕"],[[64122,64122],"mapped","喙"],[[64123,64123],"mapped","嗢"],[[64124,64124],"mapped","塚"],[[64125,64125],"mapped","墳"],[[64126,64126],"mapped","奄"],[[64127,64127],"mapped","奔"],[[64128,64128],"mapped","婢"],[[64129,64129],"mapped","嬨"],[[64130,64130],"mapped","廒"],[[64131,64131],"mapped","廙"],[[64132,64132],"mapped","彩"],[[64133,64133],"mapped","徭"],[[64134,64134],"mapped","惘"],[[64135,64135],"mapped","慎"],[[64136,64136],"mapped","愈"],[[64137,64137],"mapped","憎"],[[64138,64138],"mapped","慠"],[[64139,64139],"mapped","懲"],[[64140,64140],"mapped","戴"],[[64141,64141],"mapped","揄"],[[64142,64142],"mapped","搜"],[[64143,64143],"mapped","摒"],[[64144,64144],"mapped","敖"],[[64145,64145],"mapped","晴"],[[64146,64146],"mapped","朗"],[[64147,64147],"mapped","望"],[[64148,64148],"mapped","杖"],[[64149,64149],"mapped","歹"],[[64150,64150],"mapped","殺"],[[64151,64151],"mapped","流"],[[64152,64152],"mapped","滛"],[[64153,64153],"mapped","滋"],[[64154,64154],"mapped","漢"],[[64155,64155],"mapped","瀞"],[[64156,64156],"mapped","煮"],[[64157,64157],"mapped","瞧"],[[64158,64158],"mapped","爵"],[[64159,64159],"mapped","犯"],[[64160,64160],"mapped","猪"],[[64161,64161],"mapped","瑱"],[[64162,64162],"mapped","甆"],[[64163,64163],"mapped","画"],[[64164,64164],"mapped","瘝"],[[64165,64165],"mapped","瘟"],[[64166,64166],"mapped","益"],[[64167,64167],"mapped","盛"],[[64168,64168],"mapped","直"],[[64169,64169],"mapped","睊"],[[64170,64170],"mapped","着"],[[64171,64171],"mapped","磌"],[[64172,64172],"mapped","窱"],[[64173,64173],"mapped","節"],[[64174,64174],"mapped","类"],[[64175,64175],"mapped","絛"],[[64176,64176],"mapped","練"],[[64177,64177],"mapped","缾"],[[64178,64178],"mapped","者"],[[64179,64179],"mapped","荒"],[[64180,64180],"mapped","華"],[[64181,64181],"mapped","蝹"],[[64182,64182],"mapped","襁"],[[64183,64183],"mapped","覆"],[[64184,64184],"mapped","視"],[[64185,64185],"mapped","調"],[[64186,64186],"mapped","諸"],[[64187,64187],"mapped","請"],[[64188,64188],"mapped","謁"],[[64189,64189],"mapped","諾"],[[64190,64190],"mapped","諭"],[[64191,64191],"mapped","謹"],[[64192,64192],"mapped","變"],[[64193,64193],"mapped","贈"],[[64194,64194],"mapped","輸"],[[64195,64195],"mapped","遲"],[[64196,64196],"mapped","醙"],[[64197,64197],"mapped","鉶"],[[64198,64198],"mapped","陼"],[[64199,64199],"mapped","難"],[[64200,64200],"mapped","靖"],[[64201,64201],"mapped","韛"],[[64202,64202],"mapped","響"],[[64203,64203],"mapped","頋"],[[64204,64204],"mapped","頻"],[[64205,64205],"mapped","鬒"],[[64206,64206],"mapped","龜"],[[64207,64207],"mapped","𢡊"],[[64208,64208],"mapped","𢡄"],[[64209,64209],"mapped","𣏕"],[[64210,64210],"mapped","㮝"],[[64211,64211],"mapped","䀘"],[[64212,64212],"mapped","䀹"],[[64213,64213],"mapped","𥉉"],[[64214,64214],"mapped","𥳐"],[[64215,64215],"mapped","𧻓"],[[64216,64216],"mapped","齃"],[[64217,64217],"mapped","龎"],[[64218,64255],"disallowed"],[[64256,64256],"mapped","ff"],[[64257,64257],"mapped","fi"],[[64258,64258],"mapped","fl"],[[64259,64259],"mapped","ffi"],[[64260,64260],"mapped","ffl"],[[64261,64262],"mapped","st"],[[64263,64274],"disallowed"],[[64275,64275],"mapped","մն"],[[64276,64276],"mapped","մե"],[[64277,64277],"mapped","մի"],[[64278,64278],"mapped","վն"],[[64279,64279],"mapped","մխ"],[[64280,64284],"disallowed"],[[64285,64285],"mapped","יִ"],[[64286,64286],"valid"],[[64287,64287],"mapped","ײַ"],[[64288,64288],"mapped","ע"],[[64289,64289],"mapped","א"],[[64290,64290],"mapped","ד"],[[64291,64291],"mapped","ה"],[[64292,64292],"mapped","כ"],[[64293,64293],"mapped","ל"],[[64294,64294],"mapped","ם"],[[64295,64295],"mapped","ר"],[[64296,64296],"mapped","ת"],[[64297,64297],"disallowed_STD3_mapped","+"],[[64298,64298],"mapped","שׁ"],[[64299,64299],"mapped","שׂ"],[[64300,64300],"mapped","שּׁ"],[[64301,64301],"mapped","שּׂ"],[[64302,64302],"mapped","אַ"],[[64303,64303],"mapped","אָ"],[[64304,64304],"mapped","אּ"],[[64305,64305],"mapped","בּ"],[[64306,64306],"mapped","גּ"],[[64307,64307],"mapped","דּ"],[[64308,64308],"mapped","הּ"],[[64309,64309],"mapped","וּ"],[[64310,64310],"mapped","זּ"],[[64311,64311],"disallowed"],[[64312,64312],"mapped","טּ"],[[64313,64313],"mapped","יּ"],[[64314,64314],"mapped","ךּ"],[[64315,64315],"mapped","כּ"],[[64316,64316],"mapped","לּ"],[[64317,64317],"disallowed"],[[64318,64318],"mapped","מּ"],[[64319,64319],"disallowed"],[[64320,64320],"mapped","נּ"],[[64321,64321],"mapped","סּ"],[[64322,64322],"disallowed"],[[64323,64323],"mapped","ףּ"],[[64324,64324],"mapped","פּ"],[[64325,64325],"disallowed"],[[64326,64326],"mapped","צּ"],[[64327,64327],"mapped","קּ"],[[64328,64328],"mapped","רּ"],[[64329,64329],"mapped","שּ"],[[64330,64330],"mapped","תּ"],[[64331,64331],"mapped","וֹ"],[[64332,64332],"mapped","בֿ"],[[64333,64333],"mapped","כֿ"],[[64334,64334],"mapped","פֿ"],[[64335,64335],"mapped","אל"],[[64336,64337],"mapped","ٱ"],[[64338,64341],"mapped","ٻ"],[[64342,64345],"mapped","پ"],[[64346,64349],"mapped","ڀ"],[[64350,64353],"mapped","ٺ"],[[64354,64357],"mapped","ٿ"],[[64358,64361],"mapped","ٹ"],[[64362,64365],"mapped","ڤ"],[[64366,64369],"mapped","ڦ"],[[64370,64373],"mapped","ڄ"],[[64374,64377],"mapped","ڃ"],[[64378,64381],"mapped","چ"],[[64382,64385],"mapped","ڇ"],[[64386,64387],"mapped","ڍ"],[[64388,64389],"mapped","ڌ"],[[64390,64391],"mapped","ڎ"],[[64392,64393],"mapped","ڈ"],[[64394,64395],"mapped","ژ"],[[64396,64397],"mapped","ڑ"],[[64398,64401],"mapped","ک"],[[64402,64405],"mapped","گ"],[[64406,64409],"mapped","ڳ"],[[64410,64413],"mapped","ڱ"],[[64414,64415],"mapped","ں"],[[64416,64419],"mapped","ڻ"],[[64420,64421],"mapped","ۀ"],[[64422,64425],"mapped","ہ"],[[64426,64429],"mapped","ھ"],[[64430,64431],"mapped","ے"],[[64432,64433],"mapped","ۓ"],[[64434,64449],"valid","","NV8"],[[64450,64466],"disallowed"],[[64467,64470],"mapped","ڭ"],[[64471,64472],"mapped","ۇ"],[[64473,64474],"mapped","ۆ"],[[64475,64476],"mapped","ۈ"],[[64477,64477],"mapped","ۇٴ"],[[64478,64479],"mapped","ۋ"],[[64480,64481],"mapped","ۅ"],[[64482,64483],"mapped","ۉ"],[[64484,64487],"mapped","ې"],[[64488,64489],"mapped","ى"],[[64490,64491],"mapped","ئا"],[[64492,64493],"mapped","ئە"],[[64494,64495],"mapped","ئو"],[[64496,64497],"mapped","ئۇ"],[[64498,64499],"mapped","ئۆ"],[[64500,64501],"mapped","ئۈ"],[[64502,64504],"mapped","ئې"],[[64505,64507],"mapped","ئى"],[[64508,64511],"mapped","ی"],[[64512,64512],"mapped","ئج"],[[64513,64513],"mapped","ئح"],[[64514,64514],"mapped","ئم"],[[64515,64515],"mapped","ئى"],[[64516,64516],"mapped","ئي"],[[64517,64517],"mapped","بج"],[[64518,64518],"mapped","بح"],[[64519,64519],"mapped","بخ"],[[64520,64520],"mapped","بم"],[[64521,64521],"mapped","بى"],[[64522,64522],"mapped","بي"],[[64523,64523],"mapped","تج"],[[64524,64524],"mapped","تح"],[[64525,64525],"mapped","تخ"],[[64526,64526],"mapped","تم"],[[64527,64527],"mapped","تى"],[[64528,64528],"mapped","تي"],[[64529,64529],"mapped","ثج"],[[64530,64530],"mapped","ثم"],[[64531,64531],"mapped","ثى"],[[64532,64532],"mapped","ثي"],[[64533,64533],"mapped","جح"],[[64534,64534],"mapped","جم"],[[64535,64535],"mapped","حج"],[[64536,64536],"mapped","حم"],[[64537,64537],"mapped","خج"],[[64538,64538],"mapped","خح"],[[64539,64539],"mapped","خم"],[[64540,64540],"mapped","سج"],[[64541,64541],"mapped","سح"],[[64542,64542],"mapped","سخ"],[[64543,64543],"mapped","سم"],[[64544,64544],"mapped","صح"],[[64545,64545],"mapped","صم"],[[64546,64546],"mapped","ضج"],[[64547,64547],"mapped","ضح"],[[64548,64548],"mapped","ضخ"],[[64549,64549],"mapped","ضم"],[[64550,64550],"mapped","طح"],[[64551,64551],"mapped","طم"],[[64552,64552],"mapped","ظم"],[[64553,64553],"mapped","عج"],[[64554,64554],"mapped","عم"],[[64555,64555],"mapped","غج"],[[64556,64556],"mapped","غم"],[[64557,64557],"mapped","فج"],[[64558,64558],"mapped","فح"],[[64559,64559],"mapped","فخ"],[[64560,64560],"mapped","فم"],[[64561,64561],"mapped","فى"],[[64562,64562],"mapped","في"],[[64563,64563],"mapped","قح"],[[64564,64564],"mapped","قم"],[[64565,64565],"mapped","قى"],[[64566,64566],"mapped","قي"],[[64567,64567],"mapped","كا"],[[64568,64568],"mapped","كج"],[[64569,64569],"mapped","كح"],[[64570,64570],"mapped","كخ"],[[64571,64571],"mapped","كل"],[[64572,64572],"mapped","كم"],[[64573,64573],"mapped","كى"],[[64574,64574],"mapped","كي"],[[64575,64575],"mapped","لج"],[[64576,64576],"mapped","لح"],[[64577,64577],"mapped","لخ"],[[64578,64578],"mapped","لم"],[[64579,64579],"mapped","لى"],[[64580,64580],"mapped","لي"],[[64581,64581],"mapped","مج"],[[64582,64582],"mapped","مح"],[[64583,64583],"mapped","مخ"],[[64584,64584],"mapped","مم"],[[64585,64585],"mapped","مى"],[[64586,64586],"mapped","مي"],[[64587,64587],"mapped","نج"],[[64588,64588],"mapped","نح"],[[64589,64589],"mapped","نخ"],[[64590,64590],"mapped","نم"],[[64591,64591],"mapped","نى"],[[64592,64592],"mapped","ني"],[[64593,64593],"mapped","هج"],[[64594,64594],"mapped","هم"],[[64595,64595],"mapped","هى"],[[64596,64596],"mapped","هي"],[[64597,64597],"mapped","يج"],[[64598,64598],"mapped","يح"],[[64599,64599],"mapped","يخ"],[[64600,64600],"mapped","يم"],[[64601,64601],"mapped","يى"],[[64602,64602],"mapped","يي"],[[64603,64603],"mapped","ذٰ"],[[64604,64604],"mapped","رٰ"],[[64605,64605],"mapped","ىٰ"],[[64606,64606],"disallowed_STD3_mapped"," ٌّ"],[[64607,64607],"disallowed_STD3_mapped"," ٍّ"],[[64608,64608],"disallowed_STD3_mapped"," َّ"],[[64609,64609],"disallowed_STD3_mapped"," ُّ"],[[64610,64610],"disallowed_STD3_mapped"," ِّ"],[[64611,64611],"disallowed_STD3_mapped"," ّٰ"],[[64612,64612],"mapped","ئر"],[[64613,64613],"mapped","ئز"],[[64614,64614],"mapped","ئم"],[[64615,64615],"mapped","ئن"],[[64616,64616],"mapped","ئى"],[[64617,64617],"mapped","ئي"],[[64618,64618],"mapped","بر"],[[64619,64619],"mapped","بز"],[[64620,64620],"mapped","بم"],[[64621,64621],"mapped","بن"],[[64622,64622],"mapped","بى"],[[64623,64623],"mapped","بي"],[[64624,64624],"mapped","تر"],[[64625,64625],"mapped","تز"],[[64626,64626],"mapped","تم"],[[64627,64627],"mapped","تن"],[[64628,64628],"mapped","تى"],[[64629,64629],"mapped","تي"],[[64630,64630],"mapped","ثر"],[[64631,64631],"mapped","ثز"],[[64632,64632],"mapped","ثم"],[[64633,64633],"mapped","ثن"],[[64634,64634],"mapped","ثى"],[[64635,64635],"mapped","ثي"],[[64636,64636],"mapped","فى"],[[64637,64637],"mapped","في"],[[64638,64638],"mapped","قى"],[[64639,64639],"mapped","قي"],[[64640,64640],"mapped","كا"],[[64641,64641],"mapped","كل"],[[64642,64642],"mapped","كم"],[[64643,64643],"mapped","كى"],[[64644,64644],"mapped","كي"],[[64645,64645],"mapped","لم"],[[64646,64646],"mapped","لى"],[[64647,64647],"mapped","لي"],[[64648,64648],"mapped","ما"],[[64649,64649],"mapped","مم"],[[64650,64650],"mapped","نر"],[[64651,64651],"mapped","نز"],[[64652,64652],"mapped","نم"],[[64653,64653],"mapped","نن"],[[64654,64654],"mapped","نى"],[[64655,64655],"mapped","ني"],[[64656,64656],"mapped","ىٰ"],[[64657,64657],"mapped","ير"],[[64658,64658],"mapped","يز"],[[64659,64659],"mapped","يم"],[[64660,64660],"mapped","ين"],[[64661,64661],"mapped","يى"],[[64662,64662],"mapped","يي"],[[64663,64663],"mapped","ئج"],[[64664,64664],"mapped","ئح"],[[64665,64665],"mapped","ئخ"],[[64666,64666],"mapped","ئم"],[[64667,64667],"mapped","ئه"],[[64668,64668],"mapped","بج"],[[64669,64669],"mapped","بح"],[[64670,64670],"mapped","بخ"],[[64671,64671],"mapped","بم"],[[64672,64672],"mapped","به"],[[64673,64673],"mapped","تج"],[[64674,64674],"mapped","تح"],[[64675,64675],"mapped","تخ"],[[64676,64676],"mapped","تم"],[[64677,64677],"mapped","ته"],[[64678,64678],"mapped","ثم"],[[64679,64679],"mapped","جح"],[[64680,64680],"mapped","جم"],[[64681,64681],"mapped","حج"],[[64682,64682],"mapped","حم"],[[64683,64683],"mapped","خج"],[[64684,64684],"mapped","خم"],[[64685,64685],"mapped","سج"],[[64686,64686],"mapped","سح"],[[64687,64687],"mapped","سخ"],[[64688,64688],"mapped","سم"],[[64689,64689],"mapped","صح"],[[64690,64690],"mapped","صخ"],[[64691,64691],"mapped","صم"],[[64692,64692],"mapped","ضج"],[[64693,64693],"mapped","ضح"],[[64694,64694],"mapped","ضخ"],[[64695,64695],"mapped","ضم"],[[64696,64696],"mapped","طح"],[[64697,64697],"mapped","ظم"],[[64698,64698],"mapped","عج"],[[64699,64699],"mapped","عم"],[[64700,64700],"mapped","غج"],[[64701,64701],"mapped","غم"],[[64702,64702],"mapped","فج"],[[64703,64703],"mapped","فح"],[[64704,64704],"mapped","فخ"],[[64705,64705],"mapped","فم"],[[64706,64706],"mapped","قح"],[[64707,64707],"mapped","قم"],[[64708,64708],"mapped","كج"],[[64709,64709],"mapped","كح"],[[64710,64710],"mapped","كخ"],[[64711,64711],"mapped","كل"],[[64712,64712],"mapped","كم"],[[64713,64713],"mapped","لج"],[[64714,64714],"mapped","لح"],[[64715,64715],"mapped","لخ"],[[64716,64716],"mapped","لم"],[[64717,64717],"mapped","له"],[[64718,64718],"mapped","مج"],[[64719,64719],"mapped","مح"],[[64720,64720],"mapped","مخ"],[[64721,64721],"mapped","مم"],[[64722,64722],"mapped","نج"],[[64723,64723],"mapped","نح"],[[64724,64724],"mapped","نخ"],[[64725,64725],"mapped","نم"],[[64726,64726],"mapped","نه"],[[64727,64727],"mapped","هج"],[[64728,64728],"mapped","هم"],[[64729,64729],"mapped","هٰ"],[[64730,64730],"mapped","يج"],[[64731,64731],"mapped","يح"],[[64732,64732],"mapped","يخ"],[[64733,64733],"mapped","يم"],[[64734,64734],"mapped","يه"],[[64735,64735],"mapped","ئم"],[[64736,64736],"mapped","ئه"],[[64737,64737],"mapped","بم"],[[64738,64738],"mapped","به"],[[64739,64739],"mapped","تم"],[[64740,64740],"mapped","ته"],[[64741,64741],"mapped","ثم"],[[64742,64742],"mapped","ثه"],[[64743,64743],"mapped","سم"],[[64744,64744],"mapped","سه"],[[64745,64745],"mapped","شم"],[[64746,64746],"mapped","شه"],[[64747,64747],"mapped","كل"],[[64748,64748],"mapped","كم"],[[64749,64749],"mapped","لم"],[[64750,64750],"mapped","نم"],[[64751,64751],"mapped","نه"],[[64752,64752],"mapped","يم"],[[64753,64753],"mapped","يه"],[[64754,64754],"mapped","ـَّ"],[[64755,64755],"mapped","ـُّ"],[[64756,64756],"mapped","ـِّ"],[[64757,64757],"mapped","طى"],[[64758,64758],"mapped","طي"],[[64759,64759],"mapped","عى"],[[64760,64760],"mapped","عي"],[[64761,64761],"mapped","غى"],[[64762,64762],"mapped","غي"],[[64763,64763],"mapped","سى"],[[64764,64764],"mapped","سي"],[[64765,64765],"mapped","شى"],[[64766,64766],"mapped","شي"],[[64767,64767],"mapped","حى"],[[64768,64768],"mapped","حي"],[[64769,64769],"mapped","جى"],[[64770,64770],"mapped","جي"],[[64771,64771],"mapped","خى"],[[64772,64772],"mapped","خي"],[[64773,64773],"mapped","صى"],[[64774,64774],"mapped","صي"],[[64775,64775],"mapped","ضى"],[[64776,64776],"mapped","ضي"],[[64777,64777],"mapped","شج"],[[64778,64778],"mapped","شح"],[[64779,64779],"mapped","شخ"],[[64780,64780],"mapped","شم"],[[64781,64781],"mapped","شر"],[[64782,64782],"mapped","سر"],[[64783,64783],"mapped","صر"],[[64784,64784],"mapped","ضر"],[[64785,64785],"mapped","طى"],[[64786,64786],"mapped","طي"],[[64787,64787],"mapped","عى"],[[64788,64788],"mapped","عي"],[[64789,64789],"mapped","غى"],[[64790,64790],"mapped","غي"],[[64791,64791],"mapped","سى"],[[64792,64792],"mapped","سي"],[[64793,64793],"mapped","شى"],[[64794,64794],"mapped","شي"],[[64795,64795],"mapped","حى"],[[64796,64796],"mapped","حي"],[[64797,64797],"mapped","جى"],[[64798,64798],"mapped","جي"],[[64799,64799],"mapped","خى"],[[64800,64800],"mapped","خي"],[[64801,64801],"mapped","صى"],[[64802,64802],"mapped","صي"],[[64803,64803],"mapped","ضى"],[[64804,64804],"mapped","ضي"],[[64805,64805],"mapped","شج"],[[64806,64806],"mapped","شح"],[[64807,64807],"mapped","شخ"],[[64808,64808],"mapped","شم"],[[64809,64809],"mapped","شر"],[[64810,64810],"mapped","سر"],[[64811,64811],"mapped","صر"],[[64812,64812],"mapped","ضر"],[[64813,64813],"mapped","شج"],[[64814,64814],"mapped","شح"],[[64815,64815],"mapped","شخ"],[[64816,64816],"mapped","شم"],[[64817,64817],"mapped","سه"],[[64818,64818],"mapped","شه"],[[64819,64819],"mapped","طم"],[[64820,64820],"mapped","سج"],[[64821,64821],"mapped","سح"],[[64822,64822],"mapped","سخ"],[[64823,64823],"mapped","شج"],[[64824,64824],"mapped","شح"],[[64825,64825],"mapped","شخ"],[[64826,64826],"mapped","طم"],[[64827,64827],"mapped","ظم"],[[64828,64829],"mapped","اً"],[[64830,64831],"valid","","NV8"],[[64832,64847],"disallowed"],[[64848,64848],"mapped","تجم"],[[64849,64850],"mapped","تحج"],[[64851,64851],"mapped","تحم"],[[64852,64852],"mapped","تخم"],[[64853,64853],"mapped","تمج"],[[64854,64854],"mapped","تمح"],[[64855,64855],"mapped","تمخ"],[[64856,64857],"mapped","جمح"],[[64858,64858],"mapped","حمي"],[[64859,64859],"mapped","حمى"],[[64860,64860],"mapped","سحج"],[[64861,64861],"mapped","سجح"],[[64862,64862],"mapped","سجى"],[[64863,64864],"mapped","سمح"],[[64865,64865],"mapped","سمج"],[[64866,64867],"mapped","سمم"],[[64868,64869],"mapped","صحح"],[[64870,64870],"mapped","صمم"],[[64871,64872],"mapped","شحم"],[[64873,64873],"mapped","شجي"],[[64874,64875],"mapped","شمخ"],[[64876,64877],"mapped","شمم"],[[64878,64878],"mapped","ضحى"],[[64879,64880],"mapped","ضخم"],[[64881,64882],"mapped","طمح"],[[64883,64883],"mapped","طمم"],[[64884,64884],"mapped","طمي"],[[64885,64885],"mapped","عجم"],[[64886,64887],"mapped","عمم"],[[64888,64888],"mapped","عمى"],[[64889,64889],"mapped","غمم"],[[64890,64890],"mapped","غمي"],[[64891,64891],"mapped","غمى"],[[64892,64893],"mapped","فخم"],[[64894,64894],"mapped","قمح"],[[64895,64895],"mapped","قمم"],[[64896,64896],"mapped","لحم"],[[64897,64897],"mapped","لحي"],[[64898,64898],"mapped","لحى"],[[64899,64900],"mapped","لجج"],[[64901,64902],"mapped","لخم"],[[64903,64904],"mapped","لمح"],[[64905,64905],"mapped","محج"],[[64906,64906],"mapped","محم"],[[64907,64907],"mapped","محي"],[[64908,64908],"mapped","مجح"],[[64909,64909],"mapped","مجم"],[[64910,64910],"mapped","مخج"],[[64911,64911],"mapped","مخم"],[[64912,64913],"disallowed"],[[64914,64914],"mapped","مجخ"],[[64915,64915],"mapped","همج"],[[64916,64916],"mapped","همم"],[[64917,64917],"mapped","نحم"],[[64918,64918],"mapped","نحى"],[[64919,64920],"mapped","نجم"],[[64921,64921],"mapped","نجى"],[[64922,64922],"mapped","نمي"],[[64923,64923],"mapped","نمى"],[[64924,64925],"mapped","يمم"],[[64926,64926],"mapped","بخي"],[[64927,64927],"mapped","تجي"],[[64928,64928],"mapped","تجى"],[[64929,64929],"mapped","تخي"],[[64930,64930],"mapped","تخى"],[[64931,64931],"mapped","تمي"],[[64932,64932],"mapped","تمى"],[[64933,64933],"mapped","جمي"],[[64934,64934],"mapped","جحى"],[[64935,64935],"mapped","جمى"],[[64936,64936],"mapped","سخى"],[[64937,64937],"mapped","صحي"],[[64938,64938],"mapped","شحي"],[[64939,64939],"mapped","ضحي"],[[64940,64940],"mapped","لجي"],[[64941,64941],"mapped","لمي"],[[64942,64942],"mapped","يحي"],[[64943,64943],"mapped","يجي"],[[64944,64944],"mapped","يمي"],[[64945,64945],"mapped","ممي"],[[64946,64946],"mapped","قمي"],[[64947,64947],"mapped","نحي"],[[64948,64948],"mapped","قمح"],[[64949,64949],"mapped","لحم"],[[64950,64950],"mapped","عمي"],[[64951,64951],"mapped","كمي"],[[64952,64952],"mapped","نجح"],[[64953,64953],"mapped","مخي"],[[64954,64954],"mapped","لجم"],[[64955,64955],"mapped","كمم"],[[64956,64956],"mapped","لجم"],[[64957,64957],"mapped","نجح"],[[64958,64958],"mapped","جحي"],[[64959,64959],"mapped","حجي"],[[64960,64960],"mapped","مجي"],[[64961,64961],"mapped","فمي"],[[64962,64962],"mapped","بحي"],[[64963,64963],"mapped","كمم"],[[64964,64964],"mapped","عجم"],[[64965,64965],"mapped","صمم"],[[64966,64966],"mapped","سخي"],[[64967,64967],"mapped","نجي"],[[64968,64975],"disallowed"],[[64976,65007],"disallowed"],[[65008,65008],"mapped","صلے"],[[65009,65009],"mapped","قلے"],[[65010,65010],"mapped","الله"],[[65011,65011],"mapped","اكبر"],[[65012,65012],"mapped","محمد"],[[65013,65013],"mapped","صلعم"],[[65014,65014],"mapped","رسول"],[[65015,65015],"mapped","عليه"],[[65016,65016],"mapped","وسلم"],[[65017,65017],"mapped","صلى"],[[65018,65018],"disallowed_STD3_mapped","صلى الله عليه وسلم"],[[65019,65019],"disallowed_STD3_mapped","جل جلاله"],[[65020,65020],"mapped","ریال"],[[65021,65021],"valid","","NV8"],[[65022,65023],"disallowed"],[[65024,65039],"ignored"],[[65040,65040],"disallowed_STD3_mapped",","],[[65041,65041],"mapped","、"],[[65042,65042],"disallowed"],[[65043,65043],"disallowed_STD3_mapped",":"],[[65044,65044],"disallowed_STD3_mapped",";"],[[65045,65045],"disallowed_STD3_mapped","!"],[[65046,65046],"disallowed_STD3_mapped","?"],[[65047,65047],"mapped","〖"],[[65048,65048],"mapped","〗"],[[65049,65049],"disallowed"],[[65050,65055],"disallowed"],[[65056,65059],"valid"],[[65060,65062],"valid"],[[65063,65069],"valid"],[[65070,65071],"valid"],[[65072,65072],"disallowed"],[[65073,65073],"mapped","—"],[[65074,65074],"mapped","–"],[[65075,65076],"disallowed_STD3_mapped","_"],[[65077,65077],"disallowed_STD3_mapped","("],[[65078,65078],"disallowed_STD3_mapped",")"],[[65079,65079],"disallowed_STD3_mapped","{"],[[65080,65080],"disallowed_STD3_mapped","}"],[[65081,65081],"mapped","〔"],[[65082,65082],"mapped","〕"],[[65083,65083],"mapped","【"],[[65084,65084],"mapped","】"],[[65085,65085],"mapped","《"],[[65086,65086],"mapped","》"],[[65087,65087],"mapped","〈"],[[65088,65088],"mapped","〉"],[[65089,65089],"mapped","「"],[[65090,65090],"mapped","」"],[[65091,65091],"mapped","『"],[[65092,65092],"mapped","』"],[[65093,65094],"valid","","NV8"],[[65095,65095],"disallowed_STD3_mapped","["],[[65096,65096],"disallowed_STD3_mapped","]"],[[65097,65100],"disallowed_STD3_mapped"," ̅"],[[65101,65103],"disallowed_STD3_mapped","_"],[[65104,65104],"disallowed_STD3_mapped",","],[[65105,65105],"mapped","、"],[[65106,65106],"disallowed"],[[65107,65107],"disallowed"],[[65108,65108],"disallowed_STD3_mapped",";"],[[65109,65109],"disallowed_STD3_mapped",":"],[[65110,65110],"disallowed_STD3_mapped","?"],[[65111,65111],"disallowed_STD3_mapped","!"],[[65112,65112],"mapped","—"],[[65113,65113],"disallowed_STD3_mapped","("],[[65114,65114],"disallowed_STD3_mapped",")"],[[65115,65115],"disallowed_STD3_mapped","{"],[[65116,65116],"disallowed_STD3_mapped","}"],[[65117,65117],"mapped","〔"],[[65118,65118],"mapped","〕"],[[65119,65119],"disallowed_STD3_mapped","#"],[[65120,65120],"disallowed_STD3_mapped","&"],[[65121,65121],"disallowed_STD3_mapped","*"],[[65122,65122],"disallowed_STD3_mapped","+"],[[65123,65123],"mapped","-"],[[65124,65124],"disallowed_STD3_mapped","<"],[[65125,65125],"disallowed_STD3_mapped",">"],[[65126,65126],"disallowed_STD3_mapped","="],[[65127,65127],"disallowed"],[[65128,65128],"disallowed_STD3_mapped","\\"],[[65129,65129],"disallowed_STD3_mapped","$"],[[65130,65130],"disallowed_STD3_mapped","%"],[[65131,65131],"disallowed_STD3_mapped","@"],[[65132,65135],"disallowed"],[[65136,65136],"disallowed_STD3_mapped"," ً"],[[65137,65137],"mapped","ـً"],[[65138,65138],"disallowed_STD3_mapped"," ٌ"],[[65139,65139],"valid"],[[65140,65140],"disallowed_STD3_mapped"," ٍ"],[[65141,65141],"disallowed"],[[65142,65142],"disallowed_STD3_mapped"," َ"],[[65143,65143],"mapped","ـَ"],[[65144,65144],"disallowed_STD3_mapped"," ُ"],[[65145,65145],"mapped","ـُ"],[[65146,65146],"disallowed_STD3_mapped"," ِ"],[[65147,65147],"mapped","ـِ"],[[65148,65148],"disallowed_STD3_mapped"," ّ"],[[65149,65149],"mapped","ـّ"],[[65150,65150],"disallowed_STD3_mapped"," ْ"],[[65151,65151],"mapped","ـْ"],[[65152,65152],"mapped","ء"],[[65153,65154],"mapped","آ"],[[65155,65156],"mapped","أ"],[[65157,65158],"mapped","ؤ"],[[65159,65160],"mapped","إ"],[[65161,65164],"mapped","ئ"],[[65165,65166],"mapped","ا"],[[65167,65170],"mapped","ب"],[[65171,65172],"mapped","ة"],[[65173,65176],"mapped","ت"],[[65177,65180],"mapped","ث"],[[65181,65184],"mapped","ج"],[[65185,65188],"mapped","ح"],[[65189,65192],"mapped","خ"],[[65193,65194],"mapped","د"],[[65195,65196],"mapped","ذ"],[[65197,65198],"mapped","ر"],[[65199,65200],"mapped","ز"],[[65201,65204],"mapped","س"],[[65205,65208],"mapped","ش"],[[65209,65212],"mapped","ص"],[[65213,65216],"mapped","ض"],[[65217,65220],"mapped","ط"],[[65221,65224],"mapped","ظ"],[[65225,65228],"mapped","ع"],[[65229,65232],"mapped","غ"],[[65233,65236],"mapped","ف"],[[65237,65240],"mapped","ق"],[[65241,65244],"mapped","ك"],[[65245,65248],"mapped","ل"],[[65249,65252],"mapped","م"],[[65253,65256],"mapped","ن"],[[65257,65260],"mapped","ه"],[[65261,65262],"mapped","و"],[[65263,65264],"mapped","ى"],[[65265,65268],"mapped","ي"],[[65269,65270],"mapped","لآ"],[[65271,65272],"mapped","لأ"],[[65273,65274],"mapped","لإ"],[[65275,65276],"mapped","لا"],[[65277,65278],"disallowed"],[[65279,65279],"ignored"],[[65280,65280],"disallowed"],[[65281,65281],"disallowed_STD3_mapped","!"],[[65282,65282],"disallowed_STD3_mapped","\""],[[65283,65283],"disallowed_STD3_mapped","#"],[[65284,65284],"disallowed_STD3_mapped","$"],[[65285,65285],"disallowed_STD3_mapped","%"],[[65286,65286],"disallowed_STD3_mapped","&"],[[65287,65287],"disallowed_STD3_mapped","'"],[[65288,65288],"disallowed_STD3_mapped","("],[[65289,65289],"disallowed_STD3_mapped",")"],[[65290,65290],"disallowed_STD3_mapped","*"],[[65291,65291],"disallowed_STD3_mapped","+"],[[65292,65292],"disallowed_STD3_mapped",","],[[65293,65293],"mapped","-"],[[65294,65294],"mapped","."],[[65295,65295],"disallowed_STD3_mapped","/"],[[65296,65296],"mapped","0"],[[65297,65297],"mapped","1"],[[65298,65298],"mapped","2"],[[65299,65299],"mapped","3"],[[65300,65300],"mapped","4"],[[65301,65301],"mapped","5"],[[65302,65302],"mapped","6"],[[65303,65303],"mapped","7"],[[65304,65304],"mapped","8"],[[65305,65305],"mapped","9"],[[65306,65306],"disallowed_STD3_mapped",":"],[[65307,65307],"disallowed_STD3_mapped",";"],[[65308,65308],"disallowed_STD3_mapped","<"],[[65309,65309],"disallowed_STD3_mapped","="],[[65310,65310],"disallowed_STD3_mapped",">"],[[65311,65311],"disallowed_STD3_mapped","?"],[[65312,65312],"disallowed_STD3_mapped","@"],[[65313,65313],"mapped","a"],[[65314,65314],"mapped","b"],[[65315,65315],"mapped","c"],[[65316,65316],"mapped","d"],[[65317,65317],"mapped","e"],[[65318,65318],"mapped","f"],[[65319,65319],"mapped","g"],[[65320,65320],"mapped","h"],[[65321,65321],"mapped","i"],[[65322,65322],"mapped","j"],[[65323,65323],"mapped","k"],[[65324,65324],"mapped","l"],[[65325,65325],"mapped","m"],[[65326,65326],"mapped","n"],[[65327,65327],"mapped","o"],[[65328,65328],"mapped","p"],[[65329,65329],"mapped","q"],[[65330,65330],"mapped","r"],[[65331,65331],"mapped","s"],[[65332,65332],"mapped","t"],[[65333,65333],"mapped","u"],[[65334,65334],"mapped","v"],[[65335,65335],"mapped","w"],[[65336,65336],"mapped","x"],[[65337,65337],"mapped","y"],[[65338,65338],"mapped","z"],[[65339,65339],"disallowed_STD3_mapped","["],[[65340,65340],"disallowed_STD3_mapped","\\"],[[65341,65341],"disallowed_STD3_mapped","]"],[[65342,65342],"disallowed_STD3_mapped","^"],[[65343,65343],"disallowed_STD3_mapped","_"],[[65344,65344],"disallowed_STD3_mapped","`"],[[65345,65345],"mapped","a"],[[65346,65346],"mapped","b"],[[65347,65347],"mapped","c"],[[65348,65348],"mapped","d"],[[65349,65349],"mapped","e"],[[65350,65350],"mapped","f"],[[65351,65351],"mapped","g"],[[65352,65352],"mapped","h"],[[65353,65353],"mapped","i"],[[65354,65354],"mapped","j"],[[65355,65355],"mapped","k"],[[65356,65356],"mapped","l"],[[65357,65357],"mapped","m"],[[65358,65358],"mapped","n"],[[65359,65359],"mapped","o"],[[65360,65360],"mapped","p"],[[65361,65361],"mapped","q"],[[65362,65362],"mapped","r"],[[65363,65363],"mapped","s"],[[65364,65364],"mapped","t"],[[65365,65365],"mapped","u"],[[65366,65366],"mapped","v"],[[65367,65367],"mapped","w"],[[65368,65368],"mapped","x"],[[65369,65369],"mapped","y"],[[65370,65370],"mapped","z"],[[65371,65371],"disallowed_STD3_mapped","{"],[[65372,65372],"disallowed_STD3_mapped","|"],[[65373,65373],"disallowed_STD3_mapped","}"],[[65374,65374],"disallowed_STD3_mapped","~"],[[65375,65375],"mapped","⦅"],[[65376,65376],"mapped","⦆"],[[65377,65377],"mapped","."],[[65378,65378],"mapped","「"],[[65379,65379],"mapped","」"],[[65380,65380],"mapped","、"],[[65381,65381],"mapped","・"],[[65382,65382],"mapped","ヲ"],[[65383,65383],"mapped","ァ"],[[65384,65384],"mapped","ィ"],[[65385,65385],"mapped","ゥ"],[[65386,65386],"mapped","ェ"],[[65387,65387],"mapped","ォ"],[[65388,65388],"mapped","ャ"],[[65389,65389],"mapped","ュ"],[[65390,65390],"mapped","ョ"],[[65391,65391],"mapped","ッ"],[[65392,65392],"mapped","ー"],[[65393,65393],"mapped","ア"],[[65394,65394],"mapped","イ"],[[65395,65395],"mapped","ウ"],[[65396,65396],"mapped","エ"],[[65397,65397],"mapped","オ"],[[65398,65398],"mapped","カ"],[[65399,65399],"mapped","キ"],[[65400,65400],"mapped","ク"],[[65401,65401],"mapped","ケ"],[[65402,65402],"mapped","コ"],[[65403,65403],"mapped","サ"],[[65404,65404],"mapped","シ"],[[65405,65405],"mapped","ス"],[[65406,65406],"mapped","セ"],[[65407,65407],"mapped","ソ"],[[65408,65408],"mapped","タ"],[[65409,65409],"mapped","チ"],[[65410,65410],"mapped","ツ"],[[65411,65411],"mapped","テ"],[[65412,65412],"mapped","ト"],[[65413,65413],"mapped","ナ"],[[65414,65414],"mapped","ニ"],[[65415,65415],"mapped","ヌ"],[[65416,65416],"mapped","ネ"],[[65417,65417],"mapped","ノ"],[[65418,65418],"mapped","ハ"],[[65419,65419],"mapped","ヒ"],[[65420,65420],"mapped","フ"],[[65421,65421],"mapped","ヘ"],[[65422,65422],"mapped","ホ"],[[65423,65423],"mapped","マ"],[[65424,65424],"mapped","ミ"],[[65425,65425],"mapped","ム"],[[65426,65426],"mapped","メ"],[[65427,65427],"mapped","モ"],[[65428,65428],"mapped","ヤ"],[[65429,65429],"mapped","ユ"],[[65430,65430],"mapped","ヨ"],[[65431,65431],"mapped","ラ"],[[65432,65432],"mapped","リ"],[[65433,65433],"mapped","ル"],[[65434,65434],"mapped","レ"],[[65435,65435],"mapped","ロ"],[[65436,65436],"mapped","ワ"],[[65437,65437],"mapped","ン"],[[65438,65438],"mapped","゙"],[[65439,65439],"mapped","゚"],[[65440,65440],"disallowed"],[[65441,65441],"mapped","ᄀ"],[[65442,65442],"mapped","ᄁ"],[[65443,65443],"mapped","ᆪ"],[[65444,65444],"mapped","ᄂ"],[[65445,65445],"mapped","ᆬ"],[[65446,65446],"mapped","ᆭ"],[[65447,65447],"mapped","ᄃ"],[[65448,65448],"mapped","ᄄ"],[[65449,65449],"mapped","ᄅ"],[[65450,65450],"mapped","ᆰ"],[[65451,65451],"mapped","ᆱ"],[[65452,65452],"mapped","ᆲ"],[[65453,65453],"mapped","ᆳ"],[[65454,65454],"mapped","ᆴ"],[[65455,65455],"mapped","ᆵ"],[[65456,65456],"mapped","ᄚ"],[[65457,65457],"mapped","ᄆ"],[[65458,65458],"mapped","ᄇ"],[[65459,65459],"mapped","ᄈ"],[[65460,65460],"mapped","ᄡ"],[[65461,65461],"mapped","ᄉ"],[[65462,65462],"mapped","ᄊ"],[[65463,65463],"mapped","ᄋ"],[[65464,65464],"mapped","ᄌ"],[[65465,65465],"mapped","ᄍ"],[[65466,65466],"mapped","ᄎ"],[[65467,65467],"mapped","ᄏ"],[[65468,65468],"mapped","ᄐ"],[[65469,65469],"mapped","ᄑ"],[[65470,65470],"mapped","ᄒ"],[[65471,65473],"disallowed"],[[65474,65474],"mapped","ᅡ"],[[65475,65475],"mapped","ᅢ"],[[65476,65476],"mapped","ᅣ"],[[65477,65477],"mapped","ᅤ"],[[65478,65478],"mapped","ᅥ"],[[65479,65479],"mapped","ᅦ"],[[65480,65481],"disallowed"],[[65482,65482],"mapped","ᅧ"],[[65483,65483],"mapped","ᅨ"],[[65484,65484],"mapped","ᅩ"],[[65485,65485],"mapped","ᅪ"],[[65486,65486],"mapped","ᅫ"],[[65487,65487],"mapped","ᅬ"],[[65488,65489],"disallowed"],[[65490,65490],"mapped","ᅭ"],[[65491,65491],"mapped","ᅮ"],[[65492,65492],"mapped","ᅯ"],[[65493,65493],"mapped","ᅰ"],[[65494,65494],"mapped","ᅱ"],[[65495,65495],"mapped","ᅲ"],[[65496,65497],"disallowed"],[[65498,65498],"mapped","ᅳ"],[[65499,65499],"mapped","ᅴ"],[[65500,65500],"mapped","ᅵ"],[[65501,65503],"disallowed"],[[65504,65504],"mapped","¢"],[[65505,65505],"mapped","£"],[[65506,65506],"mapped","¬"],[[65507,65507],"disallowed_STD3_mapped"," ̄"],[[65508,65508],"mapped","¦"],[[65509,65509],"mapped","¥"],[[65510,65510],"mapped","₩"],[[65511,65511],"disallowed"],[[65512,65512],"mapped","│"],[[65513,65513],"mapped","←"],[[65514,65514],"mapped","↑"],[[65515,65515],"mapped","→"],[[65516,65516],"mapped","↓"],[[65517,65517],"mapped","■"],[[65518,65518],"mapped","○"],[[65519,65528],"disallowed"],[[65529,65531],"disallowed"],[[65532,65532],"disallowed"],[[65533,65533],"disallowed"],[[65534,65535],"disallowed"],[[65536,65547],"valid"],[[65548,65548],"disallowed"],[[65549,65574],"valid"],[[65575,65575],"disallowed"],[[65576,65594],"valid"],[[65595,65595],"disallowed"],[[65596,65597],"valid"],[[65598,65598],"disallowed"],[[65599,65613],"valid"],[[65614,65615],"disallowed"],[[65616,65629],"valid"],[[65630,65663],"disallowed"],[[65664,65786],"valid"],[[65787,65791],"disallowed"],[[65792,65794],"valid","","NV8"],[[65795,65798],"disallowed"],[[65799,65843],"valid","","NV8"],[[65844,65846],"disallowed"],[[65847,65855],"valid","","NV8"],[[65856,65930],"valid","","NV8"],[[65931,65932],"valid","","NV8"],[[65933,65934],"valid","","NV8"],[[65935,65935],"disallowed"],[[65936,65947],"valid","","NV8"],[[65948,65951],"disallowed"],[[65952,65952],"valid","","NV8"],[[65953,65999],"disallowed"],[[66000,66044],"valid","","NV8"],[[66045,66045],"valid"],[[66046,66175],"disallowed"],[[66176,66204],"valid"],[[66205,66207],"disallowed"],[[66208,66256],"valid"],[[66257,66271],"disallowed"],[[66272,66272],"valid"],[[66273,66299],"valid","","NV8"],[[66300,66303],"disallowed"],[[66304,66334],"valid"],[[66335,66335],"valid"],[[66336,66339],"valid","","NV8"],[[66340,66348],"disallowed"],[[66349,66351],"valid"],[[66352,66368],"valid"],[[66369,66369],"valid","","NV8"],[[66370,66377],"valid"],[[66378,66378],"valid","","NV8"],[[66379,66383],"disallowed"],[[66384,66426],"valid"],[[66427,66431],"disallowed"],[[66432,66461],"valid"],[[66462,66462],"disallowed"],[[66463,66463],"valid","","NV8"],[[66464,66499],"valid"],[[66500,66503],"disallowed"],[[66504,66511],"valid"],[[66512,66517],"valid","","NV8"],[[66518,66559],"disallowed"],[[66560,66560],"mapped","𐐨"],[[66561,66561],"mapped","𐐩"],[[66562,66562],"mapped","𐐪"],[[66563,66563],"mapped","𐐫"],[[66564,66564],"mapped","𐐬"],[[66565,66565],"mapped","𐐭"],[[66566,66566],"mapped","𐐮"],[[66567,66567],"mapped","𐐯"],[[66568,66568],"mapped","𐐰"],[[66569,66569],"mapped","𐐱"],[[66570,66570],"mapped","𐐲"],[[66571,66571],"mapped","𐐳"],[[66572,66572],"mapped","𐐴"],[[66573,66573],"mapped","𐐵"],[[66574,66574],"mapped","𐐶"],[[66575,66575],"mapped","𐐷"],[[66576,66576],"mapped","𐐸"],[[66577,66577],"mapped","𐐹"],[[66578,66578],"mapped","𐐺"],[[66579,66579],"mapped","𐐻"],[[66580,66580],"mapped","𐐼"],[[66581,66581],"mapped","𐐽"],[[66582,66582],"mapped","𐐾"],[[66583,66583],"mapped","𐐿"],[[66584,66584],"mapped","𐑀"],[[66585,66585],"mapped","𐑁"],[[66586,66586],"mapped","𐑂"],[[66587,66587],"mapped","𐑃"],[[66588,66588],"mapped","𐑄"],[[66589,66589],"mapped","𐑅"],[[66590,66590],"mapped","𐑆"],[[66591,66591],"mapped","𐑇"],[[66592,66592],"mapped","𐑈"],[[66593,66593],"mapped","𐑉"],[[66594,66594],"mapped","𐑊"],[[66595,66595],"mapped","𐑋"],[[66596,66596],"mapped","𐑌"],[[66597,66597],"mapped","𐑍"],[[66598,66598],"mapped","𐑎"],[[66599,66599],"mapped","𐑏"],[[66600,66637],"valid"],[[66638,66717],"valid"],[[66718,66719],"disallowed"],[[66720,66729],"valid"],[[66730,66735],"disallowed"],[[66736,66736],"mapped","𐓘"],[[66737,66737],"mapped","𐓙"],[[66738,66738],"mapped","𐓚"],[[66739,66739],"mapped","𐓛"],[[66740,66740],"mapped","𐓜"],[[66741,66741],"mapped","𐓝"],[[66742,66742],"mapped","𐓞"],[[66743,66743],"mapped","𐓟"],[[66744,66744],"mapped","𐓠"],[[66745,66745],"mapped","𐓡"],[[66746,66746],"mapped","𐓢"],[[66747,66747],"mapped","𐓣"],[[66748,66748],"mapped","𐓤"],[[66749,66749],"mapped","𐓥"],[[66750,66750],"mapped","𐓦"],[[66751,66751],"mapped","𐓧"],[[66752,66752],"mapped","𐓨"],[[66753,66753],"mapped","𐓩"],[[66754,66754],"mapped","𐓪"],[[66755,66755],"mapped","𐓫"],[[66756,66756],"mapped","𐓬"],[[66757,66757],"mapped","𐓭"],[[66758,66758],"mapped","𐓮"],[[66759,66759],"mapped","𐓯"],[[66760,66760],"mapped","𐓰"],[[66761,66761],"mapped","𐓱"],[[66762,66762],"mapped","𐓲"],[[66763,66763],"mapped","𐓳"],[[66764,66764],"mapped","𐓴"],[[66765,66765],"mapped","𐓵"],[[66766,66766],"mapped","𐓶"],[[66767,66767],"mapped","𐓷"],[[66768,66768],"mapped","𐓸"],[[66769,66769],"mapped","𐓹"],[[66770,66770],"mapped","𐓺"],[[66771,66771],"mapped","𐓻"],[[66772,66775],"disallowed"],[[66776,66811],"valid"],[[66812,66815],"disallowed"],[[66816,66855],"valid"],[[66856,66863],"disallowed"],[[66864,66915],"valid"],[[66916,66926],"disallowed"],[[66927,66927],"valid","","NV8"],[[66928,67071],"disallowed"],[[67072,67382],"valid"],[[67383,67391],"disallowed"],[[67392,67413],"valid"],[[67414,67423],"disallowed"],[[67424,67431],"valid"],[[67432,67583],"disallowed"],[[67584,67589],"valid"],[[67590,67591],"disallowed"],[[67592,67592],"valid"],[[67593,67593],"disallowed"],[[67594,67637],"valid"],[[67638,67638],"disallowed"],[[67639,67640],"valid"],[[67641,67643],"disallowed"],[[67644,67644],"valid"],[[67645,67646],"disallowed"],[[67647,67647],"valid"],[[67648,67669],"valid"],[[67670,67670],"disallowed"],[[67671,67679],"valid","","NV8"],[[67680,67702],"valid"],[[67703,67711],"valid","","NV8"],[[67712,67742],"valid"],[[67743,67750],"disallowed"],[[67751,67759],"valid","","NV8"],[[67760,67807],"disallowed"],[[67808,67826],"valid"],[[67827,67827],"disallowed"],[[67828,67829],"valid"],[[67830,67834],"disallowed"],[[67835,67839],"valid","","NV8"],[[67840,67861],"valid"],[[67862,67865],"valid","","NV8"],[[67866,67867],"valid","","NV8"],[[67868,67870],"disallowed"],[[67871,67871],"valid","","NV8"],[[67872,67897],"valid"],[[67898,67902],"disallowed"],[[67903,67903],"valid","","NV8"],[[67904,67967],"disallowed"],[[67968,68023],"valid"],[[68024,68027],"disallowed"],[[68028,68029],"valid","","NV8"],[[68030,68031],"valid"],[[68032,68047],"valid","","NV8"],[[68048,68049],"disallowed"],[[68050,68095],"valid","","NV8"],[[68096,68099],"valid"],[[68100,68100],"disallowed"],[[68101,68102],"valid"],[[68103,68107],"disallowed"],[[68108,68115],"valid"],[[68116,68116],"disallowed"],[[68117,68119],"valid"],[[68120,68120],"disallowed"],[[68121,68147],"valid"],[[68148,68151],"disallowed"],[[68152,68154],"valid"],[[68155,68158],"disallowed"],[[68159,68159],"valid"],[[68160,68167],"valid","","NV8"],[[68168,68175],"disallowed"],[[68176,68184],"valid","","NV8"],[[68185,68191],"disallowed"],[[68192,68220],"valid"],[[68221,68223],"valid","","NV8"],[[68224,68252],"valid"],[[68253,68255],"valid","","NV8"],[[68256,68287],"disallowed"],[[68288,68295],"valid"],[[68296,68296],"valid","","NV8"],[[68297,68326],"valid"],[[68327,68330],"disallowed"],[[68331,68342],"valid","","NV8"],[[68343,68351],"disallowed"],[[68352,68405],"valid"],[[68406,68408],"disallowed"],[[68409,68415],"valid","","NV8"],[[68416,68437],"valid"],[[68438,68439],"disallowed"],[[68440,68447],"valid","","NV8"],[[68448,68466],"valid"],[[68467,68471],"disallowed"],[[68472,68479],"valid","","NV8"],[[68480,68497],"valid"],[[68498,68504],"disallowed"],[[68505,68508],"valid","","NV8"],[[68509,68520],"disallowed"],[[68521,68527],"valid","","NV8"],[[68528,68607],"disallowed"],[[68608,68680],"valid"],[[68681,68735],"disallowed"],[[68736,68736],"mapped","𐳀"],[[68737,68737],"mapped","𐳁"],[[68738,68738],"mapped","𐳂"],[[68739,68739],"mapped","𐳃"],[[68740,68740],"mapped","𐳄"],[[68741,68741],"mapped","𐳅"],[[68742,68742],"mapped","𐳆"],[[68743,68743],"mapped","𐳇"],[[68744,68744],"mapped","𐳈"],[[68745,68745],"mapped","𐳉"],[[68746,68746],"mapped","𐳊"],[[68747,68747],"mapped","𐳋"],[[68748,68748],"mapped","𐳌"],[[68749,68749],"mapped","𐳍"],[[68750,68750],"mapped","𐳎"],[[68751,68751],"mapped","𐳏"],[[68752,68752],"mapped","𐳐"],[[68753,68753],"mapped","𐳑"],[[68754,68754],"mapped","𐳒"],[[68755,68755],"mapped","𐳓"],[[68756,68756],"mapped","𐳔"],[[68757,68757],"mapped","𐳕"],[[68758,68758],"mapped","𐳖"],[[68759,68759],"mapped","𐳗"],[[68760,68760],"mapped","𐳘"],[[68761,68761],"mapped","𐳙"],[[68762,68762],"mapped","𐳚"],[[68763,68763],"mapped","𐳛"],[[68764,68764],"mapped","𐳜"],[[68765,68765],"mapped","𐳝"],[[68766,68766],"mapped","𐳞"],[[68767,68767],"mapped","𐳟"],[[68768,68768],"mapped","𐳠"],[[68769,68769],"mapped","𐳡"],[[68770,68770],"mapped","𐳢"],[[68771,68771],"mapped","𐳣"],[[68772,68772],"mapped","𐳤"],[[68773,68773],"mapped","𐳥"],[[68774,68774],"mapped","𐳦"],[[68775,68775],"mapped","𐳧"],[[68776,68776],"mapped","𐳨"],[[68777,68777],"mapped","𐳩"],[[68778,68778],"mapped","𐳪"],[[68779,68779],"mapped","𐳫"],[[68780,68780],"mapped","𐳬"],[[68781,68781],"mapped","𐳭"],[[68782,68782],"mapped","𐳮"],[[68783,68783],"mapped","𐳯"],[[68784,68784],"mapped","𐳰"],[[68785,68785],"mapped","𐳱"],[[68786,68786],"mapped","𐳲"],[[68787,68799],"disallowed"],[[68800,68850],"valid"],[[68851,68857],"disallowed"],[[68858,68863],"valid","","NV8"],[[68864,69215],"disallowed"],[[69216,69246],"valid","","NV8"],[[69247,69631],"disallowed"],[[69632,69702],"valid"],[[69703,69709],"valid","","NV8"],[[69710,69713],"disallowed"],[[69714,69733],"valid","","NV8"],[[69734,69743],"valid"],[[69744,69758],"disallowed"],[[69759,69759],"valid"],[[69760,69818],"valid"],[[69819,69820],"valid","","NV8"],[[69821,69821],"disallowed"],[[69822,69825],"valid","","NV8"],[[69826,69839],"disallowed"],[[69840,69864],"valid"],[[69865,69871],"disallowed"],[[69872,69881],"valid"],[[69882,69887],"disallowed"],[[69888,69940],"valid"],[[69941,69941],"disallowed"],[[69942,69951],"valid"],[[69952,69955],"valid","","NV8"],[[69956,69967],"disallowed"],[[69968,70003],"valid"],[[70004,70005],"valid","","NV8"],[[70006,70006],"valid"],[[70007,70015],"disallowed"],[[70016,70084],"valid"],[[70085,70088],"valid","","NV8"],[[70089,70089],"valid","","NV8"],[[70090,70092],"valid"],[[70093,70093],"valid","","NV8"],[[70094,70095],"disallowed"],[[70096,70105],"valid"],[[70106,70106],"valid"],[[70107,70107],"valid","","NV8"],[[70108,70108],"valid"],[[70109,70111],"valid","","NV8"],[[70112,70112],"disallowed"],[[70113,70132],"valid","","NV8"],[[70133,70143],"disallowed"],[[70144,70161],"valid"],[[70162,70162],"disallowed"],[[70163,70199],"valid"],[[70200,70205],"valid","","NV8"],[[70206,70206],"valid"],[[70207,70271],"disallowed"],[[70272,70278],"valid"],[[70279,70279],"disallowed"],[[70280,70280],"valid"],[[70281,70281],"disallowed"],[[70282,70285],"valid"],[[70286,70286],"disallowed"],[[70287,70301],"valid"],[[70302,70302],"disallowed"],[[70303,70312],"valid"],[[70313,70313],"valid","","NV8"],[[70314,70319],"disallowed"],[[70320,70378],"valid"],[[70379,70383],"disallowed"],[[70384,70393],"valid"],[[70394,70399],"disallowed"],[[70400,70400],"valid"],[[70401,70403],"valid"],[[70404,70404],"disallowed"],[[70405,70412],"valid"],[[70413,70414],"disallowed"],[[70415,70416],"valid"],[[70417,70418],"disallowed"],[[70419,70440],"valid"],[[70441,70441],"disallowed"],[[70442,70448],"valid"],[[70449,70449],"disallowed"],[[70450,70451],"valid"],[[70452,70452],"disallowed"],[[70453,70457],"valid"],[[70458,70459],"disallowed"],[[70460,70468],"valid"],[[70469,70470],"disallowed"],[[70471,70472],"valid"],[[70473,70474],"disallowed"],[[70475,70477],"valid"],[[70478,70479],"disallowed"],[[70480,70480],"valid"],[[70481,70486],"disallowed"],[[70487,70487],"valid"],[[70488,70492],"disallowed"],[[70493,70499],"valid"],[[70500,70501],"disallowed"],[[70502,70508],"valid"],[[70509,70511],"disallowed"],[[70512,70516],"valid"],[[70517,70655],"disallowed"],[[70656,70730],"valid"],[[70731,70735],"valid","","NV8"],[[70736,70745],"valid"],[[70746,70746],"disallowed"],[[70747,70747],"valid","","NV8"],[[70748,70748],"disallowed"],[[70749,70749],"valid","","NV8"],[[70750,70783],"disallowed"],[[70784,70853],"valid"],[[70854,70854],"valid","","NV8"],[[70855,70855],"valid"],[[70856,70863],"disallowed"],[[70864,70873],"valid"],[[70874,71039],"disallowed"],[[71040,71093],"valid"],[[71094,71095],"disallowed"],[[71096,71104],"valid"],[[71105,71113],"valid","","NV8"],[[71114,71127],"valid","","NV8"],[[71128,71133],"valid"],[[71134,71167],"disallowed"],[[71168,71232],"valid"],[[71233,71235],"valid","","NV8"],[[71236,71236],"valid"],[[71237,71247],"disallowed"],[[71248,71257],"valid"],[[71258,71263],"disallowed"],[[71264,71276],"valid","","NV8"],[[71277,71295],"disallowed"],[[71296,71351],"valid"],[[71352,71359],"disallowed"],[[71360,71369],"valid"],[[71370,71423],"disallowed"],[[71424,71449],"valid"],[[71450,71452],"disallowed"],[[71453,71467],"valid"],[[71468,71471],"disallowed"],[[71472,71481],"valid"],[[71482,71487],"valid","","NV8"],[[71488,71839],"disallowed"],[[71840,71840],"mapped","𑣀"],[[71841,71841],"mapped","𑣁"],[[71842,71842],"mapped","𑣂"],[[71843,71843],"mapped","𑣃"],[[71844,71844],"mapped","𑣄"],[[71845,71845],"mapped","𑣅"],[[71846,71846],"mapped","𑣆"],[[71847,71847],"mapped","𑣇"],[[71848,71848],"mapped","𑣈"],[[71849,71849],"mapped","𑣉"],[[71850,71850],"mapped","𑣊"],[[71851,71851],"mapped","𑣋"],[[71852,71852],"mapped","𑣌"],[[71853,71853],"mapped","𑣍"],[[71854,71854],"mapped","𑣎"],[[71855,71855],"mapped","𑣏"],[[71856,71856],"mapped","𑣐"],[[71857,71857],"mapped","𑣑"],[[71858,71858],"mapped","𑣒"],[[71859,71859],"mapped","𑣓"],[[71860,71860],"mapped","𑣔"],[[71861,71861],"mapped","𑣕"],[[71862,71862],"mapped","𑣖"],[[71863,71863],"mapped","𑣗"],[[71864,71864],"mapped","𑣘"],[[71865,71865],"mapped","𑣙"],[[71866,71866],"mapped","𑣚"],[[71867,71867],"mapped","𑣛"],[[71868,71868],"mapped","𑣜"],[[71869,71869],"mapped","𑣝"],[[71870,71870],"mapped","𑣞"],[[71871,71871],"mapped","𑣟"],[[71872,71913],"valid"],[[71914,71922],"valid","","NV8"],[[71923,71934],"disallowed"],[[71935,71935],"valid"],[[71936,72191],"disallowed"],[[72192,72254],"valid"],[[72255,72262],"valid","","NV8"],[[72263,72263],"valid"],[[72264,72271],"disallowed"],[[72272,72323],"valid"],[[72324,72325],"disallowed"],[[72326,72345],"valid"],[[72346,72348],"valid","","NV8"],[[72349,72349],"disallowed"],[[72350,72354],"valid","","NV8"],[[72355,72383],"disallowed"],[[72384,72440],"valid"],[[72441,72703],"disallowed"],[[72704,72712],"valid"],[[72713,72713],"disallowed"],[[72714,72758],"valid"],[[72759,72759],"disallowed"],[[72760,72768],"valid"],[[72769,72773],"valid","","NV8"],[[72774,72783],"disallowed"],[[72784,72793],"valid"],[[72794,72812],"valid","","NV8"],[[72813,72815],"disallowed"],[[72816,72817],"valid","","NV8"],[[72818,72847],"valid"],[[72848,72849],"disallowed"],[[72850,72871],"valid"],[[72872,72872],"disallowed"],[[72873,72886],"valid"],[[72887,72959],"disallowed"],[[72960,72966],"valid"],[[72967,72967],"disallowed"],[[72968,72969],"valid"],[[72970,72970],"disallowed"],[[72971,73014],"valid"],[[73015,73017],"disallowed"],[[73018,73018],"valid"],[[73019,73019],"disallowed"],[[73020,73021],"valid"],[[73022,73022],"disallowed"],[[73023,73031],"valid"],[[73032,73039],"disallowed"],[[73040,73049],"valid"],[[73050,73727],"disallowed"],[[73728,74606],"valid"],[[74607,74648],"valid"],[[74649,74649],"valid"],[[74650,74751],"disallowed"],[[74752,74850],"valid","","NV8"],[[74851,74862],"valid","","NV8"],[[74863,74863],"disallowed"],[[74864,74867],"valid","","NV8"],[[74868,74868],"valid","","NV8"],[[74869,74879],"disallowed"],[[74880,75075],"valid"],[[75076,77823],"disallowed"],[[77824,78894],"valid"],[[78895,82943],"disallowed"],[[82944,83526],"valid"],[[83527,92159],"disallowed"],[[92160,92728],"valid"],[[92729,92735],"disallowed"],[[92736,92766],"valid"],[[92767,92767],"disallowed"],[[92768,92777],"valid"],[[92778,92781],"disallowed"],[[92782,92783],"valid","","NV8"],[[92784,92879],"disallowed"],[[92880,92909],"valid"],[[92910,92911],"disallowed"],[[92912,92916],"valid"],[[92917,92917],"valid","","NV8"],[[92918,92927],"disallowed"],[[92928,92982],"valid"],[[92983,92991],"valid","","NV8"],[[92992,92995],"valid"],[[92996,92997],"valid","","NV8"],[[92998,93007],"disallowed"],[[93008,93017],"valid"],[[93018,93018],"disallowed"],[[93019,93025],"valid","","NV8"],[[93026,93026],"disallowed"],[[93027,93047],"valid"],[[93048,93052],"disallowed"],[[93053,93071],"valid"],[[93072,93951],"disallowed"],[[93952,94020],"valid"],[[94021,94031],"disallowed"],[[94032,94078],"valid"],[[94079,94094],"disallowed"],[[94095,94111],"valid"],[[94112,94175],"disallowed"],[[94176,94176],"valid"],[[94177,94177],"valid"],[[94178,94207],"disallowed"],[[94208,100332],"valid"],[[100333,100351],"disallowed"],[[100352,101106],"valid"],[[101107,110591],"disallowed"],[[110592,110593],"valid"],[[110594,110878],"valid"],[[110879,110959],"disallowed"],[[110960,111355],"valid"],[[111356,113663],"disallowed"],[[113664,113770],"valid"],[[113771,113775],"disallowed"],[[113776,113788],"valid"],[[113789,113791],"disallowed"],[[113792,113800],"valid"],[[113801,113807],"disallowed"],[[113808,113817],"valid"],[[113818,113819],"disallowed"],[[113820,113820],"valid","","NV8"],[[113821,113822],"valid"],[[113823,113823],"valid","","NV8"],[[113824,113827],"ignored"],[[113828,118783],"disallowed"],[[118784,119029],"valid","","NV8"],[[119030,119039],"disallowed"],[[119040,119078],"valid","","NV8"],[[119079,119080],"disallowed"],[[119081,119081],"valid","","NV8"],[[119082,119133],"valid","","NV8"],[[119134,119134],"mapped","𝅗𝅥"],[[119135,119135],"mapped","𝅘𝅥"],[[119136,119136],"mapped","𝅘𝅥𝅮"],[[119137,119137],"mapped","𝅘𝅥𝅯"],[[119138,119138],"mapped","𝅘𝅥𝅰"],[[119139,119139],"mapped","𝅘𝅥𝅱"],[[119140,119140],"mapped","𝅘𝅥𝅲"],[[119141,119154],"valid","","NV8"],[[119155,119162],"disallowed"],[[119163,119226],"valid","","NV8"],[[119227,119227],"mapped","𝆹𝅥"],[[119228,119228],"mapped","𝆺𝅥"],[[119229,119229],"mapped","𝆹𝅥𝅮"],[[119230,119230],"mapped","𝆺𝅥𝅮"],[[119231,119231],"mapped","𝆹𝅥𝅯"],[[119232,119232],"mapped","𝆺𝅥𝅯"],[[119233,119261],"valid","","NV8"],[[119262,119272],"valid","","NV8"],[[119273,119295],"disallowed"],[[119296,119365],"valid","","NV8"],[[119366,119551],"disallowed"],[[119552,119638],"valid","","NV8"],[[119639,119647],"disallowed"],[[119648,119665],"valid","","NV8"],[[119666,119807],"disallowed"],[[119808,119808],"mapped","a"],[[119809,119809],"mapped","b"],[[119810,119810],"mapped","c"],[[119811,119811],"mapped","d"],[[119812,119812],"mapped","e"],[[119813,119813],"mapped","f"],[[119814,119814],"mapped","g"],[[119815,119815],"mapped","h"],[[119816,119816],"mapped","i"],[[119817,119817],"mapped","j"],[[119818,119818],"mapped","k"],[[119819,119819],"mapped","l"],[[119820,119820],"mapped","m"],[[119821,119821],"mapped","n"],[[119822,119822],"mapped","o"],[[119823,119823],"mapped","p"],[[119824,119824],"mapped","q"],[[119825,119825],"mapped","r"],[[119826,119826],"mapped","s"],[[119827,119827],"mapped","t"],[[119828,119828],"mapped","u"],[[119829,119829],"mapped","v"],[[119830,119830],"mapped","w"],[[119831,119831],"mapped","x"],[[119832,119832],"mapped","y"],[[119833,119833],"mapped","z"],[[119834,119834],"mapped","a"],[[119835,119835],"mapped","b"],[[119836,119836],"mapped","c"],[[119837,119837],"mapped","d"],[[119838,119838],"mapped","e"],[[119839,119839],"mapped","f"],[[119840,119840],"mapped","g"],[[119841,119841],"mapped","h"],[[119842,119842],"mapped","i"],[[119843,119843],"mapped","j"],[[119844,119844],"mapped","k"],[[119845,119845],"mapped","l"],[[119846,119846],"mapped","m"],[[119847,119847],"mapped","n"],[[119848,119848],"mapped","o"],[[119849,119849],"mapped","p"],[[119850,119850],"mapped","q"],[[119851,119851],"mapped","r"],[[119852,119852],"mapped","s"],[[119853,119853],"mapped","t"],[[119854,119854],"mapped","u"],[[119855,119855],"mapped","v"],[[119856,119856],"mapped","w"],[[119857,119857],"mapped","x"],[[119858,119858],"mapped","y"],[[119859,119859],"mapped","z"],[[119860,119860],"mapped","a"],[[119861,119861],"mapped","b"],[[119862,119862],"mapped","c"],[[119863,119863],"mapped","d"],[[119864,119864],"mapped","e"],[[119865,119865],"mapped","f"],[[119866,119866],"mapped","g"],[[119867,119867],"mapped","h"],[[119868,119868],"mapped","i"],[[119869,119869],"mapped","j"],[[119870,119870],"mapped","k"],[[119871,119871],"mapped","l"],[[119872,119872],"mapped","m"],[[119873,119873],"mapped","n"],[[119874,119874],"mapped","o"],[[119875,119875],"mapped","p"],[[119876,119876],"mapped","q"],[[119877,119877],"mapped","r"],[[119878,119878],"mapped","s"],[[119879,119879],"mapped","t"],[[119880,119880],"mapped","u"],[[119881,119881],"mapped","v"],[[119882,119882],"mapped","w"],[[119883,119883],"mapped","x"],[[119884,119884],"mapped","y"],[[119885,119885],"mapped","z"],[[119886,119886],"mapped","a"],[[119887,119887],"mapped","b"],[[119888,119888],"mapped","c"],[[119889,119889],"mapped","d"],[[119890,119890],"mapped","e"],[[119891,119891],"mapped","f"],[[119892,119892],"mapped","g"],[[119893,119893],"disallowed"],[[119894,119894],"mapped","i"],[[119895,119895],"mapped","j"],[[119896,119896],"mapped","k"],[[119897,119897],"mapped","l"],[[119898,119898],"mapped","m"],[[119899,119899],"mapped","n"],[[119900,119900],"mapped","o"],[[119901,119901],"mapped","p"],[[119902,119902],"mapped","q"],[[119903,119903],"mapped","r"],[[119904,119904],"mapped","s"],[[119905,119905],"mapped","t"],[[119906,119906],"mapped","u"],[[119907,119907],"mapped","v"],[[119908,119908],"mapped","w"],[[119909,119909],"mapped","x"],[[119910,119910],"mapped","y"],[[119911,119911],"mapped","z"],[[119912,119912],"mapped","a"],[[119913,119913],"mapped","b"],[[119914,119914],"mapped","c"],[[119915,119915],"mapped","d"],[[119916,119916],"mapped","e"],[[119917,119917],"mapped","f"],[[119918,119918],"mapped","g"],[[119919,119919],"mapped","h"],[[119920,119920],"mapped","i"],[[119921,119921],"mapped","j"],[[119922,119922],"mapped","k"],[[119923,119923],"mapped","l"],[[119924,119924],"mapped","m"],[[119925,119925],"mapped","n"],[[119926,119926],"mapped","o"],[[119927,119927],"mapped","p"],[[119928,119928],"mapped","q"],[[119929,119929],"mapped","r"],[[119930,119930],"mapped","s"],[[119931,119931],"mapped","t"],[[119932,119932],"mapped","u"],[[119933,119933],"mapped","v"],[[119934,119934],"mapped","w"],[[119935,119935],"mapped","x"],[[119936,119936],"mapped","y"],[[119937,119937],"mapped","z"],[[119938,119938],"mapped","a"],[[119939,119939],"mapped","b"],[[119940,119940],"mapped","c"],[[119941,119941],"mapped","d"],[[119942,119942],"mapped","e"],[[119943,119943],"mapped","f"],[[119944,119944],"mapped","g"],[[119945,119945],"mapped","h"],[[119946,119946],"mapped","i"],[[119947,119947],"mapped","j"],[[119948,119948],"mapped","k"],[[119949,119949],"mapped","l"],[[119950,119950],"mapped","m"],[[119951,119951],"mapped","n"],[[119952,119952],"mapped","o"],[[119953,119953],"mapped","p"],[[119954,119954],"mapped","q"],[[119955,119955],"mapped","r"],[[119956,119956],"mapped","s"],[[119957,119957],"mapped","t"],[[119958,119958],"mapped","u"],[[119959,119959],"mapped","v"],[[119960,119960],"mapped","w"],[[119961,119961],"mapped","x"],[[119962,119962],"mapped","y"],[[119963,119963],"mapped","z"],[[119964,119964],"mapped","a"],[[119965,119965],"disallowed"],[[119966,119966],"mapped","c"],[[119967,119967],"mapped","d"],[[119968,119969],"disallowed"],[[119970,119970],"mapped","g"],[[119971,119972],"disallowed"],[[119973,119973],"mapped","j"],[[119974,119974],"mapped","k"],[[119975,119976],"disallowed"],[[119977,119977],"mapped","n"],[[119978,119978],"mapped","o"],[[119979,119979],"mapped","p"],[[119980,119980],"mapped","q"],[[119981,119981],"disallowed"],[[119982,119982],"mapped","s"],[[119983,119983],"mapped","t"],[[119984,119984],"mapped","u"],[[119985,119985],"mapped","v"],[[119986,119986],"mapped","w"],[[119987,119987],"mapped","x"],[[119988,119988],"mapped","y"],[[119989,119989],"mapped","z"],[[119990,119990],"mapped","a"],[[119991,119991],"mapped","b"],[[119992,119992],"mapped","c"],[[119993,119993],"mapped","d"],[[119994,119994],"disallowed"],[[119995,119995],"mapped","f"],[[119996,119996],"disallowed"],[[119997,119997],"mapped","h"],[[119998,119998],"mapped","i"],[[119999,119999],"mapped","j"],[[120000,120000],"mapped","k"],[[120001,120001],"mapped","l"],[[120002,120002],"mapped","m"],[[120003,120003],"mapped","n"],[[120004,120004],"disallowed"],[[120005,120005],"mapped","p"],[[120006,120006],"mapped","q"],[[120007,120007],"mapped","r"],[[120008,120008],"mapped","s"],[[120009,120009],"mapped","t"],[[120010,120010],"mapped","u"],[[120011,120011],"mapped","v"],[[120012,120012],"mapped","w"],[[120013,120013],"mapped","x"],[[120014,120014],"mapped","y"],[[120015,120015],"mapped","z"],[[120016,120016],"mapped","a"],[[120017,120017],"mapped","b"],[[120018,120018],"mapped","c"],[[120019,120019],"mapped","d"],[[120020,120020],"mapped","e"],[[120021,120021],"mapped","f"],[[120022,120022],"mapped","g"],[[120023,120023],"mapped","h"],[[120024,120024],"mapped","i"],[[120025,120025],"mapped","j"],[[120026,120026],"mapped","k"],[[120027,120027],"mapped","l"],[[120028,120028],"mapped","m"],[[120029,120029],"mapped","n"],[[120030,120030],"mapped","o"],[[120031,120031],"mapped","p"],[[120032,120032],"mapped","q"],[[120033,120033],"mapped","r"],[[120034,120034],"mapped","s"],[[120035,120035],"mapped","t"],[[120036,120036],"mapped","u"],[[120037,120037],"mapped","v"],[[120038,120038],"mapped","w"],[[120039,120039],"mapped","x"],[[120040,120040],"mapped","y"],[[120041,120041],"mapped","z"],[[120042,120042],"mapped","a"],[[120043,120043],"mapped","b"],[[120044,120044],"mapped","c"],[[120045,120045],"mapped","d"],[[120046,120046],"mapped","e"],[[120047,120047],"mapped","f"],[[120048,120048],"mapped","g"],[[120049,120049],"mapped","h"],[[120050,120050],"mapped","i"],[[120051,120051],"mapped","j"],[[120052,120052],"mapped","k"],[[120053,120053],"mapped","l"],[[120054,120054],"mapped","m"],[[120055,120055],"mapped","n"],[[120056,120056],"mapped","o"],[[120057,120057],"mapped","p"],[[120058,120058],"mapped","q"],[[120059,120059],"mapped","r"],[[120060,120060],"mapped","s"],[[120061,120061],"mapped","t"],[[120062,120062],"mapped","u"],[[120063,120063],"mapped","v"],[[120064,120064],"mapped","w"],[[120065,120065],"mapped","x"],[[120066,120066],"mapped","y"],[[120067,120067],"mapped","z"],[[120068,120068],"mapped","a"],[[120069,120069],"mapped","b"],[[120070,120070],"disallowed"],[[120071,120071],"mapped","d"],[[120072,120072],"mapped","e"],[[120073,120073],"mapped","f"],[[120074,120074],"mapped","g"],[[120075,120076],"disallowed"],[[120077,120077],"mapped","j"],[[120078,120078],"mapped","k"],[[120079,120079],"mapped","l"],[[120080,120080],"mapped","m"],[[120081,120081],"mapped","n"],[[120082,120082],"mapped","o"],[[120083,120083],"mapped","p"],[[120084,120084],"mapped","q"],[[120085,120085],"disallowed"],[[120086,120086],"mapped","s"],[[120087,120087],"mapped","t"],[[120088,120088],"mapped","u"],[[120089,120089],"mapped","v"],[[120090,120090],"mapped","w"],[[120091,120091],"mapped","x"],[[120092,120092],"mapped","y"],[[120093,120093],"disallowed"],[[120094,120094],"mapped","a"],[[120095,120095],"mapped","b"],[[120096,120096],"mapped","c"],[[120097,120097],"mapped","d"],[[120098,120098],"mapped","e"],[[120099,120099],"mapped","f"],[[120100,120100],"mapped","g"],[[120101,120101],"mapped","h"],[[120102,120102],"mapped","i"],[[120103,120103],"mapped","j"],[[120104,120104],"mapped","k"],[[120105,120105],"mapped","l"],[[120106,120106],"mapped","m"],[[120107,120107],"mapped","n"],[[120108,120108],"mapped","o"],[[120109,120109],"mapped","p"],[[120110,120110],"mapped","q"],[[120111,120111],"mapped","r"],[[120112,120112],"mapped","s"],[[120113,120113],"mapped","t"],[[120114,120114],"mapped","u"],[[120115,120115],"mapped","v"],[[120116,120116],"mapped","w"],[[120117,120117],"mapped","x"],[[120118,120118],"mapped","y"],[[120119,120119],"mapped","z"],[[120120,120120],"mapped","a"],[[120121,120121],"mapped","b"],[[120122,120122],"disallowed"],[[120123,120123],"mapped","d"],[[120124,120124],"mapped","e"],[[120125,120125],"mapped","f"],[[120126,120126],"mapped","g"],[[120127,120127],"disallowed"],[[120128,120128],"mapped","i"],[[120129,120129],"mapped","j"],[[120130,120130],"mapped","k"],[[120131,120131],"mapped","l"],[[120132,120132],"mapped","m"],[[120133,120133],"disallowed"],[[120134,120134],"mapped","o"],[[120135,120137],"disallowed"],[[120138,120138],"mapped","s"],[[120139,120139],"mapped","t"],[[120140,120140],"mapped","u"],[[120141,120141],"mapped","v"],[[120142,120142],"mapped","w"],[[120143,120143],"mapped","x"],[[120144,120144],"mapped","y"],[[120145,120145],"disallowed"],[[120146,120146],"mapped","a"],[[120147,120147],"mapped","b"],[[120148,120148],"mapped","c"],[[120149,120149],"mapped","d"],[[120150,120150],"mapped","e"],[[120151,120151],"mapped","f"],[[120152,120152],"mapped","g"],[[120153,120153],"mapped","h"],[[120154,120154],"mapped","i"],[[120155,120155],"mapped","j"],[[120156,120156],"mapped","k"],[[120157,120157],"mapped","l"],[[120158,120158],"mapped","m"],[[120159,120159],"mapped","n"],[[120160,120160],"mapped","o"],[[120161,120161],"mapped","p"],[[120162,120162],"mapped","q"],[[120163,120163],"mapped","r"],[[120164,120164],"mapped","s"],[[120165,120165],"mapped","t"],[[120166,120166],"mapped","u"],[[120167,120167],"mapped","v"],[[120168,120168],"mapped","w"],[[120169,120169],"mapped","x"],[[120170,120170],"mapped","y"],[[120171,120171],"mapped","z"],[[120172,120172],"mapped","a"],[[120173,120173],"mapped","b"],[[120174,120174],"mapped","c"],[[120175,120175],"mapped","d"],[[120176,120176],"mapped","e"],[[120177,120177],"mapped","f"],[[120178,120178],"mapped","g"],[[120179,120179],"mapped","h"],[[120180,120180],"mapped","i"],[[120181,120181],"mapped","j"],[[120182,120182],"mapped","k"],[[120183,120183],"mapped","l"],[[120184,120184],"mapped","m"],[[120185,120185],"mapped","n"],[[120186,120186],"mapped","o"],[[120187,120187],"mapped","p"],[[120188,120188],"mapped","q"],[[120189,120189],"mapped","r"],[[120190,120190],"mapped","s"],[[120191,120191],"mapped","t"],[[120192,120192],"mapped","u"],[[120193,120193],"mapped","v"],[[120194,120194],"mapped","w"],[[120195,120195],"mapped","x"],[[120196,120196],"mapped","y"],[[120197,120197],"mapped","z"],[[120198,120198],"mapped","a"],[[120199,120199],"mapped","b"],[[120200,120200],"mapped","c"],[[120201,120201],"mapped","d"],[[120202,120202],"mapped","e"],[[120203,120203],"mapped","f"],[[120204,120204],"mapped","g"],[[120205,120205],"mapped","h"],[[120206,120206],"mapped","i"],[[120207,120207],"mapped","j"],[[120208,120208],"mapped","k"],[[120209,120209],"mapped","l"],[[120210,120210],"mapped","m"],[[120211,120211],"mapped","n"],[[120212,120212],"mapped","o"],[[120213,120213],"mapped","p"],[[120214,120214],"mapped","q"],[[120215,120215],"mapped","r"],[[120216,120216],"mapped","s"],[[120217,120217],"mapped","t"],[[120218,120218],"mapped","u"],[[120219,120219],"mapped","v"],[[120220,120220],"mapped","w"],[[120221,120221],"mapped","x"],[[120222,120222],"mapped","y"],[[120223,120223],"mapped","z"],[[120224,120224],"mapped","a"],[[120225,120225],"mapped","b"],[[120226,120226],"mapped","c"],[[120227,120227],"mapped","d"],[[120228,120228],"mapped","e"],[[120229,120229],"mapped","f"],[[120230,120230],"mapped","g"],[[120231,120231],"mapped","h"],[[120232,120232],"mapped","i"],[[120233,120233],"mapped","j"],[[120234,120234],"mapped","k"],[[120235,120235],"mapped","l"],[[120236,120236],"mapped","m"],[[120237,120237],"mapped","n"],[[120238,120238],"mapped","o"],[[120239,120239],"mapped","p"],[[120240,120240],"mapped","q"],[[120241,120241],"mapped","r"],[[120242,120242],"mapped","s"],[[120243,120243],"mapped","t"],[[120244,120244],"mapped","u"],[[120245,120245],"mapped","v"],[[120246,120246],"mapped","w"],[[120247,120247],"mapped","x"],[[120248,120248],"mapped","y"],[[120249,120249],"mapped","z"],[[120250,120250],"mapped","a"],[[120251,120251],"mapped","b"],[[120252,120252],"mapped","c"],[[120253,120253],"mapped","d"],[[120254,120254],"mapped","e"],[[120255,120255],"mapped","f"],[[120256,120256],"mapped","g"],[[120257,120257],"mapped","h"],[[120258,120258],"mapped","i"],[[120259,120259],"mapped","j"],[[120260,120260],"mapped","k"],[[120261,120261],"mapped","l"],[[120262,120262],"mapped","m"],[[120263,120263],"mapped","n"],[[120264,120264],"mapped","o"],[[120265,120265],"mapped","p"],[[120266,120266],"mapped","q"],[[120267,120267],"mapped","r"],[[120268,120268],"mapped","s"],[[120269,120269],"mapped","t"],[[120270,120270],"mapped","u"],[[120271,120271],"mapped","v"],[[120272,120272],"mapped","w"],[[120273,120273],"mapped","x"],[[120274,120274],"mapped","y"],[[120275,120275],"mapped","z"],[[120276,120276],"mapped","a"],[[120277,120277],"mapped","b"],[[120278,120278],"mapped","c"],[[120279,120279],"mapped","d"],[[120280,120280],"mapped","e"],[[120281,120281],"mapped","f"],[[120282,120282],"mapped","g"],[[120283,120283],"mapped","h"],[[120284,120284],"mapped","i"],[[120285,120285],"mapped","j"],[[120286,120286],"mapped","k"],[[120287,120287],"mapped","l"],[[120288,120288],"mapped","m"],[[120289,120289],"mapped","n"],[[120290,120290],"mapped","o"],[[120291,120291],"mapped","p"],[[120292,120292],"mapped","q"],[[120293,120293],"mapped","r"],[[120294,120294],"mapped","s"],[[120295,120295],"mapped","t"],[[120296,120296],"mapped","u"],[[120297,120297],"mapped","v"],[[120298,120298],"mapped","w"],[[120299,120299],"mapped","x"],[[120300,120300],"mapped","y"],[[120301,120301],"mapped","z"],[[120302,120302],"mapped","a"],[[120303,120303],"mapped","b"],[[120304,120304],"mapped","c"],[[120305,120305],"mapped","d"],[[120306,120306],"mapped","e"],[[120307,120307],"mapped","f"],[[120308,120308],"mapped","g"],[[120309,120309],"mapped","h"],[[120310,120310],"mapped","i"],[[120311,120311],"mapped","j"],[[120312,120312],"mapped","k"],[[120313,120313],"mapped","l"],[[120314,120314],"mapped","m"],[[120315,120315],"mapped","n"],[[120316,120316],"mapped","o"],[[120317,120317],"mapped","p"],[[120318,120318],"mapped","q"],[[120319,120319],"mapped","r"],[[120320,120320],"mapped","s"],[[120321,120321],"mapped","t"],[[120322,120322],"mapped","u"],[[120323,120323],"mapped","v"],[[120324,120324],"mapped","w"],[[120325,120325],"mapped","x"],[[120326,120326],"mapped","y"],[[120327,120327],"mapped","z"],[[120328,120328],"mapped","a"],[[120329,120329],"mapped","b"],[[120330,120330],"mapped","c"],[[120331,120331],"mapped","d"],[[120332,120332],"mapped","e"],[[120333,120333],"mapped","f"],[[120334,120334],"mapped","g"],[[120335,120335],"mapped","h"],[[120336,120336],"mapped","i"],[[120337,120337],"mapped","j"],[[120338,120338],"mapped","k"],[[120339,120339],"mapped","l"],[[120340,120340],"mapped","m"],[[120341,120341],"mapped","n"],[[120342,120342],"mapped","o"],[[120343,120343],"mapped","p"],[[120344,120344],"mapped","q"],[[120345,120345],"mapped","r"],[[120346,120346],"mapped","s"],[[120347,120347],"mapped","t"],[[120348,120348],"mapped","u"],[[120349,120349],"mapped","v"],[[120350,120350],"mapped","w"],[[120351,120351],"mapped","x"],[[120352,120352],"mapped","y"],[[120353,120353],"mapped","z"],[[120354,120354],"mapped","a"],[[120355,120355],"mapped","b"],[[120356,120356],"mapped","c"],[[120357,120357],"mapped","d"],[[120358,120358],"mapped","e"],[[120359,120359],"mapped","f"],[[120360,120360],"mapped","g"],[[120361,120361],"mapped","h"],[[120362,120362],"mapped","i"],[[120363,120363],"mapped","j"],[[120364,120364],"mapped","k"],[[120365,120365],"mapped","l"],[[120366,120366],"mapped","m"],[[120367,120367],"mapped","n"],[[120368,120368],"mapped","o"],[[120369,120369],"mapped","p"],[[120370,120370],"mapped","q"],[[120371,120371],"mapped","r"],[[120372,120372],"mapped","s"],[[120373,120373],"mapped","t"],[[120374,120374],"mapped","u"],[[120375,120375],"mapped","v"],[[120376,120376],"mapped","w"],[[120377,120377],"mapped","x"],[[120378,120378],"mapped","y"],[[120379,120379],"mapped","z"],[[120380,120380],"mapped","a"],[[120381,120381],"mapped","b"],[[120382,120382],"mapped","c"],[[120383,120383],"mapped","d"],[[120384,120384],"mapped","e"],[[120385,120385],"mapped","f"],[[120386,120386],"mapped","g"],[[120387,120387],"mapped","h"],[[120388,120388],"mapped","i"],[[120389,120389],"mapped","j"],[[120390,120390],"mapped","k"],[[120391,120391],"mapped","l"],[[120392,120392],"mapped","m"],[[120393,120393],"mapped","n"],[[120394,120394],"mapped","o"],[[120395,120395],"mapped","p"],[[120396,120396],"mapped","q"],[[120397,120397],"mapped","r"],[[120398,120398],"mapped","s"],[[120399,120399],"mapped","t"],[[120400,120400],"mapped","u"],[[120401,120401],"mapped","v"],[[120402,120402],"mapped","w"],[[120403,120403],"mapped","x"],[[120404,120404],"mapped","y"],[[120405,120405],"mapped","z"],[[120406,120406],"mapped","a"],[[120407,120407],"mapped","b"],[[120408,120408],"mapped","c"],[[120409,120409],"mapped","d"],[[120410,120410],"mapped","e"],[[120411,120411],"mapped","f"],[[120412,120412],"mapped","g"],[[120413,120413],"mapped","h"],[[120414,120414],"mapped","i"],[[120415,120415],"mapped","j"],[[120416,120416],"mapped","k"],[[120417,120417],"mapped","l"],[[120418,120418],"mapped","m"],[[120419,120419],"mapped","n"],[[120420,120420],"mapped","o"],[[120421,120421],"mapped","p"],[[120422,120422],"mapped","q"],[[120423,120423],"mapped","r"],[[120424,120424],"mapped","s"],[[120425,120425],"mapped","t"],[[120426,120426],"mapped","u"],[[120427,120427],"mapped","v"],[[120428,120428],"mapped","w"],[[120429,120429],"mapped","x"],[[120430,120430],"mapped","y"],[[120431,120431],"mapped","z"],[[120432,120432],"mapped","a"],[[120433,120433],"mapped","b"],[[120434,120434],"mapped","c"],[[120435,120435],"mapped","d"],[[120436,120436],"mapped","e"],[[120437,120437],"mapped","f"],[[120438,120438],"mapped","g"],[[120439,120439],"mapped","h"],[[120440,120440],"mapped","i"],[[120441,120441],"mapped","j"],[[120442,120442],"mapped","k"],[[120443,120443],"mapped","l"],[[120444,120444],"mapped","m"],[[120445,120445],"mapped","n"],[[120446,120446],"mapped","o"],[[120447,120447],"mapped","p"],[[120448,120448],"mapped","q"],[[120449,120449],"mapped","r"],[[120450,120450],"mapped","s"],[[120451,120451],"mapped","t"],[[120452,120452],"mapped","u"],[[120453,120453],"mapped","v"],[[120454,120454],"mapped","w"],[[120455,120455],"mapped","x"],[[120456,120456],"mapped","y"],[[120457,120457],"mapped","z"],[[120458,120458],"mapped","a"],[[120459,120459],"mapped","b"],[[120460,120460],"mapped","c"],[[120461,120461],"mapped","d"],[[120462,120462],"mapped","e"],[[120463,120463],"mapped","f"],[[120464,120464],"mapped","g"],[[120465,120465],"mapped","h"],[[120466,120466],"mapped","i"],[[120467,120467],"mapped","j"],[[120468,120468],"mapped","k"],[[120469,120469],"mapped","l"],[[120470,120470],"mapped","m"],[[120471,120471],"mapped","n"],[[120472,120472],"mapped","o"],[[120473,120473],"mapped","p"],[[120474,120474],"mapped","q"],[[120475,120475],"mapped","r"],[[120476,120476],"mapped","s"],[[120477,120477],"mapped","t"],[[120478,120478],"mapped","u"],[[120479,120479],"mapped","v"],[[120480,120480],"mapped","w"],[[120481,120481],"mapped","x"],[[120482,120482],"mapped","y"],[[120483,120483],"mapped","z"],[[120484,120484],"mapped","ı"],[[120485,120485],"mapped","ȷ"],[[120486,120487],"disallowed"],[[120488,120488],"mapped","α"],[[120489,120489],"mapped","β"],[[120490,120490],"mapped","γ"],[[120491,120491],"mapped","δ"],[[120492,120492],"mapped","ε"],[[120493,120493],"mapped","ζ"],[[120494,120494],"mapped","η"],[[120495,120495],"mapped","θ"],[[120496,120496],"mapped","ι"],[[120497,120497],"mapped","κ"],[[120498,120498],"mapped","λ"],[[120499,120499],"mapped","μ"],[[120500,120500],"mapped","ν"],[[120501,120501],"mapped","ξ"],[[120502,120502],"mapped","ο"],[[120503,120503],"mapped","π"],[[120504,120504],"mapped","ρ"],[[120505,120505],"mapped","θ"],[[120506,120506],"mapped","σ"],[[120507,120507],"mapped","τ"],[[120508,120508],"mapped","υ"],[[120509,120509],"mapped","φ"],[[120510,120510],"mapped","χ"],[[120511,120511],"mapped","ψ"],[[120512,120512],"mapped","ω"],[[120513,120513],"mapped","∇"],[[120514,120514],"mapped","α"],[[120515,120515],"mapped","β"],[[120516,120516],"mapped","γ"],[[120517,120517],"mapped","δ"],[[120518,120518],"mapped","ε"],[[120519,120519],"mapped","ζ"],[[120520,120520],"mapped","η"],[[120521,120521],"mapped","θ"],[[120522,120522],"mapped","ι"],[[120523,120523],"mapped","κ"],[[120524,120524],"mapped","λ"],[[120525,120525],"mapped","μ"],[[120526,120526],"mapped","ν"],[[120527,120527],"mapped","ξ"],[[120528,120528],"mapped","ο"],[[120529,120529],"mapped","π"],[[120530,120530],"mapped","ρ"],[[120531,120532],"mapped","σ"],[[120533,120533],"mapped","τ"],[[120534,120534],"mapped","υ"],[[120535,120535],"mapped","φ"],[[120536,120536],"mapped","χ"],[[120537,120537],"mapped","ψ"],[[120538,120538],"mapped","ω"],[[120539,120539],"mapped","∂"],[[120540,120540],"mapped","ε"],[[120541,120541],"mapped","θ"],[[120542,120542],"mapped","κ"],[[120543,120543],"mapped","φ"],[[120544,120544],"mapped","ρ"],[[120545,120545],"mapped","π"],[[120546,120546],"mapped","α"],[[120547,120547],"mapped","β"],[[120548,120548],"mapped","γ"],[[120549,120549],"mapped","δ"],[[120550,120550],"mapped","ε"],[[120551,120551],"mapped","ζ"],[[120552,120552],"mapped","η"],[[120553,120553],"mapped","θ"],[[120554,120554],"mapped","ι"],[[120555,120555],"mapped","κ"],[[120556,120556],"mapped","λ"],[[120557,120557],"mapped","μ"],[[120558,120558],"mapped","ν"],[[120559,120559],"mapped","ξ"],[[120560,120560],"mapped","ο"],[[120561,120561],"mapped","π"],[[120562,120562],"mapped","ρ"],[[120563,120563],"mapped","θ"],[[120564,120564],"mapped","σ"],[[120565,120565],"mapped","τ"],[[120566,120566],"mapped","υ"],[[120567,120567],"mapped","φ"],[[120568,120568],"mapped","χ"],[[120569,120569],"mapped","ψ"],[[120570,120570],"mapped","ω"],[[120571,120571],"mapped","∇"],[[120572,120572],"mapped","α"],[[120573,120573],"mapped","β"],[[120574,120574],"mapped","γ"],[[120575,120575],"mapped","δ"],[[120576,120576],"mapped","ε"],[[120577,120577],"mapped","ζ"],[[120578,120578],"mapped","η"],[[120579,120579],"mapped","θ"],[[120580,120580],"mapped","ι"],[[120581,120581],"mapped","κ"],[[120582,120582],"mapped","λ"],[[120583,120583],"mapped","μ"],[[120584,120584],"mapped","ν"],[[120585,120585],"mapped","ξ"],[[120586,120586],"mapped","ο"],[[120587,120587],"mapped","π"],[[120588,120588],"mapped","ρ"],[[120589,120590],"mapped","σ"],[[120591,120591],"mapped","τ"],[[120592,120592],"mapped","υ"],[[120593,120593],"mapped","φ"],[[120594,120594],"mapped","χ"],[[120595,120595],"mapped","ψ"],[[120596,120596],"mapped","ω"],[[120597,120597],"mapped","∂"],[[120598,120598],"mapped","ε"],[[120599,120599],"mapped","θ"],[[120600,120600],"mapped","κ"],[[120601,120601],"mapped","φ"],[[120602,120602],"mapped","ρ"],[[120603,120603],"mapped","π"],[[120604,120604],"mapped","α"],[[120605,120605],"mapped","β"],[[120606,120606],"mapped","γ"],[[120607,120607],"mapped","δ"],[[120608,120608],"mapped","ε"],[[120609,120609],"mapped","ζ"],[[120610,120610],"mapped","η"],[[120611,120611],"mapped","θ"],[[120612,120612],"mapped","ι"],[[120613,120613],"mapped","κ"],[[120614,120614],"mapped","λ"],[[120615,120615],"mapped","μ"],[[120616,120616],"mapped","ν"],[[120617,120617],"mapped","ξ"],[[120618,120618],"mapped","ο"],[[120619,120619],"mapped","π"],[[120620,120620],"mapped","ρ"],[[120621,120621],"mapped","θ"],[[120622,120622],"mapped","σ"],[[120623,120623],"mapped","τ"],[[120624,120624],"mapped","υ"],[[120625,120625],"mapped","φ"],[[120626,120626],"mapped","χ"],[[120627,120627],"mapped","ψ"],[[120628,120628],"mapped","ω"],[[120629,120629],"mapped","∇"],[[120630,120630],"mapped","α"],[[120631,120631],"mapped","β"],[[120632,120632],"mapped","γ"],[[120633,120633],"mapped","δ"],[[120634,120634],"mapped","ε"],[[120635,120635],"mapped","ζ"],[[120636,120636],"mapped","η"],[[120637,120637],"mapped","θ"],[[120638,120638],"mapped","ι"],[[120639,120639],"mapped","κ"],[[120640,120640],"mapped","λ"],[[120641,120641],"mapped","μ"],[[120642,120642],"mapped","ν"],[[120643,120643],"mapped","ξ"],[[120644,120644],"mapped","ο"],[[120645,120645],"mapped","π"],[[120646,120646],"mapped","ρ"],[[120647,120648],"mapped","σ"],[[120649,120649],"mapped","τ"],[[120650,120650],"mapped","υ"],[[120651,120651],"mapped","φ"],[[120652,120652],"mapped","χ"],[[120653,120653],"mapped","ψ"],[[120654,120654],"mapped","ω"],[[120655,120655],"mapped","∂"],[[120656,120656],"mapped","ε"],[[120657,120657],"mapped","θ"],[[120658,120658],"mapped","κ"],[[120659,120659],"mapped","φ"],[[120660,120660],"mapped","ρ"],[[120661,120661],"mapped","π"],[[120662,120662],"mapped","α"],[[120663,120663],"mapped","β"],[[120664,120664],"mapped","γ"],[[120665,120665],"mapped","δ"],[[120666,120666],"mapped","ε"],[[120667,120667],"mapped","ζ"],[[120668,120668],"mapped","η"],[[120669,120669],"mapped","θ"],[[120670,120670],"mapped","ι"],[[120671,120671],"mapped","κ"],[[120672,120672],"mapped","λ"],[[120673,120673],"mapped","μ"],[[120674,120674],"mapped","ν"],[[120675,120675],"mapped","ξ"],[[120676,120676],"mapped","ο"],[[120677,120677],"mapped","π"],[[120678,120678],"mapped","ρ"],[[120679,120679],"mapped","θ"],[[120680,120680],"mapped","σ"],[[120681,120681],"mapped","τ"],[[120682,120682],"mapped","υ"],[[120683,120683],"mapped","φ"],[[120684,120684],"mapped","χ"],[[120685,120685],"mapped","ψ"],[[120686,120686],"mapped","ω"],[[120687,120687],"mapped","∇"],[[120688,120688],"mapped","α"],[[120689,120689],"mapped","β"],[[120690,120690],"mapped","γ"],[[120691,120691],"mapped","δ"],[[120692,120692],"mapped","ε"],[[120693,120693],"mapped","ζ"],[[120694,120694],"mapped","η"],[[120695,120695],"mapped","θ"],[[120696,120696],"mapped","ι"],[[120697,120697],"mapped","κ"],[[120698,120698],"mapped","λ"],[[120699,120699],"mapped","μ"],[[120700,120700],"mapped","ν"],[[120701,120701],"mapped","ξ"],[[120702,120702],"mapped","ο"],[[120703,120703],"mapped","π"],[[120704,120704],"mapped","ρ"],[[120705,120706],"mapped","σ"],[[120707,120707],"mapped","τ"],[[120708,120708],"mapped","υ"],[[120709,120709],"mapped","φ"],[[120710,120710],"mapped","χ"],[[120711,120711],"mapped","ψ"],[[120712,120712],"mapped","ω"],[[120713,120713],"mapped","∂"],[[120714,120714],"mapped","ε"],[[120715,120715],"mapped","θ"],[[120716,120716],"mapped","κ"],[[120717,120717],"mapped","φ"],[[120718,120718],"mapped","ρ"],[[120719,120719],"mapped","π"],[[120720,120720],"mapped","α"],[[120721,120721],"mapped","β"],[[120722,120722],"mapped","γ"],[[120723,120723],"mapped","δ"],[[120724,120724],"mapped","ε"],[[120725,120725],"mapped","ζ"],[[120726,120726],"mapped","η"],[[120727,120727],"mapped","θ"],[[120728,120728],"mapped","ι"],[[120729,120729],"mapped","κ"],[[120730,120730],"mapped","λ"],[[120731,120731],"mapped","μ"],[[120732,120732],"mapped","ν"],[[120733,120733],"mapped","ξ"],[[120734,120734],"mapped","ο"],[[120735,120735],"mapped","π"],[[120736,120736],"mapped","ρ"],[[120737,120737],"mapped","θ"],[[120738,120738],"mapped","σ"],[[120739,120739],"mapped","τ"],[[120740,120740],"mapped","υ"],[[120741,120741],"mapped","φ"],[[120742,120742],"mapped","χ"],[[120743,120743],"mapped","ψ"],[[120744,120744],"mapped","ω"],[[120745,120745],"mapped","∇"],[[120746,120746],"mapped","α"],[[120747,120747],"mapped","β"],[[120748,120748],"mapped","γ"],[[120749,120749],"mapped","δ"],[[120750,120750],"mapped","ε"],[[120751,120751],"mapped","ζ"],[[120752,120752],"mapped","η"],[[120753,120753],"mapped","θ"],[[120754,120754],"mapped","ι"],[[120755,120755],"mapped","κ"],[[120756,120756],"mapped","λ"],[[120757,120757],"mapped","μ"],[[120758,120758],"mapped","ν"],[[120759,120759],"mapped","ξ"],[[120760,120760],"mapped","ο"],[[120761,120761],"mapped","π"],[[120762,120762],"mapped","ρ"],[[120763,120764],"mapped","σ"],[[120765,120765],"mapped","τ"],[[120766,120766],"mapped","υ"],[[120767,120767],"mapped","φ"],[[120768,120768],"mapped","χ"],[[120769,120769],"mapped","ψ"],[[120770,120770],"mapped","ω"],[[120771,120771],"mapped","∂"],[[120772,120772],"mapped","ε"],[[120773,120773],"mapped","θ"],[[120774,120774],"mapped","κ"],[[120775,120775],"mapped","φ"],[[120776,120776],"mapped","ρ"],[[120777,120777],"mapped","π"],[[120778,120779],"mapped","ϝ"],[[120780,120781],"disallowed"],[[120782,120782],"mapped","0"],[[120783,120783],"mapped","1"],[[120784,120784],"mapped","2"],[[120785,120785],"mapped","3"],[[120786,120786],"mapped","4"],[[120787,120787],"mapped","5"],[[120788,120788],"mapped","6"],[[120789,120789],"mapped","7"],[[120790,120790],"mapped","8"],[[120791,120791],"mapped","9"],[[120792,120792],"mapped","0"],[[120793,120793],"mapped","1"],[[120794,120794],"mapped","2"],[[120795,120795],"mapped","3"],[[120796,120796],"mapped","4"],[[120797,120797],"mapped","5"],[[120798,120798],"mapped","6"],[[120799,120799],"mapped","7"],[[120800,120800],"mapped","8"],[[120801,120801],"mapped","9"],[[120802,120802],"mapped","0"],[[120803,120803],"mapped","1"],[[120804,120804],"mapped","2"],[[120805,120805],"mapped","3"],[[120806,120806],"mapped","4"],[[120807,120807],"mapped","5"],[[120808,120808],"mapped","6"],[[120809,120809],"mapped","7"],[[120810,120810],"mapped","8"],[[120811,120811],"mapped","9"],[[120812,120812],"mapped","0"],[[120813,120813],"mapped","1"],[[120814,120814],"mapped","2"],[[120815,120815],"mapped","3"],[[120816,120816],"mapped","4"],[[120817,120817],"mapped","5"],[[120818,120818],"mapped","6"],[[120819,120819],"mapped","7"],[[120820,120820],"mapped","8"],[[120821,120821],"mapped","9"],[[120822,120822],"mapped","0"],[[120823,120823],"mapped","1"],[[120824,120824],"mapped","2"],[[120825,120825],"mapped","3"],[[120826,120826],"mapped","4"],[[120827,120827],"mapped","5"],[[120828,120828],"mapped","6"],[[120829,120829],"mapped","7"],[[120830,120830],"mapped","8"],[[120831,120831],"mapped","9"],[[120832,121343],"valid","","NV8"],[[121344,121398],"valid"],[[121399,121402],"valid","","NV8"],[[121403,121452],"valid"],[[121453,121460],"valid","","NV8"],[[121461,121461],"valid"],[[121462,121475],"valid","","NV8"],[[121476,121476],"valid"],[[121477,121483],"valid","","NV8"],[[121484,121498],"disallowed"],[[121499,121503],"valid"],[[121504,121504],"disallowed"],[[121505,121519],"valid"],[[121520,122879],"disallowed"],[[122880,122886],"valid"],[[122887,122887],"disallowed"],[[122888,122904],"valid"],[[122905,122906],"disallowed"],[[122907,122913],"valid"],[[122914,122914],"disallowed"],[[122915,122916],"valid"],[[122917,122917],"disallowed"],[[122918,122922],"valid"],[[122923,124927],"disallowed"],[[124928,125124],"valid"],[[125125,125126],"disallowed"],[[125127,125135],"valid","","NV8"],[[125136,125142],"valid"],[[125143,125183],"disallowed"],[[125184,125184],"mapped","𞤢"],[[125185,125185],"mapped","𞤣"],[[125186,125186],"mapped","𞤤"],[[125187,125187],"mapped","𞤥"],[[125188,125188],"mapped","𞤦"],[[125189,125189],"mapped","𞤧"],[[125190,125190],"mapped","𞤨"],[[125191,125191],"mapped","𞤩"],[[125192,125192],"mapped","𞤪"],[[125193,125193],"mapped","𞤫"],[[125194,125194],"mapped","𞤬"],[[125195,125195],"mapped","𞤭"],[[125196,125196],"mapped","𞤮"],[[125197,125197],"mapped","𞤯"],[[125198,125198],"mapped","𞤰"],[[125199,125199],"mapped","𞤱"],[[125200,125200],"mapped","𞤲"],[[125201,125201],"mapped","𞤳"],[[125202,125202],"mapped","𞤴"],[[125203,125203],"mapped","𞤵"],[[125204,125204],"mapped","𞤶"],[[125205,125205],"mapped","𞤷"],[[125206,125206],"mapped","𞤸"],[[125207,125207],"mapped","𞤹"],[[125208,125208],"mapped","𞤺"],[[125209,125209],"mapped","𞤻"],[[125210,125210],"mapped","𞤼"],[[125211,125211],"mapped","𞤽"],[[125212,125212],"mapped","𞤾"],[[125213,125213],"mapped","𞤿"],[[125214,125214],"mapped","𞥀"],[[125215,125215],"mapped","𞥁"],[[125216,125216],"mapped","𞥂"],[[125217,125217],"mapped","𞥃"],[[125218,125258],"valid"],[[125259,125263],"disallowed"],[[125264,125273],"valid"],[[125274,125277],"disallowed"],[[125278,125279],"valid","","NV8"],[[125280,126463],"disallowed"],[[126464,126464],"mapped","ا"],[[126465,126465],"mapped","ب"],[[126466,126466],"mapped","ج"],[[126467,126467],"mapped","د"],[[126468,126468],"disallowed"],[[126469,126469],"mapped","و"],[[126470,126470],"mapped","ز"],[[126471,126471],"mapped","ح"],[[126472,126472],"mapped","ط"],[[126473,126473],"mapped","ي"],[[126474,126474],"mapped","ك"],[[126475,126475],"mapped","ل"],[[126476,126476],"mapped","م"],[[126477,126477],"mapped","ن"],[[126478,126478],"mapped","س"],[[126479,126479],"mapped","ع"],[[126480,126480],"mapped","ف"],[[126481,126481],"mapped","ص"],[[126482,126482],"mapped","ق"],[[126483,126483],"mapped","ر"],[[126484,126484],"mapped","ش"],[[126485,126485],"mapped","ت"],[[126486,126486],"mapped","ث"],[[126487,126487],"mapped","خ"],[[126488,126488],"mapped","ذ"],[[126489,126489],"mapped","ض"],[[126490,126490],"mapped","ظ"],[[126491,126491],"mapped","غ"],[[126492,126492],"mapped","ٮ"],[[126493,126493],"mapped","ں"],[[126494,126494],"mapped","ڡ"],[[126495,126495],"mapped","ٯ"],[[126496,126496],"disallowed"],[[126497,126497],"mapped","ب"],[[126498,126498],"mapped","ج"],[[126499,126499],"disallowed"],[[126500,126500],"mapped","ه"],[[126501,126502],"disallowed"],[[126503,126503],"mapped","ح"],[[126504,126504],"disallowed"],[[126505,126505],"mapped","ي"],[[126506,126506],"mapped","ك"],[[126507,126507],"mapped","ل"],[[126508,126508],"mapped","م"],[[126509,126509],"mapped","ن"],[[126510,126510],"mapped","س"],[[126511,126511],"mapped","ع"],[[126512,126512],"mapped","ف"],[[126513,126513],"mapped","ص"],[[126514,126514],"mapped","ق"],[[126515,126515],"disallowed"],[[126516,126516],"mapped","ش"],[[126517,126517],"mapped","ت"],[[126518,126518],"mapped","ث"],[[126519,126519],"mapped","خ"],[[126520,126520],"disallowed"],[[126521,126521],"mapped","ض"],[[126522,126522],"disallowed"],[[126523,126523],"mapped","غ"],[[126524,126529],"disallowed"],[[126530,126530],"mapped","ج"],[[126531,126534],"disallowed"],[[126535,126535],"mapped","ح"],[[126536,126536],"disallowed"],[[126537,126537],"mapped","ي"],[[126538,126538],"disallowed"],[[126539,126539],"mapped","ل"],[[126540,126540],"disallowed"],[[126541,126541],"mapped","ن"],[[126542,126542],"mapped","س"],[[126543,126543],"mapped","ع"],[[126544,126544],"disallowed"],[[126545,126545],"mapped","ص"],[[126546,126546],"mapped","ق"],[[126547,126547],"disallowed"],[[126548,126548],"mapped","ش"],[[126549,126550],"disallowed"],[[126551,126551],"mapped","خ"],[[126552,126552],"disallowed"],[[126553,126553],"mapped","ض"],[[126554,126554],"disallowed"],[[126555,126555],"mapped","غ"],[[126556,126556],"disallowed"],[[126557,126557],"mapped","ں"],[[126558,126558],"disallowed"],[[126559,126559],"mapped","ٯ"],[[126560,126560],"disallowed"],[[126561,126561],"mapped","ب"],[[126562,126562],"mapped","ج"],[[126563,126563],"disallowed"],[[126564,126564],"mapped","ه"],[[126565,126566],"disallowed"],[[126567,126567],"mapped","ح"],[[126568,126568],"mapped","ط"],[[126569,126569],"mapped","ي"],[[126570,126570],"mapped","ك"],[[126571,126571],"disallowed"],[[126572,126572],"mapped","م"],[[126573,126573],"mapped","ن"],[[126574,126574],"mapped","س"],[[126575,126575],"mapped","ع"],[[126576,126576],"mapped","ف"],[[126577,126577],"mapped","ص"],[[126578,126578],"mapped","ق"],[[126579,126579],"disallowed"],[[126580,126580],"mapped","ش"],[[126581,126581],"mapped","ت"],[[126582,126582],"mapped","ث"],[[126583,126583],"mapped","خ"],[[126584,126584],"disallowed"],[[126585,126585],"mapped","ض"],[[126586,126586],"mapped","ظ"],[[126587,126587],"mapped","غ"],[[126588,126588],"mapped","ٮ"],[[126589,126589],"disallowed"],[[126590,126590],"mapped","ڡ"],[[126591,126591],"disallowed"],[[126592,126592],"mapped","ا"],[[126593,126593],"mapped","ب"],[[126594,126594],"mapped","ج"],[[126595,126595],"mapped","د"],[[126596,126596],"mapped","ه"],[[126597,126597],"mapped","و"],[[126598,126598],"mapped","ز"],[[126599,126599],"mapped","ح"],[[126600,126600],"mapped","ط"],[[126601,126601],"mapped","ي"],[[126602,126602],"disallowed"],[[126603,126603],"mapped","ل"],[[126604,126604],"mapped","م"],[[126605,126605],"mapped","ن"],[[126606,126606],"mapped","س"],[[126607,126607],"mapped","ع"],[[126608,126608],"mapped","ف"],[[126609,126609],"mapped","ص"],[[126610,126610],"mapped","ق"],[[126611,126611],"mapped","ر"],[[126612,126612],"mapped","ش"],[[126613,126613],"mapped","ت"],[[126614,126614],"mapped","ث"],[[126615,126615],"mapped","خ"],[[126616,126616],"mapped","ذ"],[[126617,126617],"mapped","ض"],[[126618,126618],"mapped","ظ"],[[126619,126619],"mapped","غ"],[[126620,126624],"disallowed"],[[126625,126625],"mapped","ب"],[[126626,126626],"mapped","ج"],[[126627,126627],"mapped","د"],[[126628,126628],"disallowed"],[[126629,126629],"mapped","و"],[[126630,126630],"mapped","ز"],[[126631,126631],"mapped","ح"],[[126632,126632],"mapped","ط"],[[126633,126633],"mapped","ي"],[[126634,126634],"disallowed"],[[126635,126635],"mapped","ل"],[[126636,126636],"mapped","م"],[[126637,126637],"mapped","ن"],[[126638,126638],"mapped","س"],[[126639,126639],"mapped","ع"],[[126640,126640],"mapped","ف"],[[126641,126641],"mapped","ص"],[[126642,126642],"mapped","ق"],[[126643,126643],"mapped","ر"],[[126644,126644],"mapped","ش"],[[126645,126645],"mapped","ت"],[[126646,126646],"mapped","ث"],[[126647,126647],"mapped","خ"],[[126648,126648],"mapped","ذ"],[[126649,126649],"mapped","ض"],[[126650,126650],"mapped","ظ"],[[126651,126651],"mapped","غ"],[[126652,126703],"disallowed"],[[126704,126705],"valid","","NV8"],[[126706,126975],"disallowed"],[[126976,127019],"valid","","NV8"],[[127020,127023],"disallowed"],[[127024,127123],"valid","","NV8"],[[127124,127135],"disallowed"],[[127136,127150],"valid","","NV8"],[[127151,127152],"disallowed"],[[127153,127166],"valid","","NV8"],[[127167,127167],"valid","","NV8"],[[127168,127168],"disallowed"],[[127169,127183],"valid","","NV8"],[[127184,127184],"disallowed"],[[127185,127199],"valid","","NV8"],[[127200,127221],"valid","","NV8"],[[127222,127231],"disallowed"],[[127232,127232],"disallowed"],[[127233,127233],"disallowed_STD3_mapped","0,"],[[127234,127234],"disallowed_STD3_mapped","1,"],[[127235,127235],"disallowed_STD3_mapped","2,"],[[127236,127236],"disallowed_STD3_mapped","3,"],[[127237,127237],"disallowed_STD3_mapped","4,"],[[127238,127238],"disallowed_STD3_mapped","5,"],[[127239,127239],"disallowed_STD3_mapped","6,"],[[127240,127240],"disallowed_STD3_mapped","7,"],[[127241,127241],"disallowed_STD3_mapped","8,"],[[127242,127242],"disallowed_STD3_mapped","9,"],[[127243,127244],"valid","","NV8"],[[127245,127247],"disallowed"],[[127248,127248],"disallowed_STD3_mapped","(a)"],[[127249,127249],"disallowed_STD3_mapped","(b)"],[[127250,127250],"disallowed_STD3_mapped","(c)"],[[127251,127251],"disallowed_STD3_mapped","(d)"],[[127252,127252],"disallowed_STD3_mapped","(e)"],[[127253,127253],"disallowed_STD3_mapped","(f)"],[[127254,127254],"disallowed_STD3_mapped","(g)"],[[127255,127255],"disallowed_STD3_mapped","(h)"],[[127256,127256],"disallowed_STD3_mapped","(i)"],[[127257,127257],"disallowed_STD3_mapped","(j)"],[[127258,127258],"disallowed_STD3_mapped","(k)"],[[127259,127259],"disallowed_STD3_mapped","(l)"],[[127260,127260],"disallowed_STD3_mapped","(m)"],[[127261,127261],"disallowed_STD3_mapped","(n)"],[[127262,127262],"disallowed_STD3_mapped","(o)"],[[127263,127263],"disallowed_STD3_mapped","(p)"],[[127264,127264],"disallowed_STD3_mapped","(q)"],[[127265,127265],"disallowed_STD3_mapped","(r)"],[[127266,127266],"disallowed_STD3_mapped","(s)"],[[127267,127267],"disallowed_STD3_mapped","(t)"],[[127268,127268],"disallowed_STD3_mapped","(u)"],[[127269,127269],"disallowed_STD3_mapped","(v)"],[[127270,127270],"disallowed_STD3_mapped","(w)"],[[127271,127271],"disallowed_STD3_mapped","(x)"],[[127272,127272],"disallowed_STD3_mapped","(y)"],[[127273,127273],"disallowed_STD3_mapped","(z)"],[[127274,127274],"mapped","〔s〕"],[[127275,127275],"mapped","c"],[[127276,127276],"mapped","r"],[[127277,127277],"mapped","cd"],[[127278,127278],"mapped","wz"],[[127279,127279],"disallowed"],[[127280,127280],"mapped","a"],[[127281,127281],"mapped","b"],[[127282,127282],"mapped","c"],[[127283,127283],"mapped","d"],[[127284,127284],"mapped","e"],[[127285,127285],"mapped","f"],[[127286,127286],"mapped","g"],[[127287,127287],"mapped","h"],[[127288,127288],"mapped","i"],[[127289,127289],"mapped","j"],[[127290,127290],"mapped","k"],[[127291,127291],"mapped","l"],[[127292,127292],"mapped","m"],[[127293,127293],"mapped","n"],[[127294,127294],"mapped","o"],[[127295,127295],"mapped","p"],[[127296,127296],"mapped","q"],[[127297,127297],"mapped","r"],[[127298,127298],"mapped","s"],[[127299,127299],"mapped","t"],[[127300,127300],"mapped","u"],[[127301,127301],"mapped","v"],[[127302,127302],"mapped","w"],[[127303,127303],"mapped","x"],[[127304,127304],"mapped","y"],[[127305,127305],"mapped","z"],[[127306,127306],"mapped","hv"],[[127307,127307],"mapped","mv"],[[127308,127308],"mapped","sd"],[[127309,127309],"mapped","ss"],[[127310,127310],"mapped","ppv"],[[127311,127311],"mapped","wc"],[[127312,127318],"valid","","NV8"],[[127319,127319],"valid","","NV8"],[[127320,127326],"valid","","NV8"],[[127327,127327],"valid","","NV8"],[[127328,127337],"valid","","NV8"],[[127338,127338],"mapped","mc"],[[127339,127339],"mapped","md"],[[127340,127343],"disallowed"],[[127344,127352],"valid","","NV8"],[[127353,127353],"valid","","NV8"],[[127354,127354],"valid","","NV8"],[[127355,127356],"valid","","NV8"],[[127357,127358],"valid","","NV8"],[[127359,127359],"valid","","NV8"],[[127360,127369],"valid","","NV8"],[[127370,127373],"valid","","NV8"],[[127374,127375],"valid","","NV8"],[[127376,127376],"mapped","dj"],[[127377,127386],"valid","","NV8"],[[127387,127404],"valid","","NV8"],[[127405,127461],"disallowed"],[[127462,127487],"valid","","NV8"],[[127488,127488],"mapped","ほか"],[[127489,127489],"mapped","ココ"],[[127490,127490],"mapped","サ"],[[127491,127503],"disallowed"],[[127504,127504],"mapped","手"],[[127505,127505],"mapped","字"],[[127506,127506],"mapped","双"],[[127507,127507],"mapped","デ"],[[127508,127508],"mapped","二"],[[127509,127509],"mapped","多"],[[127510,127510],"mapped","解"],[[127511,127511],"mapped","天"],[[127512,127512],"mapped","交"],[[127513,127513],"mapped","映"],[[127514,127514],"mapped","無"],[[127515,127515],"mapped","料"],[[127516,127516],"mapped","前"],[[127517,127517],"mapped","後"],[[127518,127518],"mapped","再"],[[127519,127519],"mapped","新"],[[127520,127520],"mapped","初"],[[127521,127521],"mapped","終"],[[127522,127522],"mapped","生"],[[127523,127523],"mapped","販"],[[127524,127524],"mapped","声"],[[127525,127525],"mapped","吹"],[[127526,127526],"mapped","演"],[[127527,127527],"mapped","投"],[[127528,127528],"mapped","捕"],[[127529,127529],"mapped","一"],[[127530,127530],"mapped","三"],[[127531,127531],"mapped","遊"],[[127532,127532],"mapped","左"],[[127533,127533],"mapped","中"],[[127534,127534],"mapped","右"],[[127535,127535],"mapped","指"],[[127536,127536],"mapped","走"],[[127537,127537],"mapped","打"],[[127538,127538],"mapped","禁"],[[127539,127539],"mapped","空"],[[127540,127540],"mapped","合"],[[127541,127541],"mapped","満"],[[127542,127542],"mapped","有"],[[127543,127543],"mapped","月"],[[127544,127544],"mapped","申"],[[127545,127545],"mapped","割"],[[127546,127546],"mapped","営"],[[127547,127547],"mapped","配"],[[127548,127551],"disallowed"],[[127552,127552],"mapped","〔本〕"],[[127553,127553],"mapped","〔三〕"],[[127554,127554],"mapped","〔二〕"],[[127555,127555],"mapped","〔安〕"],[[127556,127556],"mapped","〔点〕"],[[127557,127557],"mapped","〔打〕"],[[127558,127558],"mapped","〔盗〕"],[[127559,127559],"mapped","〔勝〕"],[[127560,127560],"mapped","〔敗〕"],[[127561,127567],"disallowed"],[[127568,127568],"mapped","得"],[[127569,127569],"mapped","可"],[[127570,127583],"disallowed"],[[127584,127589],"valid","","NV8"],[[127590,127743],"disallowed"],[[127744,127776],"valid","","NV8"],[[127777,127788],"valid","","NV8"],[[127789,127791],"valid","","NV8"],[[127792,127797],"valid","","NV8"],[[127798,127798],"valid","","NV8"],[[127799,127868],"valid","","NV8"],[[127869,127869],"valid","","NV8"],[[127870,127871],"valid","","NV8"],[[127872,127891],"valid","","NV8"],[[127892,127903],"valid","","NV8"],[[127904,127940],"valid","","NV8"],[[127941,127941],"valid","","NV8"],[[127942,127946],"valid","","NV8"],[[127947,127950],"valid","","NV8"],[[127951,127955],"valid","","NV8"],[[127956,127967],"valid","","NV8"],[[127968,127984],"valid","","NV8"],[[127985,127991],"valid","","NV8"],[[127992,127999],"valid","","NV8"],[[128000,128062],"valid","","NV8"],[[128063,128063],"valid","","NV8"],[[128064,128064],"valid","","NV8"],[[128065,128065],"valid","","NV8"],[[128066,128247],"valid","","NV8"],[[128248,128248],"valid","","NV8"],[[128249,128252],"valid","","NV8"],[[128253,128254],"valid","","NV8"],[[128255,128255],"valid","","NV8"],[[128256,128317],"valid","","NV8"],[[128318,128319],"valid","","NV8"],[[128320,128323],"valid","","NV8"],[[128324,128330],"valid","","NV8"],[[128331,128335],"valid","","NV8"],[[128336,128359],"valid","","NV8"],[[128360,128377],"valid","","NV8"],[[128378,128378],"valid","","NV8"],[[128379,128419],"valid","","NV8"],[[128420,128420],"valid","","NV8"],[[128421,128506],"valid","","NV8"],[[128507,128511],"valid","","NV8"],[[128512,128512],"valid","","NV8"],[[128513,128528],"valid","","NV8"],[[128529,128529],"valid","","NV8"],[[128530,128532],"valid","","NV8"],[[128533,128533],"valid","","NV8"],[[128534,128534],"valid","","NV8"],[[128535,128535],"valid","","NV8"],[[128536,128536],"valid","","NV8"],[[128537,128537],"valid","","NV8"],[[128538,128538],"valid","","NV8"],[[128539,128539],"valid","","NV8"],[[128540,128542],"valid","","NV8"],[[128543,128543],"valid","","NV8"],[[128544,128549],"valid","","NV8"],[[128550,128551],"valid","","NV8"],[[128552,128555],"valid","","NV8"],[[128556,128556],"valid","","NV8"],[[128557,128557],"valid","","NV8"],[[128558,128559],"valid","","NV8"],[[128560,128563],"valid","","NV8"],[[128564,128564],"valid","","NV8"],[[128565,128576],"valid","","NV8"],[[128577,128578],"valid","","NV8"],[[128579,128580],"valid","","NV8"],[[128581,128591],"valid","","NV8"],[[128592,128639],"valid","","NV8"],[[128640,128709],"valid","","NV8"],[[128710,128719],"valid","","NV8"],[[128720,128720],"valid","","NV8"],[[128721,128722],"valid","","NV8"],[[128723,128724],"valid","","NV8"],[[128725,128735],"disallowed"],[[128736,128748],"valid","","NV8"],[[128749,128751],"disallowed"],[[128752,128755],"valid","","NV8"],[[128756,128758],"valid","","NV8"],[[128759,128760],"valid","","NV8"],[[128761,128767],"disallowed"],[[128768,128883],"valid","","NV8"],[[128884,128895],"disallowed"],[[128896,128980],"valid","","NV8"],[[128981,129023],"disallowed"],[[129024,129035],"valid","","NV8"],[[129036,129039],"disallowed"],[[129040,129095],"valid","","NV8"],[[129096,129103],"disallowed"],[[129104,129113],"valid","","NV8"],[[129114,129119],"disallowed"],[[129120,129159],"valid","","NV8"],[[129160,129167],"disallowed"],[[129168,129197],"valid","","NV8"],[[129198,129279],"disallowed"],[[129280,129291],"valid","","NV8"],[[129292,129295],"disallowed"],[[129296,129304],"valid","","NV8"],[[129305,129310],"valid","","NV8"],[[129311,129311],"valid","","NV8"],[[129312,129319],"valid","","NV8"],[[129320,129327],"valid","","NV8"],[[129328,129328],"valid","","NV8"],[[129329,129330],"valid","","NV8"],[[129331,129342],"valid","","NV8"],[[129343,129343],"disallowed"],[[129344,129355],"valid","","NV8"],[[129356,129356],"valid","","NV8"],[[129357,129359],"disallowed"],[[129360,129374],"valid","","NV8"],[[129375,129387],"valid","","NV8"],[[129388,129407],"disallowed"],[[129408,129412],"valid","","NV8"],[[129413,129425],"valid","","NV8"],[[129426,129431],"valid","","NV8"],[[129432,129471],"disallowed"],[[129472,129472],"valid","","NV8"],[[129473,129487],"disallowed"],[[129488,129510],"valid","","NV8"],[[129511,131069],"disallowed"],[[131070,131071],"disallowed"],[[131072,173782],"valid"],[[173783,173823],"disallowed"],[[173824,177972],"valid"],[[177973,177983],"disallowed"],[[177984,178205],"valid"],[[178206,178207],"disallowed"],[[178208,183969],"valid"],[[183970,183983],"disallowed"],[[183984,191456],"valid"],[[191457,194559],"disallowed"],[[194560,194560],"mapped","丽"],[[194561,194561],"mapped","丸"],[[194562,194562],"mapped","乁"],[[194563,194563],"mapped","𠄢"],[[194564,194564],"mapped","你"],[[194565,194565],"mapped","侮"],[[194566,194566],"mapped","侻"],[[194567,194567],"mapped","倂"],[[194568,194568],"mapped","偺"],[[194569,194569],"mapped","備"],[[194570,194570],"mapped","僧"],[[194571,194571],"mapped","像"],[[194572,194572],"mapped","㒞"],[[194573,194573],"mapped","𠘺"],[[194574,194574],"mapped","免"],[[194575,194575],"mapped","兔"],[[194576,194576],"mapped","兤"],[[194577,194577],"mapped","具"],[[194578,194578],"mapped","𠔜"],[[194579,194579],"mapped","㒹"],[[194580,194580],"mapped","內"],[[194581,194581],"mapped","再"],[[194582,194582],"mapped","𠕋"],[[194583,194583],"mapped","冗"],[[194584,194584],"mapped","冤"],[[194585,194585],"mapped","仌"],[[194586,194586],"mapped","冬"],[[194587,194587],"mapped","况"],[[194588,194588],"mapped","𩇟"],[[194589,194589],"mapped","凵"],[[194590,194590],"mapped","刃"],[[194591,194591],"mapped","㓟"],[[194592,194592],"mapped","刻"],[[194593,194593],"mapped","剆"],[[194594,194594],"mapped","割"],[[194595,194595],"mapped","剷"],[[194596,194596],"mapped","㔕"],[[194597,194597],"mapped","勇"],[[194598,194598],"mapped","勉"],[[194599,194599],"mapped","勤"],[[194600,194600],"mapped","勺"],[[194601,194601],"mapped","包"],[[194602,194602],"mapped","匆"],[[194603,194603],"mapped","北"],[[194604,194604],"mapped","卉"],[[194605,194605],"mapped","卑"],[[194606,194606],"mapped","博"],[[194607,194607],"mapped","即"],[[194608,194608],"mapped","卽"],[[194609,194611],"mapped","卿"],[[194612,194612],"mapped","𠨬"],[[194613,194613],"mapped","灰"],[[194614,194614],"mapped","及"],[[194615,194615],"mapped","叟"],[[194616,194616],"mapped","𠭣"],[[194617,194617],"mapped","叫"],[[194618,194618],"mapped","叱"],[[194619,194619],"mapped","吆"],[[194620,194620],"mapped","咞"],[[194621,194621],"mapped","吸"],[[194622,194622],"mapped","呈"],[[194623,194623],"mapped","周"],[[194624,194624],"mapped","咢"],[[194625,194625],"mapped","哶"],[[194626,194626],"mapped","唐"],[[194627,194627],"mapped","啓"],[[194628,194628],"mapped","啣"],[[194629,194630],"mapped","善"],[[194631,194631],"mapped","喙"],[[194632,194632],"mapped","喫"],[[194633,194633],"mapped","喳"],[[194634,194634],"mapped","嗂"],[[194635,194635],"mapped","圖"],[[194636,194636],"mapped","嘆"],[[194637,194637],"mapped","圗"],[[194638,194638],"mapped","噑"],[[194639,194639],"mapped","噴"],[[194640,194640],"mapped","切"],[[194641,194641],"mapped","壮"],[[194642,194642],"mapped","城"],[[194643,194643],"mapped","埴"],[[194644,194644],"mapped","堍"],[[194645,194645],"mapped","型"],[[194646,194646],"mapped","堲"],[[194647,194647],"mapped","報"],[[194648,194648],"mapped","墬"],[[194649,194649],"mapped","𡓤"],[[194650,194650],"mapped","売"],[[194651,194651],"mapped","壷"],[[194652,194652],"mapped","夆"],[[194653,194653],"mapped","多"],[[194654,194654],"mapped","夢"],[[194655,194655],"mapped","奢"],[[194656,194656],"mapped","𡚨"],[[194657,194657],"mapped","𡛪"],[[194658,194658],"mapped","姬"],[[194659,194659],"mapped","娛"],[[194660,194660],"mapped","娧"],[[194661,194661],"mapped","姘"],[[194662,194662],"mapped","婦"],[[194663,194663],"mapped","㛮"],[[194664,194664],"disallowed"],[[194665,194665],"mapped","嬈"],[[194666,194667],"mapped","嬾"],[[194668,194668],"mapped","𡧈"],[[194669,194669],"mapped","寃"],[[194670,194670],"mapped","寘"],[[194671,194671],"mapped","寧"],[[194672,194672],"mapped","寳"],[[194673,194673],"mapped","𡬘"],[[194674,194674],"mapped","寿"],[[194675,194675],"mapped","将"],[[194676,194676],"disallowed"],[[194677,194677],"mapped","尢"],[[194678,194678],"mapped","㞁"],[[194679,194679],"mapped","屠"],[[194680,194680],"mapped","屮"],[[194681,194681],"mapped","峀"],[[194682,194682],"mapped","岍"],[[194683,194683],"mapped","𡷤"],[[194684,194684],"mapped","嵃"],[[194685,194685],"mapped","𡷦"],[[194686,194686],"mapped","嵮"],[[194687,194687],"mapped","嵫"],[[194688,194688],"mapped","嵼"],[[194689,194689],"mapped","巡"],[[194690,194690],"mapped","巢"],[[194691,194691],"mapped","㠯"],[[194692,194692],"mapped","巽"],[[194693,194693],"mapped","帨"],[[194694,194694],"mapped","帽"],[[194695,194695],"mapped","幩"],[[194696,194696],"mapped","㡢"],[[194697,194697],"mapped","𢆃"],[[194698,194698],"mapped","㡼"],[[194699,194699],"mapped","庰"],[[194700,194700],"mapped","庳"],[[194701,194701],"mapped","庶"],[[194702,194702],"mapped","廊"],[[194703,194703],"mapped","𪎒"],[[194704,194704],"mapped","廾"],[[194705,194706],"mapped","𢌱"],[[194707,194707],"mapped","舁"],[[194708,194709],"mapped","弢"],[[194710,194710],"mapped","㣇"],[[194711,194711],"mapped","𣊸"],[[194712,194712],"mapped","𦇚"],[[194713,194713],"mapped","形"],[[194714,194714],"mapped","彫"],[[194715,194715],"mapped","㣣"],[[194716,194716],"mapped","徚"],[[194717,194717],"mapped","忍"],[[194718,194718],"mapped","志"],[[194719,194719],"mapped","忹"],[[194720,194720],"mapped","悁"],[[194721,194721],"mapped","㤺"],[[194722,194722],"mapped","㤜"],[[194723,194723],"mapped","悔"],[[194724,194724],"mapped","𢛔"],[[194725,194725],"mapped","惇"],[[194726,194726],"mapped","慈"],[[194727,194727],"mapped","慌"],[[194728,194728],"mapped","慎"],[[194729,194729],"mapped","慌"],[[194730,194730],"mapped","慺"],[[194731,194731],"mapped","憎"],[[194732,194732],"mapped","憲"],[[194733,194733],"mapped","憤"],[[194734,194734],"mapped","憯"],[[194735,194735],"mapped","懞"],[[194736,194736],"mapped","懲"],[[194737,194737],"mapped","懶"],[[194738,194738],"mapped","成"],[[194739,194739],"mapped","戛"],[[194740,194740],"mapped","扝"],[[194741,194741],"mapped","抱"],[[194742,194742],"mapped","拔"],[[194743,194743],"mapped","捐"],[[194744,194744],"mapped","𢬌"],[[194745,194745],"mapped","挽"],[[194746,194746],"mapped","拼"],[[194747,194747],"mapped","捨"],[[194748,194748],"mapped","掃"],[[194749,194749],"mapped","揤"],[[194750,194750],"mapped","𢯱"],[[194751,194751],"mapped","搢"],[[194752,194752],"mapped","揅"],[[194753,194753],"mapped","掩"],[[194754,194754],"mapped","㨮"],[[194755,194755],"mapped","摩"],[[194756,194756],"mapped","摾"],[[194757,194757],"mapped","撝"],[[194758,194758],"mapped","摷"],[[194759,194759],"mapped","㩬"],[[194760,194760],"mapped","敏"],[[194761,194761],"mapped","敬"],[[194762,194762],"mapped","𣀊"],[[194763,194763],"mapped","旣"],[[194764,194764],"mapped","書"],[[194765,194765],"mapped","晉"],[[194766,194766],"mapped","㬙"],[[194767,194767],"mapped","暑"],[[194768,194768],"mapped","㬈"],[[194769,194769],"mapped","㫤"],[[194770,194770],"mapped","冒"],[[194771,194771],"mapped","冕"],[[194772,194772],"mapped","最"],[[194773,194773],"mapped","暜"],[[194774,194774],"mapped","肭"],[[194775,194775],"mapped","䏙"],[[194776,194776],"mapped","朗"],[[194777,194777],"mapped","望"],[[194778,194778],"mapped","朡"],[[194779,194779],"mapped","杞"],[[194780,194780],"mapped","杓"],[[194781,194781],"mapped","𣏃"],[[194782,194782],"mapped","㭉"],[[194783,194783],"mapped","柺"],[[194784,194784],"mapped","枅"],[[194785,194785],"mapped","桒"],[[194786,194786],"mapped","梅"],[[194787,194787],"mapped","𣑭"],[[194788,194788],"mapped","梎"],[[194789,194789],"mapped","栟"],[[194790,194790],"mapped","椔"],[[194791,194791],"mapped","㮝"],[[194792,194792],"mapped","楂"],[[194793,194793],"mapped","榣"],[[194794,194794],"mapped","槪"],[[194795,194795],"mapped","檨"],[[194796,194796],"mapped","𣚣"],[[194797,194797],"mapped","櫛"],[[194798,194798],"mapped","㰘"],[[194799,194799],"mapped","次"],[[194800,194800],"mapped","𣢧"],[[194801,194801],"mapped","歔"],[[194802,194802],"mapped","㱎"],[[194803,194803],"mapped","歲"],[[194804,194804],"mapped","殟"],[[194805,194805],"mapped","殺"],[[194806,194806],"mapped","殻"],[[194807,194807],"mapped","𣪍"],[[194808,194808],"mapped","𡴋"],[[194809,194809],"mapped","𣫺"],[[194810,194810],"mapped","汎"],[[194811,194811],"mapped","𣲼"],[[194812,194812],"mapped","沿"],[[194813,194813],"mapped","泍"],[[194814,194814],"mapped","汧"],[[194815,194815],"mapped","洖"],[[194816,194816],"mapped","派"],[[194817,194817],"mapped","海"],[[194818,194818],"mapped","流"],[[194819,194819],"mapped","浩"],[[194820,194820],"mapped","浸"],[[194821,194821],"mapped","涅"],[[194822,194822],"mapped","𣴞"],[[194823,194823],"mapped","洴"],[[194824,194824],"mapped","港"],[[194825,194825],"mapped","湮"],[[194826,194826],"mapped","㴳"],[[194827,194827],"mapped","滋"],[[194828,194828],"mapped","滇"],[[194829,194829],"mapped","𣻑"],[[194830,194830],"mapped","淹"],[[194831,194831],"mapped","潮"],[[194832,194832],"mapped","𣽞"],[[194833,194833],"mapped","𣾎"],[[194834,194834],"mapped","濆"],[[194835,194835],"mapped","瀹"],[[194836,194836],"mapped","瀞"],[[194837,194837],"mapped","瀛"],[[194838,194838],"mapped","㶖"],[[194839,194839],"mapped","灊"],[[194840,194840],"mapped","災"],[[194841,194841],"mapped","灷"],[[194842,194842],"mapped","炭"],[[194843,194843],"mapped","𠔥"],[[194844,194844],"mapped","煅"],[[194845,194845],"mapped","𤉣"],[[194846,194846],"mapped","熜"],[[194847,194847],"disallowed"],[[194848,194848],"mapped","爨"],[[194849,194849],"mapped","爵"],[[194850,194850],"mapped","牐"],[[194851,194851],"mapped","𤘈"],[[194852,194852],"mapped","犀"],[[194853,194853],"mapped","犕"],[[194854,194854],"mapped","𤜵"],[[194855,194855],"mapped","𤠔"],[[194856,194856],"mapped","獺"],[[194857,194857],"mapped","王"],[[194858,194858],"mapped","㺬"],[[194859,194859],"mapped","玥"],[[194860,194861],"mapped","㺸"],[[194862,194862],"mapped","瑇"],[[194863,194863],"mapped","瑜"],[[194864,194864],"mapped","瑱"],[[194865,194865],"mapped","璅"],[[194866,194866],"mapped","瓊"],[[194867,194867],"mapped","㼛"],[[194868,194868],"mapped","甤"],[[194869,194869],"mapped","𤰶"],[[194870,194870],"mapped","甾"],[[194871,194871],"mapped","𤲒"],[[194872,194872],"mapped","異"],[[194873,194873],"mapped","𢆟"],[[194874,194874],"mapped","瘐"],[[194875,194875],"mapped","𤾡"],[[194876,194876],"mapped","𤾸"],[[194877,194877],"mapped","𥁄"],[[194878,194878],"mapped","㿼"],[[194879,194879],"mapped","䀈"],[[194880,194880],"mapped","直"],[[194881,194881],"mapped","𥃳"],[[194882,194882],"mapped","𥃲"],[[194883,194883],"mapped","𥄙"],[[194884,194884],"mapped","𥄳"],[[194885,194885],"mapped","眞"],[[194886,194887],"mapped","真"],[[194888,194888],"mapped","睊"],[[194889,194889],"mapped","䀹"],[[194890,194890],"mapped","瞋"],[[194891,194891],"mapped","䁆"],[[194892,194892],"mapped","䂖"],[[194893,194893],"mapped","𥐝"],[[194894,194894],"mapped","硎"],[[194895,194895],"mapped","碌"],[[194896,194896],"mapped","磌"],[[194897,194897],"mapped","䃣"],[[194898,194898],"mapped","𥘦"],[[194899,194899],"mapped","祖"],[[194900,194900],"mapped","𥚚"],[[194901,194901],"mapped","𥛅"],[[194902,194902],"mapped","福"],[[194903,194903],"mapped","秫"],[[194904,194904],"mapped","䄯"],[[194905,194905],"mapped","穀"],[[194906,194906],"mapped","穊"],[[194907,194907],"mapped","穏"],[[194908,194908],"mapped","𥥼"],[[194909,194910],"mapped","𥪧"],[[194911,194911],"disallowed"],[[194912,194912],"mapped","䈂"],[[194913,194913],"mapped","𥮫"],[[194914,194914],"mapped","篆"],[[194915,194915],"mapped","築"],[[194916,194916],"mapped","䈧"],[[194917,194917],"mapped","𥲀"],[[194918,194918],"mapped","糒"],[[194919,194919],"mapped","䊠"],[[194920,194920],"mapped","糨"],[[194921,194921],"mapped","糣"],[[194922,194922],"mapped","紀"],[[194923,194923],"mapped","𥾆"],[[194924,194924],"mapped","絣"],[[194925,194925],"mapped","䌁"],[[194926,194926],"mapped","緇"],[[194927,194927],"mapped","縂"],[[194928,194928],"mapped","繅"],[[194929,194929],"mapped","䌴"],[[194930,194930],"mapped","𦈨"],[[194931,194931],"mapped","𦉇"],[[194932,194932],"mapped","䍙"],[[194933,194933],"mapped","𦋙"],[[194934,194934],"mapped","罺"],[[194935,194935],"mapped","𦌾"],[[194936,194936],"mapped","羕"],[[194937,194937],"mapped","翺"],[[194938,194938],"mapped","者"],[[194939,194939],"mapped","𦓚"],[[194940,194940],"mapped","𦔣"],[[194941,194941],"mapped","聠"],[[194942,194942],"mapped","𦖨"],[[194943,194943],"mapped","聰"],[[194944,194944],"mapped","𣍟"],[[194945,194945],"mapped","䏕"],[[194946,194946],"mapped","育"],[[194947,194947],"mapped","脃"],[[194948,194948],"mapped","䐋"],[[194949,194949],"mapped","脾"],[[194950,194950],"mapped","媵"],[[194951,194951],"mapped","𦞧"],[[194952,194952],"mapped","𦞵"],[[194953,194953],"mapped","𣎓"],[[194954,194954],"mapped","𣎜"],[[194955,194955],"mapped","舁"],[[194956,194956],"mapped","舄"],[[194957,194957],"mapped","辞"],[[194958,194958],"mapped","䑫"],[[194959,194959],"mapped","芑"],[[194960,194960],"mapped","芋"],[[194961,194961],"mapped","芝"],[[194962,194962],"mapped","劳"],[[194963,194963],"mapped","花"],[[194964,194964],"mapped","芳"],[[194965,194965],"mapped","芽"],[[194966,194966],"mapped","苦"],[[194967,194967],"mapped","𦬼"],[[194968,194968],"mapped","若"],[[194969,194969],"mapped","茝"],[[194970,194970],"mapped","荣"],[[194971,194971],"mapped","莭"],[[194972,194972],"mapped","茣"],[[194973,194973],"mapped","莽"],[[194974,194974],"mapped","菧"],[[194975,194975],"mapped","著"],[[194976,194976],"mapped","荓"],[[194977,194977],"mapped","菊"],[[194978,194978],"mapped","菌"],[[194979,194979],"mapped","菜"],[[194980,194980],"mapped","𦰶"],[[194981,194981],"mapped","𦵫"],[[194982,194982],"mapped","𦳕"],[[194983,194983],"mapped","䔫"],[[194984,194984],"mapped","蓱"],[[194985,194985],"mapped","蓳"],[[194986,194986],"mapped","蔖"],[[194987,194987],"mapped","𧏊"],[[194988,194988],"mapped","蕤"],[[194989,194989],"mapped","𦼬"],[[194990,194990],"mapped","䕝"],[[194991,194991],"mapped","䕡"],[[194992,194992],"mapped","𦾱"],[[194993,194993],"mapped","𧃒"],[[194994,194994],"mapped","䕫"],[[194995,194995],"mapped","虐"],[[194996,194996],"mapped","虜"],[[194997,194997],"mapped","虧"],[[194998,194998],"mapped","虩"],[[194999,194999],"mapped","蚩"],[[195000,195000],"mapped","蚈"],[[195001,195001],"mapped","蜎"],[[195002,195002],"mapped","蛢"],[[195003,195003],"mapped","蝹"],[[195004,195004],"mapped","蜨"],[[195005,195005],"mapped","蝫"],[[195006,195006],"mapped","螆"],[[195007,195007],"disallowed"],[[195008,195008],"mapped","蟡"],[[195009,195009],"mapped","蠁"],[[195010,195010],"mapped","䗹"],[[195011,195011],"mapped","衠"],[[195012,195012],"mapped","衣"],[[195013,195013],"mapped","𧙧"],[[195014,195014],"mapped","裗"],[[195015,195015],"mapped","裞"],[[195016,195016],"mapped","䘵"],[[195017,195017],"mapped","裺"],[[195018,195018],"mapped","㒻"],[[195019,195019],"mapped","𧢮"],[[195020,195020],"mapped","𧥦"],[[195021,195021],"mapped","䚾"],[[195022,195022],"mapped","䛇"],[[195023,195023],"mapped","誠"],[[195024,195024],"mapped","諭"],[[195025,195025],"mapped","變"],[[195026,195026],"mapped","豕"],[[195027,195027],"mapped","𧲨"],[[195028,195028],"mapped","貫"],[[195029,195029],"mapped","賁"],[[195030,195030],"mapped","贛"],[[195031,195031],"mapped","起"],[[195032,195032],"mapped","𧼯"],[[195033,195033],"mapped","𠠄"],[[195034,195034],"mapped","跋"],[[195035,195035],"mapped","趼"],[[195036,195036],"mapped","跰"],[[195037,195037],"mapped","𠣞"],[[195038,195038],"mapped","軔"],[[195039,195039],"mapped","輸"],[[195040,195040],"mapped","𨗒"],[[195041,195041],"mapped","𨗭"],[[195042,195042],"mapped","邔"],[[195043,195043],"mapped","郱"],[[195044,195044],"mapped","鄑"],[[195045,195045],"mapped","𨜮"],[[195046,195046],"mapped","鄛"],[[195047,195047],"mapped","鈸"],[[195048,195048],"mapped","鋗"],[[195049,195049],"mapped","鋘"],[[195050,195050],"mapped","鉼"],[[195051,195051],"mapped","鏹"],[[195052,195052],"mapped","鐕"],[[195053,195053],"mapped","𨯺"],[[195054,195054],"mapped","開"],[[195055,195055],"mapped","䦕"],[[195056,195056],"mapped","閷"],[[195057,195057],"mapped","𨵷"],[[195058,195058],"mapped","䧦"],[[195059,195059],"mapped","雃"],[[195060,195060],"mapped","嶲"],[[195061,195061],"mapped","霣"],[[195062,195062],"mapped","𩅅"],[[195063,195063],"mapped","𩈚"],[[195064,195064],"mapped","䩮"],[[195065,195065],"mapped","䩶"],[[195066,195066],"mapped","韠"],[[195067,195067],"mapped","𩐊"],[[195068,195068],"mapped","䪲"],[[195069,195069],"mapped","𩒖"],[[195070,195071],"mapped","頋"],[[195072,195072],"mapped","頩"],[[195073,195073],"mapped","𩖶"],[[195074,195074],"mapped","飢"],[[195075,195075],"mapped","䬳"],[[195076,195076],"mapped","餩"],[[195077,195077],"mapped","馧"],[[195078,195078],"mapped","駂"],[[195079,195079],"mapped","駾"],[[195080,195080],"mapped","䯎"],[[195081,195081],"mapped","𩬰"],[[195082,195082],"mapped","鬒"],[[195083,195083],"mapped","鱀"],[[195084,195084],"mapped","鳽"],[[195085,195085],"mapped","䳎"],[[195086,195086],"mapped","䳭"],[[195087,195087],"mapped","鵧"],[[195088,195088],"mapped","𪃎"],[[195089,195089],"mapped","䳸"],[[195090,195090],"mapped","𪄅"],[[195091,195091],"mapped","𪈎"],[[195092,195092],"mapped","𪊑"],[[195093,195093],"mapped","麻"],[[195094,195094],"mapped","䵖"],[[195095,195095],"mapped","黹"],[[195096,195096],"mapped","黾"],[[195097,195097],"mapped","鼅"],[[195098,195098],"mapped","鼏"],[[195099,195099],"mapped","鼖"],[[195100,195100],"mapped","鼻"],[[195101,195101],"mapped","𪘀"],[[195102,196605],"disallowed"],[[196606,196607],"disallowed"],[[196608,262141],"disallowed"],[[262142,262143],"disallowed"],[[262144,327677],"disallowed"],[[327678,327679],"disallowed"],[[327680,393213],"disallowed"],[[393214,393215],"disallowed"],[[393216,458749],"disallowed"],[[458750,458751],"disallowed"],[[458752,524285],"disallowed"],[[524286,524287],"disallowed"],[[524288,589821],"disallowed"],[[589822,589823],"disallowed"],[[589824,655357],"disallowed"],[[655358,655359],"disallowed"],[[655360,720893],"disallowed"],[[720894,720895],"disallowed"],[[720896,786429],"disallowed"],[[786430,786431],"disallowed"],[[786432,851965],"disallowed"],[[851966,851967],"disallowed"],[[851968,917501],"disallowed"],[[917502,917503],"disallowed"],[[917504,917504],"disallowed"],[[917505,917505],"disallowed"],[[917506,917535],"disallowed"],[[917536,917631],"disallowed"],[[917632,917759],"disallowed"],[[917760,917999],"ignored"],[[918000,983037],"disallowed"],[[983038,983039],"disallowed"],[[983040,1048573],"disallowed"],[[1048574,1048575],"disallowed"],[[1048576,1114109],"disallowed"],[[1114110,1114111],"disallowed"]] + +/***/ }), + +/***/ 401: +/***/ (function(module, exports, __webpack_require__) { + +/* WEBPACK VAR INJECTION */(function(global, module) {/** + * lodash (Custom Build) + * Build: `lodash modularize exports="npm" -o ./` + * Copyright jQuery Foundation and other contributors + * Released under MIT license + * Based on Underscore.js 1.8.3 + * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + */ + +/** Used as the size to enable large array optimizations. */ +var LARGE_ARRAY_SIZE = 200; + +/** Used as the `TypeError` message for "Functions" methods. */ +var FUNC_ERROR_TEXT = 'Expected a function'; + +/** Used to stand-in for `undefined` hash values. */ +var HASH_UNDEFINED = '__lodash_hash_undefined__'; + +/** Used to compose bitmasks for comparison styles. */ +var UNORDERED_COMPARE_FLAG = 1, + PARTIAL_COMPARE_FLAG = 2; + +/** Used as references for various `Number` constants. */ +var INFINITY = 1 / 0, + MAX_SAFE_INTEGER = 9007199254740991; + +/** `Object#toString` result references. */ +var argsTag = '[object Arguments]', + arrayTag = '[object Array]', + boolTag = '[object Boolean]', + dateTag = '[object Date]', + errorTag = '[object Error]', + funcTag = '[object Function]', + genTag = '[object GeneratorFunction]', + mapTag = '[object Map]', + numberTag = '[object Number]', + objectTag = '[object Object]', + promiseTag = '[object Promise]', + regexpTag = '[object RegExp]', + setTag = '[object Set]', + stringTag = '[object String]', + symbolTag = '[object Symbol]', + weakMapTag = '[object WeakMap]'; + +var arrayBufferTag = '[object ArrayBuffer]', + dataViewTag = '[object DataView]', + float32Tag = '[object Float32Array]', + float64Tag = '[object Float64Array]', + int8Tag = '[object Int8Array]', + int16Tag = '[object Int16Array]', + int32Tag = '[object Int32Array]', + uint8Tag = '[object Uint8Array]', + uint8ClampedTag = '[object Uint8ClampedArray]', + uint16Tag = '[object Uint16Array]', + uint32Tag = '[object Uint32Array]'; + +/** Used to match property names within property paths. */ +var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/, + reIsPlainProp = /^\w*$/, + reLeadingDot = /^\./, + rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g; + +/** + * Used to match `RegExp` + * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns). + */ +var reRegExpChar = /[\\^$.*+?()[\]{}|]/g; + +/** Used to match backslashes in property paths. */ +var reEscapeChar = /\\(\\)?/g; + +/** Used to detect host constructors (Safari). */ +var reIsHostCtor = /^\[object .+?Constructor\]$/; + +/** Used to detect unsigned integer values. */ +var reIsUint = /^(?:0|[1-9]\d*)$/; + +/** Used to identify `toStringTag` values of typed arrays. */ +var typedArrayTags = {}; +typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = +typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = +typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = +typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = +typedArrayTags[uint32Tag] = true; +typedArrayTags[argsTag] = typedArrayTags[arrayTag] = +typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = +typedArrayTags[dataViewTag] = typedArrayTags[dateTag] = +typedArrayTags[errorTag] = typedArrayTags[funcTag] = +typedArrayTags[mapTag] = typedArrayTags[numberTag] = +typedArrayTags[objectTag] = typedArrayTags[regexpTag] = +typedArrayTags[setTag] = typedArrayTags[stringTag] = +typedArrayTags[weakMapTag] = false; + +/** Detect free variable `global` from Node.js. */ +var freeGlobal = typeof global == 'object' && global && global.Object === Object && global; + +/** Detect free variable `self`. */ +var freeSelf = typeof self == 'object' && self && self.Object === Object && self; + +/** Used as a reference to the global object. */ +var root = freeGlobal || freeSelf || Function('return this')(); + +/** Detect free variable `exports`. */ +var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports; + +/** Detect free variable `module`. */ +var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module; + +/** Detect the popular CommonJS extension `module.exports`. */ +var moduleExports = freeModule && freeModule.exports === freeExports; + +/** Detect free variable `process` from Node.js. */ +var freeProcess = moduleExports && freeGlobal.process; + +/** Used to access faster Node.js helpers. */ +var nodeUtil = (function() { + try { + return freeProcess && freeProcess.binding('util'); + } catch (e) {} +}()); + +/* Node.js helper references. */ +var nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray; + +/** + * A faster alternative to `Function#apply`, this function invokes `func` + * with the `this` binding of `thisArg` and the arguments of `args`. + * + * @private + * @param {Function} func The function to invoke. + * @param {*} thisArg The `this` binding of `func`. + * @param {Array} args The arguments to invoke `func` with. + * @returns {*} Returns the result of `func`. + */ +function apply(func, thisArg, args) { + switch (args.length) { + case 0: return func.call(thisArg); + case 1: return func.call(thisArg, args[0]); + case 2: return func.call(thisArg, args[0], args[1]); + case 3: return func.call(thisArg, args[0], args[1], args[2]); + } + return func.apply(thisArg, args); +} + +/** + * A specialized version of `_.map` for arrays without support for iteratee + * shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the new mapped array. + */ +function arrayMap(array, iteratee) { + var index = -1, + length = array ? array.length : 0, + result = Array(length); + + while (++index < length) { + result[index] = iteratee(array[index], index, array); + } + return result; +} + +/** + * Appends the elements of `values` to `array`. + * + * @private + * @param {Array} array The array to modify. + * @param {Array} values The values to append. + * @returns {Array} Returns `array`. + */ +function arrayPush(array, values) { + var index = -1, + length = values.length, + offset = array.length; + + while (++index < length) { + array[offset + index] = values[index]; + } + return array; +} + +/** + * A specialized version of `_.some` for arrays without support for iteratee + * shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if any element passes the predicate check, + * else `false`. + */ +function arraySome(array, predicate) { + var index = -1, + length = array ? array.length : 0; + + while (++index < length) { + if (predicate(array[index], index, array)) { + return true; + } + } + return false; +} + +/** + * The base implementation of `_.property` without support for deep paths. + * + * @private + * @param {string} key The key of the property to get. + * @returns {Function} Returns the new accessor function. + */ +function baseProperty(key) { + return function(object) { + return object == null ? undefined : object[key]; + }; +} + +/** + * The base implementation of `_.sortBy` which uses `comparer` to define the + * sort order of `array` and replaces criteria objects with their corresponding + * values. + * + * @private + * @param {Array} array The array to sort. + * @param {Function} comparer The function to define sort order. + * @returns {Array} Returns `array`. + */ +function baseSortBy(array, comparer) { + var length = array.length; + + array.sort(comparer); + while (length--) { + array[length] = array[length].value; + } + return array; +} + +/** + * The base implementation of `_.times` without support for iteratee shorthands + * or max array length checks. + * + * @private + * @param {number} n The number of times to invoke `iteratee`. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the array of results. + */ +function baseTimes(n, iteratee) { + var index = -1, + result = Array(n); + + while (++index < n) { + result[index] = iteratee(index); + } + return result; +} + +/** + * The base implementation of `_.unary` without support for storing metadata. + * + * @private + * @param {Function} func The function to cap arguments for. + * @returns {Function} Returns the new capped function. + */ +function baseUnary(func) { + return function(value) { + return func(value); + }; +} + +/** + * Gets the value at `key` of `object`. + * + * @private + * @param {Object} [object] The object to query. + * @param {string} key The key of the property to get. + * @returns {*} Returns the property value. + */ +function getValue(object, key) { + return object == null ? undefined : object[key]; +} + +/** + * Checks if `value` is a host object in IE < 9. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a host object, else `false`. + */ +function isHostObject(value) { + // Many host objects are `Object` objects that can coerce to strings + // despite having improperly defined `toString` methods. + var result = false; + if (value != null && typeof value.toString != 'function') { + try { + result = !!(value + ''); + } catch (e) {} + } + return result; +} + +/** + * Converts `map` to its key-value pairs. + * + * @private + * @param {Object} map The map to convert. + * @returns {Array} Returns the key-value pairs. + */ +function mapToArray(map) { + var index = -1, + result = Array(map.size); + + map.forEach(function(value, key) { + result[++index] = [key, value]; + }); + return result; +} + +/** + * Creates a unary function that invokes `func` with its argument transformed. + * + * @private + * @param {Function} func The function to wrap. + * @param {Function} transform The argument transform. + * @returns {Function} Returns the new function. + */ +function overArg(func, transform) { + return function(arg) { + return func(transform(arg)); + }; +} + +/** + * Converts `set` to an array of its values. + * + * @private + * @param {Object} set The set to convert. + * @returns {Array} Returns the values. + */ +function setToArray(set) { + var index = -1, + result = Array(set.size); + + set.forEach(function(value) { + result[++index] = value; + }); + return result; +} + +/** Used for built-in method references. */ +var arrayProto = Array.prototype, + funcProto = Function.prototype, + objectProto = Object.prototype; + +/** Used to detect overreaching core-js shims. */ +var coreJsData = root['__core-js_shared__']; + +/** Used to detect methods masquerading as native. */ +var maskSrcKey = (function() { + var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || ''); + return uid ? ('Symbol(src)_1.' + uid) : ''; +}()); + +/** Used to resolve the decompiled source of functions. */ +var funcToString = funcProto.toString; + +/** Used to check objects for own properties. */ +var hasOwnProperty = objectProto.hasOwnProperty; + +/** + * Used to resolve the + * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) + * of values. + */ +var objectToString = objectProto.toString; + +/** Used to detect if a method is native. */ +var reIsNative = RegExp('^' + + funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&') + .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' +); + +/** Built-in value references. */ +var Symbol = root.Symbol, + Uint8Array = root.Uint8Array, + propertyIsEnumerable = objectProto.propertyIsEnumerable, + splice = arrayProto.splice, + spreadableSymbol = Symbol ? Symbol.isConcatSpreadable : undefined; + +/* Built-in method references for those with the same name as other `lodash` methods. */ +var nativeKeys = overArg(Object.keys, Object), + nativeMax = Math.max; + +/* Built-in method references that are verified to be native. */ +var DataView = getNative(root, 'DataView'), + Map = getNative(root, 'Map'), + Promise = getNative(root, 'Promise'), + Set = getNative(root, 'Set'), + WeakMap = getNative(root, 'WeakMap'), + nativeCreate = getNative(Object, 'create'); + +/** Used to detect maps, sets, and weakmaps. */ +var dataViewCtorString = toSource(DataView), + mapCtorString = toSource(Map), + promiseCtorString = toSource(Promise), + setCtorString = toSource(Set), + weakMapCtorString = toSource(WeakMap); + +/** Used to convert symbols to primitives and strings. */ +var symbolProto = Symbol ? Symbol.prototype : undefined, + symbolValueOf = symbolProto ? symbolProto.valueOf : undefined, + symbolToString = symbolProto ? symbolProto.toString : undefined; + +/** + * Creates a hash object. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ +function Hash(entries) { + var index = -1, + length = entries ? entries.length : 0; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } +} + +/** + * Removes all key-value entries from the hash. + * + * @private + * @name clear + * @memberOf Hash + */ +function hashClear() { + this.__data__ = nativeCreate ? nativeCreate(null) : {}; +} + +/** + * Removes `key` and its value from the hash. + * + * @private + * @name delete + * @memberOf Hash + * @param {Object} hash The hash to modify. + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ +function hashDelete(key) { + return this.has(key) && delete this.__data__[key]; +} + +/** + * Gets the hash value for `key`. + * + * @private + * @name get + * @memberOf Hash + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ +function hashGet(key) { + var data = this.__data__; + if (nativeCreate) { + var result = data[key]; + return result === HASH_UNDEFINED ? undefined : result; + } + return hasOwnProperty.call(data, key) ? data[key] : undefined; +} + +/** + * Checks if a hash value for `key` exists. + * + * @private + * @name has + * @memberOf Hash + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ +function hashHas(key) { + var data = this.__data__; + return nativeCreate ? data[key] !== undefined : hasOwnProperty.call(data, key); +} + +/** + * Sets the hash `key` to `value`. + * + * @private + * @name set + * @memberOf Hash + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the hash instance. + */ +function hashSet(key, value) { + var data = this.__data__; + data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value; + return this; +} + +// Add methods to `Hash`. +Hash.prototype.clear = hashClear; +Hash.prototype['delete'] = hashDelete; +Hash.prototype.get = hashGet; +Hash.prototype.has = hashHas; +Hash.prototype.set = hashSet; + +/** + * Creates an list cache object. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ +function ListCache(entries) { + var index = -1, + length = entries ? entries.length : 0; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } +} + +/** + * Removes all key-value entries from the list cache. + * + * @private + * @name clear + * @memberOf ListCache + */ +function listCacheClear() { + this.__data__ = []; +} + +/** + * Removes `key` and its value from the list cache. + * + * @private + * @name delete + * @memberOf ListCache + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ +function listCacheDelete(key) { + var data = this.__data__, + index = assocIndexOf(data, key); + + if (index < 0) { + return false; + } + var lastIndex = data.length - 1; + if (index == lastIndex) { + data.pop(); + } else { + splice.call(data, index, 1); + } + return true; +} + +/** + * Gets the list cache value for `key`. + * + * @private + * @name get + * @memberOf ListCache + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ +function listCacheGet(key) { + var data = this.__data__, + index = assocIndexOf(data, key); + + return index < 0 ? undefined : data[index][1]; +} + +/** + * Checks if a list cache value for `key` exists. + * + * @private + * @name has + * @memberOf ListCache + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ +function listCacheHas(key) { + return assocIndexOf(this.__data__, key) > -1; +} + +/** + * Sets the list cache `key` to `value`. + * + * @private + * @name set + * @memberOf ListCache + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the list cache instance. + */ +function listCacheSet(key, value) { + var data = this.__data__, + index = assocIndexOf(data, key); + + if (index < 0) { + data.push([key, value]); + } else { + data[index][1] = value; + } + return this; +} + +// Add methods to `ListCache`. +ListCache.prototype.clear = listCacheClear; +ListCache.prototype['delete'] = listCacheDelete; +ListCache.prototype.get = listCacheGet; +ListCache.prototype.has = listCacheHas; +ListCache.prototype.set = listCacheSet; + +/** + * Creates a map cache object to store key-value pairs. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ +function MapCache(entries) { + var index = -1, + length = entries ? entries.length : 0; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } +} + +/** + * Removes all key-value entries from the map. + * + * @private + * @name clear + * @memberOf MapCache + */ +function mapCacheClear() { + this.__data__ = { + 'hash': new Hash, + 'map': new (Map || ListCache), + 'string': new Hash + }; +} + +/** + * Removes `key` and its value from the map. + * + * @private + * @name delete + * @memberOf MapCache + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ +function mapCacheDelete(key) { + return getMapData(this, key)['delete'](key); +} + +/** + * Gets the map value for `key`. + * + * @private + * @name get + * @memberOf MapCache + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ +function mapCacheGet(key) { + return getMapData(this, key).get(key); +} + +/** + * Checks if a map value for `key` exists. + * + * @private + * @name has + * @memberOf MapCache + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ +function mapCacheHas(key) { + return getMapData(this, key).has(key); +} + +/** + * Sets the map `key` to `value`. + * + * @private + * @name set + * @memberOf MapCache + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the map cache instance. + */ +function mapCacheSet(key, value) { + getMapData(this, key).set(key, value); + return this; +} + +// Add methods to `MapCache`. +MapCache.prototype.clear = mapCacheClear; +MapCache.prototype['delete'] = mapCacheDelete; +MapCache.prototype.get = mapCacheGet; +MapCache.prototype.has = mapCacheHas; +MapCache.prototype.set = mapCacheSet; + +/** + * + * Creates an array cache object to store unique values. + * + * @private + * @constructor + * @param {Array} [values] The values to cache. + */ +function SetCache(values) { + var index = -1, + length = values ? values.length : 0; + + this.__data__ = new MapCache; + while (++index < length) { + this.add(values[index]); + } +} + +/** + * Adds `value` to the array cache. + * + * @private + * @name add + * @memberOf SetCache + * @alias push + * @param {*} value The value to cache. + * @returns {Object} Returns the cache instance. + */ +function setCacheAdd(value) { + this.__data__.set(value, HASH_UNDEFINED); + return this; +} + +/** + * Checks if `value` is in the array cache. + * + * @private + * @name has + * @memberOf SetCache + * @param {*} value The value to search for. + * @returns {number} Returns `true` if `value` is found, else `false`. + */ +function setCacheHas(value) { + return this.__data__.has(value); +} + +// Add methods to `SetCache`. +SetCache.prototype.add = SetCache.prototype.push = setCacheAdd; +SetCache.prototype.has = setCacheHas; + +/** + * Creates a stack cache object to store key-value pairs. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ +function Stack(entries) { + this.__data__ = new ListCache(entries); +} + +/** + * Removes all key-value entries from the stack. + * + * @private + * @name clear + * @memberOf Stack + */ +function stackClear() { + this.__data__ = new ListCache; +} + +/** + * Removes `key` and its value from the stack. + * + * @private + * @name delete + * @memberOf Stack + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ +function stackDelete(key) { + return this.__data__['delete'](key); +} + +/** + * Gets the stack value for `key`. + * + * @private + * @name get + * @memberOf Stack + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ +function stackGet(key) { + return this.__data__.get(key); +} + +/** + * Checks if a stack value for `key` exists. + * + * @private + * @name has + * @memberOf Stack + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ +function stackHas(key) { + return this.__data__.has(key); +} + +/** + * Sets the stack `key` to `value`. + * + * @private + * @name set + * @memberOf Stack + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the stack cache instance. + */ +function stackSet(key, value) { + var cache = this.__data__; + if (cache instanceof ListCache) { + var pairs = cache.__data__; + if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) { + pairs.push([key, value]); + return this; + } + cache = this.__data__ = new MapCache(pairs); + } + cache.set(key, value); + return this; +} + +// Add methods to `Stack`. +Stack.prototype.clear = stackClear; +Stack.prototype['delete'] = stackDelete; +Stack.prototype.get = stackGet; +Stack.prototype.has = stackHas; +Stack.prototype.set = stackSet; + +/** + * Creates an array of the enumerable property names of the array-like `value`. + * + * @private + * @param {*} value The value to query. + * @param {boolean} inherited Specify returning inherited property names. + * @returns {Array} Returns the array of property names. + */ +function arrayLikeKeys(value, inherited) { + // Safari 8.1 makes `arguments.callee` enumerable in strict mode. + // Safari 9 makes `arguments.length` enumerable in strict mode. + var result = (isArray(value) || isArguments(value)) + ? baseTimes(value.length, String) + : []; + + var length = result.length, + skipIndexes = !!length; + + for (var key in value) { + if ((inherited || hasOwnProperty.call(value, key)) && + !(skipIndexes && (key == 'length' || isIndex(key, length)))) { + result.push(key); + } + } + return result; +} + +/** + * Gets the index at which the `key` is found in `array` of key-value pairs. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} key The key to search for. + * @returns {number} Returns the index of the matched value, else `-1`. + */ +function assocIndexOf(array, key) { + var length = array.length; + while (length--) { + if (eq(array[length][0], key)) { + return length; + } + } + return -1; +} + +/** + * The base implementation of `_.forEach` without support for iteratee shorthands. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array|Object} Returns `collection`. + */ +var baseEach = createBaseEach(baseForOwn); + +/** + * The base implementation of `_.flatten` with support for restricting flattening. + * + * @private + * @param {Array} array The array to flatten. + * @param {number} depth The maximum recursion depth. + * @param {boolean} [predicate=isFlattenable] The function invoked per iteration. + * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks. + * @param {Array} [result=[]] The initial result value. + * @returns {Array} Returns the new flattened array. + */ +function baseFlatten(array, depth, predicate, isStrict, result) { + var index = -1, + length = array.length; + + predicate || (predicate = isFlattenable); + result || (result = []); + + while (++index < length) { + var value = array[index]; + if (depth > 0 && predicate(value)) { + if (depth > 1) { + // Recursively flatten arrays (susceptible to call stack limits). + baseFlatten(value, depth - 1, predicate, isStrict, result); + } else { + arrayPush(result, value); + } + } else if (!isStrict) { + result[result.length] = value; + } + } + return result; +} + +/** + * The base implementation of `baseForOwn` which iterates over `object` + * properties returned by `keysFunc` and invokes `iteratee` for each property. + * Iteratee functions may exit iteration early by explicitly returning `false`. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {Function} keysFunc The function to get the keys of `object`. + * @returns {Object} Returns `object`. + */ +var baseFor = createBaseFor(); + +/** + * The base implementation of `_.forOwn` without support for iteratee shorthands. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Object} Returns `object`. + */ +function baseForOwn(object, iteratee) { + return object && baseFor(object, iteratee, keys); +} + +/** + * The base implementation of `_.get` without support for default values. + * + * @private + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to get. + * @returns {*} Returns the resolved value. + */ +function baseGet(object, path) { + path = isKey(path, object) ? [path] : castPath(path); + + var index = 0, + length = path.length; + + while (object != null && index < length) { + object = object[toKey(path[index++])]; + } + return (index && index == length) ? object : undefined; +} + +/** + * The base implementation of `getTag`. + * + * @private + * @param {*} value The value to query. + * @returns {string} Returns the `toStringTag`. + */ +function baseGetTag(value) { + return objectToString.call(value); +} + +/** + * The base implementation of `_.hasIn` without support for deep paths. + * + * @private + * @param {Object} [object] The object to query. + * @param {Array|string} key The key to check. + * @returns {boolean} Returns `true` if `key` exists, else `false`. + */ +function baseHasIn(object, key) { + return object != null && key in Object(object); +} + +/** + * The base implementation of `_.isEqual` which supports partial comparisons + * and tracks traversed objects. + * + * @private + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @param {Function} [customizer] The function to customize comparisons. + * @param {boolean} [bitmask] The bitmask of comparison flags. + * The bitmask may be composed of the following flags: + * 1 - Unordered comparison + * 2 - Partial comparison + * @param {Object} [stack] Tracks traversed `value` and `other` objects. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + */ +function baseIsEqual(value, other, customizer, bitmask, stack) { + if (value === other) { + return true; + } + if (value == null || other == null || (!isObject(value) && !isObjectLike(other))) { + return value !== value && other !== other; + } + return baseIsEqualDeep(value, other, baseIsEqual, customizer, bitmask, stack); +} + +/** + * A specialized version of `baseIsEqual` for arrays and objects which performs + * deep comparisons and tracks traversed objects enabling objects with circular + * references to be compared. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Function} [customizer] The function to customize comparisons. + * @param {number} [bitmask] The bitmask of comparison flags. See `baseIsEqual` + * for more details. + * @param {Object} [stack] Tracks traversed `object` and `other` objects. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ +function baseIsEqualDeep(object, other, equalFunc, customizer, bitmask, stack) { + var objIsArr = isArray(object), + othIsArr = isArray(other), + objTag = arrayTag, + othTag = arrayTag; + + if (!objIsArr) { + objTag = getTag(object); + objTag = objTag == argsTag ? objectTag : objTag; + } + if (!othIsArr) { + othTag = getTag(other); + othTag = othTag == argsTag ? objectTag : othTag; + } + var objIsObj = objTag == objectTag && !isHostObject(object), + othIsObj = othTag == objectTag && !isHostObject(other), + isSameTag = objTag == othTag; + + if (isSameTag && !objIsObj) { + stack || (stack = new Stack); + return (objIsArr || isTypedArray(object)) + ? equalArrays(object, other, equalFunc, customizer, bitmask, stack) + : equalByTag(object, other, objTag, equalFunc, customizer, bitmask, stack); + } + if (!(bitmask & PARTIAL_COMPARE_FLAG)) { + var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'), + othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__'); + + if (objIsWrapped || othIsWrapped) { + var objUnwrapped = objIsWrapped ? object.value() : object, + othUnwrapped = othIsWrapped ? other.value() : other; + + stack || (stack = new Stack); + return equalFunc(objUnwrapped, othUnwrapped, customizer, bitmask, stack); + } + } + if (!isSameTag) { + return false; + } + stack || (stack = new Stack); + return equalObjects(object, other, equalFunc, customizer, bitmask, stack); +} + +/** + * The base implementation of `_.isMatch` without support for iteratee shorthands. + * + * @private + * @param {Object} object The object to inspect. + * @param {Object} source The object of property values to match. + * @param {Array} matchData The property names, values, and compare flags to match. + * @param {Function} [customizer] The function to customize comparisons. + * @returns {boolean} Returns `true` if `object` is a match, else `false`. + */ +function baseIsMatch(object, source, matchData, customizer) { + var index = matchData.length, + length = index, + noCustomizer = !customizer; + + if (object == null) { + return !length; + } + object = Object(object); + while (index--) { + var data = matchData[index]; + if ((noCustomizer && data[2]) + ? data[1] !== object[data[0]] + : !(data[0] in object) + ) { + return false; + } + } + while (++index < length) { + data = matchData[index]; + var key = data[0], + objValue = object[key], + srcValue = data[1]; + + if (noCustomizer && data[2]) { + if (objValue === undefined && !(key in object)) { + return false; + } + } else { + var stack = new Stack; + if (customizer) { + var result = customizer(objValue, srcValue, key, object, source, stack); + } + if (!(result === undefined + ? baseIsEqual(srcValue, objValue, customizer, UNORDERED_COMPARE_FLAG | PARTIAL_COMPARE_FLAG, stack) + : result + )) { + return false; + } + } + } + return true; +} + +/** + * The base implementation of `_.isNative` without bad shim checks. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a native function, + * else `false`. + */ +function baseIsNative(value) { + if (!isObject(value) || isMasked(value)) { + return false; + } + var pattern = (isFunction(value) || isHostObject(value)) ? reIsNative : reIsHostCtor; + return pattern.test(toSource(value)); +} + +/** + * The base implementation of `_.isTypedArray` without Node.js optimizations. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. + */ +function baseIsTypedArray(value) { + return isObjectLike(value) && + isLength(value.length) && !!typedArrayTags[objectToString.call(value)]; +} + +/** + * The base implementation of `_.iteratee`. + * + * @private + * @param {*} [value=_.identity] The value to convert to an iteratee. + * @returns {Function} Returns the iteratee. + */ +function baseIteratee(value) { + // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9. + // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details. + if (typeof value == 'function') { + return value; + } + if (value == null) { + return identity; + } + if (typeof value == 'object') { + return isArray(value) + ? baseMatchesProperty(value[0], value[1]) + : baseMatches(value); + } + return property(value); +} + +/** + * The base implementation of `_.keys` which doesn't treat sparse arrays as dense. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + */ +function baseKeys(object) { + if (!isPrototype(object)) { + return nativeKeys(object); + } + var result = []; + for (var key in Object(object)) { + if (hasOwnProperty.call(object, key) && key != 'constructor') { + result.push(key); + } + } + return result; +} + +/** + * The base implementation of `_.map` without support for iteratee shorthands. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the new mapped array. + */ +function baseMap(collection, iteratee) { + var index = -1, + result = isArrayLike(collection) ? Array(collection.length) : []; + + baseEach(collection, function(value, key, collection) { + result[++index] = iteratee(value, key, collection); + }); + return result; +} + +/** + * The base implementation of `_.matches` which doesn't clone `source`. + * + * @private + * @param {Object} source The object of property values to match. + * @returns {Function} Returns the new spec function. + */ +function baseMatches(source) { + var matchData = getMatchData(source); + if (matchData.length == 1 && matchData[0][2]) { + return matchesStrictComparable(matchData[0][0], matchData[0][1]); + } + return function(object) { + return object === source || baseIsMatch(object, source, matchData); + }; +} + +/** + * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`. + * + * @private + * @param {string} path The path of the property to get. + * @param {*} srcValue The value to match. + * @returns {Function} Returns the new spec function. + */ +function baseMatchesProperty(path, srcValue) { + if (isKey(path) && isStrictComparable(srcValue)) { + return matchesStrictComparable(toKey(path), srcValue); + } + return function(object) { + var objValue = get(object, path); + return (objValue === undefined && objValue === srcValue) + ? hasIn(object, path) + : baseIsEqual(srcValue, objValue, undefined, UNORDERED_COMPARE_FLAG | PARTIAL_COMPARE_FLAG); + }; +} + +/** + * The base implementation of `_.orderBy` without param guards. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by. + * @param {string[]} orders The sort orders of `iteratees`. + * @returns {Array} Returns the new sorted array. + */ +function baseOrderBy(collection, iteratees, orders) { + var index = -1; + iteratees = arrayMap(iteratees.length ? iteratees : [identity], baseUnary(baseIteratee)); + + var result = baseMap(collection, function(value, key, collection) { + var criteria = arrayMap(iteratees, function(iteratee) { + return iteratee(value); + }); + return { 'criteria': criteria, 'index': ++index, 'value': value }; + }); + + return baseSortBy(result, function(object, other) { + return compareMultiple(object, other, orders); + }); +} + +/** + * A specialized version of `baseProperty` which supports deep paths. + * + * @private + * @param {Array|string} path The path of the property to get. + * @returns {Function} Returns the new accessor function. + */ +function basePropertyDeep(path) { + return function(object) { + return baseGet(object, path); + }; +} + +/** + * The base implementation of `_.rest` which doesn't validate or coerce arguments. + * + * @private + * @param {Function} func The function to apply a rest parameter to. + * @param {number} [start=func.length-1] The start position of the rest parameter. + * @returns {Function} Returns the new function. + */ +function baseRest(func, start) { + start = nativeMax(start === undefined ? (func.length - 1) : start, 0); + return function() { + var args = arguments, + index = -1, + length = nativeMax(args.length - start, 0), + array = Array(length); + + while (++index < length) { + array[index] = args[start + index]; + } + index = -1; + var otherArgs = Array(start + 1); + while (++index < start) { + otherArgs[index] = args[index]; + } + otherArgs[start] = array; + return apply(func, this, otherArgs); + }; +} + +/** + * The base implementation of `_.toString` which doesn't convert nullish + * values to empty strings. + * + * @private + * @param {*} value The value to process. + * @returns {string} Returns the string. + */ +function baseToString(value) { + // Exit early for strings to avoid a performance hit in some environments. + if (typeof value == 'string') { + return value; + } + if (isSymbol(value)) { + return symbolToString ? symbolToString.call(value) : ''; + } + var result = (value + ''); + return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; +} + +/** + * Casts `value` to a path array if it's not one. + * + * @private + * @param {*} value The value to inspect. + * @returns {Array} Returns the cast property path array. + */ +function castPath(value) { + return isArray(value) ? value : stringToPath(value); +} + +/** + * Compares values to sort them in ascending order. + * + * @private + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {number} Returns the sort order indicator for `value`. + */ +function compareAscending(value, other) { + if (value !== other) { + var valIsDefined = value !== undefined, + valIsNull = value === null, + valIsReflexive = value === value, + valIsSymbol = isSymbol(value); + + var othIsDefined = other !== undefined, + othIsNull = other === null, + othIsReflexive = other === other, + othIsSymbol = isSymbol(other); + + if ((!othIsNull && !othIsSymbol && !valIsSymbol && value > other) || + (valIsSymbol && othIsDefined && othIsReflexive && !othIsNull && !othIsSymbol) || + (valIsNull && othIsDefined && othIsReflexive) || + (!valIsDefined && othIsReflexive) || + !valIsReflexive) { + return 1; + } + if ((!valIsNull && !valIsSymbol && !othIsSymbol && value < other) || + (othIsSymbol && valIsDefined && valIsReflexive && !valIsNull && !valIsSymbol) || + (othIsNull && valIsDefined && valIsReflexive) || + (!othIsDefined && valIsReflexive) || + !othIsReflexive) { + return -1; + } + } + return 0; +} + +/** + * Used by `_.orderBy` to compare multiple properties of a value to another + * and stable sort them. + * + * If `orders` is unspecified, all values are sorted in ascending order. Otherwise, + * specify an order of "desc" for descending or "asc" for ascending sort order + * of corresponding values. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {boolean[]|string[]} orders The order to sort by for each property. + * @returns {number} Returns the sort order indicator for `object`. + */ +function compareMultiple(object, other, orders) { + var index = -1, + objCriteria = object.criteria, + othCriteria = other.criteria, + length = objCriteria.length, + ordersLength = orders.length; + + while (++index < length) { + var result = compareAscending(objCriteria[index], othCriteria[index]); + if (result) { + if (index >= ordersLength) { + return result; + } + var order = orders[index]; + return result * (order == 'desc' ? -1 : 1); + } + } + // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications + // that causes it, under certain circumstances, to provide the same value for + // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247 + // for more details. + // + // This also ensures a stable sort in V8 and other engines. + // See https://bugs.chromium.org/p/v8/issues/detail?id=90 for more details. + return object.index - other.index; +} + +/** + * Creates a `baseEach` or `baseEachRight` function. + * + * @private + * @param {Function} eachFunc The function to iterate over a collection. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new base function. + */ +function createBaseEach(eachFunc, fromRight) { + return function(collection, iteratee) { + if (collection == null) { + return collection; + } + if (!isArrayLike(collection)) { + return eachFunc(collection, iteratee); + } + var length = collection.length, + index = fromRight ? length : -1, + iterable = Object(collection); + + while ((fromRight ? index-- : ++index < length)) { + if (iteratee(iterable[index], index, iterable) === false) { + break; + } + } + return collection; + }; +} + +/** + * Creates a base function for methods like `_.forIn` and `_.forOwn`. + * + * @private + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new base function. + */ +function createBaseFor(fromRight) { + return function(object, iteratee, keysFunc) { + var index = -1, + iterable = Object(object), + props = keysFunc(object), + length = props.length; + + while (length--) { + var key = props[fromRight ? length : ++index]; + if (iteratee(iterable[key], key, iterable) === false) { + break; + } + } + return object; + }; +} + +/** + * A specialized version of `baseIsEqualDeep` for arrays with support for + * partial deep comparisons. + * + * @private + * @param {Array} array The array to compare. + * @param {Array} other The other array to compare. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Function} customizer The function to customize comparisons. + * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual` + * for more details. + * @param {Object} stack Tracks traversed `array` and `other` objects. + * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`. + */ +function equalArrays(array, other, equalFunc, customizer, bitmask, stack) { + var isPartial = bitmask & PARTIAL_COMPARE_FLAG, + arrLength = array.length, + othLength = other.length; + + if (arrLength != othLength && !(isPartial && othLength > arrLength)) { + return false; + } + // Assume cyclic values are equal. + var stacked = stack.get(array); + if (stacked && stack.get(other)) { + return stacked == other; + } + var index = -1, + result = true, + seen = (bitmask & UNORDERED_COMPARE_FLAG) ? new SetCache : undefined; + + stack.set(array, other); + stack.set(other, array); + + // Ignore non-index properties. + while (++index < arrLength) { + var arrValue = array[index], + othValue = other[index]; + + if (customizer) { + var compared = isPartial + ? customizer(othValue, arrValue, index, other, array, stack) + : customizer(arrValue, othValue, index, array, other, stack); + } + if (compared !== undefined) { + if (compared) { + continue; + } + result = false; + break; + } + // Recursively compare arrays (susceptible to call stack limits). + if (seen) { + if (!arraySome(other, function(othValue, othIndex) { + if (!seen.has(othIndex) && + (arrValue === othValue || equalFunc(arrValue, othValue, customizer, bitmask, stack))) { + return seen.add(othIndex); + } + })) { + result = false; + break; + } + } else if (!( + arrValue === othValue || + equalFunc(arrValue, othValue, customizer, bitmask, stack) + )) { + result = false; + break; + } + } + stack['delete'](array); + stack['delete'](other); + return result; +} + +/** + * A specialized version of `baseIsEqualDeep` for comparing objects of + * the same `toStringTag`. + * + * **Note:** This function only supports comparing values with tags of + * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {string} tag The `toStringTag` of the objects to compare. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Function} customizer The function to customize comparisons. + * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual` + * for more details. + * @param {Object} stack Tracks traversed `object` and `other` objects. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ +function equalByTag(object, other, tag, equalFunc, customizer, bitmask, stack) { + switch (tag) { + case dataViewTag: + if ((object.byteLength != other.byteLength) || + (object.byteOffset != other.byteOffset)) { + return false; + } + object = object.buffer; + other = other.buffer; + + case arrayBufferTag: + if ((object.byteLength != other.byteLength) || + !equalFunc(new Uint8Array(object), new Uint8Array(other))) { + return false; + } + return true; + + case boolTag: + case dateTag: + case numberTag: + // Coerce booleans to `1` or `0` and dates to milliseconds. + // Invalid dates are coerced to `NaN`. + return eq(+object, +other); + + case errorTag: + return object.name == other.name && object.message == other.message; + + case regexpTag: + case stringTag: + // Coerce regexes to strings and treat strings, primitives and objects, + // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring + // for more details. + return object == (other + ''); + + case mapTag: + var convert = mapToArray; + + case setTag: + var isPartial = bitmask & PARTIAL_COMPARE_FLAG; + convert || (convert = setToArray); + + if (object.size != other.size && !isPartial) { + return false; + } + // Assume cyclic values are equal. + var stacked = stack.get(object); + if (stacked) { + return stacked == other; + } + bitmask |= UNORDERED_COMPARE_FLAG; + + // Recursively compare objects (susceptible to call stack limits). + stack.set(object, other); + var result = equalArrays(convert(object), convert(other), equalFunc, customizer, bitmask, stack); + stack['delete'](object); + return result; + + case symbolTag: + if (symbolValueOf) { + return symbolValueOf.call(object) == symbolValueOf.call(other); + } + } + return false; +} + +/** + * A specialized version of `baseIsEqualDeep` for objects with support for + * partial deep comparisons. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Function} customizer The function to customize comparisons. + * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual` + * for more details. + * @param {Object} stack Tracks traversed `object` and `other` objects. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ +function equalObjects(object, other, equalFunc, customizer, bitmask, stack) { + var isPartial = bitmask & PARTIAL_COMPARE_FLAG, + objProps = keys(object), + objLength = objProps.length, + othProps = keys(other), + othLength = othProps.length; + + if (objLength != othLength && !isPartial) { + return false; + } + var index = objLength; + while (index--) { + var key = objProps[index]; + if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) { + return false; + } + } + // Assume cyclic values are equal. + var stacked = stack.get(object); + if (stacked && stack.get(other)) { + return stacked == other; + } + var result = true; + stack.set(object, other); + stack.set(other, object); + + var skipCtor = isPartial; + while (++index < objLength) { + key = objProps[index]; + var objValue = object[key], + othValue = other[key]; + + if (customizer) { + var compared = isPartial + ? customizer(othValue, objValue, key, other, object, stack) + : customizer(objValue, othValue, key, object, other, stack); + } + // Recursively compare objects (susceptible to call stack limits). + if (!(compared === undefined + ? (objValue === othValue || equalFunc(objValue, othValue, customizer, bitmask, stack)) + : compared + )) { + result = false; + break; + } + skipCtor || (skipCtor = key == 'constructor'); + } + if (result && !skipCtor) { + var objCtor = object.constructor, + othCtor = other.constructor; + + // Non `Object` object instances with different constructors are not equal. + if (objCtor != othCtor && + ('constructor' in object && 'constructor' in other) && + !(typeof objCtor == 'function' && objCtor instanceof objCtor && + typeof othCtor == 'function' && othCtor instanceof othCtor)) { + result = false; + } + } + stack['delete'](object); + stack['delete'](other); + return result; +} + +/** + * Gets the data for `map`. + * + * @private + * @param {Object} map The map to query. + * @param {string} key The reference key. + * @returns {*} Returns the map data. + */ +function getMapData(map, key) { + var data = map.__data__; + return isKeyable(key) + ? data[typeof key == 'string' ? 'string' : 'hash'] + : data.map; +} + +/** + * Gets the property names, values, and compare flags of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the match data of `object`. + */ +function getMatchData(object) { + var result = keys(object), + length = result.length; + + while (length--) { + var key = result[length], + value = object[key]; + + result[length] = [key, value, isStrictComparable(value)]; + } + return result; +} + +/** + * Gets the native function at `key` of `object`. + * + * @private + * @param {Object} object The object to query. + * @param {string} key The key of the method to get. + * @returns {*} Returns the function if it's native, else `undefined`. + */ +function getNative(object, key) { + var value = getValue(object, key); + return baseIsNative(value) ? value : undefined; +} + +/** + * Gets the `toStringTag` of `value`. + * + * @private + * @param {*} value The value to query. + * @returns {string} Returns the `toStringTag`. + */ +var getTag = baseGetTag; + +// Fallback for data views, maps, sets, and weak maps in IE 11, +// for data views in Edge < 14, and promises in Node.js. +if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) || + (Map && getTag(new Map) != mapTag) || + (Promise && getTag(Promise.resolve()) != promiseTag) || + (Set && getTag(new Set) != setTag) || + (WeakMap && getTag(new WeakMap) != weakMapTag)) { + getTag = function(value) { + var result = objectToString.call(value), + Ctor = result == objectTag ? value.constructor : undefined, + ctorString = Ctor ? toSource(Ctor) : undefined; + + if (ctorString) { + switch (ctorString) { + case dataViewCtorString: return dataViewTag; + case mapCtorString: return mapTag; + case promiseCtorString: return promiseTag; + case setCtorString: return setTag; + case weakMapCtorString: return weakMapTag; + } + } + return result; + }; +} + +/** + * Checks if `path` exists on `object`. + * + * @private + * @param {Object} object The object to query. + * @param {Array|string} path The path to check. + * @param {Function} hasFunc The function to check properties. + * @returns {boolean} Returns `true` if `path` exists, else `false`. + */ +function hasPath(object, path, hasFunc) { + path = isKey(path, object) ? [path] : castPath(path); + + var result, + index = -1, + length = path.length; + + while (++index < length) { + var key = toKey(path[index]); + if (!(result = object != null && hasFunc(object, key))) { + break; + } + object = object[key]; + } + if (result) { + return result; + } + var length = object ? object.length : 0; + return !!length && isLength(length) && isIndex(key, length) && + (isArray(object) || isArguments(object)); +} + +/** + * Checks if `value` is a flattenable `arguments` object or array. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is flattenable, else `false`. + */ +function isFlattenable(value) { + return isArray(value) || isArguments(value) || + !!(spreadableSymbol && value && value[spreadableSymbol]); +} + +/** + * Checks if `value` is a valid array-like index. + * + * @private + * @param {*} value The value to check. + * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. + * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. + */ +function isIndex(value, length) { + length = length == null ? MAX_SAFE_INTEGER : length; + return !!length && + (typeof value == 'number' || reIsUint.test(value)) && + (value > -1 && value % 1 == 0 && value < length); +} + +/** + * Checks if the given arguments are from an iteratee call. + * + * @private + * @param {*} value The potential iteratee value argument. + * @param {*} index The potential iteratee index or key argument. + * @param {*} object The potential iteratee object argument. + * @returns {boolean} Returns `true` if the arguments are from an iteratee call, + * else `false`. + */ +function isIterateeCall(value, index, object) { + if (!isObject(object)) { + return false; + } + var type = typeof index; + if (type == 'number' + ? (isArrayLike(object) && isIndex(index, object.length)) + : (type == 'string' && index in object) + ) { + return eq(object[index], value); + } + return false; +} + +/** + * Checks if `value` is a property name and not a property path. + * + * @private + * @param {*} value The value to check. + * @param {Object} [object] The object to query keys on. + * @returns {boolean} Returns `true` if `value` is a property name, else `false`. + */ +function isKey(value, object) { + if (isArray(value)) { + return false; + } + var type = typeof value; + if (type == 'number' || type == 'symbol' || type == 'boolean' || + value == null || isSymbol(value)) { + return true; + } + return reIsPlainProp.test(value) || !reIsDeepProp.test(value) || + (object != null && value in Object(object)); +} + +/** + * Checks if `value` is suitable for use as unique object key. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is suitable, else `false`. + */ +function isKeyable(value) { + var type = typeof value; + return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean') + ? (value !== '__proto__') + : (value === null); +} + +/** + * Checks if `func` has its source masked. + * + * @private + * @param {Function} func The function to check. + * @returns {boolean} Returns `true` if `func` is masked, else `false`. + */ +function isMasked(func) { + return !!maskSrcKey && (maskSrcKey in func); +} + +/** + * Checks if `value` is likely a prototype object. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a prototype, else `false`. + */ +function isPrototype(value) { + var Ctor = value && value.constructor, + proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto; + + return value === proto; +} + +/** + * Checks if `value` is suitable for strict equality comparisons, i.e. `===`. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` if suitable for strict + * equality comparisons, else `false`. + */ +function isStrictComparable(value) { + return value === value && !isObject(value); +} + +/** + * A specialized version of `matchesProperty` for source values suitable + * for strict equality comparisons, i.e. `===`. + * + * @private + * @param {string} key The key of the property to get. + * @param {*} srcValue The value to match. + * @returns {Function} Returns the new spec function. + */ +function matchesStrictComparable(key, srcValue) { + return function(object) { + if (object == null) { + return false; + } + return object[key] === srcValue && + (srcValue !== undefined || (key in Object(object))); + }; +} + +/** + * Converts `string` to a property path array. + * + * @private + * @param {string} string The string to convert. + * @returns {Array} Returns the property path array. + */ +var stringToPath = memoize(function(string) { + string = toString(string); + + var result = []; + if (reLeadingDot.test(string)) { + result.push(''); + } + string.replace(rePropName, function(match, number, quote, string) { + result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match)); + }); + return result; +}); + +/** + * Converts `value` to a string key if it's not a string or symbol. + * + * @private + * @param {*} value The value to inspect. + * @returns {string|symbol} Returns the key. + */ +function toKey(value) { + if (typeof value == 'string' || isSymbol(value)) { + return value; + } + var result = (value + ''); + return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; +} + +/** + * Converts `func` to its source code. + * + * @private + * @param {Function} func The function to process. + * @returns {string} Returns the source code. + */ +function toSource(func) { + if (func != null) { + try { + return funcToString.call(func); + } catch (e) {} + try { + return (func + ''); + } catch (e) {} + } + return ''; +} + +/** + * Creates an array of elements, sorted in ascending order by the results of + * running each element in a collection thru each iteratee. This method + * performs a stable sort, that is, it preserves the original sort order of + * equal elements. The iteratees are invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {...(Function|Function[])} [iteratees=[_.identity]] + * The iteratees to sort by. + * @returns {Array} Returns the new sorted array. + * @example + * + * var users = [ + * { 'user': 'fred', 'age': 48 }, + * { 'user': 'barney', 'age': 36 }, + * { 'user': 'fred', 'age': 40 }, + * { 'user': 'barney', 'age': 34 } + * ]; + * + * _.sortBy(users, function(o) { return o.user; }); + * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]] + * + * _.sortBy(users, ['user', 'age']); + * // => objects for [['barney', 34], ['barney', 36], ['fred', 40], ['fred', 48]] + * + * _.sortBy(users, 'user', function(o) { + * return Math.floor(o.age / 10); + * }); + * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]] + */ +var sortBy = baseRest(function(collection, iteratees) { + if (collection == null) { + return []; + } + var length = iteratees.length; + if (length > 1 && isIterateeCall(collection, iteratees[0], iteratees[1])) { + iteratees = []; + } else if (length > 2 && isIterateeCall(iteratees[0], iteratees[1], iteratees[2])) { + iteratees = [iteratees[0]]; + } + return baseOrderBy(collection, baseFlatten(iteratees, 1), []); +}); + +/** + * Creates a function that memoizes the result of `func`. If `resolver` is + * provided, it determines the cache key for storing the result based on the + * arguments provided to the memoized function. By default, the first argument + * provided to the memoized function is used as the map cache key. The `func` + * is invoked with the `this` binding of the memoized function. + * + * **Note:** The cache is exposed as the `cache` property on the memoized + * function. Its creation may be customized by replacing the `_.memoize.Cache` + * constructor with one whose instances implement the + * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object) + * method interface of `delete`, `get`, `has`, and `set`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to have its output memoized. + * @param {Function} [resolver] The function to resolve the cache key. + * @returns {Function} Returns the new memoized function. + * @example + * + * var object = { 'a': 1, 'b': 2 }; + * var other = { 'c': 3, 'd': 4 }; + * + * var values = _.memoize(_.values); + * values(object); + * // => [1, 2] + * + * values(other); + * // => [3, 4] + * + * object.a = 2; + * values(object); + * // => [1, 2] + * + * // Modify the result cache. + * values.cache.set(object, ['a', 'b']); + * values(object); + * // => ['a', 'b'] + * + * // Replace `_.memoize.Cache`. + * _.memoize.Cache = WeakMap; + */ +function memoize(func, resolver) { + if (typeof func != 'function' || (resolver && typeof resolver != 'function')) { + throw new TypeError(FUNC_ERROR_TEXT); + } + var memoized = function() { + var args = arguments, + key = resolver ? resolver.apply(this, args) : args[0], + cache = memoized.cache; + + if (cache.has(key)) { + return cache.get(key); + } + var result = func.apply(this, args); + memoized.cache = cache.set(key, result); + return result; + }; + memoized.cache = new (memoize.Cache || MapCache); + return memoized; +} + +// Assign cache to `_.memoize`. +memoize.Cache = MapCache; + +/** + * Performs a + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * comparison between two values to determine if they are equivalent. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + * @example + * + * var object = { 'a': 1 }; + * var other = { 'a': 1 }; + * + * _.eq(object, object); + * // => true + * + * _.eq(object, other); + * // => false + * + * _.eq('a', 'a'); + * // => true + * + * _.eq('a', Object('a')); + * // => false + * + * _.eq(NaN, NaN); + * // => true + */ +function eq(value, other) { + return value === other || (value !== value && other !== other); +} + +/** + * Checks if `value` is likely an `arguments` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an `arguments` object, + * else `false`. + * @example + * + * _.isArguments(function() { return arguments; }()); + * // => true + * + * _.isArguments([1, 2, 3]); + * // => false + */ +function isArguments(value) { + // Safari 8.1 makes `arguments.callee` enumerable in strict mode. + return isArrayLikeObject(value) && hasOwnProperty.call(value, 'callee') && + (!propertyIsEnumerable.call(value, 'callee') || objectToString.call(value) == argsTag); +} + +/** + * Checks if `value` is classified as an `Array` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array, else `false`. + * @example + * + * _.isArray([1, 2, 3]); + * // => true + * + * _.isArray(document.body.children); + * // => false + * + * _.isArray('abc'); + * // => false + * + * _.isArray(_.noop); + * // => false + */ +var isArray = Array.isArray; + +/** + * Checks if `value` is array-like. A value is considered array-like if it's + * not a function and has a `value.length` that's an integer greater than or + * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is array-like, else `false`. + * @example + * + * _.isArrayLike([1, 2, 3]); + * // => true + * + * _.isArrayLike(document.body.children); + * // => true + * + * _.isArrayLike('abc'); + * // => true + * + * _.isArrayLike(_.noop); + * // => false + */ +function isArrayLike(value) { + return value != null && isLength(value.length) && !isFunction(value); +} + +/** + * This method is like `_.isArrayLike` except that it also checks if `value` + * is an object. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array-like object, + * else `false`. + * @example + * + * _.isArrayLikeObject([1, 2, 3]); + * // => true + * + * _.isArrayLikeObject(document.body.children); + * // => true + * + * _.isArrayLikeObject('abc'); + * // => false + * + * _.isArrayLikeObject(_.noop); + * // => false + */ +function isArrayLikeObject(value) { + return isObjectLike(value) && isArrayLike(value); +} + +/** + * Checks if `value` is classified as a `Function` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a function, else `false`. + * @example + * + * _.isFunction(_); + * // => true + * + * _.isFunction(/abc/); + * // => false + */ +function isFunction(value) { + // The use of `Object#toString` avoids issues with the `typeof` operator + // in Safari 8-9 which returns 'object' for typed array and other constructors. + var tag = isObject(value) ? objectToString.call(value) : ''; + return tag == funcTag || tag == genTag; +} + +/** + * Checks if `value` is a valid array-like length. + * + * **Note:** This method is loosely based on + * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. + * @example + * + * _.isLength(3); + * // => true + * + * _.isLength(Number.MIN_VALUE); + * // => false + * + * _.isLength(Infinity); + * // => false + * + * _.isLength('3'); + * // => false + */ +function isLength(value) { + return typeof value == 'number' && + value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; +} + +/** + * Checks if `value` is the + * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) + * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(_.noop); + * // => true + * + * _.isObject(null); + * // => false + */ +function isObject(value) { + var type = typeof value; + return !!value && (type == 'object' || type == 'function'); +} + +/** + * Checks if `value` is object-like. A value is object-like if it's not `null` + * and has a `typeof` result of "object". + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + * @example + * + * _.isObjectLike({}); + * // => true + * + * _.isObjectLike([1, 2, 3]); + * // => true + * + * _.isObjectLike(_.noop); + * // => false + * + * _.isObjectLike(null); + * // => false + */ +function isObjectLike(value) { + return !!value && typeof value == 'object'; +} + +/** + * Checks if `value` is classified as a `Symbol` primitive or object. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. + * @example + * + * _.isSymbol(Symbol.iterator); + * // => true + * + * _.isSymbol('abc'); + * // => false + */ +function isSymbol(value) { + return typeof value == 'symbol' || + (isObjectLike(value) && objectToString.call(value) == symbolTag); +} + +/** + * Checks if `value` is classified as a typed array. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. + * @example + * + * _.isTypedArray(new Uint8Array); + * // => true + * + * _.isTypedArray([]); + * // => false + */ +var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray; + +/** + * Converts `value` to a string. An empty string is returned for `null` + * and `undefined` values. The sign of `-0` is preserved. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to process. + * @returns {string} Returns the string. + * @example + * + * _.toString(null); + * // => '' + * + * _.toString(-0); + * // => '-0' + * + * _.toString([1, 2, 3]); + * // => '1,2,3' + */ +function toString(value) { + return value == null ? '' : baseToString(value); +} + +/** + * Gets the value at `path` of `object`. If the resolved value is + * `undefined`, the `defaultValue` is returned in its place. + * + * @static + * @memberOf _ + * @since 3.7.0 + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to get. + * @param {*} [defaultValue] The value returned for `undefined` resolved values. + * @returns {*} Returns the resolved value. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }] }; + * + * _.get(object, 'a[0].b.c'); + * // => 3 + * + * _.get(object, ['a', '0', 'b', 'c']); + * // => 3 + * + * _.get(object, 'a.b.c', 'default'); + * // => 'default' + */ +function get(object, path, defaultValue) { + var result = object == null ? undefined : baseGet(object, path); + return result === undefined ? defaultValue : result; +} + +/** + * Checks if `path` is a direct or inherited property of `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path to check. + * @returns {boolean} Returns `true` if `path` exists, else `false`. + * @example + * + * var object = _.create({ 'a': _.create({ 'b': 2 }) }); + * + * _.hasIn(object, 'a'); + * // => true + * + * _.hasIn(object, 'a.b'); + * // => true + * + * _.hasIn(object, ['a', 'b']); + * // => true + * + * _.hasIn(object, 'b'); + * // => false + */ +function hasIn(object, path) { + return object != null && hasPath(object, path, baseHasIn); +} + +/** + * Creates an array of the own enumerable property names of `object`. + * + * **Note:** Non-object values are coerced to objects. See the + * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) + * for more details. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.keys(new Foo); + * // => ['a', 'b'] (iteration order is not guaranteed) + * + * _.keys('hi'); + * // => ['0', '1'] + */ +function keys(object) { + return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object); +} + +/** + * This method returns the first argument it receives. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Util + * @param {*} value Any value. + * @returns {*} Returns `value`. + * @example + * + * var object = { 'a': 1 }; + * + * console.log(_.identity(object) === object); + * // => true + */ +function identity(value) { + return value; +} + +/** + * Creates a function that returns the value at `path` of a given object. + * + * @static + * @memberOf _ + * @since 2.4.0 + * @category Util + * @param {Array|string} path The path of the property to get. + * @returns {Function} Returns the new accessor function. + * @example + * + * var objects = [ + * { 'a': { 'b': 2 } }, + * { 'a': { 'b': 1 } } + * ]; + * + * _.map(objects, _.property('a.b')); + * // => [2, 1] + * + * _.map(_.sortBy(objects, _.property(['a', 'b'])), 'a.b'); + * // => [1, 2] + */ +function property(path) { + return isKey(path) ? baseProperty(toKey(path)) : basePropertyDeep(path); +} + +module.exports = sortBy; + +/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(15), __webpack_require__(22)(module))) + +/***/ }), + /***/ 413: /***/ (function(module, exports, __webpack_require__) { @@ -2142,6 +5471,10 @@ var _reselect = __webpack_require__(444); var reselect = _interopRequireWildcard(_reselect); +var _whatwgUrl = __webpack_require__(497); + +var whatwgUrl = _interopRequireWildcard(_whatwgUrl); + var _classnames = __webpack_require__(67); var _classnames2 = _interopRequireDefault(_classnames); @@ -2162,25 +5495,6 @@ function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; // (eg. "my-module/Test") which is why they are nested in "vendored". // The keys of the vendored object should match the module names // !!! Should remain synchronized with .babel/transform-mc.js !!! - -// $FlowIgnore -const vendored = exports.vendored = { - classnames: _classnames2.default, - "devtools-components": devtoolsComponents, - "devtools-config": devtoolsConfig, - "devtools-contextmenu": devtoolsContextmenu, - "devtools-environment": devtoolsEnvironment, - "devtools-modules": devtoolsModules, - "devtools-splitter": _devtoolsSplitter2.default, - "devtools-utils": devtoolsUtils, - "fuzzaldrin-plus": fuzzaldrinPlus, - "lodash-move": _lodashMove2.default, - "react-aria-components/src/tabs": reactAriaComponentsTabs, - "react-transition-group/Transition": transition, - reselect -}; - -// Modules imported without destructuring /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at . */ @@ -2198,6 +5512,26 @@ const vendored = exports.vendored = { */ // Modules imported with destructuring +const vendored = exports.vendored = { + classnames: _classnames2.default, + "devtools-components": devtoolsComponents, + "devtools-config": devtoolsConfig, + "devtools-contextmenu": devtoolsContextmenu, + "devtools-environment": devtoolsEnvironment, + "devtools-modules": devtoolsModules, + "devtools-splitter": _devtoolsSplitter2.default, + "devtools-utils": devtoolsUtils, + "fuzzaldrin-plus": fuzzaldrinPlus, + "lodash-move": _lodashMove2.default, + "react-aria-components/src/tabs": reactAriaComponentsTabs, + "react-transition-group/Transition": transition, + "whatwg-url": whatwgUrl, + reselect +}; + +// Modules imported without destructuring + +// $FlowIgnore /***/ }), @@ -2526,7 +5860,7 @@ var substr = 'ab'.substr(-1) === 'b' * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ const Menu = __webpack_require__(490); -const MenuItem = __webpack_require__(491); +const MenuItem = __webpack_require__(423); function inToolbox() { try { @@ -2706,6 +6040,79 @@ module.exports = p; /***/ }), +/***/ 423: +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * A partial implementation of the MenuItem API provided by electron: + * https://github.com/electron/electron/blob/master/docs/api/menu-item.md. + * + * Missing features: + * - id String - Unique within a single menu. If defined then it can be used + * as a reference to this item by the position attribute. + * - role String - Define the action of the menu item; when specified the + * click property will be ignored + * - sublabel String + * - icon NativeImage + * - position String - This field allows fine-grained definition of the + * specific location within a given menu. + * + * Implemented features: + * @param Object options + * Function click + * Will be called with click(menuItem, browserWindow) when the menu item + * is clicked + * String type + * Can be normal, separator, submenu, checkbox or radio + * String label + * Boolean enabled + * If false, the menu item will be greyed out and unclickable. + * Boolean checked + * Should only be specified for checkbox or radio type menu items. + * Menu submenu + * Should be specified for submenu type menu items. If submenu is specified, + * the type: 'submenu' can be omitted. If the value is not a Menu then it + * will be automatically converted to one using Menu.buildFromTemplate. + * Boolean visible + * If false, the menu item will be entirely hidden. + * String accelerator + * If specified, will be used as accelerator text for MenuItem + */ +function MenuItem({ + accesskey = null, + checked = false, + click = () => {}, + disabled = false, + label = "", + id = null, + submenu = null, + type = "normal", + visible = true, + accelerator = "" +} = {}) { + this.accesskey = accesskey; + this.checked = checked; + this.click = click; + this.disabled = disabled; + this.id = id; + this.label = label; + this.submenu = submenu; + this.type = type; + this.visible = visible; + this.accelerator = accelerator; +} + +module.exports = MenuItem; + +/***/ }), + /***/ 424: /***/ (function(module, exports, __webpack_require__) { @@ -6103,10 +9510,4662 @@ module.exports = __WEBPACK_EXTERNAL_MODULE_490__; /***/ }), -/***/ 491: -/***/ (function(module, exports) { +/***/ 492: +/***/ (function(module, exports, __webpack_require__) { -module.exports = __WEBPACK_EXTERNAL_MODULE_491__; +"use strict"; +/* WEBPACK VAR INJECTION */(function(Buffer) { +const punycode = __webpack_require__(62); +const tr46 = __webpack_require__(397); + +const infra = __webpack_require__(495); +const { percentEncode, percentDecode } = __webpack_require__(493); + +const specialSchemes = { + ftp: 21, + file: null, + gopher: 70, + http: 80, + https: 443, + ws: 80, + wss: 443 +}; + +const failure = Symbol("failure"); + +function countSymbols(str) { + return punycode.ucs2.decode(str).length; +} + +function at(input, idx) { + const c = input[idx]; + return isNaN(c) ? undefined : String.fromCodePoint(c); +} + +function isSingleDot(buffer) { + return buffer === "." || buffer.toLowerCase() === "%2e"; +} + +function isDoubleDot(buffer) { + buffer = buffer.toLowerCase(); + return buffer === ".." || buffer === "%2e." || buffer === ".%2e" || buffer === "%2e%2e"; +} + +function isWindowsDriveLetterCodePoints(cp1, cp2) { + return infra.isASCIIAlpha(cp1) && (cp2 === 58 || cp2 === 124); +} + +function isWindowsDriveLetterString(string) { + return string.length === 2 && infra.isASCIIAlpha(string.codePointAt(0)) && (string[1] === ":" || string[1] === "|"); +} + +function isNormalizedWindowsDriveLetterString(string) { + return string.length === 2 && infra.isASCIIAlpha(string.codePointAt(0)) && string[1] === ":"; +} + +function containsForbiddenHostCodePoint(string) { + return string.search(/\u0000|\u0009|\u000A|\u000D|\u0020|#|%|\/|:|\?|@|\[|\\|\]/) !== -1; +} + +function containsForbiddenHostCodePointExcludingPercent(string) { + return string.search(/\u0000|\u0009|\u000A|\u000D|\u0020|#|\/|:|\?|@|\[|\\|\]/) !== -1; +} + +function isSpecialScheme(scheme) { + return specialSchemes[scheme] !== undefined; +} + +function isSpecial(url) { + return isSpecialScheme(url.scheme); +} + +function isNotSpecial(url) { + return !isSpecialScheme(url.scheme); +} + +function defaultPort(scheme) { + return specialSchemes[scheme]; +} + +function utf8PercentEncode(c) { + const buf = Buffer.from(c); + + let str = ""; + + for (let i = 0; i < buf.length; ++i) { + str += percentEncode(buf[i]); + } + + return str; +} + +function isC0ControlPercentEncode(c) { + return c <= 0x1F || c > 0x7E; +} + +const extraUserinfoPercentEncodeSet = + new Set([47, 58, 59, 61, 64, 91, 92, 93, 94, 124]); +function isUserinfoPercentEncode(c) { + return isPathPercentEncode(c) || extraUserinfoPercentEncodeSet.has(c); +} + +const extraFragmentPercentEncodeSet = new Set([32, 34, 60, 62, 96]); +function isFragmentPercentEncode(c) { + return isC0ControlPercentEncode(c) || extraFragmentPercentEncodeSet.has(c); +} + +const extraPathPercentEncodeSet = new Set([35, 63, 123, 125]); +function isPathPercentEncode(c) { + return isFragmentPercentEncode(c) || extraPathPercentEncodeSet.has(c); +} + +function percentEncodeChar(c, encodeSetPredicate) { + const cStr = String.fromCodePoint(c); + + if (encodeSetPredicate(c)) { + return utf8PercentEncode(cStr); + } + + return cStr; +} + +function parseIPv4Number(input) { + let R = 10; + + if (input.length >= 2 && input.charAt(0) === "0" && input.charAt(1).toLowerCase() === "x") { + input = input.substring(2); + R = 16; + } else if (input.length >= 2 && input.charAt(0) === "0") { + input = input.substring(1); + R = 8; + } + + if (input === "") { + return 0; + } + + let regex = /[^0-7]/; + if (R === 10) { + regex = /[^0-9]/; + } + if (R === 16) { + regex = /[^0-9A-Fa-f]/; + } + + if (regex.test(input)) { + return failure; + } + + return parseInt(input, R); +} + +function parseIPv4(input) { + const parts = input.split("."); + if (parts[parts.length - 1] === "") { + if (parts.length > 1) { + parts.pop(); + } + } + + if (parts.length > 4) { + return input; + } + + const numbers = []; + for (const part of parts) { + if (part === "") { + return input; + } + const n = parseIPv4Number(part); + if (n === failure) { + return input; + } + + numbers.push(n); + } + + for (let i = 0; i < numbers.length - 1; ++i) { + if (numbers[i] > 255) { + return failure; + } + } + if (numbers[numbers.length - 1] >= Math.pow(256, 5 - numbers.length)) { + return failure; + } + + let ipv4 = numbers.pop(); + let counter = 0; + + for (const n of numbers) { + ipv4 += n * Math.pow(256, 3 - counter); + ++counter; + } + + return ipv4; +} + +function serializeIPv4(address) { + let output = ""; + let n = address; + + for (let i = 1; i <= 4; ++i) { + output = String(n % 256) + output; + if (i !== 4) { + output = "." + output; + } + n = Math.floor(n / 256); + } + + return output; +} + +function parseIPv6(input) { + const address = [0, 0, 0, 0, 0, 0, 0, 0]; + let pieceIndex = 0; + let compress = null; + let pointer = 0; + + input = punycode.ucs2.decode(input); + + if (input[pointer] === 58) { + if (input[pointer + 1] !== 58) { + return failure; + } + + pointer += 2; + ++pieceIndex; + compress = pieceIndex; + } + + while (pointer < input.length) { + if (pieceIndex === 8) { + return failure; + } + + if (input[pointer] === 58) { + if (compress !== null) { + return failure; + } + ++pointer; + ++pieceIndex; + compress = pieceIndex; + continue; + } + + let value = 0; + let length = 0; + + while (length < 4 && infra.isASCIIHex(input[pointer])) { + value = value * 0x10 + parseInt(at(input, pointer), 16); + ++pointer; + ++length; + } + + if (input[pointer] === 46) { + if (length === 0) { + return failure; + } + + pointer -= length; + + if (pieceIndex > 6) { + return failure; + } + + let numbersSeen = 0; + + while (input[pointer] !== undefined) { + let ipv4Piece = null; + + if (numbersSeen > 0) { + if (input[pointer] === 46 && numbersSeen < 4) { + ++pointer; + } else { + return failure; + } + } + + if (!infra.isASCIIDigit(input[pointer])) { + return failure; + } + + while (infra.isASCIIDigit(input[pointer])) { + const number = parseInt(at(input, pointer)); + if (ipv4Piece === null) { + ipv4Piece = number; + } else if (ipv4Piece === 0) { + return failure; + } else { + ipv4Piece = ipv4Piece * 10 + number; + } + if (ipv4Piece > 255) { + return failure; + } + ++pointer; + } + + address[pieceIndex] = address[pieceIndex] * 0x100 + ipv4Piece; + + ++numbersSeen; + + if (numbersSeen === 2 || numbersSeen === 4) { + ++pieceIndex; + } + } + + if (numbersSeen !== 4) { + return failure; + } + + break; + } else if (input[pointer] === 58) { + ++pointer; + if (input[pointer] === undefined) { + return failure; + } + } else if (input[pointer] !== undefined) { + return failure; + } + + address[pieceIndex] = value; + ++pieceIndex; + } + + if (compress !== null) { + let swaps = pieceIndex - compress; + pieceIndex = 7; + while (pieceIndex !== 0 && swaps > 0) { + const temp = address[compress + swaps - 1]; + address[compress + swaps - 1] = address[pieceIndex]; + address[pieceIndex] = temp; + --pieceIndex; + --swaps; + } + } else if (compress === null && pieceIndex !== 8) { + return failure; + } + + return address; +} + +function serializeIPv6(address) { + let output = ""; + const seqResult = findLongestZeroSequence(address); + const compress = seqResult.idx; + let ignore0 = false; + + for (let pieceIndex = 0; pieceIndex <= 7; ++pieceIndex) { + if (ignore0 && address[pieceIndex] === 0) { + continue; + } else if (ignore0) { + ignore0 = false; + } + + if (compress === pieceIndex) { + const separator = pieceIndex === 0 ? "::" : ":"; + output += separator; + ignore0 = true; + continue; + } + + output += address[pieceIndex].toString(16); + + if (pieceIndex !== 7) { + output += ":"; + } + } + + return output; +} + +function parseHost(input, isNotSpecialArg = false) { + if (input[0] === "[") { + if (input[input.length - 1] !== "]") { + return failure; + } + + return parseIPv6(input.substring(1, input.length - 1)); + } + + if (isNotSpecialArg) { + return parseOpaqueHost(input); + } + + const domain = percentDecode(Buffer.from(input)).toString(); + const asciiDomain = domainToASCII(domain); + if (asciiDomain === failure) { + return failure; + } + + if (containsForbiddenHostCodePoint(asciiDomain)) { + return failure; + } + + const ipv4Host = parseIPv4(asciiDomain); + if (typeof ipv4Host === "number" || ipv4Host === failure) { + return ipv4Host; + } + + return asciiDomain; +} + +function parseOpaqueHost(input) { + if (containsForbiddenHostCodePointExcludingPercent(input)) { + return failure; + } + + let output = ""; + const decoded = punycode.ucs2.decode(input); + for (let i = 0; i < decoded.length; ++i) { + output += percentEncodeChar(decoded[i], isC0ControlPercentEncode); + } + return output; +} + +function findLongestZeroSequence(arr) { + let maxIdx = null; + let maxLen = 1; // only find elements > 1 + let currStart = null; + let currLen = 0; + + for (let i = 0; i < arr.length; ++i) { + if (arr[i] !== 0) { + if (currLen > maxLen) { + maxIdx = currStart; + maxLen = currLen; + } + + currStart = null; + currLen = 0; + } else { + if (currStart === null) { + currStart = i; + } + ++currLen; + } + } + + // if trailing zeros + if (currLen > maxLen) { + maxIdx = currStart; + maxLen = currLen; + } + + return { + idx: maxIdx, + len: maxLen + }; +} + +function serializeHost(host) { + if (typeof host === "number") { + return serializeIPv4(host); + } + + // IPv6 serializer + if (host instanceof Array) { + return "[" + serializeIPv6(host) + "]"; + } + + return host; +} + +function domainToASCII(domain, beStrict = false) { + const result = tr46.toASCII(domain, { + checkBidi: true, + checkHyphens: false, + checkJoiners: true, + useSTD3ASCIIRules: beStrict, + verifyDNSLength: beStrict + }); + if (result === null) { + return failure; + } + return result; +} + +function trimControlChars(url) { + return url.replace(/^[\u0000-\u001F\u0020]+|[\u0000-\u001F\u0020]+$/g, ""); +} + +function trimTabAndNewline(url) { + return url.replace(/\u0009|\u000A|\u000D/g, ""); +} + +function shortenPath(url) { + const { path } = url; + if (path.length === 0) { + return; + } + if (url.scheme === "file" && path.length === 1 && isNormalizedWindowsDriveLetter(path[0])) { + return; + } + + path.pop(); +} + +function includesCredentials(url) { + return url.username !== "" || url.password !== ""; +} + +function cannotHaveAUsernamePasswordPort(url) { + return url.host === null || url.host === "" || url.cannotBeABaseURL || url.scheme === "file"; +} + +function isNormalizedWindowsDriveLetter(string) { + return /^[A-Za-z]:$/.test(string); +} + +function URLStateMachine(input, base, encodingOverride, url, stateOverride) { + this.pointer = 0; + this.input = input; + this.base = base || null; + this.encodingOverride = encodingOverride || "utf-8"; + this.stateOverride = stateOverride; + this.url = url; + this.failure = false; + this.parseError = false; + + if (!this.url) { + this.url = { + scheme: "", + username: "", + password: "", + host: null, + port: null, + path: [], + query: null, + fragment: null, + + cannotBeABaseURL: false + }; + + const res = trimControlChars(this.input); + if (res !== this.input) { + this.parseError = true; + } + this.input = res; + } + + const res = trimTabAndNewline(this.input); + if (res !== this.input) { + this.parseError = true; + } + this.input = res; + + this.state = stateOverride || "scheme start"; + + this.buffer = ""; + this.atFlag = false; + this.arrFlag = false; + this.passwordTokenSeenFlag = false; + + this.input = punycode.ucs2.decode(this.input); + + for (; this.pointer <= this.input.length; ++this.pointer) { + const c = this.input[this.pointer]; + const cStr = isNaN(c) ? undefined : String.fromCodePoint(c); + + // exec state machine + const ret = this["parse " + this.state](c, cStr); + if (!ret) { + break; // terminate algorithm + } else if (ret === failure) { + this.failure = true; + break; + } + } +} + +URLStateMachine.prototype["parse scheme start"] = function parseSchemeStart(c, cStr) { + if (infra.isASCIIAlpha(c)) { + this.buffer += cStr.toLowerCase(); + this.state = "scheme"; + } else if (!this.stateOverride) { + this.state = "no scheme"; + --this.pointer; + } else { + this.parseError = true; + return failure; + } + + return true; +}; + +URLStateMachine.prototype["parse scheme"] = function parseScheme(c, cStr) { + if (infra.isASCIIAlphanumeric(c) || c === 43 || c === 45 || c === 46) { + this.buffer += cStr.toLowerCase(); + } else if (c === 58) { + if (this.stateOverride) { + if (isSpecial(this.url) && !isSpecialScheme(this.buffer)) { + return false; + } + + if (!isSpecial(this.url) && isSpecialScheme(this.buffer)) { + return false; + } + + if ((includesCredentials(this.url) || this.url.port !== null) && this.buffer === "file") { + return false; + } + + if (this.url.scheme === "file" && (this.url.host === "" || this.url.host === null)) { + return false; + } + } + this.url.scheme = this.buffer; + if (this.stateOverride) { + if (this.url.port === defaultPort(this.url.scheme)) { + this.url.port = null; + } + return false; + } + this.buffer = ""; + if (this.url.scheme === "file") { + if (this.input[this.pointer + 1] !== 47 || this.input[this.pointer + 2] !== 47) { + this.parseError = true; + } + this.state = "file"; + } else if (isSpecial(this.url) && this.base !== null && this.base.scheme === this.url.scheme) { + this.state = "special relative or authority"; + } else if (isSpecial(this.url)) { + this.state = "special authority slashes"; + } else if (this.input[this.pointer + 1] === 47) { + this.state = "path or authority"; + ++this.pointer; + } else { + this.url.cannotBeABaseURL = true; + this.url.path.push(""); + this.state = "cannot-be-a-base-URL path"; + } + } else if (!this.stateOverride) { + this.buffer = ""; + this.state = "no scheme"; + this.pointer = -1; + } else { + this.parseError = true; + return failure; + } + + return true; +}; + +URLStateMachine.prototype["parse no scheme"] = function parseNoScheme(c) { + if (this.base === null || (this.base.cannotBeABaseURL && c !== 35)) { + return failure; + } else if (this.base.cannotBeABaseURL && c === 35) { + this.url.scheme = this.base.scheme; + this.url.path = this.base.path.slice(); + this.url.query = this.base.query; + this.url.fragment = ""; + this.url.cannotBeABaseURL = true; + this.state = "fragment"; + } else if (this.base.scheme === "file") { + this.state = "file"; + --this.pointer; + } else { + this.state = "relative"; + --this.pointer; + } + + return true; +}; + +URLStateMachine.prototype["parse special relative or authority"] = function parseSpecialRelativeOrAuthority(c) { + if (c === 47 && this.input[this.pointer + 1] === 47) { + this.state = "special authority ignore slashes"; + ++this.pointer; + } else { + this.parseError = true; + this.state = "relative"; + --this.pointer; + } + + return true; +}; + +URLStateMachine.prototype["parse path or authority"] = function parsePathOrAuthority(c) { + if (c === 47) { + this.state = "authority"; + } else { + this.state = "path"; + --this.pointer; + } + + return true; +}; + +URLStateMachine.prototype["parse relative"] = function parseRelative(c) { + this.url.scheme = this.base.scheme; + if (isNaN(c)) { + this.url.username = this.base.username; + this.url.password = this.base.password; + this.url.host = this.base.host; + this.url.port = this.base.port; + this.url.path = this.base.path.slice(); + this.url.query = this.base.query; + } else if (c === 47) { + this.state = "relative slash"; + } else if (c === 63) { + this.url.username = this.base.username; + this.url.password = this.base.password; + this.url.host = this.base.host; + this.url.port = this.base.port; + this.url.path = this.base.path.slice(); + this.url.query = ""; + this.state = "query"; + } else if (c === 35) { + this.url.username = this.base.username; + this.url.password = this.base.password; + this.url.host = this.base.host; + this.url.port = this.base.port; + this.url.path = this.base.path.slice(); + this.url.query = this.base.query; + this.url.fragment = ""; + this.state = "fragment"; + } else if (isSpecial(this.url) && c === 92) { + this.parseError = true; + this.state = "relative slash"; + } else { + this.url.username = this.base.username; + this.url.password = this.base.password; + this.url.host = this.base.host; + this.url.port = this.base.port; + this.url.path = this.base.path.slice(0, this.base.path.length - 1); + + this.state = "path"; + --this.pointer; + } + + return true; +}; + +URLStateMachine.prototype["parse relative slash"] = function parseRelativeSlash(c) { + if (isSpecial(this.url) && (c === 47 || c === 92)) { + if (c === 92) { + this.parseError = true; + } + this.state = "special authority ignore slashes"; + } else if (c === 47) { + this.state = "authority"; + } else { + this.url.username = this.base.username; + this.url.password = this.base.password; + this.url.host = this.base.host; + this.url.port = this.base.port; + this.state = "path"; + --this.pointer; + } + + return true; +}; + +URLStateMachine.prototype["parse special authority slashes"] = function parseSpecialAuthoritySlashes(c) { + if (c === 47 && this.input[this.pointer + 1] === 47) { + this.state = "special authority ignore slashes"; + ++this.pointer; + } else { + this.parseError = true; + this.state = "special authority ignore slashes"; + --this.pointer; + } + + return true; +}; + +URLStateMachine.prototype["parse special authority ignore slashes"] = function parseSpecialAuthorityIgnoreSlashes(c) { + if (c !== 47 && c !== 92) { + this.state = "authority"; + --this.pointer; + } else { + this.parseError = true; + } + + return true; +}; + +URLStateMachine.prototype["parse authority"] = function parseAuthority(c, cStr) { + if (c === 64) { + this.parseError = true; + if (this.atFlag) { + this.buffer = "%40" + this.buffer; + } + this.atFlag = true; + + // careful, this is based on buffer and has its own pointer (this.pointer != pointer) and inner chars + const len = countSymbols(this.buffer); + for (let pointer = 0; pointer < len; ++pointer) { + const codePoint = this.buffer.codePointAt(pointer); + + if (codePoint === 58 && !this.passwordTokenSeenFlag) { + this.passwordTokenSeenFlag = true; + continue; + } + const encodedCodePoints = percentEncodeChar(codePoint, isUserinfoPercentEncode); + if (this.passwordTokenSeenFlag) { + this.url.password += encodedCodePoints; + } else { + this.url.username += encodedCodePoints; + } + } + this.buffer = ""; + } else if (isNaN(c) || c === 47 || c === 63 || c === 35 || + (isSpecial(this.url) && c === 92)) { + if (this.atFlag && this.buffer === "") { + this.parseError = true; + return failure; + } + this.pointer -= countSymbols(this.buffer) + 1; + this.buffer = ""; + this.state = "host"; + } else { + this.buffer += cStr; + } + + return true; +}; + +URLStateMachine.prototype["parse hostname"] = +URLStateMachine.prototype["parse host"] = function parseHostName(c, cStr) { + if (this.stateOverride && this.url.scheme === "file") { + --this.pointer; + this.state = "file host"; + } else if (c === 58 && !this.arrFlag) { + if (this.buffer === "") { + this.parseError = true; + return failure; + } + + const host = parseHost(this.buffer, isNotSpecial(this.url)); + if (host === failure) { + return failure; + } + + this.url.host = host; + this.buffer = ""; + this.state = "port"; + if (this.stateOverride === "hostname") { + return false; + } + } else if (isNaN(c) || c === 47 || c === 63 || c === 35 || + (isSpecial(this.url) && c === 92)) { + --this.pointer; + if (isSpecial(this.url) && this.buffer === "") { + this.parseError = true; + return failure; + } else if (this.stateOverride && this.buffer === "" && + (includesCredentials(this.url) || this.url.port !== null)) { + this.parseError = true; + return false; + } + + const host = parseHost(this.buffer, isNotSpecial(this.url)); + if (host === failure) { + return failure; + } + + this.url.host = host; + this.buffer = ""; + this.state = "path start"; + if (this.stateOverride) { + return false; + } + } else { + if (c === 91) { + this.arrFlag = true; + } else if (c === 93) { + this.arrFlag = false; + } + this.buffer += cStr; + } + + return true; +}; + +URLStateMachine.prototype["parse port"] = function parsePort(c, cStr) { + if (infra.isASCIIDigit(c)) { + this.buffer += cStr; + } else if (isNaN(c) || c === 47 || c === 63 || c === 35 || + (isSpecial(this.url) && c === 92) || + this.stateOverride) { + if (this.buffer !== "") { + const port = parseInt(this.buffer); + if (port > Math.pow(2, 16) - 1) { + this.parseError = true; + return failure; + } + this.url.port = port === defaultPort(this.url.scheme) ? null : port; + this.buffer = ""; + } + if (this.stateOverride) { + return false; + } + this.state = "path start"; + --this.pointer; + } else { + this.parseError = true; + return failure; + } + + return true; +}; + +const fileOtherwiseCodePoints = new Set([47, 92, 63, 35]); + +function startsWithWindowsDriveLetter(input, pointer) { + const length = input.length - pointer; + return length >= 2 && + isWindowsDriveLetterCodePoints(input[pointer], input[pointer + 1]) && + (length === 2 || fileOtherwiseCodePoints.has(input[pointer + 2])); +} + +URLStateMachine.prototype["parse file"] = function parseFile(c) { + this.url.scheme = "file"; + + if (c === 47 || c === 92) { + if (c === 92) { + this.parseError = true; + } + this.state = "file slash"; + } else if (this.base !== null && this.base.scheme === "file") { + if (isNaN(c)) { + this.url.host = this.base.host; + this.url.path = this.base.path.slice(); + this.url.query = this.base.query; + } else if (c === 63) { + this.url.host = this.base.host; + this.url.path = this.base.path.slice(); + this.url.query = ""; + this.state = "query"; + } else if (c === 35) { + this.url.host = this.base.host; + this.url.path = this.base.path.slice(); + this.url.query = this.base.query; + this.url.fragment = ""; + this.state = "fragment"; + } else { + if (!startsWithWindowsDriveLetter(this.input, this.pointer)) { + this.url.host = this.base.host; + this.url.path = this.base.path.slice(); + shortenPath(this.url); + } else { + this.parseError = true; + } + + this.state = "path"; + --this.pointer; + } + } else { + this.state = "path"; + --this.pointer; + } + + return true; +}; + +URLStateMachine.prototype["parse file slash"] = function parseFileSlash(c) { + if (c === 47 || c === 92) { + if (c === 92) { + this.parseError = true; + } + this.state = "file host"; + } else { + if (this.base !== null && this.base.scheme === "file" && + !startsWithWindowsDriveLetter(this.input, this.pointer)) { + if (isNormalizedWindowsDriveLetterString(this.base.path[0])) { + this.url.path.push(this.base.path[0]); + } else { + this.url.host = this.base.host; + } + } + this.state = "path"; + --this.pointer; + } + + return true; +}; + +URLStateMachine.prototype["parse file host"] = function parseFileHost(c, cStr) { + if (isNaN(c) || c === 47 || c === 92 || c === 63 || c === 35) { + --this.pointer; + if (!this.stateOverride && isWindowsDriveLetterString(this.buffer)) { + this.parseError = true; + this.state = "path"; + } else if (this.buffer === "") { + this.url.host = ""; + if (this.stateOverride) { + return false; + } + this.state = "path start"; + } else { + let host = parseHost(this.buffer, isNotSpecial(this.url)); + if (host === failure) { + return failure; + } + if (host === "localhost") { + host = ""; + } + this.url.host = host; + + if (this.stateOverride) { + return false; + } + + this.buffer = ""; + this.state = "path start"; + } + } else { + this.buffer += cStr; + } + + return true; +}; + +URLStateMachine.prototype["parse path start"] = function parsePathStart(c) { + if (isSpecial(this.url)) { + if (c === 92) { + this.parseError = true; + } + this.state = "path"; + + if (c !== 47 && c !== 92) { + --this.pointer; + } + } else if (!this.stateOverride && c === 63) { + this.url.query = ""; + this.state = "query"; + } else if (!this.stateOverride && c === 35) { + this.url.fragment = ""; + this.state = "fragment"; + } else if (c !== undefined) { + this.state = "path"; + if (c !== 47) { + --this.pointer; + } + } + + return true; +}; + +URLStateMachine.prototype["parse path"] = function parsePath(c) { + if (isNaN(c) || c === 47 || (isSpecial(this.url) && c === 92) || + (!this.stateOverride && (c === 63 || c === 35))) { + if (isSpecial(this.url) && c === 92) { + this.parseError = true; + } + + if (isDoubleDot(this.buffer)) { + shortenPath(this.url); + if (c !== 47 && !(isSpecial(this.url) && c === 92)) { + this.url.path.push(""); + } + } else if (isSingleDot(this.buffer) && c !== 47 && + !(isSpecial(this.url) && c === 92)) { + this.url.path.push(""); + } else if (!isSingleDot(this.buffer)) { + if (this.url.scheme === "file" && this.url.path.length === 0 && isWindowsDriveLetterString(this.buffer)) { + if (this.url.host !== "" && this.url.host !== null) { + this.parseError = true; + this.url.host = ""; + } + this.buffer = this.buffer[0] + ":"; + } + this.url.path.push(this.buffer); + } + this.buffer = ""; + if (this.url.scheme === "file" && (c === undefined || c === 63 || c === 35)) { + while (this.url.path.length > 1 && this.url.path[0] === "") { + this.parseError = true; + this.url.path.shift(); + } + } + if (c === 63) { + this.url.query = ""; + this.state = "query"; + } + if (c === 35) { + this.url.fragment = ""; + this.state = "fragment"; + } + } else { + // TODO: If c is not a URL code point and not "%", parse error. + + if (c === 37 && + (!infra.isASCIIHex(this.input[this.pointer + 1]) || + !infra.isASCIIHex(this.input[this.pointer + 2]))) { + this.parseError = true; + } + + this.buffer += percentEncodeChar(c, isPathPercentEncode); + } + + return true; +}; + +URLStateMachine.prototype["parse cannot-be-a-base-URL path"] = function parseCannotBeABaseURLPath(c) { + if (c === 63) { + this.url.query = ""; + this.state = "query"; + } else if (c === 35) { + this.url.fragment = ""; + this.state = "fragment"; + } else { + // TODO: Add: not a URL code point + if (!isNaN(c) && c !== 37) { + this.parseError = true; + } + + if (c === 37 && + (!infra.isASCIIHex(this.input[this.pointer + 1]) || + !infra.isASCIIHex(this.input[this.pointer + 2]))) { + this.parseError = true; + } + + if (!isNaN(c)) { + this.url.path[0] = this.url.path[0] + percentEncodeChar(c, isC0ControlPercentEncode); + } + } + + return true; +}; + +URLStateMachine.prototype["parse query"] = function parseQuery(c, cStr) { + if (isNaN(c) || (!this.stateOverride && c === 35)) { + if (!isSpecial(this.url) || this.url.scheme === "ws" || this.url.scheme === "wss") { + this.encodingOverride = "utf-8"; + } + + const buffer = Buffer.from(this.buffer); // TODO: Use encoding override instead + for (let i = 0; i < buffer.length; ++i) { + if (buffer[i] < 0x21 || + buffer[i] > 0x7E || + buffer[i] === 0x22 || buffer[i] === 0x23 || buffer[i] === 0x3C || buffer[i] === 0x3E || + (buffer[i] === 0x27 && isSpecial(this.url))) { + this.url.query += percentEncode(buffer[i]); + } else { + this.url.query += String.fromCodePoint(buffer[i]); + } + } + + this.buffer = ""; + if (c === 35) { + this.url.fragment = ""; + this.state = "fragment"; + } + } else { + // TODO: If c is not a URL code point and not "%", parse error. + if (c === 37 && + (!infra.isASCIIHex(this.input[this.pointer + 1]) || + !infra.isASCIIHex(this.input[this.pointer + 2]))) { + this.parseError = true; + } + + this.buffer += cStr; + } + + return true; +}; + +URLStateMachine.prototype["parse fragment"] = function parseFragment(c) { + if (isNaN(c)) { // do nothing + } else if (c === 0x0) { + this.parseError = true; + } else { + // TODO: If c is not a URL code point and not "%", parse error. + if (c === 37 && + (!infra.isASCIIHex(this.input[this.pointer + 1]) || + !infra.isASCIIHex(this.input[this.pointer + 2]))) { + this.parseError = true; + } + + this.url.fragment += percentEncodeChar(c, isFragmentPercentEncode); + } + + return true; +}; + +function serializeURL(url, excludeFragment) { + let output = url.scheme + ":"; + if (url.host !== null) { + output += "//"; + + if (url.username !== "" || url.password !== "") { + output += url.username; + if (url.password !== "") { + output += ":" + url.password; + } + output += "@"; + } + + output += serializeHost(url.host); + + if (url.port !== null) { + output += ":" + url.port; + } + } else if (url.host === null && url.scheme === "file") { + output += "//"; + } + + if (url.cannotBeABaseURL) { + output += url.path[0]; + } else { + for (const string of url.path) { + output += "/" + string; + } + } + + if (url.query !== null) { + output += "?" + url.query; + } + + if (!excludeFragment && url.fragment !== null) { + output += "#" + url.fragment; + } + + return output; +} + +function serializeOrigin(tuple) { + let result = tuple.scheme + "://"; + result += serializeHost(tuple.host); + + if (tuple.port !== null) { + result += ":" + tuple.port; + } + + return result; +} + +module.exports.serializeURL = serializeURL; + +module.exports.serializeURLOrigin = function (url) { + // https://url.spec.whatwg.org/#concept-url-origin + switch (url.scheme) { + case "blob": + try { + return module.exports.serializeURLOrigin(module.exports.parseURL(url.path[0])); + } catch (e) { + // serializing an opaque origin returns "null" + return "null"; + } + case "ftp": + case "gopher": + case "http": + case "https": + case "ws": + case "wss": + return serializeOrigin({ + scheme: url.scheme, + host: url.host, + port: url.port + }); + case "file": + // spec says "exercise to the reader", chrome says "file://" + return "file://"; + default: + // serializing an opaque origin returns "null" + return "null"; + } +}; + +module.exports.basicURLParse = function (input, options) { + if (options === undefined) { + options = {}; + } + + const usm = new URLStateMachine(input, options.baseURL, options.encodingOverride, options.url, options.stateOverride); + if (usm.failure) { + return null; + } + + return usm.url; +}; + +module.exports.setTheUsername = function (url, username) { + url.username = ""; + const decoded = punycode.ucs2.decode(username); + for (let i = 0; i < decoded.length; ++i) { + url.username += percentEncodeChar(decoded[i], isUserinfoPercentEncode); + } +}; + +module.exports.setThePassword = function (url, password) { + url.password = ""; + const decoded = punycode.ucs2.decode(password); + for (let i = 0; i < decoded.length; ++i) { + url.password += percentEncodeChar(decoded[i], isUserinfoPercentEncode); + } +}; + +module.exports.serializeHost = serializeHost; + +module.exports.cannotHaveAUsernamePasswordPort = cannotHaveAUsernamePasswordPort; + +module.exports.serializeInteger = function (integer) { + return String(integer); +}; + +module.exports.parseURL = function (input, options) { + if (options === undefined) { + options = {}; + } + + // We don't handle blobs, so this just delegates: + return module.exports.basicURLParse(input, { baseURL: options.baseURL, encodingOverride: options.encodingOverride }); +}; + +/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(58).Buffer)) + +/***/ }), + +/***/ 493: +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/* WEBPACK VAR INJECTION */(function(Buffer) { +const { isASCIIHex } = __webpack_require__(495); + +function strictlySplitByteSequence(buf, cp) { + const list = []; + let last = 0; + let i = buf.indexOf(cp); + while (i >= 0) { + list.push(buf.slice(last, i)); + last = i + 1; + i = buf.indexOf(cp, last); + } + if (last !== buf.length) { + list.push(buf.slice(last)); + } + return list; +} + +function replaceByteInByteSequence(buf, from, to) { + let i = buf.indexOf(from); + while (i >= 0) { + buf[i] = to; + i = buf.indexOf(from, i + 1); + } + return buf; +} + +function percentEncode(c) { + let hex = c.toString(16).toUpperCase(); + if (hex.length === 1) { + hex = "0" + hex; + } + + return "%" + hex; +} + +function percentDecode(input) { + const output = Buffer.alloc(input.byteLength); + let ptr = 0; + for (let i = 0; i < input.length; ++i) { + if (input[i] !== 37 || !isASCIIHex(input[i + 1]) || !isASCIIHex(input[i + 2])) { + output[ptr++] = input[i]; + } else { + output[ptr++] = parseInt(input.slice(i + 1, i + 3).toString(), 16); + i += 2; + } + } + return output.slice(0, ptr); +} + +function parseUrlencoded(input) { + const sequences = strictlySplitByteSequence(input, 38); + const output = []; + for (const bytes of sequences) { + if (bytes.length === 0) { + continue; + } + + let name; + let value; + const indexOfEqual = bytes.indexOf(61); + + if (indexOfEqual >= 0) { + name = bytes.slice(0, indexOfEqual); + value = bytes.slice(indexOfEqual + 1); + } else { + name = bytes; + value = Buffer.alloc(0); + } + + name = replaceByteInByteSequence(Buffer.from(name), 43, 32); + value = replaceByteInByteSequence(Buffer.from(value), 43, 32); + + output.push([percentDecode(name).toString(), percentDecode(value).toString()]); + } + return output; +} + +function serializeUrlencodedByte(input) { + let output = ""; + for (const byte of input) { + if (byte === 32) { + output += "+"; + } else if (byte === 42 || + byte === 45 || + byte === 46 || + (byte >= 48 && byte <= 57) || + (byte >= 65 && byte <= 90) || + byte === 95 || + (byte >= 97 && byte <= 122)) { + output += String.fromCodePoint(byte); + } else { + output += percentEncode(byte); + } + } + return output; +} + +function serializeUrlencoded(tuples, encodingOverride = undefined) { + let encoding = "utf-8"; + if (encodingOverride !== undefined) { + encoding = encodingOverride; + } + + let output = ""; + for (const [i, tuple] of tuples.entries()) { + // TODO: handle encoding override + const name = serializeUrlencodedByte(Buffer.from(tuple[0])); + let value = tuple[1]; + if (tuple.length > 2 && tuple[2] !== undefined) { + if (tuple[2] === "hidden" && name === "_charset_") { + value = encoding; + } else if (tuple[2] === "file") { + // value is a File object + value = value.name; + } + } + value = serializeUrlencodedByte(Buffer.from(value)); + if (i !== 0) { + output += "&"; + } + output += `${name}=${value}`; + } + return output; +} + +module.exports = { + percentEncode, + percentDecode, + + // application/x-www-form-urlencoded string parser + parseUrlencoded(input) { + return parseUrlencoded(Buffer.from(input)); + }, + + // application/x-www-form-urlencoded serializer + serializeUrlencoded +}; + +/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(58).Buffer)) + +/***/ }), + +/***/ 494: +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/* WEBPACK VAR INJECTION */(function(Buffer) { + +// Returns "Type(value) is Object" in ES terminology. +function isObject(value) { + return typeof value === "object" && value !== null || typeof value === "function"; +} + +function getReferenceToBytes(bufferSource) { + // Node.js' Buffer does not allow subclassing for now, so we can get away with a prototype object check for perf. + if (Object.getPrototypeOf(bufferSource) === Buffer.prototype) { + return bufferSource; + } + if (bufferSource instanceof ArrayBuffer) { + return Buffer.from(bufferSource); + } + return Buffer.from(bufferSource.buffer, bufferSource.byteOffset, bufferSource.byteLength); +} + +function getCopyToBytes(bufferSource) { + return Buffer.from(getReferenceToBytes(bufferSource)); +} + +function mixin(target, source) { + const keys = Object.getOwnPropertyNames(source); + for (let i = 0; i < keys.length; ++i) { + if (keys[i] in target) { + continue; + } + + Object.defineProperty(target, keys[i], Object.getOwnPropertyDescriptor(source, keys[i])); + } +} + +const wrapperSymbol = Symbol("wrapper"); +const implSymbol = Symbol("impl"); +const sameObjectCaches = Symbol("SameObject caches"); + +function getSameObject(wrapper, prop, creator) { + if (!wrapper[sameObjectCaches]) { + wrapper[sameObjectCaches] = Object.create(null); + } + + if (prop in wrapper[sameObjectCaches]) { + return wrapper[sameObjectCaches][prop]; + } + + wrapper[sameObjectCaches][prop] = creator(); + return wrapper[sameObjectCaches][prop]; +} + +function wrapperForImpl(impl) { + return impl ? impl[wrapperSymbol] : null; +} + +function implForWrapper(wrapper) { + return wrapper ? wrapper[implSymbol] : null; +} + +function tryWrapperForImpl(impl) { + const wrapper = wrapperForImpl(impl); + return wrapper ? wrapper : impl; +} + +function tryImplForWrapper(wrapper) { + const impl = implForWrapper(wrapper); + return impl ? impl : wrapper; +} + +const iterInternalSymbol = Symbol("internal"); +const IteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]())); + +function isArrayIndexPropName(P) { + if (typeof P !== "string") { + return false; + } + const i = P >>> 0; + if (i === Math.pow(2, 32) - 1) { + return false; + } + const s = `${i}`; + if (P !== s) { + return false; + } + return true; +} + +const supportsPropertyIndex = Symbol("supports property index"); +const supportedPropertyIndices = Symbol("supported property indices"); +const supportsPropertyName = Symbol("supports property name"); +const supportedPropertyNames = Symbol("supported property names"); +const indexedGet = Symbol("indexed property get"); +const indexedSetNew = Symbol("indexed property set new"); +const indexedSetExisting = Symbol("indexed property set existing"); +const namedGet = Symbol("named property get"); +const namedSetNew = Symbol("named property set new"); +const namedSetExisting = Symbol("named property set existing"); +const namedDelete = Symbol("named property delete"); + +module.exports = exports = { + isObject, + getReferenceToBytes, + getCopyToBytes, + mixin, + wrapperSymbol, + implSymbol, + getSameObject, + wrapperForImpl, + implForWrapper, + tryWrapperForImpl, + tryImplForWrapper, + iterInternalSymbol, + IteratorPrototype, + isArrayIndexPropName, + supportsPropertyIndex, + supportedPropertyIndices, + supportsPropertyName, + supportedPropertyNames, + indexedGet, + indexedSetNew, + indexedSetExisting, + namedGet, + namedSetNew, + namedSetExisting, + namedDelete +}; + +/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(58).Buffer)) + +/***/ }), + +/***/ 495: +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +function isASCIIDigit(c) { + return c >= 0x30 && c <= 0x39; +} + +function isASCIIAlpha(c) { + return (c >= 0x41 && c <= 0x5A) || (c >= 0x61 && c <= 0x7A); +} + +function isASCIIAlphanumeric(c) { + return isASCIIAlpha(c) || isASCIIDigit(c); +} + +function isASCIIHex(c) { + return isASCIIDigit(c) || (c >= 0x41 && c <= 0x46) || (c >= 0x61 && c <= 0x66); +} + +module.exports = { + isASCIIDigit, + isASCIIAlpha, + isASCIIAlphanumeric, + isASCIIHex +}; + + +/***/ }), + +/***/ 496: +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +const conversions = __webpack_require__(173); +const utils = __webpack_require__(494); + +const impl = utils.implSymbol; + +const IteratorPrototype = Object.create(utils.IteratorPrototype, { + next: { + value: function next() { + const internal = this[utils.iterInternalSymbol]; + const { target, kind, index } = internal; + const values = Array.from(target[impl]); + const len = values.length; + if (index >= len) { + return { value: undefined, done: true }; + } + + const pair = values[index]; + internal.index = index + 1; + const [key, value] = pair.map(utils.tryWrapperForImpl); + + let result; + switch (kind) { + case "key": + result = key; + break; + case "value": + result = value; + break; + case "key+value": + result = [key, value]; + break; + } + return { value: result, done: false }; + }, + writable: true, + enumerable: true, + configurable: true + }, + [Symbol.toStringTag]: { + value: "URLSearchParamsIterator", + writable: false, + enumerable: false, + configurable: true + } +}); + +function URLSearchParams() { + const args = []; + for (let i = 0; i < arguments.length && i < 1; ++i) { + args[i] = arguments[i]; + } + + if (args[0] !== undefined) { + if (utils.isObject(args[0])) { + if (args[0][Symbol.iterator] !== undefined) { + if (!utils.isObject(args[0])) { + throw new TypeError( + "Failed to construct 'URLSearchParams': parameter 1" + " sequence" + " is not an iterable object." + ); + } else { + const V = []; + const tmp = args[0]; + for (let nextItem of tmp) { + if (!utils.isObject(nextItem)) { + throw new TypeError( + "Failed to construct 'URLSearchParams': parameter 1" + + " sequence" + + "'s element" + + " is not an iterable object." + ); + } else { + const V = []; + const tmp = nextItem; + for (let nextItem of tmp) { + nextItem = conversions["USVString"](nextItem, { + context: + "Failed to construct 'URLSearchParams': parameter 1" + " sequence" + "'s element" + "'s element" + }); + + V.push(nextItem); + } + nextItem = V; + } + + V.push(nextItem); + } + args[0] = V; + } + } else { + if (!utils.isObject(args[0])) { + throw new TypeError("Failed to construct 'URLSearchParams': parameter 1" + " record" + " is not an object."); + } else { + const result = Object.create(null); + for (const key of Reflect.ownKeys(args[0])) { + const desc = Object.getOwnPropertyDescriptor(args[0], key); + if (desc && desc.enumerable) { + let typedKey = key; + let typedValue = args[0][key]; + + typedKey = conversions["USVString"](typedKey, { + context: "Failed to construct 'URLSearchParams': parameter 1" + " record" + "'s key" + }); + + typedValue = conversions["USVString"](typedValue, { + context: "Failed to construct 'URLSearchParams': parameter 1" + " record" + "'s value" + }); + + result[typedKey] = typedValue; + } + } + args[0] = result; + } + } + } else { + args[0] = conversions["USVString"](args[0], { context: "Failed to construct 'URLSearchParams': parameter 1" }); + } + } else { + args[0] = ""; + } + + iface.setup(this, args); +} + +Object.defineProperty(URLSearchParams, "prototype", { + value: URLSearchParams.prototype, + writable: false, + enumerable: false, + configurable: false +}); + +Object.defineProperty(URLSearchParams.prototype, Symbol.iterator, { + writable: true, + enumerable: false, + configurable: true, + value: function entries() { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + return module.exports.createDefaultIterator(this, "key+value"); + } +}); +URLSearchParams.prototype.forEach = function forEach(callback) { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + if (arguments.length < 1) { + throw new TypeError( + "Failed to execute 'forEach' on 'URLSearchParams': 1 argument required, " + "but only 0 present." + ); + } + if (typeof callback !== "function") { + throw new TypeError( + "Failed to execute 'forEach' on 'URLSearchParams': The callback provided " + "as parameter 1 is not a function." + ); + } + const thisArg = arguments[1]; + let pairs = Array.from(this[impl]); + let i = 0; + while (i < pairs.length) { + const [key, value] = pairs[i].map(utils.tryWrapperForImpl); + callback.call(thisArg, value, key, this); + pairs = Array.from(this[impl]); + i++; + } +}; +URLSearchParams.prototype.append = function append(name, value) { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + + if (arguments.length < 2) { + throw new TypeError( + "Failed to execute 'append' on 'URLSearchParams': 2 " + + "arguments required, but only " + + arguments.length + + " present." + ); + } + + const args = []; + for (let i = 0; i < arguments.length && i < 2; ++i) { + args[i] = arguments[i]; + } + + args[0] = conversions["USVString"](args[0], { + context: "Failed to execute 'append' on 'URLSearchParams': parameter 1" + }); + + args[1] = conversions["USVString"](args[1], { + context: "Failed to execute 'append' on 'URLSearchParams': parameter 2" + }); + + return this[impl].append(...args); +}; + +URLSearchParams.prototype.delete = function _(name) { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + + if (arguments.length < 1) { + throw new TypeError( + "Failed to execute 'delete' on 'URLSearchParams': 1 " + + "argument required, but only " + + arguments.length + + " present." + ); + } + + const args = []; + for (let i = 0; i < arguments.length && i < 1; ++i) { + args[i] = arguments[i]; + } + + args[0] = conversions["USVString"](args[0], { + context: "Failed to execute 'delete' on 'URLSearchParams': parameter 1" + }); + + return this[impl].delete(...args); +}; + +URLSearchParams.prototype.get = function get(name) { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + + if (arguments.length < 1) { + throw new TypeError( + "Failed to execute 'get' on 'URLSearchParams': 1 " + + "argument required, but only " + + arguments.length + + " present." + ); + } + + const args = []; + for (let i = 0; i < arguments.length && i < 1; ++i) { + args[i] = arguments[i]; + } + + args[0] = conversions["USVString"](args[0], { context: "Failed to execute 'get' on 'URLSearchParams': parameter 1" }); + + return this[impl].get(...args); +}; + +URLSearchParams.prototype.getAll = function getAll(name) { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + + if (arguments.length < 1) { + throw new TypeError( + "Failed to execute 'getAll' on 'URLSearchParams': 1 " + + "argument required, but only " + + arguments.length + + " present." + ); + } + + const args = []; + for (let i = 0; i < arguments.length && i < 1; ++i) { + args[i] = arguments[i]; + } + + args[0] = conversions["USVString"](args[0], { + context: "Failed to execute 'getAll' on 'URLSearchParams': parameter 1" + }); + + return utils.tryWrapperForImpl(this[impl].getAll(...args)); +}; + +URLSearchParams.prototype.has = function has(name) { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + + if (arguments.length < 1) { + throw new TypeError( + "Failed to execute 'has' on 'URLSearchParams': 1 " + + "argument required, but only " + + arguments.length + + " present." + ); + } + + const args = []; + for (let i = 0; i < arguments.length && i < 1; ++i) { + args[i] = arguments[i]; + } + + args[0] = conversions["USVString"](args[0], { context: "Failed to execute 'has' on 'URLSearchParams': parameter 1" }); + + return this[impl].has(...args); +}; + +URLSearchParams.prototype.set = function set(name, value) { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + + if (arguments.length < 2) { + throw new TypeError( + "Failed to execute 'set' on 'URLSearchParams': 2 " + + "arguments required, but only " + + arguments.length + + " present." + ); + } + + const args = []; + for (let i = 0; i < arguments.length && i < 2; ++i) { + args[i] = arguments[i]; + } + + args[0] = conversions["USVString"](args[0], { context: "Failed to execute 'set' on 'URLSearchParams': parameter 1" }); + + args[1] = conversions["USVString"](args[1], { context: "Failed to execute 'set' on 'URLSearchParams': parameter 2" }); + + return this[impl].set(...args); +}; + +URLSearchParams.prototype.sort = function sort() { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + + return this[impl].sort(); +}; + +URLSearchParams.prototype.toString = function toString() { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + + return this[impl].toString(); +}; + +URLSearchParams.prototype.entries = URLSearchParams.prototype[Symbol.iterator]; + +URLSearchParams.prototype.keys = function keys() { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + return module.exports.createDefaultIterator(this, "key"); +}; + +URLSearchParams.prototype.values = function values() { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + return module.exports.createDefaultIterator(this, "value"); +}; + +Object.defineProperty(URLSearchParams.prototype, Symbol.toStringTag, { + value: "URLSearchParams", + writable: false, + enumerable: false, + configurable: true +}); + +const iface = { + mixedInto: [], + is(obj) { + if (obj) { + if (obj[impl] instanceof Impl.implementation) { + return true; + } + for (let i = 0; i < module.exports.mixedInto.length; ++i) { + if (obj instanceof module.exports.mixedInto[i]) { + return true; + } + } + } + return false; + }, + isImpl(obj) { + if (obj) { + if (obj instanceof Impl.implementation) { + return true; + } + + const wrapper = utils.wrapperForImpl(obj); + for (let i = 0; i < module.exports.mixedInto.length; ++i) { + if (wrapper instanceof module.exports.mixedInto[i]) { + return true; + } + } + } + return false; + }, + convert(obj, { context = "The provided value" } = {}) { + if (module.exports.is(obj)) { + return utils.implForWrapper(obj); + } + throw new TypeError(`${context} is not of type 'URLSearchParams'.`); + }, + + createDefaultIterator(target, kind) { + const iterator = Object.create(IteratorPrototype); + Object.defineProperty(iterator, utils.iterInternalSymbol, { + value: { target, kind, index: 0 }, + writable: false, + enumerable: false, + configurable: true + }); + return iterator; + }, + + create(constructorArgs, privateData) { + let obj = Object.create(URLSearchParams.prototype); + obj = this.setup(obj, constructorArgs, privateData); + return obj; + }, + createImpl(constructorArgs, privateData) { + let obj = Object.create(URLSearchParams.prototype); + obj = this.setup(obj, constructorArgs, privateData); + return utils.implForWrapper(obj); + }, + _internalSetup(obj) {}, + setup(obj, constructorArgs, privateData) { + if (!privateData) privateData = {}; + + privateData.wrapper = obj; + + this._internalSetup(obj); + Object.defineProperty(obj, impl, { + value: new Impl.implementation(constructorArgs, privateData), + writable: false, + enumerable: false, + configurable: true + }); + + obj[impl][utils.wrapperSymbol] = obj; + if (Impl.init) { + Impl.init(obj[impl], privateData); + } + return obj; + }, + interface: URLSearchParams, + expose: { + Window: { URLSearchParams }, + Worker: { URLSearchParams } + } +}; // iface +module.exports = iface; + +const Impl = __webpack_require__(500); + + +/***/ }), + +/***/ 497: +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +exports.URL = __webpack_require__(498).interface; +exports.URLSearchParams = __webpack_require__(496).interface; + +exports.parseURL = __webpack_require__(492).parseURL; +exports.basicURLParse = __webpack_require__(492).basicURLParse; +exports.serializeURL = __webpack_require__(492).serializeURL; +exports.serializeHost = __webpack_require__(492).serializeHost; +exports.serializeInteger = __webpack_require__(492).serializeInteger; +exports.serializeURLOrigin = __webpack_require__(492).serializeURLOrigin; +exports.setTheUsername = __webpack_require__(492).setTheUsername; +exports.setThePassword = __webpack_require__(492).setThePassword; +exports.cannotHaveAUsernamePasswordPort = __webpack_require__(492).cannotHaveAUsernamePasswordPort; + +exports.percentDecode = __webpack_require__(493).percentDecode; + + +/***/ }), + +/***/ 498: +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +const conversions = __webpack_require__(173); +const utils = __webpack_require__(494); + +const impl = utils.implSymbol; + +function URL(url) { + if (!new.target) { + throw new TypeError( + "Failed to construct 'URL'. Please use the 'new' operator; this constructor " + "cannot be called as a function." + ); + } + if (arguments.length < 1) { + throw new TypeError( + "Failed to construct 'URL': 1 " + "argument required, but only " + arguments.length + " present." + ); + } + + const args = []; + for (let i = 0; i < arguments.length && i < 2; ++i) { + args[i] = arguments[i]; + } + + args[0] = conversions["USVString"](args[0], { context: "Failed to construct 'URL': parameter 1" }); + + if (args[1] !== undefined) { + args[1] = conversions["USVString"](args[1], { context: "Failed to construct 'URL': parameter 2" }); + } + + iface.setup(this, args); +} + +Object.defineProperty(URL, "prototype", { + value: URL.prototype, + writable: false, + enumerable: false, + configurable: false +}); + +URL.prototype.toJSON = function toJSON() { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + + return this[impl].toJSON(); +}; + +Object.defineProperty(URL.prototype, "href", { + get() { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + + return this[impl]["href"]; + }, + + set(V) { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + + V = conversions["USVString"](V, { context: "Failed to set the 'href' property on 'URL': The provided value" }); + + this[impl]["href"] = V; + }, + + enumerable: true, + configurable: true +}); + +URL.prototype.toString = function toString() { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + return this[impl]["href"]; +}; + +Object.defineProperty(URL.prototype, "origin", { + get() { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + + return this[impl]["origin"]; + }, + + enumerable: true, + configurable: true +}); + +Object.defineProperty(URL.prototype, "protocol", { + get() { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + + return this[impl]["protocol"]; + }, + + set(V) { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + + V = conversions["USVString"](V, { context: "Failed to set the 'protocol' property on 'URL': The provided value" }); + + this[impl]["protocol"] = V; + }, + + enumerable: true, + configurable: true +}); + +Object.defineProperty(URL.prototype, "username", { + get() { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + + return this[impl]["username"]; + }, + + set(V) { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + + V = conversions["USVString"](V, { context: "Failed to set the 'username' property on 'URL': The provided value" }); + + this[impl]["username"] = V; + }, + + enumerable: true, + configurable: true +}); + +Object.defineProperty(URL.prototype, "password", { + get() { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + + return this[impl]["password"]; + }, + + set(V) { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + + V = conversions["USVString"](V, { context: "Failed to set the 'password' property on 'URL': The provided value" }); + + this[impl]["password"] = V; + }, + + enumerable: true, + configurable: true +}); + +Object.defineProperty(URL.prototype, "host", { + get() { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + + return this[impl]["host"]; + }, + + set(V) { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + + V = conversions["USVString"](V, { context: "Failed to set the 'host' property on 'URL': The provided value" }); + + this[impl]["host"] = V; + }, + + enumerable: true, + configurable: true +}); + +Object.defineProperty(URL.prototype, "hostname", { + get() { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + + return this[impl]["hostname"]; + }, + + set(V) { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + + V = conversions["USVString"](V, { context: "Failed to set the 'hostname' property on 'URL': The provided value" }); + + this[impl]["hostname"] = V; + }, + + enumerable: true, + configurable: true +}); + +Object.defineProperty(URL.prototype, "port", { + get() { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + + return this[impl]["port"]; + }, + + set(V) { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + + V = conversions["USVString"](V, { context: "Failed to set the 'port' property on 'URL': The provided value" }); + + this[impl]["port"] = V; + }, + + enumerable: true, + configurable: true +}); + +Object.defineProperty(URL.prototype, "pathname", { + get() { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + + return this[impl]["pathname"]; + }, + + set(V) { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + + V = conversions["USVString"](V, { context: "Failed to set the 'pathname' property on 'URL': The provided value" }); + + this[impl]["pathname"] = V; + }, + + enumerable: true, + configurable: true +}); + +Object.defineProperty(URL.prototype, "search", { + get() { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + + return this[impl]["search"]; + }, + + set(V) { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + + V = conversions["USVString"](V, { context: "Failed to set the 'search' property on 'URL': The provided value" }); + + this[impl]["search"] = V; + }, + + enumerable: true, + configurable: true +}); + +Object.defineProperty(URL.prototype, "searchParams", { + get() { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + + return utils.getSameObject(this, "searchParams", () => { + return utils.tryWrapperForImpl(this[impl]["searchParams"]); + }); + }, + + enumerable: true, + configurable: true +}); + +Object.defineProperty(URL.prototype, "hash", { + get() { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + + return this[impl]["hash"]; + }, + + set(V) { + if (!this || !module.exports.is(this)) { + throw new TypeError("Illegal invocation"); + } + + V = conversions["USVString"](V, { context: "Failed to set the 'hash' property on 'URL': The provided value" }); + + this[impl]["hash"] = V; + }, + + enumerable: true, + configurable: true +}); + +Object.defineProperty(URL.prototype, Symbol.toStringTag, { + value: "URL", + writable: false, + enumerable: false, + configurable: true +}); + +const iface = { + mixedInto: [], + is(obj) { + if (obj) { + if (obj[impl] instanceof Impl.implementation) { + return true; + } + for (let i = 0; i < module.exports.mixedInto.length; ++i) { + if (obj instanceof module.exports.mixedInto[i]) { + return true; + } + } + } + return false; + }, + isImpl(obj) { + if (obj) { + if (obj instanceof Impl.implementation) { + return true; + } + + const wrapper = utils.wrapperForImpl(obj); + for (let i = 0; i < module.exports.mixedInto.length; ++i) { + if (wrapper instanceof module.exports.mixedInto[i]) { + return true; + } + } + } + return false; + }, + convert(obj, { context = "The provided value" } = {}) { + if (module.exports.is(obj)) { + return utils.implForWrapper(obj); + } + throw new TypeError(`${context} is not of type 'URL'.`); + }, + + create(constructorArgs, privateData) { + let obj = Object.create(URL.prototype); + obj = this.setup(obj, constructorArgs, privateData); + return obj; + }, + createImpl(constructorArgs, privateData) { + let obj = Object.create(URL.prototype); + obj = this.setup(obj, constructorArgs, privateData); + return utils.implForWrapper(obj); + }, + _internalSetup(obj) {}, + setup(obj, constructorArgs, privateData) { + if (!privateData) privateData = {}; + + privateData.wrapper = obj; + + this._internalSetup(obj); + Object.defineProperty(obj, impl, { + value: new Impl.implementation(constructorArgs, privateData), + writable: false, + enumerable: false, + configurable: true + }); + + obj[impl][utils.wrapperSymbol] = obj; + if (Impl.init) { + Impl.init(obj[impl], privateData); + } + return obj; + }, + interface: URL, + expose: { + Window: { URL }, + Worker: { URL } + } +}; // iface +module.exports = iface; + +const Impl = __webpack_require__(499); + + +/***/ }), + +/***/ 499: +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +const usm = __webpack_require__(492); +const urlencoded = __webpack_require__(493); +const URLSearchParams = __webpack_require__(496); + +exports.implementation = class URLImpl { + constructor(constructorArgs) { + const url = constructorArgs[0]; + const base = constructorArgs[1]; + + let parsedBase = null; + if (base !== undefined) { + parsedBase = usm.basicURLParse(base); + if (parsedBase === null) { + throw new TypeError("Invalid base URL"); + } + } + + const parsedURL = usm.basicURLParse(url, { baseURL: parsedBase }); + if (parsedURL === null) { + throw new TypeError("Invalid URL"); + } + + const query = parsedURL.query !== null ? parsedURL.query : ""; + + this._url = parsedURL; + + // We cannot invoke the "new URLSearchParams object" algorithm without going through the constructor, which strips + // question mark by default. Therefore the doNotStripQMark hack is used. + this._query = URLSearchParams.createImpl([query], { doNotStripQMark: true }); + this._query._url = this; + } + + get href() { + return usm.serializeURL(this._url); + } + + set href(v) { + const parsedURL = usm.basicURLParse(v); + if (parsedURL === null) { + throw new TypeError("Invalid URL"); + } + + this._url = parsedURL; + + this._query._list.splice(0); + const { query } = parsedURL; + if (query !== null) { + this._query._list = urlencoded.parseUrlencoded(query); + } + } + + get origin() { + return usm.serializeURLOrigin(this._url); + } + + get protocol() { + return this._url.scheme + ":"; + } + + set protocol(v) { + usm.basicURLParse(v + ":", { url: this._url, stateOverride: "scheme start" }); + } + + get username() { + return this._url.username; + } + + set username(v) { + if (usm.cannotHaveAUsernamePasswordPort(this._url)) { + return; + } + + usm.setTheUsername(this._url, v); + } + + get password() { + return this._url.password; + } + + set password(v) { + if (usm.cannotHaveAUsernamePasswordPort(this._url)) { + return; + } + + usm.setThePassword(this._url, v); + } + + get host() { + const url = this._url; + + if (url.host === null) { + return ""; + } + + if (url.port === null) { + return usm.serializeHost(url.host); + } + + return usm.serializeHost(url.host) + ":" + usm.serializeInteger(url.port); + } + + set host(v) { + if (this._url.cannotBeABaseURL) { + return; + } + + usm.basicURLParse(v, { url: this._url, stateOverride: "host" }); + } + + get hostname() { + if (this._url.host === null) { + return ""; + } + + return usm.serializeHost(this._url.host); + } + + set hostname(v) { + if (this._url.cannotBeABaseURL) { + return; + } + + usm.basicURLParse(v, { url: this._url, stateOverride: "hostname" }); + } + + get port() { + if (this._url.port === null) { + return ""; + } + + return usm.serializeInteger(this._url.port); + } + + set port(v) { + if (usm.cannotHaveAUsernamePasswordPort(this._url)) { + return; + } + + if (v === "") { + this._url.port = null; + } else { + usm.basicURLParse(v, { url: this._url, stateOverride: "port" }); + } + } + + get pathname() { + if (this._url.cannotBeABaseURL) { + return this._url.path[0]; + } + + if (this._url.path.length === 0) { + return ""; + } + + return "/" + this._url.path.join("/"); + } + + set pathname(v) { + if (this._url.cannotBeABaseURL) { + return; + } + + this._url.path = []; + usm.basicURLParse(v, { url: this._url, stateOverride: "path start" }); + } + + get search() { + if (this._url.query === null || this._url.query === "") { + return ""; + } + + return "?" + this._url.query; + } + + set search(v) { + const url = this._url; + + if (v === "") { + url.query = null; + this._query._list = []; + return; + } + + const input = v[0] === "?" ? v.substring(1) : v; + url.query = ""; + usm.basicURLParse(input, { url, stateOverride: "query" }); + this._query._list = urlencoded.parseUrlencoded(input); + } + + get searchParams() { + return this._query; + } + + get hash() { + if (this._url.fragment === null || this._url.fragment === "") { + return ""; + } + + return "#" + this._url.fragment; + } + + set hash(v) { + if (v === "") { + this._url.fragment = null; + return; + } + + const input = v[0] === "#" ? v.substring(1) : v; + this._url.fragment = ""; + usm.basicURLParse(input, { url: this._url, stateOverride: "fragment" }); + } + + toJSON() { + return this.href; + } +}; + + +/***/ }), + +/***/ 500: +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +const stableSortBy = __webpack_require__(401); +const urlencoded = __webpack_require__(493); + +exports.implementation = class URLSearchParamsImpl { + constructor(constructorArgs, { doNotStripQMark = false }) { + let init = constructorArgs[0]; + this._list = []; + this._url = null; + + if (!doNotStripQMark && typeof init === "string" && init[0] === "?") { + init = init.slice(1); + } + + if (Array.isArray(init)) { + for (const pair of init) { + if (pair.length !== 2) { + throw new TypeError("Failed to construct 'URLSearchParams': parameter 1 sequence's element does not " + + "contain exactly two elements."); + } + this._list.push([pair[0], pair[1]]); + } + } else if (typeof init === "object" && Object.getPrototypeOf(init) === null) { + for (const name of Object.keys(init)) { + const value = init[name]; + this._list.push([name, value]); + } + } else { + this._list = urlencoded.parseUrlencoded(init); + } + } + + _updateSteps() { + if (this._url !== null) { + let query = urlencoded.serializeUrlencoded(this._list); + if (query === "") { + query = null; + } + this._url._url.query = query; + } + } + + append(name, value) { + this._list.push([name, value]); + this._updateSteps(); + } + + delete(name) { + let i = 0; + while (i < this._list.length) { + if (this._list[i][0] === name) { + this._list.splice(i, 1); + } else { + i++; + } + } + this._updateSteps(); + } + + get(name) { + for (const tuple of this._list) { + if (tuple[0] === name) { + return tuple[1]; + } + } + return null; + } + + getAll(name) { + const output = []; + for (const tuple of this._list) { + if (tuple[0] === name) { + output.push(tuple[1]); + } + } + return output; + } + + has(name) { + for (const tuple of this._list) { + if (tuple[0] === name) { + return true; + } + } + return false; + } + + set(name, value) { + let found = false; + let i = 0; + while (i < this._list.length) { + if (this._list[i][0] === name) { + if (found) { + this._list.splice(i, 1); + } else { + found = true; + this._list[i][1] = value; + i++; + } + } else { + i++; + } + } + if (!found) { + this._list.push([name, value]); + } + this._updateSteps(); + } + + sort() { + this._list = stableSortBy(this._list, [0]); + this._updateSteps(); + } + + [Symbol.iterator]() { + return this._list[Symbol.iterator](); + } + + toString() { + return urlencoded.serializeUrlencoded(this._list); + } +}; + + +/***/ }), + +/***/ 58: +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/* WEBPACK VAR INJECTION */(function(global) {/*! + * The buffer module from node.js, for the browser. + * + * @author Feross Aboukhadijeh + * @license MIT + */ +/* eslint-disable no-proto */ + + + +var base64 = __webpack_require__(98) +var ieee754 = __webpack_require__(99) +var isArray = __webpack_require__(100) + +exports.Buffer = Buffer +exports.SlowBuffer = SlowBuffer +exports.INSPECT_MAX_BYTES = 50 + +/** + * If `Buffer.TYPED_ARRAY_SUPPORT`: + * === true Use Uint8Array implementation (fastest) + * === false Use Object implementation (most compatible, even IE6) + * + * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+, + * Opera 11.6+, iOS 4.2+. + * + * Due to various browser bugs, sometimes the Object implementation will be used even + * when the browser supports typed arrays. + * + * Note: + * + * - Firefox 4-29 lacks support for adding new properties to `Uint8Array` instances, + * See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438. + * + * - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function. + * + * - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of + * incorrect length in some situations. + + * We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they + * get the Object implementation, which is slower but behaves correctly. + */ +Buffer.TYPED_ARRAY_SUPPORT = global.TYPED_ARRAY_SUPPORT !== undefined + ? global.TYPED_ARRAY_SUPPORT + : typedArraySupport() + +/* + * Export kMaxLength after typed array support is determined. + */ +exports.kMaxLength = kMaxLength() + +function typedArraySupport () { + try { + var arr = new Uint8Array(1) + arr.__proto__ = {__proto__: Uint8Array.prototype, foo: function () { return 42 }} + return arr.foo() === 42 && // typed array instances can be augmented + typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray` + arr.subarray(1, 1).byteLength === 0 // ie10 has broken `subarray` + } catch (e) { + return false + } +} + +function kMaxLength () { + return Buffer.TYPED_ARRAY_SUPPORT + ? 0x7fffffff + : 0x3fffffff +} + +function createBuffer (that, length) { + if (kMaxLength() < length) { + throw new RangeError('Invalid typed array length') + } + if (Buffer.TYPED_ARRAY_SUPPORT) { + // Return an augmented `Uint8Array` instance, for best performance + that = new Uint8Array(length) + that.__proto__ = Buffer.prototype + } else { + // Fallback: Return an object instance of the Buffer class + if (that === null) { + that = new Buffer(length) + } + that.length = length + } + + return that +} + +/** + * The Buffer constructor returns instances of `Uint8Array` that have their + * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of + * `Uint8Array`, so the returned instances will have all the node `Buffer` methods + * and the `Uint8Array` methods. Square bracket notation works as expected -- it + * returns a single octet. + * + * The `Uint8Array` prototype remains unmodified. + */ + +function Buffer (arg, encodingOrOffset, length) { + if (!Buffer.TYPED_ARRAY_SUPPORT && !(this instanceof Buffer)) { + return new Buffer(arg, encodingOrOffset, length) + } + + // Common case. + if (typeof arg === 'number') { + if (typeof encodingOrOffset === 'string') { + throw new Error( + 'If encoding is specified then the first argument must be a string' + ) + } + return allocUnsafe(this, arg) + } + return from(this, arg, encodingOrOffset, length) +} + +Buffer.poolSize = 8192 // not used by this implementation + +// TODO: Legacy, not needed anymore. Remove in next major version. +Buffer._augment = function (arr) { + arr.__proto__ = Buffer.prototype + return arr +} + +function from (that, value, encodingOrOffset, length) { + if (typeof value === 'number') { + throw new TypeError('"value" argument must not be a number') + } + + if (typeof ArrayBuffer !== 'undefined' && value instanceof ArrayBuffer) { + return fromArrayBuffer(that, value, encodingOrOffset, length) + } + + if (typeof value === 'string') { + return fromString(that, value, encodingOrOffset) + } + + return fromObject(that, value) +} + +/** + * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError + * if value is a number. + * Buffer.from(str[, encoding]) + * Buffer.from(array) + * Buffer.from(buffer) + * Buffer.from(arrayBuffer[, byteOffset[, length]]) + **/ +Buffer.from = function (value, encodingOrOffset, length) { + return from(null, value, encodingOrOffset, length) +} + +if (Buffer.TYPED_ARRAY_SUPPORT) { + Buffer.prototype.__proto__ = Uint8Array.prototype + Buffer.__proto__ = Uint8Array + if (typeof Symbol !== 'undefined' && Symbol.species && + Buffer[Symbol.species] === Buffer) { + // Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97 + Object.defineProperty(Buffer, Symbol.species, { + value: null, + configurable: true + }) + } +} + +function assertSize (size) { + if (typeof size !== 'number') { + throw new TypeError('"size" argument must be a number') + } else if (size < 0) { + throw new RangeError('"size" argument must not be negative') + } +} + +function alloc (that, size, fill, encoding) { + assertSize(size) + if (size <= 0) { + return createBuffer(that, size) + } + if (fill !== undefined) { + // Only pay attention to encoding if it's a string. This + // prevents accidentally sending in a number that would + // be interpretted as a start offset. + return typeof encoding === 'string' + ? createBuffer(that, size).fill(fill, encoding) + : createBuffer(that, size).fill(fill) + } + return createBuffer(that, size) +} + +/** + * Creates a new filled Buffer instance. + * alloc(size[, fill[, encoding]]) + **/ +Buffer.alloc = function (size, fill, encoding) { + return alloc(null, size, fill, encoding) +} + +function allocUnsafe (that, size) { + assertSize(size) + that = createBuffer(that, size < 0 ? 0 : checked(size) | 0) + if (!Buffer.TYPED_ARRAY_SUPPORT) { + for (var i = 0; i < size; ++i) { + that[i] = 0 + } + } + return that +} + +/** + * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance. + * */ +Buffer.allocUnsafe = function (size) { + return allocUnsafe(null, size) +} +/** + * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance. + */ +Buffer.allocUnsafeSlow = function (size) { + return allocUnsafe(null, size) +} + +function fromString (that, string, encoding) { + if (typeof encoding !== 'string' || encoding === '') { + encoding = 'utf8' + } + + if (!Buffer.isEncoding(encoding)) { + throw new TypeError('"encoding" must be a valid string encoding') + } + + var length = byteLength(string, encoding) | 0 + that = createBuffer(that, length) + + var actual = that.write(string, encoding) + + if (actual !== length) { + // Writing a hex string, for example, that contains invalid characters will + // cause everything after the first invalid character to be ignored. (e.g. + // 'abxxcd' will be treated as 'ab') + that = that.slice(0, actual) + } + + return that +} + +function fromArrayLike (that, array) { + var length = array.length < 0 ? 0 : checked(array.length) | 0 + that = createBuffer(that, length) + for (var i = 0; i < length; i += 1) { + that[i] = array[i] & 255 + } + return that +} + +function fromArrayBuffer (that, array, byteOffset, length) { + array.byteLength // this throws if `array` is not a valid ArrayBuffer + + if (byteOffset < 0 || array.byteLength < byteOffset) { + throw new RangeError('\'offset\' is out of bounds') + } + + if (array.byteLength < byteOffset + (length || 0)) { + throw new RangeError('\'length\' is out of bounds') + } + + if (byteOffset === undefined && length === undefined) { + array = new Uint8Array(array) + } else if (length === undefined) { + array = new Uint8Array(array, byteOffset) + } else { + array = new Uint8Array(array, byteOffset, length) + } + + if (Buffer.TYPED_ARRAY_SUPPORT) { + // Return an augmented `Uint8Array` instance, for best performance + that = array + that.__proto__ = Buffer.prototype + } else { + // Fallback: Return an object instance of the Buffer class + that = fromArrayLike(that, array) + } + return that +} + +function fromObject (that, obj) { + if (Buffer.isBuffer(obj)) { + var len = checked(obj.length) | 0 + that = createBuffer(that, len) + + if (that.length === 0) { + return that + } + + obj.copy(that, 0, 0, len) + return that + } + + if (obj) { + if ((typeof ArrayBuffer !== 'undefined' && + obj.buffer instanceof ArrayBuffer) || 'length' in obj) { + if (typeof obj.length !== 'number' || isnan(obj.length)) { + return createBuffer(that, 0) + } + return fromArrayLike(that, obj) + } + + if (obj.type === 'Buffer' && isArray(obj.data)) { + return fromArrayLike(that, obj.data) + } + } + + throw new TypeError('First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.') +} + +function checked (length) { + // Note: cannot use `length < kMaxLength()` here because that fails when + // length is NaN (which is otherwise coerced to zero.) + if (length >= kMaxLength()) { + throw new RangeError('Attempt to allocate Buffer larger than maximum ' + + 'size: 0x' + kMaxLength().toString(16) + ' bytes') + } + return length | 0 +} + +function SlowBuffer (length) { + if (+length != length) { // eslint-disable-line eqeqeq + length = 0 + } + return Buffer.alloc(+length) +} + +Buffer.isBuffer = function isBuffer (b) { + return !!(b != null && b._isBuffer) +} + +Buffer.compare = function compare (a, b) { + if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) { + throw new TypeError('Arguments must be Buffers') + } + + if (a === b) return 0 + + var x = a.length + var y = b.length + + for (var i = 0, len = Math.min(x, y); i < len; ++i) { + if (a[i] !== b[i]) { + x = a[i] + y = b[i] + break + } + } + + if (x < y) return -1 + if (y < x) return 1 + return 0 +} + +Buffer.isEncoding = function isEncoding (encoding) { + switch (String(encoding).toLowerCase()) { + case 'hex': + case 'utf8': + case 'utf-8': + case 'ascii': + case 'latin1': + case 'binary': + case 'base64': + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return true + default: + return false + } +} + +Buffer.concat = function concat (list, length) { + if (!isArray(list)) { + throw new TypeError('"list" argument must be an Array of Buffers') + } + + if (list.length === 0) { + return Buffer.alloc(0) + } + + var i + if (length === undefined) { + length = 0 + for (i = 0; i < list.length; ++i) { + length += list[i].length + } + } + + var buffer = Buffer.allocUnsafe(length) + var pos = 0 + for (i = 0; i < list.length; ++i) { + var buf = list[i] + if (!Buffer.isBuffer(buf)) { + throw new TypeError('"list" argument must be an Array of Buffers') + } + buf.copy(buffer, pos) + pos += buf.length + } + return buffer +} + +function byteLength (string, encoding) { + if (Buffer.isBuffer(string)) { + return string.length + } + if (typeof ArrayBuffer !== 'undefined' && typeof ArrayBuffer.isView === 'function' && + (ArrayBuffer.isView(string) || string instanceof ArrayBuffer)) { + return string.byteLength + } + if (typeof string !== 'string') { + string = '' + string + } + + var len = string.length + if (len === 0) return 0 + + // Use a for loop to avoid recursion + var loweredCase = false + for (;;) { + switch (encoding) { + case 'ascii': + case 'latin1': + case 'binary': + return len + case 'utf8': + case 'utf-8': + case undefined: + return utf8ToBytes(string).length + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return len * 2 + case 'hex': + return len >>> 1 + case 'base64': + return base64ToBytes(string).length + default: + if (loweredCase) return utf8ToBytes(string).length // assume utf8 + encoding = ('' + encoding).toLowerCase() + loweredCase = true + } + } +} +Buffer.byteLength = byteLength + +function slowToString (encoding, start, end) { + var loweredCase = false + + // No need to verify that "this.length <= MAX_UINT32" since it's a read-only + // property of a typed array. + + // This behaves neither like String nor Uint8Array in that we set start/end + // to their upper/lower bounds if the value passed is out of range. + // undefined is handled specially as per ECMA-262 6th Edition, + // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization. + if (start === undefined || start < 0) { + start = 0 + } + // Return early if start > this.length. Done here to prevent potential uint32 + // coercion fail below. + if (start > this.length) { + return '' + } + + if (end === undefined || end > this.length) { + end = this.length + } + + if (end <= 0) { + return '' + } + + // Force coersion to uint32. This will also coerce falsey/NaN values to 0. + end >>>= 0 + start >>>= 0 + + if (end <= start) { + return '' + } + + if (!encoding) encoding = 'utf8' + + while (true) { + switch (encoding) { + case 'hex': + return hexSlice(this, start, end) + + case 'utf8': + case 'utf-8': + return utf8Slice(this, start, end) + + case 'ascii': + return asciiSlice(this, start, end) + + case 'latin1': + case 'binary': + return latin1Slice(this, start, end) + + case 'base64': + return base64Slice(this, start, end) + + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return utf16leSlice(this, start, end) + + default: + if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) + encoding = (encoding + '').toLowerCase() + loweredCase = true + } + } +} + +// The property is used by `Buffer.isBuffer` and `is-buffer` (in Safari 5-7) to detect +// Buffer instances. +Buffer.prototype._isBuffer = true + +function swap (b, n, m) { + var i = b[n] + b[n] = b[m] + b[m] = i +} + +Buffer.prototype.swap16 = function swap16 () { + var len = this.length + if (len % 2 !== 0) { + throw new RangeError('Buffer size must be a multiple of 16-bits') + } + for (var i = 0; i < len; i += 2) { + swap(this, i, i + 1) + } + return this +} + +Buffer.prototype.swap32 = function swap32 () { + var len = this.length + if (len % 4 !== 0) { + throw new RangeError('Buffer size must be a multiple of 32-bits') + } + for (var i = 0; i < len; i += 4) { + swap(this, i, i + 3) + swap(this, i + 1, i + 2) + } + return this +} + +Buffer.prototype.swap64 = function swap64 () { + var len = this.length + if (len % 8 !== 0) { + throw new RangeError('Buffer size must be a multiple of 64-bits') + } + for (var i = 0; i < len; i += 8) { + swap(this, i, i + 7) + swap(this, i + 1, i + 6) + swap(this, i + 2, i + 5) + swap(this, i + 3, i + 4) + } + return this +} + +Buffer.prototype.toString = function toString () { + var length = this.length | 0 + if (length === 0) return '' + if (arguments.length === 0) return utf8Slice(this, 0, length) + return slowToString.apply(this, arguments) +} + +Buffer.prototype.equals = function equals (b) { + if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer') + if (this === b) return true + return Buffer.compare(this, b) === 0 +} + +Buffer.prototype.inspect = function inspect () { + var str = '' + var max = exports.INSPECT_MAX_BYTES + if (this.length > 0) { + str = this.toString('hex', 0, max).match(/.{2}/g).join(' ') + if (this.length > max) str += ' ... ' + } + return '' +} + +Buffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) { + if (!Buffer.isBuffer(target)) { + throw new TypeError('Argument must be a Buffer') + } + + if (start === undefined) { + start = 0 + } + if (end === undefined) { + end = target ? target.length : 0 + } + if (thisStart === undefined) { + thisStart = 0 + } + if (thisEnd === undefined) { + thisEnd = this.length + } + + if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) { + throw new RangeError('out of range index') + } + + if (thisStart >= thisEnd && start >= end) { + return 0 + } + if (thisStart >= thisEnd) { + return -1 + } + if (start >= end) { + return 1 + } + + start >>>= 0 + end >>>= 0 + thisStart >>>= 0 + thisEnd >>>= 0 + + if (this === target) return 0 + + var x = thisEnd - thisStart + var y = end - start + var len = Math.min(x, y) + + var thisCopy = this.slice(thisStart, thisEnd) + var targetCopy = target.slice(start, end) + + for (var i = 0; i < len; ++i) { + if (thisCopy[i] !== targetCopy[i]) { + x = thisCopy[i] + y = targetCopy[i] + break + } + } + + if (x < y) return -1 + if (y < x) return 1 + return 0 +} + +// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`, +// OR the last index of `val` in `buffer` at offset <= `byteOffset`. +// +// Arguments: +// - buffer - a Buffer to search +// - val - a string, Buffer, or number +// - byteOffset - an index into `buffer`; will be clamped to an int32 +// - encoding - an optional encoding, relevant is val is a string +// - dir - true for indexOf, false for lastIndexOf +function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) { + // Empty buffer means no match + if (buffer.length === 0) return -1 + + // Normalize byteOffset + if (typeof byteOffset === 'string') { + encoding = byteOffset + byteOffset = 0 + } else if (byteOffset > 0x7fffffff) { + byteOffset = 0x7fffffff + } else if (byteOffset < -0x80000000) { + byteOffset = -0x80000000 + } + byteOffset = +byteOffset // Coerce to Number. + if (isNaN(byteOffset)) { + // byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer + byteOffset = dir ? 0 : (buffer.length - 1) + } + + // Normalize byteOffset: negative offsets start from the end of the buffer + if (byteOffset < 0) byteOffset = buffer.length + byteOffset + if (byteOffset >= buffer.length) { + if (dir) return -1 + else byteOffset = buffer.length - 1 + } else if (byteOffset < 0) { + if (dir) byteOffset = 0 + else return -1 + } + + // Normalize val + if (typeof val === 'string') { + val = Buffer.from(val, encoding) + } + + // Finally, search either indexOf (if dir is true) or lastIndexOf + if (Buffer.isBuffer(val)) { + // Special case: looking for empty string/buffer always fails + if (val.length === 0) { + return -1 + } + return arrayIndexOf(buffer, val, byteOffset, encoding, dir) + } else if (typeof val === 'number') { + val = val & 0xFF // Search for a byte value [0-255] + if (Buffer.TYPED_ARRAY_SUPPORT && + typeof Uint8Array.prototype.indexOf === 'function') { + if (dir) { + return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset) + } else { + return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset) + } + } + return arrayIndexOf(buffer, [ val ], byteOffset, encoding, dir) + } + + throw new TypeError('val must be string, number or Buffer') +} + +function arrayIndexOf (arr, val, byteOffset, encoding, dir) { + var indexSize = 1 + var arrLength = arr.length + var valLength = val.length + + if (encoding !== undefined) { + encoding = String(encoding).toLowerCase() + if (encoding === 'ucs2' || encoding === 'ucs-2' || + encoding === 'utf16le' || encoding === 'utf-16le') { + if (arr.length < 2 || val.length < 2) { + return -1 + } + indexSize = 2 + arrLength /= 2 + valLength /= 2 + byteOffset /= 2 + } + } + + function read (buf, i) { + if (indexSize === 1) { + return buf[i] + } else { + return buf.readUInt16BE(i * indexSize) + } + } + + var i + if (dir) { + var foundIndex = -1 + for (i = byteOffset; i < arrLength; i++) { + if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) { + if (foundIndex === -1) foundIndex = i + if (i - foundIndex + 1 === valLength) return foundIndex * indexSize + } else { + if (foundIndex !== -1) i -= i - foundIndex + foundIndex = -1 + } + } + } else { + if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength + for (i = byteOffset; i >= 0; i--) { + var found = true + for (var j = 0; j < valLength; j++) { + if (read(arr, i + j) !== read(val, j)) { + found = false + break + } + } + if (found) return i + } + } + + return -1 +} + +Buffer.prototype.includes = function includes (val, byteOffset, encoding) { + return this.indexOf(val, byteOffset, encoding) !== -1 +} + +Buffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) { + return bidirectionalIndexOf(this, val, byteOffset, encoding, true) +} + +Buffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) { + return bidirectionalIndexOf(this, val, byteOffset, encoding, false) +} + +function hexWrite (buf, string, offset, length) { + offset = Number(offset) || 0 + var remaining = buf.length - offset + if (!length) { + length = remaining + } else { + length = Number(length) + if (length > remaining) { + length = remaining + } + } + + // must be an even number of digits + var strLen = string.length + if (strLen % 2 !== 0) throw new TypeError('Invalid hex string') + + if (length > strLen / 2) { + length = strLen / 2 + } + for (var i = 0; i < length; ++i) { + var parsed = parseInt(string.substr(i * 2, 2), 16) + if (isNaN(parsed)) return i + buf[offset + i] = parsed + } + return i +} + +function utf8Write (buf, string, offset, length) { + return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length) +} + +function asciiWrite (buf, string, offset, length) { + return blitBuffer(asciiToBytes(string), buf, offset, length) +} + +function latin1Write (buf, string, offset, length) { + return asciiWrite(buf, string, offset, length) +} + +function base64Write (buf, string, offset, length) { + return blitBuffer(base64ToBytes(string), buf, offset, length) +} + +function ucs2Write (buf, string, offset, length) { + return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length) +} + +Buffer.prototype.write = function write (string, offset, length, encoding) { + // Buffer#write(string) + if (offset === undefined) { + encoding = 'utf8' + length = this.length + offset = 0 + // Buffer#write(string, encoding) + } else if (length === undefined && typeof offset === 'string') { + encoding = offset + length = this.length + offset = 0 + // Buffer#write(string, offset[, length][, encoding]) + } else if (isFinite(offset)) { + offset = offset | 0 + if (isFinite(length)) { + length = length | 0 + if (encoding === undefined) encoding = 'utf8' + } else { + encoding = length + length = undefined + } + // legacy write(string, encoding, offset, length) - remove in v0.13 + } else { + throw new Error( + 'Buffer.write(string, encoding, offset[, length]) is no longer supported' + ) + } + + var remaining = this.length - offset + if (length === undefined || length > remaining) length = remaining + + if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) { + throw new RangeError('Attempt to write outside buffer bounds') + } + + if (!encoding) encoding = 'utf8' + + var loweredCase = false + for (;;) { + switch (encoding) { + case 'hex': + return hexWrite(this, string, offset, length) + + case 'utf8': + case 'utf-8': + return utf8Write(this, string, offset, length) + + case 'ascii': + return asciiWrite(this, string, offset, length) + + case 'latin1': + case 'binary': + return latin1Write(this, string, offset, length) + + case 'base64': + // Warning: maxLength not taken into account in base64Write + return base64Write(this, string, offset, length) + + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return ucs2Write(this, string, offset, length) + + default: + if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) + encoding = ('' + encoding).toLowerCase() + loweredCase = true + } + } +} + +Buffer.prototype.toJSON = function toJSON () { + return { + type: 'Buffer', + data: Array.prototype.slice.call(this._arr || this, 0) + } +} + +function base64Slice (buf, start, end) { + if (start === 0 && end === buf.length) { + return base64.fromByteArray(buf) + } else { + return base64.fromByteArray(buf.slice(start, end)) + } +} + +function utf8Slice (buf, start, end) { + end = Math.min(buf.length, end) + var res = [] + + var i = start + while (i < end) { + var firstByte = buf[i] + var codePoint = null + var bytesPerSequence = (firstByte > 0xEF) ? 4 + : (firstByte > 0xDF) ? 3 + : (firstByte > 0xBF) ? 2 + : 1 + + if (i + bytesPerSequence <= end) { + var secondByte, thirdByte, fourthByte, tempCodePoint + + switch (bytesPerSequence) { + case 1: + if (firstByte < 0x80) { + codePoint = firstByte + } + break + case 2: + secondByte = buf[i + 1] + if ((secondByte & 0xC0) === 0x80) { + tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F) + if (tempCodePoint > 0x7F) { + codePoint = tempCodePoint + } + } + break + case 3: + secondByte = buf[i + 1] + thirdByte = buf[i + 2] + if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) { + tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F) + if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) { + codePoint = tempCodePoint + } + } + break + case 4: + secondByte = buf[i + 1] + thirdByte = buf[i + 2] + fourthByte = buf[i + 3] + if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) { + tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F) + if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) { + codePoint = tempCodePoint + } + } + } + } + + if (codePoint === null) { + // we did not generate a valid codePoint so insert a + // replacement char (U+FFFD) and advance only 1 byte + codePoint = 0xFFFD + bytesPerSequence = 1 + } else if (codePoint > 0xFFFF) { + // encode to utf16 (surrogate pair dance) + codePoint -= 0x10000 + res.push(codePoint >>> 10 & 0x3FF | 0xD800) + codePoint = 0xDC00 | codePoint & 0x3FF + } + + res.push(codePoint) + i += bytesPerSequence + } + + return decodeCodePointsArray(res) +} + +// Based on http://stackoverflow.com/a/22747272/680742, the browser with +// the lowest limit is Chrome, with 0x10000 args. +// We go 1 magnitude less, for safety +var MAX_ARGUMENTS_LENGTH = 0x1000 + +function decodeCodePointsArray (codePoints) { + var len = codePoints.length + if (len <= MAX_ARGUMENTS_LENGTH) { + return String.fromCharCode.apply(String, codePoints) // avoid extra slice() + } + + // Decode in chunks to avoid "call stack size exceeded". + var res = '' + var i = 0 + while (i < len) { + res += String.fromCharCode.apply( + String, + codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH) + ) + } + return res +} + +function asciiSlice (buf, start, end) { + var ret = '' + end = Math.min(buf.length, end) + + for (var i = start; i < end; ++i) { + ret += String.fromCharCode(buf[i] & 0x7F) + } + return ret +} + +function latin1Slice (buf, start, end) { + var ret = '' + end = Math.min(buf.length, end) + + for (var i = start; i < end; ++i) { + ret += String.fromCharCode(buf[i]) + } + return ret +} + +function hexSlice (buf, start, end) { + var len = buf.length + + if (!start || start < 0) start = 0 + if (!end || end < 0 || end > len) end = len + + var out = '' + for (var i = start; i < end; ++i) { + out += toHex(buf[i]) + } + return out +} + +function utf16leSlice (buf, start, end) { + var bytes = buf.slice(start, end) + var res = '' + for (var i = 0; i < bytes.length; i += 2) { + res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256) + } + return res +} + +Buffer.prototype.slice = function slice (start, end) { + var len = this.length + start = ~~start + end = end === undefined ? len : ~~end + + if (start < 0) { + start += len + if (start < 0) start = 0 + } else if (start > len) { + start = len + } + + if (end < 0) { + end += len + if (end < 0) end = 0 + } else if (end > len) { + end = len + } + + if (end < start) end = start + + var newBuf + if (Buffer.TYPED_ARRAY_SUPPORT) { + newBuf = this.subarray(start, end) + newBuf.__proto__ = Buffer.prototype + } else { + var sliceLen = end - start + newBuf = new Buffer(sliceLen, undefined) + for (var i = 0; i < sliceLen; ++i) { + newBuf[i] = this[i + start] + } + } + + return newBuf +} + +/* + * Need to make sure that buffer isn't trying to write out of bounds. + */ +function checkOffset (offset, ext, length) { + if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint') + if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length') +} + +Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) { + offset = offset | 0 + byteLength = byteLength | 0 + if (!noAssert) checkOffset(offset, byteLength, this.length) + + var val = this[offset] + var mul = 1 + var i = 0 + while (++i < byteLength && (mul *= 0x100)) { + val += this[offset + i] * mul + } + + return val +} + +Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) { + offset = offset | 0 + byteLength = byteLength | 0 + if (!noAssert) { + checkOffset(offset, byteLength, this.length) + } + + var val = this[offset + --byteLength] + var mul = 1 + while (byteLength > 0 && (mul *= 0x100)) { + val += this[offset + --byteLength] * mul + } + + return val +} + +Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) { + if (!noAssert) checkOffset(offset, 1, this.length) + return this[offset] +} + +Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 2, this.length) + return this[offset] | (this[offset + 1] << 8) +} + +Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 2, this.length) + return (this[offset] << 8) | this[offset + 1] +} + +Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 4, this.length) + + return ((this[offset]) | + (this[offset + 1] << 8) | + (this[offset + 2] << 16)) + + (this[offset + 3] * 0x1000000) +} + +Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 4, this.length) + + return (this[offset] * 0x1000000) + + ((this[offset + 1] << 16) | + (this[offset + 2] << 8) | + this[offset + 3]) +} + +Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) { + offset = offset | 0 + byteLength = byteLength | 0 + if (!noAssert) checkOffset(offset, byteLength, this.length) + + var val = this[offset] + var mul = 1 + var i = 0 + while (++i < byteLength && (mul *= 0x100)) { + val += this[offset + i] * mul + } + mul *= 0x80 + + if (val >= mul) val -= Math.pow(2, 8 * byteLength) + + return val +} + +Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) { + offset = offset | 0 + byteLength = byteLength | 0 + if (!noAssert) checkOffset(offset, byteLength, this.length) + + var i = byteLength + var mul = 1 + var val = this[offset + --i] + while (i > 0 && (mul *= 0x100)) { + val += this[offset + --i] * mul + } + mul *= 0x80 + + if (val >= mul) val -= Math.pow(2, 8 * byteLength) + + return val +} + +Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) { + if (!noAssert) checkOffset(offset, 1, this.length) + if (!(this[offset] & 0x80)) return (this[offset]) + return ((0xff - this[offset] + 1) * -1) +} + +Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 2, this.length) + var val = this[offset] | (this[offset + 1] << 8) + return (val & 0x8000) ? val | 0xFFFF0000 : val +} + +Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 2, this.length) + var val = this[offset + 1] | (this[offset] << 8) + return (val & 0x8000) ? val | 0xFFFF0000 : val +} + +Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 4, this.length) + + return (this[offset]) | + (this[offset + 1] << 8) | + (this[offset + 2] << 16) | + (this[offset + 3] << 24) +} + +Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 4, this.length) + + return (this[offset] << 24) | + (this[offset + 1] << 16) | + (this[offset + 2] << 8) | + (this[offset + 3]) +} + +Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 4, this.length) + return ieee754.read(this, offset, true, 23, 4) +} + +Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 4, this.length) + return ieee754.read(this, offset, false, 23, 4) +} + +Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 8, this.length) + return ieee754.read(this, offset, true, 52, 8) +} + +Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) { + if (!noAssert) checkOffset(offset, 8, this.length) + return ieee754.read(this, offset, false, 52, 8) +} + +function checkInt (buf, value, offset, ext, max, min) { + if (!Buffer.isBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance') + if (value > max || value < min) throw new RangeError('"value" argument is out of bounds') + if (offset + ext > buf.length) throw new RangeError('Index out of range') +} + +Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) { + value = +value + offset = offset | 0 + byteLength = byteLength | 0 + if (!noAssert) { + var maxBytes = Math.pow(2, 8 * byteLength) - 1 + checkInt(this, value, offset, byteLength, maxBytes, 0) + } + + var mul = 1 + var i = 0 + this[offset] = value & 0xFF + while (++i < byteLength && (mul *= 0x100)) { + this[offset + i] = (value / mul) & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) { + value = +value + offset = offset | 0 + byteLength = byteLength | 0 + if (!noAssert) { + var maxBytes = Math.pow(2, 8 * byteLength) - 1 + checkInt(this, value, offset, byteLength, maxBytes, 0) + } + + var i = byteLength - 1 + var mul = 1 + this[offset + i] = value & 0xFF + while (--i >= 0 && (mul *= 0x100)) { + this[offset + i] = (value / mul) & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0) + if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value) + this[offset] = (value & 0xff) + return offset + 1 +} + +function objectWriteUInt16 (buf, value, offset, littleEndian) { + if (value < 0) value = 0xffff + value + 1 + for (var i = 0, j = Math.min(buf.length - offset, 2); i < j; ++i) { + buf[offset + i] = (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>> + (littleEndian ? i : 1 - i) * 8 + } +} + +Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) + if (Buffer.TYPED_ARRAY_SUPPORT) { + this[offset] = (value & 0xff) + this[offset + 1] = (value >>> 8) + } else { + objectWriteUInt16(this, value, offset, true) + } + return offset + 2 +} + +Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) + if (Buffer.TYPED_ARRAY_SUPPORT) { + this[offset] = (value >>> 8) + this[offset + 1] = (value & 0xff) + } else { + objectWriteUInt16(this, value, offset, false) + } + return offset + 2 +} + +function objectWriteUInt32 (buf, value, offset, littleEndian) { + if (value < 0) value = 0xffffffff + value + 1 + for (var i = 0, j = Math.min(buf.length - offset, 4); i < j; ++i) { + buf[offset + i] = (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff + } +} + +Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) + if (Buffer.TYPED_ARRAY_SUPPORT) { + this[offset + 3] = (value >>> 24) + this[offset + 2] = (value >>> 16) + this[offset + 1] = (value >>> 8) + this[offset] = (value & 0xff) + } else { + objectWriteUInt32(this, value, offset, true) + } + return offset + 4 +} + +Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) + if (Buffer.TYPED_ARRAY_SUPPORT) { + this[offset] = (value >>> 24) + this[offset + 1] = (value >>> 16) + this[offset + 2] = (value >>> 8) + this[offset + 3] = (value & 0xff) + } else { + objectWriteUInt32(this, value, offset, false) + } + return offset + 4 +} + +Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) { + var limit = Math.pow(2, 8 * byteLength - 1) + + checkInt(this, value, offset, byteLength, limit - 1, -limit) + } + + var i = 0 + var mul = 1 + var sub = 0 + this[offset] = value & 0xFF + while (++i < byteLength && (mul *= 0x100)) { + if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) { + sub = 1 + } + this[offset + i] = ((value / mul) >> 0) - sub & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) { + var limit = Math.pow(2, 8 * byteLength - 1) + + checkInt(this, value, offset, byteLength, limit - 1, -limit) + } + + var i = byteLength - 1 + var mul = 1 + var sub = 0 + this[offset + i] = value & 0xFF + while (--i >= 0 && (mul *= 0x100)) { + if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) { + sub = 1 + } + this[offset + i] = ((value / mul) >> 0) - sub & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80) + if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value) + if (value < 0) value = 0xff + value + 1 + this[offset] = (value & 0xff) + return offset + 1 +} + +Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) + if (Buffer.TYPED_ARRAY_SUPPORT) { + this[offset] = (value & 0xff) + this[offset + 1] = (value >>> 8) + } else { + objectWriteUInt16(this, value, offset, true) + } + return offset + 2 +} + +Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) + if (Buffer.TYPED_ARRAY_SUPPORT) { + this[offset] = (value >>> 8) + this[offset + 1] = (value & 0xff) + } else { + objectWriteUInt16(this, value, offset, false) + } + return offset + 2 +} + +Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) + if (Buffer.TYPED_ARRAY_SUPPORT) { + this[offset] = (value & 0xff) + this[offset + 1] = (value >>> 8) + this[offset + 2] = (value >>> 16) + this[offset + 3] = (value >>> 24) + } else { + objectWriteUInt32(this, value, offset, true) + } + return offset + 4 +} + +Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) { + value = +value + offset = offset | 0 + if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) + if (value < 0) value = 0xffffffff + value + 1 + if (Buffer.TYPED_ARRAY_SUPPORT) { + this[offset] = (value >>> 24) + this[offset + 1] = (value >>> 16) + this[offset + 2] = (value >>> 8) + this[offset + 3] = (value & 0xff) + } else { + objectWriteUInt32(this, value, offset, false) + } + return offset + 4 +} + +function checkIEEE754 (buf, value, offset, ext, max, min) { + if (offset + ext > buf.length) throw new RangeError('Index out of range') + if (offset < 0) throw new RangeError('Index out of range') +} + +function writeFloat (buf, value, offset, littleEndian, noAssert) { + if (!noAssert) { + checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38) + } + ieee754.write(buf, value, offset, littleEndian, 23, 4) + return offset + 4 +} + +Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) { + return writeFloat(this, value, offset, true, noAssert) +} + +Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) { + return writeFloat(this, value, offset, false, noAssert) +} + +function writeDouble (buf, value, offset, littleEndian, noAssert) { + if (!noAssert) { + checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308) + } + ieee754.write(buf, value, offset, littleEndian, 52, 8) + return offset + 8 +} + +Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) { + return writeDouble(this, value, offset, true, noAssert) +} + +Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) { + return writeDouble(this, value, offset, false, noAssert) +} + +// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) +Buffer.prototype.copy = function copy (target, targetStart, start, end) { + if (!start) start = 0 + if (!end && end !== 0) end = this.length + if (targetStart >= target.length) targetStart = target.length + if (!targetStart) targetStart = 0 + if (end > 0 && end < start) end = start + + // Copy 0 bytes; we're done + if (end === start) return 0 + if (target.length === 0 || this.length === 0) return 0 + + // Fatal error conditions + if (targetStart < 0) { + throw new RangeError('targetStart out of bounds') + } + if (start < 0 || start >= this.length) throw new RangeError('sourceStart out of bounds') + if (end < 0) throw new RangeError('sourceEnd out of bounds') + + // Are we oob? + if (end > this.length) end = this.length + if (target.length - targetStart < end - start) { + end = target.length - targetStart + start + } + + var len = end - start + var i + + if (this === target && start < targetStart && targetStart < end) { + // descending copy from end + for (i = len - 1; i >= 0; --i) { + target[i + targetStart] = this[i + start] + } + } else if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) { + // ascending copy from start + for (i = 0; i < len; ++i) { + target[i + targetStart] = this[i + start] + } + } else { + Uint8Array.prototype.set.call( + target, + this.subarray(start, start + len), + targetStart + ) + } + + return len +} + +// Usage: +// buffer.fill(number[, offset[, end]]) +// buffer.fill(buffer[, offset[, end]]) +// buffer.fill(string[, offset[, end]][, encoding]) +Buffer.prototype.fill = function fill (val, start, end, encoding) { + // Handle string cases: + if (typeof val === 'string') { + if (typeof start === 'string') { + encoding = start + start = 0 + end = this.length + } else if (typeof end === 'string') { + encoding = end + end = this.length + } + if (val.length === 1) { + var code = val.charCodeAt(0) + if (code < 256) { + val = code + } + } + if (encoding !== undefined && typeof encoding !== 'string') { + throw new TypeError('encoding must be a string') + } + if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) { + throw new TypeError('Unknown encoding: ' + encoding) + } + } else if (typeof val === 'number') { + val = val & 255 + } + + // Invalid ranges are not set to a default, so can range check early. + if (start < 0 || this.length < start || this.length < end) { + throw new RangeError('Out of range index') + } + + if (end <= start) { + return this + } + + start = start >>> 0 + end = end === undefined ? this.length : end >>> 0 + + if (!val) val = 0 + + var i + if (typeof val === 'number') { + for (i = start; i < end; ++i) { + this[i] = val + } + } else { + var bytes = Buffer.isBuffer(val) + ? val + : utf8ToBytes(new Buffer(val, encoding).toString()) + var len = bytes.length + for (i = 0; i < end - start; ++i) { + this[i + start] = bytes[i % len] + } + } + + return this +} + +// HELPER FUNCTIONS +// ================ + +var INVALID_BASE64_RE = /[^+\/0-9A-Za-z-_]/g + +function base64clean (str) { + // Node strips out invalid characters like \n and \t from the string, base64-js does not + str = stringtrim(str).replace(INVALID_BASE64_RE, '') + // Node converts strings with length < 2 to '' + if (str.length < 2) return '' + // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not + while (str.length % 4 !== 0) { + str = str + '=' + } + return str +} + +function stringtrim (str) { + if (str.trim) return str.trim() + return str.replace(/^\s+|\s+$/g, '') +} + +function toHex (n) { + if (n < 16) return '0' + n.toString(16) + return n.toString(16) +} + +function utf8ToBytes (string, units) { + units = units || Infinity + var codePoint + var length = string.length + var leadSurrogate = null + var bytes = [] + + for (var i = 0; i < length; ++i) { + codePoint = string.charCodeAt(i) + + // is surrogate component + if (codePoint > 0xD7FF && codePoint < 0xE000) { + // last char was a lead + if (!leadSurrogate) { + // no lead yet + if (codePoint > 0xDBFF) { + // unexpected trail + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + continue + } else if (i + 1 === length) { + // unpaired lead + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + continue + } + + // valid lead + leadSurrogate = codePoint + + continue + } + + // 2 leads in a row + if (codePoint < 0xDC00) { + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + leadSurrogate = codePoint + continue + } + + // valid surrogate pair + codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000 + } else if (leadSurrogate) { + // valid bmp char, but last char was a lead + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + } + + leadSurrogate = null + + // encode utf8 + if (codePoint < 0x80) { + if ((units -= 1) < 0) break + bytes.push(codePoint) + } else if (codePoint < 0x800) { + if ((units -= 2) < 0) break + bytes.push( + codePoint >> 0x6 | 0xC0, + codePoint & 0x3F | 0x80 + ) + } else if (codePoint < 0x10000) { + if ((units -= 3) < 0) break + bytes.push( + codePoint >> 0xC | 0xE0, + codePoint >> 0x6 & 0x3F | 0x80, + codePoint & 0x3F | 0x80 + ) + } else if (codePoint < 0x110000) { + if ((units -= 4) < 0) break + bytes.push( + codePoint >> 0x12 | 0xF0, + codePoint >> 0xC & 0x3F | 0x80, + codePoint >> 0x6 & 0x3F | 0x80, + codePoint & 0x3F | 0x80 + ) + } else { + throw new Error('Invalid code point') + } + } + + return bytes +} + +function asciiToBytes (str) { + var byteArray = [] + for (var i = 0; i < str.length; ++i) { + // Node's code seems to be doing this and not & 0x7F.. + byteArray.push(str.charCodeAt(i) & 0xFF) + } + return byteArray +} + +function utf16leToBytes (str, units) { + var c, hi, lo + var byteArray = [] + for (var i = 0; i < str.length; ++i) { + if ((units -= 2) < 0) break + + c = str.charCodeAt(i) + hi = c >> 8 + lo = c % 256 + byteArray.push(lo) + byteArray.push(hi) + } + + return byteArray +} + +function base64ToBytes (str) { + return base64.toByteArray(base64clean(str)) +} + +function blitBuffer (src, dst, offset, length) { + for (var i = 0; i < length; ++i) { + if ((i + offset >= dst.length) || (i >= src.length)) break + dst[i + offset] = src[i] + } + return i +} + +function isnan (val) { + return val !== val // eslint-disable-line no-self-compare +} + +/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(15))) /***/ }), @@ -7198,6 +15257,256 @@ module.exports = { workerUtils }; +/***/ }), + +/***/ 98: +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +exports.byteLength = byteLength +exports.toByteArray = toByteArray +exports.fromByteArray = fromByteArray + +var lookup = [] +var revLookup = [] +var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array + +var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' +for (var i = 0, len = code.length; i < len; ++i) { + lookup[i] = code[i] + revLookup[code.charCodeAt(i)] = i +} + +// Support decoding URL-safe base64 strings, as Node.js does. +// See: https://en.wikipedia.org/wiki/Base64#URL_applications +revLookup['-'.charCodeAt(0)] = 62 +revLookup['_'.charCodeAt(0)] = 63 + +function getLens (b64) { + var len = b64.length + + if (len % 4 > 0) { + throw new Error('Invalid string. Length must be a multiple of 4') + } + + // Trim off extra bytes after placeholder bytes are found + // See: https://github.com/beatgammit/base64-js/issues/42 + var validLen = b64.indexOf('=') + if (validLen === -1) validLen = len + + var placeHoldersLen = validLen === len + ? 0 + : 4 - (validLen % 4) + + return [validLen, placeHoldersLen] +} + +// base64 is 4/3 + up to two characters of the original data +function byteLength (b64) { + var lens = getLens(b64) + var validLen = lens[0] + var placeHoldersLen = lens[1] + return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen +} + +function _byteLength (b64, validLen, placeHoldersLen) { + return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen +} + +function toByteArray (b64) { + var tmp + var lens = getLens(b64) + var validLen = lens[0] + var placeHoldersLen = lens[1] + + var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen)) + + var curByte = 0 + + // if there are placeholders, only get up to the last complete 4 chars + var len = placeHoldersLen > 0 + ? validLen - 4 + : validLen + + for (var i = 0; i < len; i += 4) { + tmp = + (revLookup[b64.charCodeAt(i)] << 18) | + (revLookup[b64.charCodeAt(i + 1)] << 12) | + (revLookup[b64.charCodeAt(i + 2)] << 6) | + revLookup[b64.charCodeAt(i + 3)] + arr[curByte++] = (tmp >> 16) & 0xFF + arr[curByte++] = (tmp >> 8) & 0xFF + arr[curByte++] = tmp & 0xFF + } + + if (placeHoldersLen === 2) { + tmp = + (revLookup[b64.charCodeAt(i)] << 2) | + (revLookup[b64.charCodeAt(i + 1)] >> 4) + arr[curByte++] = tmp & 0xFF + } + + if (placeHoldersLen === 1) { + tmp = + (revLookup[b64.charCodeAt(i)] << 10) | + (revLookup[b64.charCodeAt(i + 1)] << 4) | + (revLookup[b64.charCodeAt(i + 2)] >> 2) + arr[curByte++] = (tmp >> 8) & 0xFF + arr[curByte++] = tmp & 0xFF + } + + return arr +} + +function tripletToBase64 (num) { + return lookup[num >> 18 & 0x3F] + + lookup[num >> 12 & 0x3F] + + lookup[num >> 6 & 0x3F] + + lookup[num & 0x3F] +} + +function encodeChunk (uint8, start, end) { + var tmp + var output = [] + for (var i = start; i < end; i += 3) { + tmp = + ((uint8[i] << 16) & 0xFF0000) + + ((uint8[i + 1] << 8) & 0xFF00) + + (uint8[i + 2] & 0xFF) + output.push(tripletToBase64(tmp)) + } + return output.join('') +} + +function fromByteArray (uint8) { + var tmp + var len = uint8.length + var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes + var parts = [] + var maxChunkLength = 16383 // must be multiple of 3 + + // go through the array every three bytes, we'll deal with trailing stuff later + for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) { + parts.push(encodeChunk( + uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength) + )) + } + + // pad the end with zeros, but make sure to not forget the extra bytes + if (extraBytes === 1) { + tmp = uint8[len - 1] + parts.push( + lookup[tmp >> 2] + + lookup[(tmp << 4) & 0x3F] + + '==' + ) + } else if (extraBytes === 2) { + tmp = (uint8[len - 2] << 8) + uint8[len - 1] + parts.push( + lookup[tmp >> 10] + + lookup[(tmp >> 4) & 0x3F] + + lookup[(tmp << 2) & 0x3F] + + '=' + ) + } + + return parts.join('') +} + + +/***/ }), + +/***/ 99: +/***/ (function(module, exports) { + +exports.read = function (buffer, offset, isLE, mLen, nBytes) { + var e, m + var eLen = (nBytes * 8) - mLen - 1 + var eMax = (1 << eLen) - 1 + var eBias = eMax >> 1 + var nBits = -7 + var i = isLE ? (nBytes - 1) : 0 + var d = isLE ? -1 : 1 + var s = buffer[offset + i] + + i += d + + e = s & ((1 << (-nBits)) - 1) + s >>= (-nBits) + nBits += eLen + for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {} + + m = e & ((1 << (-nBits)) - 1) + e >>= (-nBits) + nBits += mLen + for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {} + + if (e === 0) { + e = 1 - eBias + } else if (e === eMax) { + return m ? NaN : ((s ? -1 : 1) * Infinity) + } else { + m = m + Math.pow(2, mLen) + e = e - eBias + } + return (s ? -1 : 1) * m * Math.pow(2, e - mLen) +} + +exports.write = function (buffer, value, offset, isLE, mLen, nBytes) { + var e, m, c + var eLen = (nBytes * 8) - mLen - 1 + var eMax = (1 << eLen) - 1 + var eBias = eMax >> 1 + var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0) + var i = isLE ? 0 : (nBytes - 1) + var d = isLE ? 1 : -1 + var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0 + + value = Math.abs(value) + + if (isNaN(value) || value === Infinity) { + m = isNaN(value) ? 1 : 0 + e = eMax + } else { + e = Math.floor(Math.log(value) / Math.LN2) + if (value * (c = Math.pow(2, -e)) < 1) { + e-- + c *= 2 + } + if (e + eBias >= 1) { + value += rt / c + } else { + value += rt * Math.pow(2, 1 - eBias) + } + if (value * c >= 2) { + e++ + c /= 2 + } + + if (e + eBias >= eMax) { + m = 0 + e = eMax + } else if (e + eBias >= 1) { + m = ((value * c) - 1) * Math.pow(2, mLen) + e = e + eBias + } else { + m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen) + e = 0 + } + } + + for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {} + + e = (e << mLen) | m + eLen += mLen + for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {} + + buffer[offset + i - d] |= s * 128 +} + + /***/ }) /******/ }); diff --git a/devtools/client/debugger/flow-typed/npm/whatwg-url_vx.x.x.js b/devtools/client/debugger/flow-typed/npm/whatwg-url_vx.x.x.js new file mode 100644 index 000000000000..a0ffa3123caa --- /dev/null +++ b/devtools/client/debugger/flow-typed/npm/whatwg-url_vx.x.x.js @@ -0,0 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at . */ + +declare module "whatwg-url" { + declare module.exports: any; +} diff --git a/devtools/client/debugger/package.json b/devtools/client/debugger/package.json index ba70dbce7695..2198a94a2794 100644 --- a/devtools/client/debugger/package.json +++ b/devtools/client/debugger/package.json @@ -77,7 +77,8 @@ "react-transition-group": "^2.2.1", "reselect": "^4.0.0", "svg-inline-react": "^3.0.0", - "wasmparser": "^0.7.0" + "wasmparser": "^0.7.0", + "whatwg-url": "^7.0.0" }, "private": true, "workspaces": [ diff --git a/devtools/client/debugger/src/utils/sources-tree/addToTree.js b/devtools/client/debugger/src/utils/sources-tree/addToTree.js index 952d2fb802aa..24ed990a0489 100644 --- a/devtools/client/debugger/src/utils/sources-tree/addToTree.js +++ b/devtools/client/debugger/src/utils/sources-tree/addToTree.js @@ -93,8 +93,8 @@ function traverseTree( source: Source, thread: string ): TreeNode { - const parts = url.path.split("/").filter(p => p !== ""); - parts.unshift(url.group); + const parts = url.path.replace(/\/$/, "").split("/"); + parts[0] = url.group; if (thread) { parts.unshift(thread); } diff --git a/devtools/client/debugger/src/utils/sources-tree/tests/__snapshots__/addToTree.spec.js.snap b/devtools/client/debugger/src/utils/sources-tree/tests/__snapshots__/addToTree.spec.js.snap index 7059aced63f5..0f16f80105d6 100644 --- a/devtools/client/debugger/src/utils/sources-tree/tests/__snapshots__/addToTree.spec.js.snap +++ b/devtools/client/debugger/src/utils/sources-tree/tests/__snapshots__/addToTree.spec.js.snap @@ -88,24 +88,3 @@ exports[`sources-tree addToTree supports data URLs 1`] = ` - data:text/html, path=data:text/html, source_id=server1.conn13.child1/39 " `; - -exports[`sources-tree addToTree uses debuggeeUrl as default 1`] = ` -" - root path= - - localhost:4242 path=localhost:4242 - - components path=localhost:4242/components - - Header.js path=localhost:4242/components/Header.js source_id=undefined - - TodoItem.js path=localhost:4242/components/TodoItem.js source_id=undefined - - TodoTextInput.js path=localhost:4242/components/TodoTextInput.js source_id=undefined - - reducers path=localhost:4242/reducers - - index.js path=localhost:4242/reducers/index.js source_id=undefined - - index.js path=localhost:4242/index.js source_id=undefined - - resource://gre path=resource://gre - - modules path=resource://gre/modules - - ExtensionContent.jsm path=resource://gre/modules/ExtensionContent.jsm source_id=undefined - - voz37vlg5.codesandbox.io path=voz37vlg5.codesandbox.io - - static path=voz37vlg5.codesandbox.io/static - - js path=voz37vlg5.codesandbox.io/static/js - - components path=voz37vlg5.codesandbox.io/static/js/components - - TodoItem.js path=voz37vlg5.codesandbox.io/static/js/components/TodoItem.js source_id=undefined -" -`; diff --git a/devtools/client/debugger/src/utils/sources-tree/tests/addToTree.spec.js b/devtools/client/debugger/src/utils/sources-tree/tests/addToTree.spec.js index 688dbf082686..2fffcee7949f 100644 --- a/devtools/client/debugger/src/utils/sources-tree/tests/addToTree.spec.js +++ b/devtools/client/debugger/src/utils/sources-tree/tests/addToTree.spec.js @@ -78,6 +78,70 @@ describe("sources-tree", () => { expect(source1Node.name).toBe("source1.js"); }); + it("builds a path-based tree for webpack URLs", () => { + const source1 = makeMockSource("webpack:///foo/source1.js", "actor1"); + const tree = createDirectoryNode("root", "", []); + + addToTree(tree, source1, "http://example.com/", ""); + expect(tree.contents).toHaveLength(1); + + const base = tree.contents[0]; + expect(base.name).toBe("webpack://"); + expect(base.contents).toHaveLength(1); + + const fooNode = base.contents[0]; + expect(fooNode.name).toBe("foo"); + expect(fooNode.contents).toHaveLength(1); + + const source1Node = fooNode.contents[0]; + expect(source1Node.name).toBe("source1.js"); + }); + + it("builds a path-based tree for webpack URLs with absolute path", () => { + const source1 = makeMockSource( + "webpack:////Users/foo/source1.js", + "actor1" + ); + const tree = createDirectoryNode("root", "", []); + + addToTree(tree, source1, "http://example.com/", ""); + expect(tree.contents).toHaveLength(1); + + const base = tree.contents[0]; + expect(base.name).toBe("webpack://"); + expect(base.contents).toHaveLength(1); + + const emptyNode = base.contents[0]; + expect(emptyNode.name).toBe(""); + expect(emptyNode.contents).toHaveLength(1); + + const userNode = emptyNode.contents[0]; + expect(userNode.name).toBe("Users"); + expect(userNode.contents).toHaveLength(1); + + const fooNode = userNode.contents[0]; + expect(fooNode.name).toBe("foo"); + expect(fooNode.contents).toHaveLength(1); + + const source1Node = fooNode.contents[0]; + expect(source1Node.name).toBe("source1.js"); + }); + + it("handles url with no filename", function() { + const source1 = makeMockSource("http://example.com/", "actor1"); + const tree = createDirectoryNode("root", "", []); + + addToTree(tree, source1, "http://example.com/", ""); + expect(tree.contents).toHaveLength(1); + + const base = tree.contents[0]; + expect(base.name).toBe("example.com"); + expect(base.contents).toHaveLength(1); + + const indexNode = base.contents[0]; + expect(indexNode.name).toBe("(index)"); + }); + it("does not mangle encoded URLs", () => { const sourceName = // eslint-disable-next-line max-len "B9724220.131821496;dc_ver=42.111;sz=468x60;u_sd=2;dc_adk=2020465299;ord=a53rpc;dc_rfl=1,https%3A%2F%2Fdavidwalsh.name%2F$0;xdt=1"; @@ -293,38 +357,5 @@ describe("sources-tree", () => { ); expect(formatTree(tree)).toMatchSnapshot(); }); - - it.skip("uses debuggeeUrl as default", () => { - const testData = [ - { - url: "components/TodoTextInput.js", - }, - { - url: "components/Header.js", - }, - { - url: "reducers/index.js", - }, - { - url: "components/TodoItem.js", - }, - { - url: "resource://gre/modules/ExtensionContent.jsm", - }, - { - url: - "https://voz37vlg5.codesandbox.io/static/js/components/TodoItem.js", - }, - { - url: "index.js", - }, - ]; - - const domain = "http://localhost:4242"; - const sources = createSourcesList(testData); - const tree = createDirectoryNode("root", "", []); - sources.forEach(source => addToTree(tree, source, domain, "FakeThread")); - expect(formatTree(tree)).toMatchSnapshot(); - }); }); }); diff --git a/devtools/client/debugger/src/utils/sources-tree/tests/getUrl.spec.js b/devtools/client/debugger/src/utils/sources-tree/tests/getUrl.spec.js index 069abf4f524e..1b176908e980 100644 --- a/devtools/client/debugger/src/utils/sources-tree/tests/getUrl.spec.js +++ b/devtools/client/debugger/src/utils/sources-tree/tests/getUrl.spec.js @@ -88,10 +88,20 @@ describe("getUrl", () => { it("creates a group name for webpack", () => { const urlObject = getURL( createMockSource({ - url: "webpack://src/component.jsx", + url: "webpack:///src/component.jsx", id: "c3", }) ); expect(urlObject.group).toBe("webpack://"); }); + + it("creates a group name for angular source", () => { + const urlObject = getURL( + createMockSource({ + url: "ng://src/component.jsx", + id: "c3", + }) + ); + expect(urlObject.group).toBe("ng://"); + }); }); diff --git a/devtools/client/debugger/src/utils/url.js b/devtools/client/debugger/src/utils/url.js index fdb18716d3ac..dfb66218093c 100644 --- a/devtools/client/debugger/src/utils/url.js +++ b/devtools/client/debugger/src/utils/url.js @@ -3,8 +3,8 @@ * file, You can obtain one at . */ // @flow - import { memoize } from "lodash"; +import { URL } from "whatwg-url"; const defaultUrl = { hash: "", diff --git a/devtools/client/debugger/src/vendors.js b/devtools/client/debugger/src/vendors.js index 5e1091f1b972..883cc9cd7ee1 100644 --- a/devtools/client/debugger/src/vendors.js +++ b/devtools/client/debugger/src/vendors.js @@ -28,6 +28,7 @@ import * as fuzzaldrinPlus from "fuzzaldrin-plus"; import * as transition from "react-transition-group/Transition"; import * as reactAriaComponentsTabs from "react-aria-components/src/tabs"; import * as reselect from "reselect"; +import * as whatwgUrl from "whatwg-url"; // Modules imported without destructuring import classnames from "classnames"; @@ -51,5 +52,6 @@ export const vendored = { "lodash-move": move, "react-aria-components/src/tabs": reactAriaComponentsTabs, "react-transition-group/Transition": transition, + "whatwg-url": whatwgUrl, reselect, }; diff --git a/devtools/client/framework/test/browser_about-devtools-toolbox_reload.js b/devtools/client/framework/test/browser_about-devtools-toolbox_reload.js index 2923b7d41659..995af689e263 100644 --- a/devtools/client/framework/test/browser_about-devtools-toolbox_reload.js +++ b/devtools/client/framework/test/browser_about-devtools-toolbox_reload.js @@ -37,7 +37,9 @@ add_task(async function() { ok(refreshedDoc.querySelector(".debug-target-info"), "about:devtools-toolbox header is correctly displayed"); + const onToolboxDestroy = gDevTools.once("toolbox-destroyed"); await removeTab(tab); + await onToolboxDestroy; await removeTab(targetTab); }); diff --git a/devtools/client/shared/build/build-debugger.js b/devtools/client/shared/build/build-debugger.js index 7c46748abc51..cf4f2e7fd54a 100644 --- a/devtools/client/shared/build/build-debugger.js +++ b/devtools/client/shared/build/build-debugger.js @@ -16,7 +16,7 @@ const EXCLUDED_FILES = { "devtools-launchpad": "devtools/shared/flags", }; -const mappings = Object.assign( +const mappings = Object.assign( { "./source-editor": "devtools/client/shared/sourceeditor/editor", "../editor/source-editor": "devtools/client/shared/sourceeditor/editor", @@ -64,6 +64,7 @@ const VENDORS = [ "react-aria-components/src/tabs", "react-transition-group/Transition", "reselect", + "whatwg-url", "Svg", ]; @@ -73,13 +74,13 @@ const moduleMapping = { }; /* -* Updates devtools-modules imports such as -* `import { Telemetry } from "devtools-modules"` -* so that we can customize how we resolve certain modules in the package -* -* In the case of multiple declarations we need to move -* the telemetry module into its own import. -*/ + * Updates devtools-modules imports such as + * `import { Telemetry } from "devtools-modules"` + * so that we can customize how we resolve certain modules in the package + * + * In the case of multiple declarations we need to move + * the telemetry module into its own import. + */ function updateDevtoolsModulesImport(path, t) { const specifiers = path.node.specifiers; @@ -106,9 +107,9 @@ function updateDevtoolsModulesImport(path, t) { } /** -* This Babel plugin is used to transpile a single Debugger module into a module that -* can be loaded in Firefox via the regular DevTools loader. -*/ + * This Babel plugin is used to transpile a single Debugger module into a module that + * can be loaded in Firefox via the regular DevTools loader. + */ function transformMC({ types: t }) { return { visitor: { diff --git a/dom/animation/test/chrome/test_animation_observers_async.html b/dom/animation/test/chrome/test_animation_observers_async.html index b601dd838c18..f7f23c2ad797 100644 --- a/dom/animation/test/chrome/test_animation_observers_async.html +++ b/dom/animation/test/chrome/test_animation_observers_async.html @@ -40,6 +40,7 @@ var gObserver = new MutationObserver(newRecords => { }); function setupAsynchronousObserver(t, options) { + gRecords = []; t.add_cleanup(() => { gObserver.disconnect(); @@ -647,5 +648,17 @@ promise_test(async t => { ); }, 'Animations automatically removed are reported'); -runTest(); +setup({explicit_done: true}); +SpecialPowers.pushPrefEnv( + { + set: [ + ["dom.animations-api.autoremove.enabled", true], + ["dom.animations-api.implicit-keyframes.enabled", true], + ], + }, + function() { + runTest(); + done(); + } +); diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp index 778be6ebf62f..ebd0b7dba221 100644 --- a/dom/base/Document.cpp +++ b/dom/base/Document.cpp @@ -7885,10 +7885,6 @@ void Document::Destroy() { mIsGoingAway = true; - if (mDocumentL10n) { - mDocumentL10n->Destroy(); - } - ScriptLoader()->Destroy(); SetScriptGlobalObject(nullptr); RemovedFromDocShell(); diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index c552e8255e2b..560a4e702b82 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -224,10 +224,6 @@ DOMInterfaces = { 'headerFile': 'mozilla/dom/DeviceMotionEvent.h', }, -'DocumentL10n': { - 'implicitJSContext': ['translateFragment'], -}, - 'DominatorTree': { 'nativeType': 'mozilla::devtools::DominatorTree' }, @@ -1175,6 +1171,11 @@ DOMInterfaces = { 'headerFile': 'WebGLExtensions.h' }, +'OVR_multiview2': { + 'nativeType': 'mozilla::WebGLExtensionMultiview', + 'headerFile': 'WebGLExtensions.h' +}, + 'WEBGL_compressed_texture_astc': { 'nativeType': 'mozilla::WebGLExtensionCompressedTextureASTC', 'headerFile': 'WebGLExtensions.h' diff --git a/dom/canvas/WebGL2Context.h b/dom/canvas/WebGL2Context.h index eee246241cfa..507d6d832c0b 100644 --- a/dom/canvas/WebGL2Context.h +++ b/dom/canvas/WebGL2Context.h @@ -58,8 +58,6 @@ class WebGL2Context : public WebGLContext { void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); - void FramebufferTextureLayer(GLenum target, GLenum attachment, - WebGLTexture* texture, GLint level, GLint layer); virtual JS::Value GetFramebufferAttachmentParameter(JSContext* cx, GLenum target, diff --git a/dom/canvas/WebGL2ContextFramebuffers.cpp b/dom/canvas/WebGL2ContextFramebuffers.cpp index bc47fa208564..682eacca0ee5 100644 --- a/dom/canvas/WebGL2ContextFramebuffers.cpp +++ b/dom/canvas/WebGL2ContextFramebuffers.cpp @@ -66,34 +66,6 @@ void WebGL2Context::BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, dstY0, dstX1, dstY1, mask, filter); } -void WebGL2Context::FramebufferTextureLayer(GLenum target, GLenum attachment, - WebGLTexture* texture, GLint level, - GLint layer) { - const FuncScope funcScope(*this, "framebufferTextureLayer"); - if (IsContextLost()) return; - - if (!ValidateFramebufferTarget(target)) return; - - WebGLFramebuffer* fb; - switch (target) { - case LOCAL_GL_FRAMEBUFFER: - case LOCAL_GL_DRAW_FRAMEBUFFER: - fb = mBoundDrawFramebuffer; - break; - - case LOCAL_GL_READ_FRAMEBUFFER: - fb = mBoundReadFramebuffer; - break; - - default: - MOZ_CRASH("GFX: Bad target."); - } - - if (!fb) return ErrorInvalidOperation("Cannot modify framebuffer 0."); - - fb->FramebufferTextureLayer(attachment, texture, level, layer); -} - JS::Value WebGL2Context::GetFramebufferAttachmentParameter( JSContext* cx, GLenum target, GLenum attachment, GLenum pname, ErrorResult& out_error) { diff --git a/dom/canvas/WebGLContext.h b/dom/canvas/WebGLContext.h index bfb2d8ea7463..5b9f0de5e183 100644 --- a/dom/canvas/WebGLContext.h +++ b/dom/canvas/WebGLContext.h @@ -99,6 +99,7 @@ class MozFramebuffer; namespace webgl { class AvailabilityRunnable; struct CachedDrawFetchLimits; +struct FbAttachInfo; struct FormatInfo; class FormatUsageAuthority; struct FormatUsageInfo; @@ -598,11 +599,24 @@ class WebGLContext : public nsICanvasRenderingContextInternal, void DrawBuffers(const dom::Sequence& buffers); void Flush(); void Finish(); + + private: + void FramebufferAttach(GLenum target, GLenum attachEnum, + TexTarget reqTexTarget, + const webgl::FbAttachInfo& toAttach) const; + + public: void FramebufferRenderbuffer(GLenum target, GLenum attachment, - GLenum rbTarget, WebGLRenderbuffer* rb); + GLenum rbTarget, WebGLRenderbuffer* rb) const; void FramebufferTexture2D(GLenum target, GLenum attachment, GLenum texImageTarget, WebGLTexture* tex, - GLint level); + GLint level) const; + void FramebufferTextureLayer(GLenum target, GLenum attachment, + WebGLTexture* tex, GLint level, + GLint layer) const; + void FramebufferTextureMultiview(GLenum target, GLenum attachment, + WebGLTexture* texture, GLint level, + GLint baseViewIndex, GLsizei numViews) const; void FrontFace(GLenum mode); already_AddRefed GetActiveAttrib(const WebGLProgram& prog, @@ -1533,6 +1547,7 @@ class WebGLContext : public nsICanvasRenderingContextInternal, uint32_t mGLMaxCubeMapTextureSize = 0; uint32_t mGLMax3DTextureSize = 0; uint32_t mGLMaxArrayTextureLayers = 0; + uint32_t mGLMaxMultiviewViews = 1; uint32_t mGLMaxRenderbufferSize = 0; public: @@ -1738,7 +1753,7 @@ class WebGLContext : public nsICanvasRenderingContextInternal, ////// public: bool ValidateObjectAllowDeleted(const char* const argName, - const WebGLContextBoundObject& object) { + const WebGLContextBoundObject& object) const { if (!object.IsCompatibleWithContext(this)) { ErrorInvalidOperation( "%s: Object from different WebGL context (or older" @@ -1752,7 +1767,7 @@ class WebGLContext : public nsICanvasRenderingContextInternal, bool ValidateObject(const char* const argName, const WebGLDeletableObject& object, - const bool isShaderOrProgram = false) { + const bool isShaderOrProgram = false) const { if (!ValidateObjectAllowDeleted(argName, object)) return false; if (isShaderOrProgram) { @@ -1789,8 +1804,10 @@ class WebGLContext : public nsICanvasRenderingContextInternal, // Program and Shader are incomplete, so we can't inline the conversion to // WebGLDeletableObject here. - bool ValidateObject(const char* const argName, const WebGLProgram& object); - bool ValidateObject(const char* const argName, const WebGLShader& object); + bool ValidateObject(const char* const argName, + const WebGLProgram& object) const; + bool ValidateObject(const char* const argName, + const WebGLShader& object) const; //// @@ -1821,7 +1838,7 @@ class WebGLContext : public nsICanvasRenderingContextInternal, WebGLRefPtr mCurrentProgram; RefPtr mActiveProgramLinkInfo; - bool ValidateFramebufferTarget(GLenum target); + bool ValidateFramebufferTarget(GLenum target) const; bool ValidateInvalidateFramebuffer(GLenum target, const dom::Sequence& attachments, ErrorResult* const out_rv, @@ -1835,6 +1852,10 @@ class WebGLContext : public nsICanvasRenderingContextInternal, WebGLRefPtr mBoundTransformFeedback; WebGLRefPtr mBoundVertexArray; + public: + const auto& BoundReadFb() const { return mBoundReadFramebuffer; } + + protected: LinkedList mBuffers; LinkedList mFramebuffers; LinkedList mPrograms; diff --git a/dom/canvas/WebGLContextDraw.cpp b/dom/canvas/WebGLContextDraw.cpp index 77154baaf924..fef344ba1326 100644 --- a/dom/canvas/WebGLContextDraw.cpp +++ b/dom/canvas/WebGLContextDraw.cpp @@ -279,6 +279,15 @@ const webgl::CachedDrawFetchLimits* ValidateDraw(WebGLContext* const webgl, const auto& tfo = webgl->mBoundTransformFeedback; if (tfo && tfo->IsActiveAndNotPaused()) { + if (fb) { + const auto& info = *fb->GetCompletenessInfo(); + if (info.isMultiview) { + webgl->ErrorInvalidOperation( + "Cannot render to multiview with transform feedback."); + return nullptr; + } + } + uint32_t numUsed; switch (linkInfo->transformFeedbackBufferMode) { case LOCAL_GL_INTERLEAVED_ATTRIBS: @@ -339,7 +348,11 @@ const webgl::CachedDrawFetchLimits* ValidateDraw(WebGLContext* const webgl, }; if (!webgl->mRasterizerDiscardEnabled) { + uint8_t fbZLayerCount = 1; if (fb) { + const auto& info = *fb->GetCompletenessInfo(); + fbZLayerCount = info.zLayerCount; + for (const auto& attach : fb->ColorDrawBuffers()) { const auto i = uint8_t(attach->mAttachmentPoint - LOCAL_GL_COLOR_ATTACHMENT0); @@ -352,6 +365,13 @@ const webgl::CachedDrawFetchLimits* ValidateDraw(WebGLContext* const webgl, if (!fnValidateFragOutputType(0, webgl::TextureBaseType::Float)) return nullptr; } + + if (fbZLayerCount != linkInfo->zLayerCount) { + webgl->ErrorInvalidOperation( + "Multiview count mismatch: shader: %u, framebuffer: %u", + uint32_t{linkInfo->zLayerCount}, uint32_t{fbZLayerCount}); + return nullptr; + } } // - diff --git a/dom/canvas/WebGLContextExtensions.cpp b/dom/canvas/WebGLContextExtensions.cpp index 27da08d36fe9..679bf1707f6d 100644 --- a/dom/canvas/WebGLContextExtensions.cpp +++ b/dom/canvas/WebGLContextExtensions.cpp @@ -51,6 +51,7 @@ namespace mozilla { WEBGL_EXTENSION_IDENTIFIER(OES_texture_half_float) WEBGL_EXTENSION_IDENTIFIER(OES_texture_half_float_linear) WEBGL_EXTENSION_IDENTIFIER(OES_vertex_array_object) + WEBGL_EXTENSION_IDENTIFIER(OVR_multiview2) WEBGL_EXTENSION_IDENTIFIER(WEBGL_color_buffer_float) WEBGL_EXTENSION_IDENTIFIER(WEBGL_compressed_texture_astc) WEBGL_EXTENSION_IDENTIFIER(WEBGL_compressed_texture_etc) @@ -186,6 +187,10 @@ bool WebGLContext::IsExtensionSupported(WebGLExtensionID ext) const { case WebGLExtensionID::OES_vertex_array_object: return !IsWebGL2(); // Always supported in webgl1. + // OVR_ + case WebGLExtensionID::OVR_multiview2: + return WebGLExtensionMultiview::IsSupported(this); + // WEBGL_ case WebGLExtensionID::WEBGL_color_buffer_float: return WebGLExtensionColorBufferFloat::IsSupported(this); @@ -389,6 +394,11 @@ void WebGLContext::EnableExtension(WebGLExtensionID ext) { obj = new WebGLExtensionVertexArray(this); break; + // WEBGL_ + case WebGLExtensionID::OVR_multiview2: + obj = new WebGLExtensionMultiview(this); + break; + // WEBGL_ case WebGLExtensionID::WEBGL_color_buffer_float: obj = new WebGLExtensionColorBufferFloat(this); diff --git a/dom/canvas/WebGLContextGL.cpp b/dom/canvas/WebGLContextGL.cpp index 793d541ce71f..8f4078fc397e 100644 --- a/dom/canvas/WebGLContextGL.cpp +++ b/dom/canvas/WebGLContextGL.cpp @@ -59,12 +59,12 @@ namespace mozilla { bool WebGLContext::ValidateObject(const char* const argName, - const WebGLProgram& object) { + const WebGLProgram& object) const { return ValidateObject(argName, object, true); } bool WebGLContext::ValidateObject(const char* const argName, - const WebGLShader& object) { + const WebGLShader& object) const { return ValidateObject(argName, object, true); } @@ -446,62 +446,204 @@ void WebGLContext::DepthRange(GLfloat zNear, GLfloat zFar) { gl->fDepthRange(zNear, zFar); } -void WebGLContext::FramebufferRenderbuffer(GLenum target, GLenum attachment, - GLenum rbtarget, - WebGLRenderbuffer* wrb) { +// - + +void WebGLContext::FramebufferAttach( + const GLenum target, const GLenum attachEnum, const TexTarget reqTexTarget, + const webgl::FbAttachInfo& toAttach) const { + if (!ValidateFramebufferTarget(target)) return; + + WebGLFramebuffer* fb = mBoundDrawFramebuffer; + if (target == LOCAL_GL_READ_FRAMEBUFFER) { + fb = mBoundReadFramebuffer; + } + if (!fb) return ErrorInvalidOperation("Cannot modify framebuffer 0."); + + // `rb` + if (toAttach.rb) { + if (!ValidateObject("rb", *toAttach.rb)) return; + + if (!toAttach.rb->mHasBeenBound) { + ErrorInvalidOperation( + "bindRenderbuffer must be called before" + " attachment."); + return; + } + } + + // `tex` + if (toAttach.tex) { + if (!ValidateObject("tex", *toAttach.tex)) return; + const auto texTarget = toAttach.tex->Target(); + + bool targetOk = bool(texTarget); + if (reqTexTarget) { + targetOk = texTarget == reqTexTarget; + } + if (!targetOk) { + ErrorInvalidOperation("`tex`'s binding target type is not valid."); + return; + } + + GLint maxMipLevel; + GLint maxZLayer; + const char* maxMipLevelText; + const char* maxZLayerText; + + switch (texTarget.get()) { + case LOCAL_GL_TEXTURE_2D: + maxMipLevel = FloorLog2(mGLMaxTextureSize); + maxMipLevelText = "log2(MAX_TEXTURE_SIZE)"; + maxZLayer = 1; + maxZLayerText = "1"; + break; + + case LOCAL_GL_TEXTURE_CUBE_MAP: + maxMipLevel = FloorLog2(mGLMaxCubeMapTextureSize); + maxMipLevelText = "log2(MAX_CUBE_MAP_TEXTURE_SIZE)"; + maxZLayer = 6; + maxZLayerText = "6"; + break; + + case LOCAL_GL_TEXTURE_3D: + maxMipLevel = FloorLog2(mGLMax3DTextureSize); + maxMipLevelText = "log2(MAX_3D_TEXTURE_SIZE)"; + maxZLayer = mGLMax3DTextureSize - 1; + maxZLayerText = "MAX_3D_TEXTURE_SIZE"; + break; + + case LOCAL_GL_TEXTURE_2D_ARRAY: + maxMipLevel = FloorLog2(mGLMaxTextureSize); + maxMipLevelText = "log2(MAX_TEXTURE_SIZE)"; + maxZLayer = mGLMaxArrayTextureLayers; + maxZLayerText = "MAX_ARRAY_TEXTURE_LAYERS"; + break; + + default: + MOZ_CRASH(); + } + if (!IsWebGL2() && + !IsExtensionEnabled(WebGLExtensionID::OES_fbo_render_mipmap)) { + maxMipLevel = 0; + maxMipLevelText = "0"; + } + + if (toAttach.mipLevel < 0) + return ErrorInvalidValue("`level` must be >= 0."); + if (toAttach.mipLevel > maxMipLevel) { + ErrorInvalidValue("`level` must be <= %s.", maxMipLevelText); + return; + } + + if (toAttach.zLayer < 0) return ErrorInvalidValue("`layer` must be >= 0."); + if (toAttach.zLayerCount < 1) + return ErrorInvalidValue("`numViews` must be >= 1."); + if (AssertedCast(toAttach.zLayerCount) > mGLMaxMultiviewViews) + return ErrorInvalidValue("`numViews` must be <= MAX_VIEWS_OVR."); + + const auto lastZLayer = toAttach.zLayer + toAttach.zLayerCount; + if (lastZLayer > maxZLayer) { + const char* formatText = "`layer` must be < %s."; + if (toAttach.zLayerCount != 1) { + formatText = "`layer` + `numViews` must be <= %s."; + } + ErrorInvalidValue(formatText, maxZLayerText); + return; + } + } + + fb->FramebufferAttach(attachEnum, toAttach); +} + +void WebGLContext::FramebufferRenderbuffer(const GLenum target, + const GLenum attachEnum, + const GLenum rbTarget, + WebGLRenderbuffer* const rb) const { const FuncScope funcScope(*this, "framebufferRenderbuffer"); if (IsContextLost()) return; - if (!ValidateFramebufferTarget(target)) return; - - WebGLFramebuffer* fb; - switch (target) { - case LOCAL_GL_FRAMEBUFFER: - case LOCAL_GL_DRAW_FRAMEBUFFER: - fb = mBoundDrawFramebuffer; - break; - - case LOCAL_GL_READ_FRAMEBUFFER: - fb = mBoundReadFramebuffer; - break; - - default: - MOZ_CRASH("GFX: Bad target."); + if (rbTarget != LOCAL_GL_RENDERBUFFER) { + ErrorInvalidEnumArg("rbTarget", rbTarget); + return; } - if (!fb) return ErrorInvalidOperation("Cannot modify framebuffer 0."); - - fb->FramebufferRenderbuffer(attachment, rbtarget, wrb); + const auto toAttach = webgl::FbAttachInfo{rb}; + FramebufferAttach(target, attachEnum, 0, toAttach); } -void WebGLContext::FramebufferTexture2D(GLenum target, GLenum attachment, - GLenum textarget, WebGLTexture* tobj, - GLint level) { +void WebGLContext::FramebufferTexture2D(const GLenum target, + const GLenum attachEnum, + const GLenum imageTarget, + WebGLTexture* const tex, + const GLint level) const { const FuncScope funcScope(*this, "framebufferTexture2D"); if (IsContextLost()) return; - if (!ValidateFramebufferTarget(target)) return; - - WebGLFramebuffer* fb; - switch (target) { - case LOCAL_GL_FRAMEBUFFER: - case LOCAL_GL_DRAW_FRAMEBUFFER: - fb = mBoundDrawFramebuffer; - break; - - case LOCAL_GL_READ_FRAMEBUFFER: - fb = mBoundReadFramebuffer; - break; - - default: - MOZ_CRASH("GFX: Bad target."); + TexTarget reqTexTarget = LOCAL_GL_TEXTURE_2D; + auto toAttach = webgl::FbAttachInfo{nullptr, tex, level, 0}; + if (toAttach.tex) { + switch (imageTarget) { + case LOCAL_GL_TEXTURE_2D: + break; + case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X: + case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y: + case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z: + case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X: + case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: + case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: + toAttach.zLayer = imageTarget - LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X; + reqTexTarget = LOCAL_GL_TEXTURE_CUBE_MAP; + break; + default: + ErrorInvalidEnumArg("texImageTarget", imageTarget); + return; + } } - if (!fb) return ErrorInvalidOperation("Cannot modify framebuffer 0."); - - fb->FramebufferTexture2D(attachment, textarget, tobj, level); + FramebufferAttach(target, attachEnum, reqTexTarget, toAttach); } +void WebGLContext::FramebufferTextureLayer(const GLenum target, + const GLenum attachEnum, + WebGLTexture* const tex, + const GLint mipLevel, + const GLint zLayer) const { + const FuncScope funcScope(*this, "framebufferTextureLayer"); + if (IsContextLost()) return; + + const auto toAttach = webgl::FbAttachInfo{nullptr, tex, mipLevel, zLayer}; + if (toAttach.tex) { + if (!ValidateObject("tex", *toAttach.tex)) + return; // Technically we need to check this first... + + switch (toAttach.tex->Target().get()) { + case LOCAL_GL_TEXTURE_3D: + case LOCAL_GL_TEXTURE_2D_ARRAY: + break; + default: + ErrorInvalidOperation( + "`texture` must be a TEXTURE_3D or" + " TEXTURE_2D_ARRAY."); + return; + } + } + + FramebufferAttach(target, attachEnum, 0, toAttach); +} + +void WebGLContext::FramebufferTextureMultiview( + const GLenum target, const GLenum attachEnum, WebGLTexture* const tex, + const GLint mipLevel, const GLint zLayerBase, + const GLsizei numViewLayers) const { + if (IsContextLost()) return; + + const auto toAttach = webgl::FbAttachInfo{nullptr, tex, mipLevel, + zLayerBase, numViewLayers, true}; + FramebufferAttach(target, attachEnum, LOCAL_GL_TEXTURE_2D_ARRAY, toAttach); +} + +// - + void WebGLContext::FrontFace(GLenum mode) { const FuncScope funcScope(*this, "frontFace"); if (IsContextLost()) return; diff --git a/dom/canvas/WebGLContextState.cpp b/dom/canvas/WebGLContextState.cpp index 56caab609f56..1bc362a69fec 100644 --- a/dom/canvas/WebGLContextState.cpp +++ b/dom/canvas/WebGLContextState.cpp @@ -410,6 +410,12 @@ JS::Value WebGLContext::GetParameter(JSContext* cx, GLenum pname, case LOCAL_GL_MAX_VARYING_VECTORS: return JS::Int32Value(mGLMaxFragmentInputVectors); + case LOCAL_GL_MAX_VIEWS_OVR: + if (IsExtensionEnabled(WebGLExtensionID::OVR_multiview2)) { + return JS::NumberValue(mGLMaxMultiviewViews); + } + break; + case LOCAL_GL_COMPRESSED_TEXTURE_FORMATS: { uint32_t length = mCompressedTextureFormats.Length(); JSObject* obj = dom::Uint32Array::Create( diff --git a/dom/canvas/WebGLContextValidate.cpp b/dom/canvas/WebGLContextValidate.cpp index b10669a02c4f..4f568dd6029e 100644 --- a/dom/canvas/WebGLContextValidate.cpp +++ b/dom/canvas/WebGLContextValidate.cpp @@ -411,6 +411,9 @@ bool WebGLContext::InitAndValidateGL(FailureReason* const out_failReason) { (GLint*)&mGLMaxArrayTextureLayers)) mGLMaxArrayTextureLayers = 0; + (void)gl->GetPotentialInteger(LOCAL_GL_MAX_VIEWS_OVR, + (GLint*)&mGLMaxMultiviewViews); + gl->GetUIntegerv(LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS, &mGLMaxFragmentTextureImageUnits); gl->GetUIntegerv(LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, @@ -676,7 +679,7 @@ bool WebGLContext::InitAndValidateGL(FailureReason* const out_failReason) { return true; } -bool WebGLContext::ValidateFramebufferTarget(GLenum target) { +bool WebGLContext::ValidateFramebufferTarget(GLenum target) const { bool isValid = true; switch (target) { case LOCAL_GL_FRAMEBUFFER: diff --git a/dom/canvas/WebGLExtensions.cpp b/dom/canvas/WebGLExtensions.cpp index 07feeff47607..805971fbf9e4 100644 --- a/dom/canvas/WebGLExtensions.cpp +++ b/dom/canvas/WebGLExtensions.cpp @@ -72,4 +72,38 @@ bool WebGLExtensionFBORenderMipmap::IsSupported( IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionFBORenderMipmap, OES_fbo_render_mipmap) +// - + +WebGLExtensionMultiview::WebGLExtensionMultiview(WebGLContext* const webgl) + : WebGLExtensionBase(webgl) { + MOZ_ASSERT(IsSupported(webgl), "Don't construct extension if unsupported."); +} + +WebGLExtensionMultiview::~WebGLExtensionMultiview() = default; + +bool WebGLExtensionMultiview::IsSupported(const WebGLContext* const webgl) { + if (!webgl->IsWebGL2()) return false; + if (!gfxPrefs::WebGLDraftExtensionsEnabled()) return false; + + const auto& gl = webgl->gl; + return gl->IsSupported(gl::GLFeature::multiview); +} + +void WebGLExtensionMultiview::FramebufferTextureMultiviewOVR( + const GLenum target, const GLenum attachment, WebGLTexture* const texture, + const GLint level, const GLint baseViewIndex, + const GLsizei numViews) const { + const WebGLContext::FuncScope funcScope(*mContext, + "framebufferTextureMultiviewOVR"); + if (mIsLost) { + mContext->ErrorInvalidOperation("Extension is lost."); + return; + } + + mContext->FramebufferTextureMultiview(target, attachment, texture, level, + baseViewIndex, numViews); +} + +IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionMultiview, OVR_multiview2) + } // namespace mozilla diff --git a/dom/canvas/WebGLExtensions.h b/dom/canvas/WebGLExtensions.h index d4b42426daec..1adcf68a16f9 100644 --- a/dom/canvas/WebGLExtensions.h +++ b/dom/canvas/WebGLExtensions.h @@ -28,8 +28,9 @@ class FormatUsageAuthority; } // namespace webgl class WebGLContext; -class WebGLShader; class WebGLQuery; +class WebGLShader; +class WebGLTexture; class WebGLVertexArray; class WebGLExtensionBase : public nsWrapperCache, @@ -405,6 +406,20 @@ class WebGLExtensionMOZDebug final : public WebGLExtensionBase { DECL_WEBGL_EXTENSION_GOOP }; +class WebGLExtensionMultiview : public WebGLExtensionBase { + public: + explicit WebGLExtensionMultiview(WebGLContext*); + virtual ~WebGLExtensionMultiview(); + static bool IsSupported(const WebGLContext*); + + void FramebufferTextureMultiviewOVR(GLenum target, GLenum attachment, + WebGLTexture* texture, GLint level, + GLint baseViewIndex, + GLsizei numViews) const; + + DECL_WEBGL_EXTENSION_GOOP +}; + } // namespace mozilla #endif // WEBGL_EXTENSIONS_H_ diff --git a/dom/canvas/WebGLFramebuffer.cpp b/dom/canvas/WebGLFramebuffer.cpp index 871a19451d8b..9126432dca9a 100644 --- a/dom/canvas/WebGLFramebuffer.cpp +++ b/dom/canvas/WebGLFramebuffer.cpp @@ -51,44 +51,31 @@ bool WebGLFBAttachPoint::IsDeleteRequested() const { : Renderbuffer() ? Renderbuffer()->IsDeleteRequested() : false; } -void WebGLFBAttachPoint::Clear() { - mRenderbufferPtr = nullptr; - mTexturePtr = nullptr; - mTexImageTarget = 0; - mTexImageLevel = 0; - mTexImageLayer = 0; -} +void WebGLFBAttachPoint::Clear() { Set(nullptr, {}); } -void WebGLFBAttachPoint::SetTexImage(gl::GLContext* const gl, - WebGLTexture* const tex, - TexImageTarget target, GLint level, - GLint layer) { - Clear(); +void WebGLFBAttachPoint::Set(gl::GLContext* const gl, + const webgl::FbAttachInfo& toAttach) { + mRenderbufferPtr = toAttach.rb; + mTexturePtr = toAttach.tex; + mTexImageLayer = AssertedCast(toAttach.zLayer); + mTexImageZLayerCount = AssertedCast(toAttach.zLayerCount); + mTexImageLevel = AssertedCast(toAttach.mipLevel); + mIsMultiview = toAttach.isMultiview; - mTexturePtr = tex; - mTexImageTarget = target; - mTexImageLevel = level; - mTexImageLayer = layer; - - if (!mDeferAttachment) { - DoAttachment(gl); - } -} - -void WebGLFBAttachPoint::SetRenderbuffer(gl::GLContext* const gl, - WebGLRenderbuffer* const rb) { - Clear(); - - mRenderbufferPtr = rb; - - if (!mDeferAttachment) { + if (gl && !mDeferAttachment) { DoAttachment(gl); } } const webgl::ImageInfo* WebGLFBAttachPoint::GetImageInfo() const { - if (mTexturePtr) - return &mTexturePtr->ImageInfoAt(mTexImageTarget, mTexImageLevel); + if (mTexturePtr) { + const auto target = Texture()->Target(); + uint8_t face = 0; + if (target == LOCAL_GL_TEXTURE_CUBE_MAP) { + face = Layer() % 6; + } + return &mTexturePtr->ImageInfoAtFace(face, mTexImageLevel); + } if (mRenderbufferPtr) return &mRenderbufferPtr->ImageInfo(); return nullptr; } @@ -99,6 +86,7 @@ bool WebGLFBAttachPoint::IsComplete(WebGLContext* webgl, const auto fnWriteErrorInfo = [&](const char* const text) { WebGLContext::EnumName(mAttachmentPoint, out_info); + out_info->AppendLiteral(": "); out_info->AppendASCII(text); }; @@ -117,7 +105,9 @@ bool WebGLFBAttachPoint::IsComplete(WebGLContext* webgl, // because immutable textures are *always* texture-complete. We need to // check immutable textures though, because checking completeness is also // when we zero invalidated/no-data tex images. - const bool complete = [&]() { + const auto attachedMipLevel = MipLevel(); + + const bool withinValidMipLevels = [&]() { const bool ensureInit = false; const auto texCompleteness = tex->CalcCompletenessInfo(ensureInit); if (!texCompleteness) // OOM @@ -126,10 +116,18 @@ bool WebGLFBAttachPoint::IsComplete(WebGLContext* webgl, const auto baseLevel = tex->BaseMipmapLevel(); const auto maxLevel = baseLevel + texCompleteness->levels - 1; - return baseLevel <= mTexImageLevel && mTexImageLevel <= maxLevel; + return baseLevel <= attachedMipLevel && attachedMipLevel <= maxLevel; }(); - if (!complete) { - fnWriteErrorInfo("Attached texture is not texture-complete."); + if (!withinValidMipLevels) { + fnWriteErrorInfo("Attached mip level is invalid for texture."); + return false; + } + + const auto& levelInfo = tex->ImageInfoAtFace(0, attachedMipLevel); + const auto faceDepth = levelInfo.mDepth * tex->FaceCount(); + const bool withinValidZLayers = Layer() + ZLayerCount() - 1 < faceDepth; + if (!withinValidZLayers) { + fnWriteErrorInfo("Attached z layer is invalid for texture."); return false; } } @@ -216,32 +214,38 @@ void WebGLFBAttachPoint::DoAttachment(gl::GLContext* const gl) const { const auto& texName = Texture()->mGLName; - switch (mTexImageTarget.get()) { + switch (Texture()->Target().get()) { case LOCAL_GL_TEXTURE_2D: - case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X: - case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X: - case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y: - case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: - case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z: - case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: + case LOCAL_GL_TEXTURE_CUBE_MAP: { + TexImageTarget imageTarget = LOCAL_GL_TEXTURE_2D; + if (Texture()->Target() == LOCAL_GL_TEXTURE_CUBE_MAP) { + imageTarget = LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + Layer(); + } + if (mAttachmentPoint == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { - gl->fFramebufferTexture2D( - LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, - mTexImageTarget.get(), texName, mTexImageLevel); - gl->fFramebufferTexture2D( - LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT, - mTexImageTarget.get(), texName, mTexImageLevel); + gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, + LOCAL_GL_DEPTH_ATTACHMENT, imageTarget.get(), + texName, MipLevel()); + gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, + LOCAL_GL_STENCIL_ATTACHMENT, + imageTarget.get(), texName, MipLevel()); } else { gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, mAttachmentPoint, - mTexImageTarget.get(), texName, - mTexImageLevel); + imageTarget.get(), texName, MipLevel()); } break; + } case LOCAL_GL_TEXTURE_2D_ARRAY: case LOCAL_GL_TEXTURE_3D: - gl->fFramebufferTextureLayer(LOCAL_GL_FRAMEBUFFER, mAttachmentPoint, - texName, mTexImageLevel, mTexImageLayer); + if (ZLayerCount() != 1) { + gl->fFramebufferTextureMultiview(LOCAL_GL_FRAMEBUFFER, mAttachmentPoint, + texName, MipLevel(), Layer(), + ZLayerCount()); + } else { + gl->fFramebufferTextureLayer(LOCAL_GL_FRAMEBUFFER, mAttachmentPoint, + texName, MipLevel(), Layer()); + } break; } } @@ -308,7 +312,7 @@ JS::Value WebGLFBAttachPoint::GetParameter(WebGLContext* webgl, JSContext* cx, if (mTexturePtr) { GLenum face = 0; if (mTexturePtr->Target() == LOCAL_GL_TEXTURE_CUBE_MAP) { - face = ImageTarget().get(); + face = LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + Layer(); } return JS::Int32Value(face); } @@ -317,13 +321,20 @@ JS::Value WebGLFBAttachPoint::GetParameter(WebGLContext* webgl, JSContext* cx, ////// case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER: - if (webgl->IsWebGL2() && mTexturePtr) { - int32_t layer = 0; - if (ImageTarget() == LOCAL_GL_TEXTURE_2D_ARRAY || - ImageTarget() == LOCAL_GL_TEXTURE_3D) { - layer = Layer(); - } - return JS::Int32Value(layer); + if (webgl->IsWebGL2()) { + return JS::Int32Value(AssertedCast(Layer())); + } + break; + + case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR: + if (webgl->IsExtensionEnabled(WebGLExtensionID::OVR_multiview2)) { + return JS::Int32Value(AssertedCast(Layer())); + } + break; + + case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR: + if (webgl->IsExtensionEnabled(WebGLExtensionID::OVR_multiview2)) { + return JS::Int32Value(ZLayerCount()); } break; @@ -674,6 +685,20 @@ FBStatus WebGLFramebuffer::PrecheckFramebufferStatus( if (depthOrStencilCount > 1) return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED; } + { + const WebGLFBAttachPoint* example = nullptr; + for (const auto& x : mAttachments) { + if (!x->HasAttachment()) continue; + if (!example) { + example = x; + continue; + } + if (x->ZLayerCount() != example->ZLayerCount()) { + return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR; + } + } + } + return LOCAL_GL_FRAMEBUFFER_COMPLETE; } @@ -736,6 +761,12 @@ bool WebGLFramebuffer::ValidateForColorRead( return false; } + if (mColorReadBuffer->ZLayerCount() > 1) { + mContext->GenerateError(LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION, + "The READ_BUFFER attachment has multiple views."); + return false; + } + const auto& imageInfo = mColorReadBuffer->GetImageInfo(); if (!imageInfo) { mContext->ErrorInvalidOperation( @@ -841,18 +872,15 @@ void WebGLFramebuffer::ResolveAttachmentData() const { }; if (imageInfo->mDepth > 1) { - // Todo: Use glClearTexImage. const auto& tex = cur->Texture(); + const gl::ScopedFramebuffer scopedFB(gl); + const gl::ScopedBindFramebuffer scopedBindFB(gl, scopedFB.FB()); for (uint32_t z = 0; z < imageInfo->mDepth; z++) { gl->fFramebufferTextureLayer(LOCAL_GL_FRAMEBUFFER, cur->mAttachmentPoint, tex->mGLName, cur->MipLevel(), z); fnClearBuffer(); } - - gl->fFramebufferTextureLayer(LOCAL_GL_FRAMEBUFFER, - cur->mAttachmentPoint, tex->mGLName, - cur->MipLevel(), cur->Layer()); } else { fnClearBuffer(); } @@ -979,6 +1007,8 @@ FBStatus WebGLFramebuffer::CheckFramebufferStatus() const { info.width = std::min(info.width, imageInfo->mWidth); info.height = std::min(info.height, imageInfo->mHeight); info.hasFloat32 |= fnIsFloat32(*imageInfo->mFormat->format); + info.zLayerCount = cur->ZLayerCount(); + info.isMultiview = cur->IsMultiview(); } mCompletenessInfo = Some(std::move(info)); return LOCAL_GL_FRAMEBUFFER_COMPLETE; @@ -1103,9 +1133,8 @@ void WebGLFramebuffer::ReadBuffer(GLenum attachPoint) { //// -void WebGLFramebuffer::FramebufferRenderbuffer(GLenum attachEnum, - GLenum rbtarget, - WebGLRenderbuffer* rb) { +void WebGLFramebuffer::FramebufferAttach(const GLenum attachEnum, + const webgl::FbAttachInfo& toAttach) { MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this || mContext->mBoundReadFramebuffer == this); @@ -1117,202 +1146,17 @@ void WebGLFramebuffer::FramebufferRenderbuffer(GLenum attachEnum, } const auto& attach = maybeAttach.value(); - // `rbTarget` - if (rbtarget != LOCAL_GL_RENDERBUFFER) { - mContext->ErrorInvalidEnumInfo("rbtarget", rbtarget); - return; - } - - // `rb` - if (rb) { - if (!mContext->ValidateObject("rb", *rb)) return; - - if (!rb->mHasBeenBound) { - mContext->ErrorInvalidOperation( - "bindRenderbuffer must be called before" - " attachment to %04x", - attachEnum); - return; - } - } - // End of validation. - const auto& gl = mContext->gl; gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mGLName); if (mContext->IsWebGL2() && attachEnum == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { - mDepthAttachment.SetRenderbuffer(gl, rb); - mStencilAttachment.SetRenderbuffer(gl, rb); + mDepthAttachment.Set(gl, toAttach); + mStencilAttachment.Set(gl, toAttach); } else { - attach->SetRenderbuffer(gl, rb); + attach->Set(gl, toAttach); } InvalidateCaches(); } -void WebGLFramebuffer::FramebufferTexture2D(GLenum attachEnum, - GLenum texImageTarget, - WebGLTexture* tex, GLint level) { - MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this || - mContext->mBoundReadFramebuffer == this); - - // `attachment` - const auto maybeAttach = GetAttachPoint(attachEnum); - if (!maybeAttach || !maybeAttach.value()) { - mContext->ErrorInvalidEnum("Bad `attachment`: 0x%x.", attachEnum); - return; - } - const auto& attach = maybeAttach.value(); - - // `texImageTarget` - if (texImageTarget != LOCAL_GL_TEXTURE_2D && - (texImageTarget < LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X || - texImageTarget > LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)) { - mContext->ErrorInvalidEnumInfo("texImageTarget", texImageTarget); - return; - } - - // `texture` - if (tex) { - if (!mContext->ValidateObject("texture", *tex)) return; - - if (!tex->Target()) { - mContext->ErrorInvalidOperation("`texture` has never been bound."); - return; - } - - const TexTarget destTexTarget = TexImageTargetToTexTarget(texImageTarget); - if (tex->Target() != destTexTarget) { - mContext->ErrorInvalidOperation("Mismatched texture and texture target."); - return; - } - } - - // `level` - if (level < 0) - return mContext->ErrorInvalidValue("`level` must not be negative."); - - if (mContext->IsWebGL2() || - mContext->IsExtensionEnabled(WebGLExtensionID::OES_fbo_render_mipmap)) { - /* GLES 3.0.4 p208: - * If textarget is one of TEXTURE_CUBE_MAP_POSITIVE_X, - * TEXTURE_CUBE_MAP_POSITIVE_Y, TEXTURE_CUBE_MAP_POSITIVE_Z, - * TEXTURE_CUBE_MAP_NEGATIVE_X, TEXTURE_CUBE_MAP_NEGATIVE_Y, - * or TEXTURE_CUBE_MAP_NEGATIVE_Z, then level must be greater - * than or equal to zero and less than or equal to log2 of the - * value of MAX_CUBE_MAP_TEXTURE_SIZE. If textarget is TEXTURE_2D, - * level must be greater than or equal to zero and no larger than - * log2 of the value of MAX_TEXTURE_SIZE. Otherwise, an - * INVALID_VALUE error is generated. - */ - - if (texImageTarget == LOCAL_GL_TEXTURE_2D) { - if (uint32_t(level) > FloorLog2(mContext->mGLMaxTextureSize)) - return mContext->ErrorInvalidValue("`level` is too large."); - } else { - MOZ_ASSERT(texImageTarget >= LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X && - texImageTarget <= LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z); - - if (uint32_t(level) > FloorLog2(mContext->mGLMaxCubeMapTextureSize)) - return mContext->ErrorInvalidValue("`level` is too large."); - } - } else if (level != 0) { - return mContext->ErrorInvalidValue("`level` must be 0."); - } - - // End of validation. - - const auto& gl = mContext->gl; - gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mGLName); - if (mContext->IsWebGL2() && attachEnum == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { - mDepthAttachment.SetTexImage(gl, tex, texImageTarget, level); - mStencilAttachment.SetTexImage(gl, tex, texImageTarget, level); - } else { - attach->SetTexImage(gl, tex, texImageTarget, level); - } - - InvalidateCaches(); -} - -void WebGLFramebuffer::FramebufferTextureLayer(GLenum attachEnum, - WebGLTexture* tex, GLint level, - GLint layer) { - MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this || - mContext->mBoundReadFramebuffer == this); - - // `attachment` - const auto maybeAttach = GetAttachPoint(attachEnum); - if (!maybeAttach || !maybeAttach.value()) { - mContext->ErrorInvalidEnum("Bad `attachment`: 0x%x.", attachEnum); - return; - } - const auto& attach = maybeAttach.value(); - - // `level`, `layer` - if (layer < 0) return mContext->ErrorInvalidValue("`layer` must be >= 0."); - - if (level < 0) return mContext->ErrorInvalidValue("`level` must be >= 0."); - - // `texture` - GLenum texImageTarget = LOCAL_GL_TEXTURE_3D; - if (tex) { - if (!mContext->ValidateObject("texture", *tex)) return; - - if (!tex->Target()) { - mContext->ErrorInvalidOperation("`texture` has never been bound."); - return; - } - - texImageTarget = tex->Target().get(); - switch (texImageTarget) { - case LOCAL_GL_TEXTURE_3D: - if (uint32_t(layer) >= mContext->mGLMax3DTextureSize) { - mContext->ErrorInvalidValue("`layer` must be < %s.", - "MAX_3D_TEXTURE_SIZE"); - return; - } - - if (uint32_t(level) > FloorLog2(mContext->mGLMax3DTextureSize)) { - mContext->ErrorInvalidValue("`level` must be <= log2(%s).", - "MAX_3D_TEXTURE_SIZE"); - return; - } - break; - - case LOCAL_GL_TEXTURE_2D_ARRAY: - if (uint32_t(layer) >= mContext->mGLMaxArrayTextureLayers) { - mContext->ErrorInvalidValue("`layer` must be < %s.", - "MAX_ARRAY_TEXTURE_LAYERS"); - return; - } - - if (uint32_t(level) > FloorLog2(mContext->mGLMaxTextureSize)) { - mContext->ErrorInvalidValue("`level` must be <= log2(%s).", - "MAX_TEXTURE_SIZE"); - return; - } - break; - - default: - mContext->ErrorInvalidOperation( - "`texture` must be a TEXTURE_3D or" - " TEXTURE_2D_ARRAY."); - return; - } - } - - // End of validation. - - const auto& gl = mContext->gl; - gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mGLName); - if (mContext->IsWebGL2() && attachEnum == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { - mDepthAttachment.SetTexImage(gl, tex, texImageTarget, level, layer); - mStencilAttachment.SetTexImage(gl, tex, texImageTarget, level, layer); - } else { - attach->SetTexImage(gl, tex, texImageTarget, level, layer); - } - - InvalidateCaches(); -} - JS::Value WebGLFramebuffer::GetAttachmentParameter( JSContext* cx, GLenum target, GLenum attachEnum, GLenum pname, ErrorResult* const out_error) { @@ -1417,6 +1261,12 @@ void WebGLFramebuffer::BlitFramebuffer(WebGLContext* webgl, GLint srcX0, const webgl::FormatInfo* srcStencilFormat; if (srcFB) { + const auto& info = *srcFB->GetCompletenessInfo(); + if (info.zLayerCount != 1) { + webgl->GenerateError(LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION, + "Source framebuffer cannot have multiple views."); + return; + } srcColorFormat = nullptr; if (srcFB->mColorReadBuffer) { const auto& imageInfo = srcFB->mColorReadBuffer->GetImageInfo(); diff --git a/dom/canvas/WebGLFramebuffer.h b/dom/canvas/WebGLFramebuffer.h index 1fe24649baa7..1136edd3e8fb 100644 --- a/dom/canvas/WebGLFramebuffer.h +++ b/dom/canvas/WebGLFramebuffer.h @@ -31,6 +31,17 @@ namespace gl { class GLContext; } // namespace gl +namespace webgl { +struct FbAttachInfo final { + WebGLRenderbuffer* rb = nullptr; + WebGLTexture* tex = nullptr; + GLint mipLevel = 0; + GLint zLayer = 0; + GLsizei zLayerCount = 1; + bool isMultiview = false; +}; +} // namespace webgl + class WebGLFBAttachPoint final { friend class WebGLFramebuffer; @@ -41,9 +52,10 @@ class WebGLFBAttachPoint final { private: WebGLRefPtr mTexturePtr; WebGLRefPtr mRenderbufferPtr; - TexImageTarget mTexImageTarget = 0; - GLint mTexImageLayer = 0; - uint32_t mTexImageLevel = 0; + uint32_t mTexImageLayer = 0; + uint8_t mTexImageZLayerCount = 1; + uint8_t mTexImageLevel = 0; + bool mIsMultiview = false; //// @@ -67,16 +79,16 @@ class WebGLFBAttachPoint final { void Clear(); - void SetTexImage(gl::GLContext* gl, WebGLTexture* tex, TexImageTarget target, - GLint level, GLint layer = 0); - void SetRenderbuffer(gl::GLContext* gl, WebGLRenderbuffer* rb); + void Set(gl::GLContext* gl, const webgl::FbAttachInfo&); WebGLTexture* Texture() const { return mTexturePtr; } WebGLRenderbuffer* Renderbuffer() const { return mRenderbufferPtr; } - TexImageTarget ImageTarget() const { return mTexImageTarget; } - GLint Layer() const { return mTexImageLayer; } - uint32_t MipLevel() const { return mTexImageLevel; } + auto Layer() const { return mTexImageLayer; } + auto ZLayerCount() const { return mTexImageZLayerCount; } + auto MipLevel() const { return mTexImageLevel; } + const auto& IsMultiview() const { return mIsMultiview; } + void AttachmentName(nsCString* out) const; const webgl::ImageInfo* GetImageInfo() const; @@ -93,8 +105,8 @@ class WebGLFBAttachPoint final { if (!HasAttachment() | !other.HasAttachment()) return false; #define _(X) (X == other.X) - return (_(mRenderbufferPtr) & _(mTexturePtr) & _(mTexImageTarget.get()) & - _(mTexImageLevel) & _(mTexImageLayer)); + return (_(mRenderbufferPtr) && _(mTexturePtr) && _(mTexImageLevel) && + _(mTexImageLayer) && _(mTexImageZLayerCount)); #undef _ } @@ -113,9 +125,9 @@ class WebGLFBAttachPoint final { ORDER_BY(mRef.mRenderbufferPtr) ORDER_BY(mRef.mTexturePtr) - ORDER_BY(mRef.mTexImageTarget.get()) ORDER_BY(mRef.mTexImageLevel) ORDER_BY(mRef.mTexImageLayer) + ORDER_BY(mRef.mTexImageZLayerCount) #undef ORDER_BY return false; @@ -167,6 +179,8 @@ class WebGLFramebuffer final : public nsWrapperCache, uint32_t width = 0; uint32_t height = 0; bool hasFloat32 = false; + uint8_t zLayerCount = 1; + bool isMultiview = false; // IsFeedback std::vector texAttachments; // Non-null @@ -256,12 +270,8 @@ class WebGLFramebuffer final : public nsWrapperCache, } FBStatus CheckFramebufferStatus() const; - void FramebufferRenderbuffer(GLenum attachment, GLenum rbtarget, - WebGLRenderbuffer* rb); - void FramebufferTexture2D(GLenum attachment, GLenum texImageTarget, - WebGLTexture* tex, GLint level); - void FramebufferTextureLayer(GLenum attachment, WebGLTexture* tex, - GLint level, GLint layer); + void FramebufferAttach(GLenum attachEnum, + const webgl::FbAttachInfo& toAttach); void DrawBuffers(const dom::Sequence& buffers); void ReadBuffer(GLenum attachPoint); diff --git a/dom/canvas/WebGLProgram.cpp b/dom/canvas/WebGLProgram.cpp index cb4d25017eff..227adc91c4c5 100644 --- a/dom/canvas/WebGLProgram.cpp +++ b/dom/canvas/WebGLProgram.cpp @@ -535,6 +535,15 @@ static RefPtr QueryProgramInfo( } } + const auto& vertShader = prog->VertShader(); + MOZ_RELEASE_ASSERT(vertShader); + MOZ_RELEASE_ASSERT(vertShader->Validator()); + const auto& handle = vertShader->Validator()->mHandle; + const auto numViews = sh::GetVertexShaderNumViews(handle); + if (numViews != -1) { + info->zLayerCount = AssertedCast(numViews); + } + return info; } diff --git a/dom/canvas/WebGLProgram.h b/dom/canvas/WebGLProgram.h index f068bfdaa108..a574dfa1aa9a 100644 --- a/dom/canvas/WebGLProgram.h +++ b/dom/canvas/WebGLProgram.h @@ -107,6 +107,7 @@ struct LinkedProgramInfo final : public RefCounted, std::vector uniformBlocks; // Owns its contents. std::vector> transformFeedbackVaryings; std::unordered_map fragOutputs; + uint8_t zLayerCount = 1; // Needed for draw call validation. std::vector uniformSamplers; @@ -207,6 +208,7 @@ class WebGLProgram final : public nsWrapperCache, return mMostRecentLinkInfo.get(); } + const auto& VertShader() const { return mVertShader; } const auto& FragShader() const { return mFragShader; } WebGLContext* GetParentObject() const { return mContext; } diff --git a/dom/canvas/WebGLShaderValidator.cpp b/dom/canvas/WebGLShaderValidator.cpp index 0d0e64de4e31..abe99b56fac5 100644 --- a/dom/canvas/WebGLShaderValidator.cpp +++ b/dom/canvas/WebGLShaderValidator.cpp @@ -182,6 +182,11 @@ webgl::ShaderValidator* WebGLContext::CreateShaderValidator( if (IsExtensionEnabled(WebGLExtensionID::EXT_shader_texture_lod)) resources.EXT_shader_texture_lod = 1; + if (IsExtensionEnabled(WebGLExtensionID::OVR_multiview2)) { + resources.OVR_multiview2 = 1; + resources.MaxViewsOVR = mGLMaxMultiviewViews; + } + // Tell ANGLE to allow highp in frag shaders. (unless disabled) // If underlying GLES doesn't have highp in frag shaders, it should complain // anyways. diff --git a/dom/canvas/WebGLStrongTypes.h b/dom/canvas/WebGLStrongTypes.h index d70ad353ff6b..097cd57b29bf 100644 --- a/dom/canvas/WebGLStrongTypes.h +++ b/dom/canvas/WebGLStrongTypes.h @@ -349,6 +349,7 @@ STRONG_GLENUM_VALUE(NONE), STRONG_GLENUM_VALUE(TEXTURE_2D), STRONG_GLENUM_VALUE(FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER), STRONG_GLENUM_VALUE(FRAMEBUFFER_INCOMPLETE_READ_BUFFER), STRONG_GLENUM_VALUE(FRAMEBUFFER_UNSUPPORTED), + STRONG_GLENUM_VALUE(FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR), STRONG_GLENUM_END(FBStatus) STRONG_GLENUM_BEGIN(RBParam) STRONG_GLENUM_VALUE(RENDERBUFFER_SAMPLES), diff --git a/dom/canvas/WebGLTexture.h b/dom/canvas/WebGLTexture.h index 030cc5e83cfd..28a06f8db1eb 100644 --- a/dom/canvas/WebGLTexture.h +++ b/dom/canvas/WebGLTexture.h @@ -154,6 +154,7 @@ class WebGLTexture final : public nsWrapperCache, const auto& Immutable() const { return mImmutable; } const auto& BaseMipmapLevel() const { return mBaseMipmapLevel; } + const auto& FaceCount() const { return mFaceCount; } // We can just max this out to 31, which is the number of unsigned bits in // GLsizei. @@ -208,7 +209,6 @@ class WebGLTexture final : public nsWrapperCache, uint32_t width, uint32_t height, uint32_t depth, webgl::ImageInfo** const out_imageInfo); - bool ValidateCopyTexImageForFeedback(uint32_t level, GLint layer = 0) const; bool ValidateUnpack(const webgl::TexUnpackBlob* blob, bool isFunc3D, const webgl::PackingInfo& srcPI) const; @@ -278,6 +278,7 @@ class WebGLTexture final : public nsWrapperCache, } } + public: auto& ImageInfoAtFace(uint8_t face, uint32_t level) { MOZ_ASSERT(face < mFaceCount); MOZ_ASSERT(level < kMaxLevelCount); @@ -289,7 +290,6 @@ class WebGLTexture final : public nsWrapperCache, return const_cast(this)->ImageInfoAtFace(face, level); } - public: auto& ImageInfoAt(TexImageTarget texImageTarget, GLint level) { const auto& face = FaceForTarget(texImageTarget); return ImageInfoAtFace(face, level); diff --git a/dom/canvas/WebGLTextureUpload.cpp b/dom/canvas/WebGLTextureUpload.cpp index 9890ff8cb08d..40176edbaba1 100644 --- a/dom/canvas/WebGLTextureUpload.cpp +++ b/dom/canvas/WebGLTextureUpload.cpp @@ -1893,22 +1893,26 @@ static const webgl::FormatUsageInfo* ValidateCopyDestUsage( return dstUsage; } -bool WebGLTexture::ValidateCopyTexImageForFeedback(uint32_t level, - GLint layer) const { - const auto& fb = mContext->mBoundReadFramebuffer; +static bool ValidateCopyTexImageForFeedback(const WebGLContext& webgl, + const WebGLTexture& tex, + const uint8_t mipLevel, + const uint32_t zLayer) { + const auto& fb = webgl.BoundReadFb(); if (fb) { - const auto& attach = fb->ColorReadBuffer(); - MOZ_ASSERT(attach); + MOZ_ASSERT(fb->ColorReadBuffer()); + const auto& attach = *fb->ColorReadBuffer(); + MOZ_ASSERT(attach.ZLayerCount() == + 1); // Multiview invalid for copyTexImage. - if (attach->Texture() == this && attach->Layer() == layer && - uint32_t(attach->MipLevel()) == level) { + if (attach.Texture() == &tex && attach.Layer() == zLayer && + attach.MipLevel() == mipLevel) { // Note that the TexImageTargets *don't* have to match for this to be // undefined per GLES 3.0.4 p211, thus an INVALID_OP in WebGL. - mContext->ErrorInvalidOperation( + webgl.ErrorInvalidOperation( "Feedback loop detected, as this texture" " is already attached to READ_FRAMEBUFFER's" " READ_BUFFER-selected COLOR_ATTACHMENT%u.", - attach->mAttachmentPoint); + attach.mAttachmentPoint); return false; } } @@ -2052,7 +2056,10 @@ void WebGLTexture::CopyTexImage2D(TexImageTarget target, GLint level, return; } - if (!ValidateCopyTexImageForFeedback(level)) return; + const uint32_t zOffset = 0; + if (!ValidateCopyTexImageForFeedback(*mContext, *this, + AssertedCast(level), zOffset)) + return; //////////////////////////////////// // Check that source and dest info are compatible @@ -2138,7 +2145,10 @@ void WebGLTexture::CopyTexSubImage(TexImageTarget target, GLint level, return; } - if (!ValidateCopyTexImageForFeedback(level, zOffset)) return; + if (!ValidateCopyTexImageForFeedback(*mContext, *this, + AssertedCast(level), + AssertedCast(zOffset))) + return; //////////////////////////////////// // Check that source and dest info are compatible diff --git a/dom/canvas/WebGLTypes.h b/dom/canvas/WebGLTypes.h index c1102873e2f6..20eea5126573 100644 --- a/dom/canvas/WebGLTypes.h +++ b/dom/canvas/WebGLTypes.h @@ -178,6 +178,7 @@ enum class WebGLExtensionID : uint8_t { OES_texture_half_float, OES_texture_half_float_linear, OES_vertex_array_object, + OVR_multiview2, WEBGL_color_buffer_float, WEBGL_compressed_texture_astc, WEBGL_compressed_texture_etc, diff --git a/dom/canvas/test/webgl-conf/generated-mochitest.ini b/dom/canvas/test/webgl-conf/generated-mochitest.ini index 8a87724830ad..fdbb97555d78 100644 --- a/dom/canvas/test/webgl-conf/generated-mochitest.ini +++ b/dom/canvas/test/webgl-conf/generated-mochitest.ini @@ -5074,6 +5074,7 @@ subsuite = webgl2-core subsuite = webgl2-core [generated/test_2_conformance2__extensions__ovr_multiview2.html] subsuite = webgl2-core +fail-if = (os == 'win') [generated/test_2_conformance2__extensions__ovr_multiview2_depth.html] subsuite = webgl2-core [generated/test_2_conformance2__extensions__ovr_multiview2_draw_buffers.html] @@ -5086,6 +5087,7 @@ subsuite = webgl2-core subsuite = webgl2-core [generated/test_2_conformance2__extensions__ovr_multiview2_single_view_operations.html] subsuite = webgl2-core +fail-if = (os == 'win') [generated/test_2_conformance2__extensions__ovr_multiview2_timer_query.html] subsuite = webgl2-core [generated/test_2_conformance2__extensions__ovr_multiview2_transform_feedback.html] diff --git a/dom/canvas/test/webgl-conf/mochitest-errata.ini b/dom/canvas/test/webgl-conf/mochitest-errata.ini index 14622bf70351..01297a17e000 100644 --- a/dom/canvas/test/webgl-conf/mochitest-errata.ini +++ b/dom/canvas/test/webgl-conf/mochitest-errata.ini @@ -79,6 +79,12 @@ skip-if = (os == 'win') [generated/test_2_conformance__extensions__webgl-compressed-texture-s3tc.html] # getError expected: NO_ERROR. Was INVALID_OPERATION : uploading a texture from a PBO subrange fail-if = 1 +[generated/test_2_conformance2__extensions__ovr_multiview2.html] +# Test fixed upstream, just needs revendor. +fail-if = (os == 'win') +[generated/test_2_conformance2__extensions__ovr_multiview2_single_view_operations.html] +# Test fixed upstream, just needs revendor. +fail-if = (os == 'win') [generated/test_conformance__offscreencanvas__context-attribute-preserve-drawing-buffer.html] # Timeout, likely issue with unsupported OffscreenCanvas. skip-if = 1 diff --git a/dom/canvas/test/webgl-mochitest/ensure-exts/test_OVR_multiview2.html b/dom/canvas/test/webgl-mochitest/ensure-exts/test_OVR_multiview2.html new file mode 100644 index 000000000000..72f04dc43fb6 --- /dev/null +++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_OVR_multiview2.html @@ -0,0 +1,20 @@ + + + + + + + + + + + + diff --git a/dom/canvas/test/webgl-mochitest/ensure-exts/test_common.html b/dom/canvas/test/webgl-mochitest/ensure-exts/test_common.html index ee616c16b9ef..032a825f79d6 100644 --- a/dom/canvas/test/webgl-mochitest/ensure-exts/test_common.html +++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_common.html @@ -54,7 +54,8 @@ var defaultExts = [ ]; var draftExts = [ - ['OES_fbo_render_mipmap', [MACHINE_SPECIFIC, FORBID ]], + ['OES_fbo_render_mipmap', [MACHINE_SPECIFIC, FORBID]], + ['OVR_multiview2' , [FORBID, MACHINE_SPECIFIC]], ]; //////////////////// diff --git a/dom/canvas/test/webgl-mochitest/mochitest.ini b/dom/canvas/test/webgl-mochitest/mochitest.ini index 824c493b9bc4..61e5db7e469f 100644 --- a/dom/canvas/test/webgl-mochitest/mochitest.ini +++ b/dom/canvas/test/webgl-mochitest/mochitest.ini @@ -38,6 +38,8 @@ fail-if = (os == 'android') || (os == 'linux') fail-if = (os == 'android') || (os == 'win') [ensure-exts/test_OES_standard_derivatives.html] fail-if = (os == 'android') +[ensure-exts/test_OVR_multiview2.html] +fail-if = (os == 'linux') || (os == 'mac') [ensure-exts/test_WEBGL_color_buffer_float.html] fail-if = (os == 'android') [ensure-exts/test_WEBGL_compressed_texture_astc.html] diff --git a/dom/events/test/mochitest.ini b/dom/events/test/mochitest.ini index 21defc574183..191a6d8f6a9b 100644 --- a/dom/events/test/mochitest.ini +++ b/dom/events/test/mochitest.ini @@ -219,3 +219,4 @@ skip-if = toolkit == 'android' # Bug 1312791 [test_slotted_mouse_event.html] [test_slotted_text_click.html] [test_unbound_before_in_active_chain.html] +[test_submitevent_on_form.html] diff --git a/dom/events/test/test_submitevent_on_form.html b/dom/events/test/test_submitevent_on_form.html new file mode 100644 index 000000000000..e8c7f4ada764 --- /dev/null +++ b/dom/events/test/test_submitevent_on_form.html @@ -0,0 +1,37 @@ + + + + Test submit event on form + + + + + +
+ +
+
+  
+  
+ + diff --git a/dom/html/HTMLFormElement.cpp b/dom/html/HTMLFormElement.cpp index afed60b97f8d..b1843ae1c34d 100644 --- a/dom/html/HTMLFormElement.cpp +++ b/dom/html/HTMLFormElement.cpp @@ -405,7 +405,10 @@ void HTMLFormElement::UnbindFromTree(bool aDeep, bool aNullParent) { void HTMLFormElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { aVisitor.mWantsWillHandleEvent = true; - if (aVisitor.mEvent->mOriginalTarget == static_cast(this)) { + // According to the UI events spec section "Trusted events", we shouldn't + // trigger UA default action with an untrusted event except click. + if (aVisitor.mEvent->mOriginalTarget == static_cast(this) && + aVisitor.mEvent->IsTrusted()) { uint32_t msg = aVisitor.mEvent->mMessage; if (msg == eFormSubmit) { if (mGeneratingSubmit) { @@ -442,7 +445,10 @@ void HTMLFormElement::WillHandleEvent(EventChainPostVisitor& aVisitor) { } nsresult HTMLFormElement::PostHandleEvent(EventChainPostVisitor& aVisitor) { - if (aVisitor.mEvent->mOriginalTarget == static_cast(this)) { + // According to the UI events spec section "Trusted events", we shouldn't + // trigger UA default action with an untrusted event except click. + if (aVisitor.mEvent->mOriginalTarget == static_cast(this) && + aVisitor.mEvent->IsTrusted()) { EventMessage msg = aVisitor.mEvent->mMessage; if (msg == eFormSubmit) { // let the form know not to defer subsequent submissions diff --git a/dom/l10n/Mutations.cpp b/dom/l10n/Mutations.cpp new file mode 100644 index 000000000000..495fd674ad89 --- /dev/null +++ b/dom/l10n/Mutations.cpp @@ -0,0 +1,178 @@ +#include "Mutations.h" +#include "mozilla/dom/DocumentInlines.h" + +namespace mozilla { +namespace dom { +namespace l10n { + +NS_IMPL_CYCLE_COLLECTION_CLASS(Mutations) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Mutations) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingElements) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingElementsHash) +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Mutations) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingElements) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingElementsHash) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Mutations) + NS_INTERFACE_MAP_ENTRY(nsIMutationObserver) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(Mutations) +NS_IMPL_CYCLE_COLLECTING_RELEASE(Mutations) + +Mutations::Mutations(DocumentL10n* aDocumentL10n) + : mDocumentL10n(aDocumentL10n) { + mObserving = true; +} + +void Mutations::AttributeChanged(dom::Element* aElement, int32_t aNameSpaceID, + nsAtom* aAttribute, int32_t aModType, + const nsAttrValue* aOldValue) { + if (!mObserving) { + return; + } + Document* uncomposedDoc = aElement->GetUncomposedDoc(); + if (uncomposedDoc) { + if (aNameSpaceID == kNameSpaceID_None && + (aAttribute == nsGkAtoms::datal10nid || + aAttribute == nsGkAtoms::datal10nargs)) { + L10nElementChanged(aElement); + } + } +} + +void Mutations::ContentAppended(nsIContent* aChild) { + if (!mObserving) { + return; + } + ErrorResult rv; + Sequence> elements; + + nsINode* node = aChild; + while (node) { + if (node->IsElement()) { + Element* elem = node->AsElement(); + + Document* uncomposedDoc = elem->GetUncomposedDoc(); + if (uncomposedDoc) { + mDocumentL10n->GetTranslatables(*node, elements, rv); + } + } + + node = node->GetNextSibling(); + } + + for (auto& elem : elements) { + L10nElementChanged(elem); + } +} + +void Mutations::ContentInserted(nsIContent* aChild) { + if (!mObserving) { + return; + } + ErrorResult rv; + Sequence> elements; + + if (!aChild->IsElement()) { + return; + } + Element* elem = aChild->AsElement(); + + Document* uncomposedDoc = elem->GetUncomposedDoc(); + if (!uncomposedDoc) { + return; + } + mDocumentL10n->GetTranslatables(*aChild, elements, rv); + + for (auto& elem : elements) { + L10nElementChanged(elem); + } +} + +void Mutations::L10nElementChanged(Element* aElement) { + if (!mPendingElementsHash.Contains(aElement)) { + mPendingElements.AppendElement(aElement); + mPendingElementsHash.PutEntry(aElement); + } + + if (!mRefreshObserver) { + StartRefreshObserver(); + } +} + +void Mutations::PauseObserving() { mObserving = false; } + +void Mutations::ResumeObserving() { mObserving = true; } + +void Mutations::WillRefresh(mozilla::TimeStamp aTime) { + StopRefreshObserver(); + FlushPendingTranslations(); +} + +void Mutations::FlushPendingTranslations() { + if (!mDocumentL10n) { + return; + } + + ErrorResult rv; + + Sequence> elements; + + for (auto& elem : mPendingElements) { + if (!elem->HasAttr(kNameSpaceID_None, nsGkAtoms::datal10nid)) { + continue; + } + + elements.AppendElement(*elem, fallible); + } + + mPendingElementsHash.Clear(); + mPendingElements.Clear(); + + RefPtr promise = mDocumentL10n->TranslateElements(elements, rv); +} + +void Mutations::Disconnect() { + StopRefreshObserver(); + mDocumentL10n = nullptr; +} + +void Mutations::StartRefreshObserver() { + if (!mDocumentL10n) { + return; + } + + if (!mRefreshDriver) { + nsPresContext* ctx = mDocumentL10n->GetDocument()->GetPresContext(); + if (!ctx) { + return; + } + mRefreshDriver = ctx->RefreshDriver(); + } + + if (mRefreshDriver) { + mRefreshDriver->AddRefreshObserver(this, FlushType::Style); + mRefreshObserver = true; + } +} + +void Mutations::StopRefreshObserver() { + if (!mDocumentL10n) { + return; + } + + if (mRefreshDriver) { + mRefreshDriver->RemoveRefreshObserver(this, FlushType::Style); + mRefreshObserver = false; + } +} + +} // namespace l10n +} // namespace dom +} // namespace mozilla diff --git a/dom/l10n/Mutations.h b/dom/l10n/Mutations.h new file mode 100644 index 000000000000..c4c80dab77e2 --- /dev/null +++ b/dom/l10n/Mutations.h @@ -0,0 +1,78 @@ +#ifndef mozilla_dom_l10n_Mutations_h__ +#define mozilla_dom_l10n_Mutations_h__ + +#include "nsRefreshDriver.h" +#include "nsStubMutationObserver.h" +#include "nsTHashtable.h" +#include "mozilla/dom/DocumentL10n.h" + +namespace mozilla { +namespace dom { +namespace l10n { + +/** + * Mutations manage observing roots for localization + * changes and coalescing pending translations into + * batches - one per animation frame. + */ +class Mutations final : public nsStubMutationObserver, + public nsARefreshObserver { + public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(Mutations, nsIMutationObserver) + NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED + NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED + NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED + + explicit Mutations(DocumentL10n* aDocumentL10n); + + /** + * Pause root observation. + * This is useful for injecting already-translated + * content into an observed root, without causing + * superflues translation. + */ + void PauseObserving(); + + /** + * Resume root observation. + */ + void ResumeObserving(); + + /** + * Disconnect roots, stop refresh observer + * and break the cycle collection deadlock + * by removing the reference to mDocumentL10n. + */ + void Disconnect(); + + protected: + bool mObserving = false; + bool mRefreshObserver = false; + RefPtr mRefreshDriver; + DocumentL10n* mDocumentL10n; + + // The hash is used to speed up lookups into mPendingElements. + nsTHashtable> mPendingElementsHash; + nsTArray> mPendingElements; + + virtual void WillRefresh(mozilla::TimeStamp aTime) override; + + void StartRefreshObserver(); + void StopRefreshObserver(); + void L10nElementChanged(Element* aElement); + void FlushPendingTranslations(); + + private: + ~Mutations() { + StopRefreshObserver(); + MOZ_ASSERT(!mDocumentL10n, + "DocumentL10n<-->Mutations cycle should be broken."); + } +}; + +} // namespace l10n +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_l10n_Mutations_h__ diff --git a/dom/l10n/moz.build b/dom/l10n/moz.build index d917134e17f6..33a4d24a7df7 100644 --- a/dom/l10n/moz.build +++ b/dom/l10n/moz.build @@ -9,10 +9,12 @@ with Files("**"): EXPORTS.mozilla.dom.l10n += [ 'DOMOverlays.h', + 'Mutations.h', ] UNIFIED_SOURCES += [ 'DOMOverlays.cpp', + 'Mutations.cpp', ] LOCAL_INCLUDES += [ diff --git a/dom/l10n/tests/gtest/TestDOMOverlays.cpp b/dom/l10n/tests/gtest/TestDOMOverlays.cpp index a2ffa68307ca..5a43c4bb3a07 100644 --- a/dom/l10n/tests/gtest/TestDOMOverlays.cpp +++ b/dom/l10n/tests/gtest/TestDOMOverlays.cpp @@ -16,7 +16,7 @@ using mozilla::NullPrincipal; using namespace mozilla::dom; using namespace mozilla::dom::l10n; -already_AddRefed SetUpDocument() { +static already_AddRefed SetUpDocument() { nsCOMPtr uri; NS_NewURI(getter_AddRefs(uri), "about:blank"); nsCOMPtr principal = diff --git a/dom/l10n/tests/mochitest/chrome.ini b/dom/l10n/tests/mochitest/chrome.ini index 3477d68cc86b..9936d6c104fd 100644 --- a/dom/l10n/tests/mochitest/chrome.ini +++ b/dom/l10n/tests/mochitest/chrome.ini @@ -3,3 +3,9 @@ [test_domoverlays_text_children.html] [test_domoverlays_extra_text_markup.html] [test_domoverlays.xul] + +[mutations/test_append_content_post_dcl.html] +[mutations/test_append_content_pre_dcl.html] +[mutations/test_append_fragment_post_dcl.html] +[mutations/test_set_attributes.html] +[mutations/test_pause_observing.html] diff --git a/dom/l10n/tests/mochitest/mutations/test_append_content_post_dcl.html b/dom/l10n/tests/mochitest/mutations/test_append_content_post_dcl.html new file mode 100644 index 000000000000..2b2cf6d88120 --- /dev/null +++ b/dom/l10n/tests/mochitest/mutations/test_append_content_post_dcl.html @@ -0,0 +1,30 @@ + + + + + Test L10n Mutations for ContentAppended after DOMContentLoaded + + + + + + + + diff --git a/dom/l10n/tests/mochitest/mutations/test_append_content_pre_dcl.html b/dom/l10n/tests/mochitest/mutations/test_append_content_pre_dcl.html new file mode 100644 index 000000000000..80255cf6497a --- /dev/null +++ b/dom/l10n/tests/mochitest/mutations/test_append_content_pre_dcl.html @@ -0,0 +1,28 @@ + + + + + Test L10n Mutations for ContentAppended before DOMContentLoaded + + + + + + + + diff --git a/dom/l10n/tests/mochitest/mutations/test_append_fragment_post_dcl.html b/dom/l10n/tests/mochitest/mutations/test_append_fragment_post_dcl.html new file mode 100644 index 000000000000..b87479868ef2 --- /dev/null +++ b/dom/l10n/tests/mochitest/mutations/test_append_fragment_post_dcl.html @@ -0,0 +1,39 @@ + + + + + Test L10n Mutations for appending a fragment after DOMContentLoaded + + + + + + + + diff --git a/dom/l10n/tests/mochitest/mutations/test_pause_observing.html b/dom/l10n/tests/mochitest/mutations/test_pause_observing.html new file mode 100644 index 000000000000..1ba6e897f3a5 --- /dev/null +++ b/dom/l10n/tests/mochitest/mutations/test_pause_observing.html @@ -0,0 +1,44 @@ + + + + + Test L10n Mutations for Pause/Resume Observing + + + + + + + + diff --git a/dom/l10n/tests/mochitest/mutations/test_set_attributes.html b/dom/l10n/tests/mochitest/mutations/test_set_attributes.html new file mode 100644 index 000000000000..16f4236f5672 --- /dev/null +++ b/dom/l10n/tests/mochitest/mutations/test_set_attributes.html @@ -0,0 +1,37 @@ + + + + + Test L10n Mutations for AttributeChange after DOMContentLoaded + + + + + + + +
+
+ + diff --git a/dom/webauthn/u2f-hid-rs/Cargo.toml b/dom/webauthn/u2f-hid-rs/Cargo.toml index cd26652cd305..c4d9a5e7a596 100644 --- a/dom/webauthn/u2f-hid-rs/Cargo.toml +++ b/dom/webauthn/u2f-hid-rs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "u2fhid" -version = "0.2.3" +version = "0.2.4" authors = ["Kyle Machulis ", "J.C. Jones ", "Tim Taubert "] [target.'cfg(target_os = "linux")'.dependencies] @@ -23,7 +23,7 @@ features = [ ] [dependencies] -rand = "0.3" +rand = "0.6" log = "0.4" libc = "^0.2" boxfnonce = "0.0.3" diff --git a/dom/webauthn/u2f-hid-rs/src/u2fprotocol.rs b/dom/webauthn/u2f-hid-rs/src/u2fprotocol.rs index 292f61add934..1409cd854fcd 100644 --- a/dom/webauthn/u2f-hid-rs/src/u2fprotocol.rs +++ b/dom/webauthn/u2f-hid-rs/src/u2fprotocol.rs @@ -6,7 +6,7 @@ extern crate std; -use rand::{thread_rng, Rng}; +use rand::{thread_rng, RngCore}; use std::ffi::CString; use std::io; use std::io::{Read, Write}; @@ -214,7 +214,7 @@ where #[cfg(test)] mod tests { - use rand::{thread_rng, Rng}; + use rand::{thread_rng, RngCore}; use super::{init_device, send_apdu, sendrecv, U2FDevice}; use consts::{CID_BROADCAST, SW_NO_ERROR, U2FHID_INIT, U2FHID_MSG, U2FHID_PING}; diff --git a/dom/webidl/WebGL2RenderingContext.webidl b/dom/webidl/WebGL2RenderingContext.webidl index 336687ff6c79..8cddccc3d8d5 100644 --- a/dom/webidl/WebGL2RenderingContext.webidl +++ b/dom/webidl/WebGL2RenderingContext.webidl @@ -700,3 +700,13 @@ WebGL2RenderingContext implements WebGL2RenderingContextBase; [NoInterfaceObject] interface EXT_color_buffer_float { }; + +[NoInterfaceObject] +interface OVR_multiview2 { + const GLenum FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR = 0x9630; + const GLenum FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR = 0x9632; + const GLenum MAX_VIEWS_OVR = 0x9631; + const GLenum FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR = 0x9633; + + void framebufferTextureMultiviewOVR(GLenum target, GLenum attachment, WebGLTexture? texture, GLint level, GLint baseViewIndex, GLsizei numViews); +}; diff --git a/gfx/2d/NativeFontResourceDWrite.cpp b/gfx/2d/NativeFontResourceDWrite.cpp index 226efdcac841..53b51d953cfc 100644 --- a/gfx/2d/NativeFontResourceDWrite.cpp +++ b/gfx/2d/NativeFontResourceDWrite.cpp @@ -101,6 +101,12 @@ class DWriteFontFileStream final : public IDWriteFontFileStream { IFACEMETHOD_(ULONG, Release)() { uint32_t count = --mRefCnt; if (count == 0) { + // Avoid locking unless necessary. Verify the refcount hasn't changed + // while locked. Delete within the scope of the lock when zero. + StaticMutexAutoLock lock(sFontFileStreamsMutex); + if (0 != mRefCnt) { + return mRefCnt; + } delete this; } return count; @@ -151,7 +157,6 @@ DWriteFontFileStream::DWriteFontFileStream(uint64_t aFontFileKey) : mRefCnt(0), mFontFileKey(aFontFileKey) {} DWriteFontFileStream::~DWriteFontFileStream() { - StaticMutexAutoLock lock(sFontFileStreamsMutex); sFontFileStreams.erase(mFontFileKey); } diff --git a/gfx/angle/checkout/include/GLSLANG/ShaderLang.h b/gfx/angle/checkout/include/GLSLANG/ShaderLang.h index e99096e23874..be8dc3692833 100644 --- a/gfx/angle/checkout/include/GLSLANG/ShaderLang.h +++ b/gfx/angle/checkout/include/GLSLANG/ShaderLang.h @@ -230,7 +230,7 @@ const ShCompileOptions SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW = UINT64_C // With the flag enabled the GLSL/ESSL vertex shader is modified to include code for viewport // selection in the following way: -// - Code to enable the extension NV_viewport_array2 is included. +// - Code to enable the extension ARB_shader_viewport_layer_array/NV_viewport_array2 is included. // - Code to select the viewport index or layer is inserted at the beginning of main after // ViewID_OVR's initialization. // - A declaration of the uniform multiviewBaseViewLayerIndex. diff --git a/gfx/angle/checkout/out/gen/angle/id/commit.h b/gfx/angle/checkout/out/gen/angle/id/commit.h index 78bfe0ecb880..8851fb09f6aa 100644 --- a/gfx/angle/checkout/out/gen/angle/id/commit.h +++ b/gfx/angle/checkout/out/gen/angle/id/commit.h @@ -1,3 +1,3 @@ -#define ANGLE_COMMIT_HASH "5814bb88b10e" +#define ANGLE_COMMIT_HASH "1d5494fc3e85" #define ANGLE_COMMIT_HASH_SIZE 12 -#define ANGLE_COMMIT_DATE "2019-05-06 18:40:01 -0700" +#define ANGLE_COMMIT_DATE "2019-05-22 18:13:00 -0700" diff --git a/gfx/angle/checkout/src/compiler/translator/Compiler.cpp b/gfx/angle/checkout/src/compiler/translator/Compiler.cpp index 77104096dc54..d3bd121ca381 100644 --- a/gfx/angle/checkout/src/compiler/translator/Compiler.cpp +++ b/gfx/angle/checkout/src/compiler/translator/Compiler.cpp @@ -1373,4 +1373,40 @@ bool TCompiler::isVaryingDefined(const char *varyingName) return false; } +void EmitMultiviewGLSL(const TCompiler &compiler, + const ShCompileOptions &compileOptions, + const TBehavior behavior, + TInfoSinkBase &sink) +{ + ASSERT(behavior != EBhUndefined); + if (behavior == EBhDisable) + return; + + const bool isVertexShader = (compiler.getShaderType() == GL_VERTEX_SHADER); + if (compileOptions & SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW) + { + // Emit ARB_shader_viewport_layer_array/NV_viewport_array2 in a vertex shader if the + // SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER option is set and the + // OVR_multiview(2) extension is requested. + if (isVertexShader && (compileOptions & SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER)) + { + sink << "#if defined(GL_ARB_shader_viewport_layer_array)\n" + << "#extension GL_ARB_shader_viewport_layer_array : require\n" + << "#elif defined(GL_NV_viewport_array2)\n" + << "#extension GL_NV_viewport_array2 : require\n" + << "#endif\n"; + } + } + else + { + sink << "#extension GL_OVR_multiview2 : " << GetBehaviorString(behavior) << "\n"; + + const auto &numViews = compiler.getNumViews(); + if (isVertexShader && numViews != -1) + { + sink << "layout(num_views=" << numViews << ") in;\n"; + } + } +} + } // namespace sh diff --git a/gfx/angle/checkout/src/compiler/translator/Compiler.h b/gfx/angle/checkout/src/compiler/translator/Compiler.h index 392f7ecd29d6..d831c4d52a12 100644 --- a/gfx/angle/checkout/src/compiler/translator/Compiler.h +++ b/gfx/angle/checkout/src/compiler/translator/Compiler.h @@ -296,6 +296,8 @@ class TCompiler : public TShHandleBase TCompiler *ConstructCompiler(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output); void DeleteCompiler(TCompiler *); +void EmitMultiviewGLSL(const TCompiler &, const ShCompileOptions &, TBehavior, TInfoSinkBase &sink); + } // namespace sh #endif // COMPILER_TRANSLATOR_COMPILER_H_ diff --git a/gfx/angle/checkout/src/compiler/translator/TranslatorESSL.cpp b/gfx/angle/checkout/src/compiler/translator/TranslatorESSL.cpp index 786a291e2c2b..799fef81effa 100644 --- a/gfx/angle/checkout/src/compiler/translator/TranslatorESSL.cpp +++ b/gfx/angle/checkout/src/compiler/translator/TranslatorESSL.cpp @@ -124,9 +124,6 @@ void TranslatorESSL::writeExtensionBehavior(ShCompileOptions compileOptions) { TInfoSinkBase &sink = getInfoSink().obj; const TExtensionBehavior &extBehavior = getExtensionBehavior(); - const bool isMultiviewExtEmulated = - (compileOptions & (SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW | - SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER)) != 0u; for (TExtensionBehavior::const_iterator iter = extBehavior.begin(); iter != extBehavior.end(); ++iter) { @@ -144,16 +141,9 @@ void TranslatorESSL::writeExtensionBehavior(ShCompileOptions compileOptions) sink << "#extension GL_NV_draw_buffers : " << GetBehaviorString(iter->second) << "\n"; } - else if (isMultiview && isMultiviewExtEmulated) + else if (isMultiview) { - if (getShaderType() == GL_VERTEX_SHADER && - (compileOptions & SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER) != 0u) - { - // Emit the NV_viewport_array2 extension in a vertex shader if the - // SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER option is set and the - // OVR_multiview2 extension is requested. - sink << "#extension GL_NV_viewport_array2 : require\n"; - } + EmitMultiviewGLSL(*this, compileOptions, iter->second, sink); } else if (iter->first == TExtension::EXT_geometry_shader) { diff --git a/gfx/angle/checkout/src/compiler/translator/TranslatorGLSL.cpp b/gfx/angle/checkout/src/compiler/translator/TranslatorGLSL.cpp index 4bd8f548db65..379f81f64c18 100644 --- a/gfx/angle/checkout/src/compiler/translator/TranslatorGLSL.cpp +++ b/gfx/angle/checkout/src/compiler/translator/TranslatorGLSL.cpp @@ -278,13 +278,9 @@ void TranslatorGLSL::writeExtensionBehavior(TIntermNode *root, ShCompileOptions } const bool isMultiview = (iter.first == TExtension::OVR_multiview2); - if (isMultiview && getShaderType() == GL_VERTEX_SHADER && - (compileOptions & SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER) != 0u) + if (isMultiview) { - // Emit the NV_viewport_array2 extension in a vertex shader if the - // SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER option is set and the OVR_multiview2(2) - // extension is requested. - sink << "#extension GL_NV_viewport_array2 : require\n"; + EmitMultiviewGLSL(*this, compileOptions, iter.second, sink); } // Support ANGLE_texture_multisample extension on GLSL300 diff --git a/gfx/angle/cherry_picks.txt b/gfx/angle/cherry_picks.txt index 28b2e8b7840a..7a76282b6d85 100644 --- a/gfx/angle/cherry_picks.txt +++ b/gfx/angle/cherry_picks.txt @@ -1,3 +1,15 @@ +commit 1d5494fc3e856d4d722e266d2b2075debac5146f +Author: Jeff Gilbert +Date: Thu Apr 25 14:57:26 2019 -0700 + + Emit OVR_multiview2 on ESSL/GLSL outputs. + + Add ARB_shader_viewport_layer_array support to + SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER. + + Bug: angleproject:3404 + Change-Id: Ia89517d0cc92400ce47c9118e8c1abf8285aec41 + commit 5814bb88b10ef903abcf297f2de72619d853b4e9 Author: Lee Salzman Date: Tue Apr 30 23:42:31 2019 -0400 diff --git a/gfx/angle/targets/angle_common/moz.build b/gfx/angle/targets/angle_common/moz.build index 7f604a8b3710..3f0e1a35a298 100644 --- a/gfx/angle/targets/angle_common/moz.build +++ b/gfx/angle/targets/angle_common/moz.build @@ -11,7 +11,7 @@ DEFINES['DYNAMIC_ANNOTATIONS_ENABLED'] = '1' #DEFINES['FULL_SAFE_BROWSING'] = True DEFINES['NOMINMAX'] = True #DEFINES['NO_TCMALLOC'] = True -DEFINES['NTDDI_VERSION'] = '0x0A000003' +DEFINES['NTDDI_VERSION'] = 'NTDDI_WIN10_RS2' #DEFINES['PSAPI_VERSION'] = '2' #DEFINES['SAFE_BROWSING_CSD'] = True #DEFINES['SAFE_BROWSING_DB_LOCAL'] = True @@ -45,7 +45,6 @@ LOCAL_INCLUDES += [ #CXXFLAGS += [ # '/bigobj', -# '/d2FastFail', # '/D__DATE__=', # '/D__TIME__=', # '/D__TIMESTAMP__=', diff --git a/gfx/angle/targets/angle_gpu_info_util/moz.build b/gfx/angle/targets/angle_gpu_info_util/moz.build index 9e9d68b43760..0a79866ae7ef 100644 --- a/gfx/angle/targets/angle_gpu_info_util/moz.build +++ b/gfx/angle/targets/angle_gpu_info_util/moz.build @@ -11,7 +11,7 @@ DEFINES['DYNAMIC_ANNOTATIONS_ENABLED'] = '1' DEFINES['GPU_INFO_USE_SETUPAPI'] = True DEFINES['NOMINMAX'] = True #DEFINES['NO_TCMALLOC'] = True -DEFINES['NTDDI_VERSION'] = '0x0A000003' +DEFINES['NTDDI_VERSION'] = 'NTDDI_WIN10_RS2' #DEFINES['PSAPI_VERSION'] = '2' #DEFINES['SAFE_BROWSING_CSD'] = True #DEFINES['SAFE_BROWSING_DB_LOCAL'] = True @@ -45,7 +45,6 @@ LOCAL_INCLUDES += [ #CXXFLAGS += [ # '/bigobj', -# '/d2FastFail', # '/D__DATE__=', # '/D__TIME__=', # '/D__TIMESTAMP__=', diff --git a/gfx/angle/targets/angle_image_util/moz.build b/gfx/angle/targets/angle_image_util/moz.build index 84546eec1ced..6c1614cd3fb8 100644 --- a/gfx/angle/targets/angle_image_util/moz.build +++ b/gfx/angle/targets/angle_image_util/moz.build @@ -10,7 +10,7 @@ DEFINES['DYNAMIC_ANNOTATIONS_ENABLED'] = '1' #DEFINES['FULL_SAFE_BROWSING'] = True DEFINES['NOMINMAX'] = True #DEFINES['NO_TCMALLOC'] = True -DEFINES['NTDDI_VERSION'] = '0x0A000003' +DEFINES['NTDDI_VERSION'] = 'NTDDI_WIN10_RS2' #DEFINES['PSAPI_VERSION'] = '2' #DEFINES['SAFE_BROWSING_CSD'] = True #DEFINES['SAFE_BROWSING_DB_LOCAL'] = True @@ -44,7 +44,6 @@ LOCAL_INCLUDES += [ #CXXFLAGS += [ # '/bigobj', -# '/d2FastFail', # '/D__DATE__=', # '/D__TIME__=', # '/D__TIMESTAMP__=', diff --git a/gfx/angle/targets/libANGLE/moz.build b/gfx/angle/targets/libANGLE/moz.build index ab468ae7b923..f98c086eb719 100644 --- a/gfx/angle/targets/libANGLE/moz.build +++ b/gfx/angle/targets/libANGLE/moz.build @@ -16,7 +16,7 @@ DEFINES['DYNAMIC_ANNOTATIONS_ENABLED'] = '1' DEFINES['LIBANGLE_IMPLEMENTATION'] = True DEFINES['NOMINMAX'] = True #DEFINES['NO_TCMALLOC'] = True -DEFINES['NTDDI_VERSION'] = '0x0A000003' +DEFINES['NTDDI_VERSION'] = 'NTDDI_WIN10_RS2' #DEFINES['PSAPI_VERSION'] = '2' #DEFINES['SAFE_BROWSING_CSD'] = True #DEFINES['SAFE_BROWSING_DB_LOCAL'] = True @@ -50,7 +50,6 @@ LOCAL_INCLUDES += [ #CXXFLAGS += [ # '/bigobj', -# '/d2FastFail', # '/D__DATE__=', # '/D__TIME__=', # '/D__TIMESTAMP__=', diff --git a/gfx/angle/targets/libEGL/moz.build b/gfx/angle/targets/libEGL/moz.build index c16b62029fcc..bc4c8db0db97 100644 --- a/gfx/angle/targets/libEGL/moz.build +++ b/gfx/angle/targets/libEGL/moz.build @@ -20,7 +20,7 @@ DEFINES['GL_GLEXT_PROTOTYPES'] = True DEFINES['LIBEGL_IMPLEMENTATION'] = True DEFINES['NOMINMAX'] = True #DEFINES['NO_TCMALLOC'] = True -DEFINES['NTDDI_VERSION'] = '0x0A000003' +DEFINES['NTDDI_VERSION'] = 'NTDDI_WIN10_RS2' #DEFINES['PSAPI_VERSION'] = '2' #DEFINES['SAFE_BROWSING_CSD'] = True #DEFINES['SAFE_BROWSING_DB_LOCAL'] = True @@ -53,7 +53,6 @@ LOCAL_INCLUDES += [ #CXXFLAGS += [ # '/bigobj', -# '/d2FastFail', # '/D__DATE__=', # '/D__TIME__=', # '/D__TIMESTAMP__=', @@ -165,7 +164,6 @@ OS_LIBS += [ #LDFLAGS += [ # '/DEBUG', # '/DYNAMICBASE', -# '/fastfail', # '/FIXED:NO', # '/ignore:4199', # '/ignore:4221', diff --git a/gfx/angle/targets/libGLESv2/moz.build b/gfx/angle/targets/libGLESv2/moz.build index 469f7704cf09..14ef57e9fe9b 100644 --- a/gfx/angle/targets/libGLESv2/moz.build +++ b/gfx/angle/targets/libGLESv2/moz.build @@ -21,7 +21,7 @@ DEFINES['LIBANGLE_IMPLEMENTATION'] = True DEFINES['LIBGLESV2_IMPLEMENTATION'] = True DEFINES['NOMINMAX'] = True #DEFINES['NO_TCMALLOC'] = True -DEFINES['NTDDI_VERSION'] = '0x0A000003' +DEFINES['NTDDI_VERSION'] = 'NTDDI_WIN10_RS2' #DEFINES['PSAPI_VERSION'] = '2' #DEFINES['SAFE_BROWSING_CSD'] = True #DEFINES['SAFE_BROWSING_DB_LOCAL'] = True @@ -55,7 +55,6 @@ LOCAL_INCLUDES += [ #CXXFLAGS += [ # '/bigobj', -# '/d2FastFail', # '/D__DATE__=', # '/D__TIME__=', # '/D__TIMESTAMP__=', @@ -173,7 +172,6 @@ OS_LIBS += [ #LDFLAGS += [ # '/DEBUG', # '/DYNAMICBASE', -# '/fastfail', # '/FIXED:NO', # '/ignore:4199', # '/ignore:4221', diff --git a/gfx/angle/targets/preprocessor/moz.build b/gfx/angle/targets/preprocessor/moz.build index b29a1b933bf3..3f2a98eec76b 100644 --- a/gfx/angle/targets/preprocessor/moz.build +++ b/gfx/angle/targets/preprocessor/moz.build @@ -10,7 +10,7 @@ DEFINES['DYNAMIC_ANNOTATIONS_ENABLED'] = '1' #DEFINES['FULL_SAFE_BROWSING'] = True DEFINES['NOMINMAX'] = True #DEFINES['NO_TCMALLOC'] = True -DEFINES['NTDDI_VERSION'] = '0x0A000003' +DEFINES['NTDDI_VERSION'] = 'NTDDI_WIN10_RS2' #DEFINES['PSAPI_VERSION'] = '2' #DEFINES['SAFE_BROWSING_CSD'] = True #DEFINES['SAFE_BROWSING_DB_LOCAL'] = True @@ -44,7 +44,6 @@ LOCAL_INCLUDES += [ #CXXFLAGS += [ # '/bigobj', -# '/d2FastFail', # '/D__DATE__=', # '/D__TIME__=', # '/D__TIMESTAMP__=', diff --git a/gfx/angle/targets/translator/moz.build b/gfx/angle/targets/translator/moz.build index 7e0f09109384..21dc6b2d09b8 100644 --- a/gfx/angle/targets/translator/moz.build +++ b/gfx/angle/targets/translator/moz.build @@ -13,7 +13,7 @@ DEFINES['DYNAMIC_ANNOTATIONS_ENABLED'] = '1' #DEFINES['FULL_SAFE_BROWSING'] = True DEFINES['NOMINMAX'] = True #DEFINES['NO_TCMALLOC'] = True -DEFINES['NTDDI_VERSION'] = '0x0A000003' +DEFINES['NTDDI_VERSION'] = 'NTDDI_WIN10_RS2' #DEFINES['PSAPI_VERSION'] = '2' #DEFINES['SAFE_BROWSING_CSD'] = True #DEFINES['SAFE_BROWSING_DB_LOCAL'] = True @@ -47,7 +47,6 @@ LOCAL_INCLUDES += [ #CXXFLAGS += [ # '/bigobj', -# '/d2FastFail', # '/D__DATE__=', # '/D__TIME__=', # '/D__TIMESTAMP__=', diff --git a/gfx/gl/GLConsts.h b/gfx/gl/GLConsts.h index 54d635d9cfef..0a5beef9e982 100644 --- a/gfx/gl/GLConsts.h +++ b/gfx/gl/GLConsts.h @@ -2,6 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +// clang-format off + #ifndef GLCONSTS_H_ #define GLCONSTS_H_ @@ -7000,3 +7002,5 @@ #define LOCAL_WGL_VIDEO_OUT_STACKED_FIELDS_2_1 0x20CC #endif // GLCONSTS_H_ + +// clang-format on diff --git a/gfx/gl/GLContext.cpp b/gfx/gl/GLContext.cpp index 5759aa5af291..12a81549f396 100644 --- a/gfx/gl/GLContext.cpp +++ b/gfx/gl/GLContext.cpp @@ -79,6 +79,7 @@ static const char* const sExtensionNames[] = { "GL_ANGLE_framebuffer_blit", "GL_ANGLE_framebuffer_multisample", "GL_ANGLE_instanced_arrays", + "GL_ANGLE_multiview", "GL_ANGLE_texture_compression_dxt3", "GL_ANGLE_texture_compression_dxt5", "GL_ANGLE_timer_query", @@ -204,7 +205,8 @@ static const char* const sExtensionNames[] = { "GL_OES_texture_half_float", "GL_OES_texture_half_float_linear", "GL_OES_texture_npot", - "GL_OES_vertex_array_object"}; + "GL_OES_vertex_array_object", + "GL_OVR_multiview2"}; static bool ShouldUseTLSIsCurrent(bool useTLSIsCurrent) { if (gfxPrefs::UseTLSIsCurrent() == 0) return useTLSIsCurrent; @@ -1384,6 +1386,17 @@ void GLContext::LoadMoreSymbols(const SymbolLoader& loader) { fnLoadForFeature(symbols, GLFeature::invalidate_framebuffer); } + if (IsSupported(GLFeature::multiview)) { + const SymLoadStruct symbols[] = { + { (PRFuncPtr*) &mSymbols.fFramebufferTextureMultiview, {{ + "glFramebufferTextureMultiviewOVR", + "glFramebufferTextureMultiviewLayeredANGLE" + }} }, + END_SYMBOLS + }; + fnLoadForFeature(symbols, GLFeature::multiview); + } + if (IsSupported(GLFeature::prim_restart)) { const SymLoadStruct symbols[] = { { (PRFuncPtr*) &mSymbols.fPrimitiveRestartIndex, {{ "glPrimitiveRestartIndex", "glPrimitiveRestartIndexNV" }} }, diff --git a/gfx/gl/GLContext.h b/gfx/gl/GLContext.h index 9206bf8bfed3..648ed9f89557 100644 --- a/gfx/gl/GLContext.h +++ b/gfx/gl/GLContext.h @@ -104,6 +104,7 @@ enum class GLFeature { internalformat_query, invalidate_framebuffer, map_buffer_range, + multiview, occlusion_query, occlusion_query_boolean, occlusion_query2, @@ -353,6 +354,7 @@ class GLContext : public GenericAtomicRefCounted, ANGLE_framebuffer_blit, ANGLE_framebuffer_multisample, ANGLE_instanced_arrays, + ANGLE_multiview, ANGLE_texture_compression_dxt3, ANGLE_texture_compression_dxt5, ANGLE_timer_query, @@ -479,6 +481,7 @@ class GLContext : public GenericAtomicRefCounted, OES_texture_half_float_linear, OES_texture_npot, OES_vertex_array_object, + OVR_multiview2, Extensions_Max, Extensions_End }; @@ -3300,6 +3303,20 @@ class GLContext : public GenericAtomicRefCounted, AFTER_GL_CALL; } + // ----------------------------------------------------------------------------- + // multiview + + void fFramebufferTextureMultiview(GLenum target, GLenum attachment, + GLuint texture, GLint level, + GLint baseViewIndex, + GLsizei numViews) const { + BEFORE_GL_CALL; + ASSERT_SYMBOL_PRESENT(fFramebufferTextureMultiview); + mSymbols.fFramebufferTextureMultiview(target, attachment, texture, level, + baseViewIndex, numViews); + AFTER_GL_CALL; + } + #undef BEFORE_GL_CALL #undef AFTER_GL_CALL #undef ASSERT_SYMBOL_PRESENT diff --git a/gfx/gl/GLContextFeatures.cpp b/gfx/gl/GLContextFeatures.cpp index 863940882b13..0ca83c8830da 100644 --- a/gfx/gl/GLContextFeatures.cpp +++ b/gfx/gl/GLContextFeatures.cpp @@ -246,6 +246,12 @@ static const FeatureInfo sFeatureInfoArr[] = { GLESVersion::ES3, GLContext::ARB_map_buffer_range, {GLContext::EXT_map_buffer_range, GLContext::Extensions_End}}, + {"multiview", + GLVersion::NONE, + GLESVersion::NONE, + GLContext::Extension_None, + {GLContext::ANGLE_multiview, GLContext::OVR_multiview2, + GLContext::Extensions_End}}, { "occlusion_query", GLVersion::GL2, diff --git a/gfx/gl/GLContextSymbols.h b/gfx/gl/GLContextSymbols.h index 30fabaaa9b0d..21c44fd3509b 100644 --- a/gfx/gl/GLContextSymbols.h +++ b/gfx/gl/GLContextSymbols.h @@ -445,6 +445,13 @@ struct GLContextSymbols final { // NV_primitive_restart void(GLAPIENTRY* fPrimitiveRestartIndex)(GLuint); + + // OVR_multiview2 + void(GLAPIENTRY* fFramebufferTextureMultiview)(GLenum target, + GLenum attachment, + GLuint texture, GLint level, + GLint baseViewIndex, + GLsizei numViews); }; } // namespace gl diff --git a/gfx/gl/GLParseRegistryXML.py b/gfx/gl/GLParseRegistryXML.py index 6503a15b50b4..4c6d49381302 100755 --- a/gfx/gl/GLParseRegistryXML.py +++ b/gfx/gl/GLParseRegistryXML.py @@ -52,6 +52,8 @@ class GLConstHeader: ' * License, v. 2.0. If a copy of the MPL was not distributed with this', ' * file, You can obtain one at http://mozilla.org/MPL/2.0/. */', '', + '// clang-format off', + '', '#ifndef GLCONSTS_H_', '#define GLCONSTS_H_', '', @@ -62,7 +64,7 @@ class GLConstHeader: ' *', ' * To generate this file, see tutorial in \'GLParseRegistryXML.py\'.', ' */', - '' + '', ]) def formatLibBegin(self, lib): @@ -89,7 +91,9 @@ class GLConstHeader: def formatFileEnd(self): self.write([ '', - '#endif // GLCONSTS_H_' + '#endif // GLCONSTS_H_', + '', + '// clang-format on', ]) diff --git a/gfx/gl/ScopedGLHelpers.h b/gfx/gl/ScopedGLHelpers.h index 63ad73b3104e..1d949bbc3a4e 100644 --- a/gfx/gl/ScopedGLHelpers.h +++ b/gfx/gl/ScopedGLHelpers.h @@ -122,7 +122,7 @@ struct ScopedFramebuffer : public ScopedGLWrapper { public: explicit ScopedFramebuffer(GLContext* aGL); - GLuint FB() { return mFB; } + const auto& FB() const { return mFB; } protected: void UnwrapImpl(); diff --git a/gfx/thebes/gfxDWriteCommon.cpp b/gfx/thebes/gfxDWriteCommon.cpp index 9c430cc89ce7..3092ba3e222f 100644 --- a/gfx/thebes/gfxDWriteCommon.cpp +++ b/gfx/thebes/gfxDWriteCommon.cpp @@ -8,11 +8,13 @@ #include #include "mozilla/Atomics.h" +#include "mozilla/StaticMutex.h" #include "mozilla/gfx/Logging.h" class gfxDWriteFontFileStream; -static mozilla::Atomic sNextFontFileKey; +static mozilla::StaticMutex sFontFileStreamsMutex; +static uint64_t sNextFontFileKey = 0; static std::unordered_map sFontFileStreams; IDWriteFontFileLoader* gfxDWriteFontFileLoader::mInstance = nullptr; @@ -46,18 +48,22 @@ class gfxDWriteFontFileStream final : public IDWriteFontFileStream { IFACEMETHOD_(ULONG, AddRef)() { MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); - ++mRefCnt; - return mRefCnt; + return ++mRefCnt; } IFACEMETHOD_(ULONG, Release)() { MOZ_ASSERT(0 != mRefCnt, "dup release"); - --mRefCnt; - if (mRefCnt == 0) { + uint32_t count = --mRefCnt; + if (count == 0) { + // Avoid locking unless necessary. Verify the refcount hasn't changed + // while locked. Delete within the scope of the lock when zero. + mozilla::StaticMutexAutoLock lock(sFontFileStreamsMutex); + if (0 != mRefCnt) { + return mRefCnt; + } delete this; - return 0; } - return mRefCnt; + return count; } // IDWriteFontFileStream methods @@ -81,7 +87,7 @@ class gfxDWriteFontFileStream final : public IDWriteFontFileStream { private: FallibleTArray mData; - nsAutoRefCnt mRefCnt; + mozilla::Atomic mRefCnt; uint64_t mFontFileKey; }; @@ -134,6 +140,7 @@ HRESULT STDMETHODCALLTYPE gfxDWriteFontFileLoader::CreateStreamFromKey( return E_POINTER; } + mozilla::StaticMutexAutoLock lock(sFontFileStreamsMutex); uint64_t fontFileKey = *static_cast(fontFileReferenceKey); auto found = sFontFileStreams.find(fontFileKey); if (found == sFontFileStreams.end()) { @@ -161,10 +168,12 @@ gfxDWriteFontFileLoader::CreateCustomFontFile( return E_FAIL; } + sFontFileStreamsMutex.Lock(); uint64_t fontFileKey = sNextFontFileKey++; RefPtr ffsRef = new gfxDWriteFontFileStream(aFontData, aLength, fontFileKey); sFontFileStreams[fontFileKey] = ffsRef; + sFontFileStreamsMutex.Unlock(); RefPtr fontFile; HRESULT hr = factory->CreateCustomFontFileReference( diff --git a/gfx/wr/Cargo.toml b/gfx/wr/Cargo.toml index 0162f26c3157..4dea0f1643ca 100644 --- a/gfx/wr/Cargo.toml +++ b/gfx/wr/Cargo.toml @@ -12,5 +12,8 @@ members = [ debug = true panic = "abort" +[profile.dev] +panic = "abort" + [patch.crates-io] serde_derive = { git = "https://github.com/servo/serde", branch = "deserialize_from_enums10", feature="deserialize_in_place" } diff --git a/gfx/wr/wrench/android.txt b/gfx/wr/wrench/android.txt index 55322a427403..5dda5f281dcd 100644 --- a/gfx/wr/wrench/android.txt +++ b/gfx/wr/wrench/android.txt @@ -56,3 +56,29 @@ Release mode: Now the APK at ../target/android-artifacts/app/build/outputs/apk/app-release-unsigned.apk should be signed and installable (you may need to uninstall the debug APK first if you have that installed). + +Running reftests like a boss: +----------------------------- + + First, compile wrench as described above (debug mode). + Then, from the root gecko source dir, run: + ./mach python testing/mozharness/scripts/android_emulator_wrench.py --config testing/mozharness/configs/android/wrench.py + This will automatically do the following: + - Download the blessed android AVDs from taskcluster + - Start the emulator (using your ~/.mozbuild/android-sdk-linux emulator binaries) + - Install the debug APK (from gfx/wr/wrench/target/.../app-debug.apk) + - Copy the reftests to the sdcard + - Write an args file to the sdcard + - Run wrench + - Wait for wrench to finish running + - Scrape the logcat for reftest output + Other logs (e.g. full logcat) can be found in your ~/.wrench/logs folder. Note that + this will also leave the android emulator running, so repeating the command will be + even faster the next time around as it won't need to redownload the AVDs or restart + the emulator. It will reinstall the APK and re-push the reftests folder though. + + If you want to use a release APK (runs much faster), build it as per the "Release mode" + instructions above and set the WRENCH_APK env var to point to the APK: + to point to it: + export WRENCH_APK=gfx/wr/target/android-artifacts/app/build/outputs/apk/app-release-unsigned.apk + ./mach python testing/mozharness/scripts/android_emulator_wrench.py --config testing/mozharness/configs/android/wrench.py diff --git a/gfx/wr/wrench/reftests/aa/reftest.list b/gfx/wr/wrench/reftests/aa/reftest.list index 6e3248bbcbc3..b6590b3bb5c4 100644 --- a/gfx/wr/wrench/reftests/aa/reftest.list +++ b/gfx/wr/wrench/reftests/aa/reftest.list @@ -1,2 +1,2 @@ -== rounded-rects.yaml rounded-rects-ref.png +skip_on(android) == rounded-rects.yaml rounded-rects-ref.png # Too wide for Android == aa-dist-bug.yaml aa-dist-bug-ref.yaml diff --git a/gfx/wr/wrench/reftests/blend/reftest.list b/gfx/wr/wrench/reftests/blend/reftest.list index 042328ed03e1..d47140da23c2 100644 --- a/gfx/wr/wrench/reftests/blend/reftest.list +++ b/gfx/wr/wrench/reftests/blend/reftest.list @@ -1,15 +1,20 @@ -== multiply.yaml multiply-ref.yaml -== multiply-2.yaml multiply-2-ref.yaml -== color_targets(3) alpha_targets(0) multiply-3.yaml multiply-2-ref.yaml -== difference.yaml difference-ref.yaml -fuzzy(1,30000) == difference-transparent.yaml difference-transparent-ref.yaml -== darken.yaml darken-ref.yaml -== lighten.yaml lighten-ref.yaml +# Some tests in this file skipped on debug Android because of panic, +# GL error 502 at blit_framebuffer. These are marked with skip_on(android) +# or skip_on(android,debug). Additionally, the ones marked skip_on(android) +# fail in opt builds. -== repeated-difference.yaml repeated-difference-ref.yaml +skip_on(android,debug) == multiply.yaml multiply-ref.yaml +skip_on(android) == multiply-2.yaml multiply-2-ref.yaml +skip_on(android) == color_targets(3) alpha_targets(0) multiply-3.yaml multiply-2-ref.yaml +skip_on(android) == difference.yaml difference-ref.yaml +skip_on(android) fuzzy(1,30000) == difference-transparent.yaml difference-transparent-ref.yaml +skip_on(android) == darken.yaml darken-ref.yaml +skip_on(android) == lighten.yaml lighten-ref.yaml + +skip_on(android,debug) == repeated-difference.yaml repeated-difference-ref.yaml == isolated.yaml isolated-ref.yaml -== isolated-2.yaml isolated-2-ref.yaml +skip_on(android) == isolated-2.yaml isolated-2-ref.yaml == isolated-with-filter.yaml isolated-ref.yaml == isolated-premultiplied.yaml blank.yaml == isolated-premultiplied-2.yaml isolated-premultiplied-2-ref.yaml @@ -21,4 +26,4 @@ fuzzy(1,30000) == difference-transparent.yaml difference-transparent-ref.yaml fuzzy(1,2502) == transparent-composite-1.yaml transparent-composite-1-ref.yaml fuzzy(1,2502) == transparent-composite-2.yaml transparent-composite-2-ref.yaml -== multi-mix-blend-mode.yaml multi-mix-blend-mode-ref.yaml +skip_on(android) == multi-mix-blend-mode.yaml multi-mix-blend-mode-ref.yaml diff --git a/gfx/wr/wrench/reftests/border/reftest.list b/gfx/wr/wrench/reftests/border/reftest.list index a4160e9954d1..2ab7edd06753 100644 --- a/gfx/wr/wrench/reftests/border/reftest.list +++ b/gfx/wr/wrench/reftests/border/reftest.list @@ -18,7 +18,7 @@ fuzzy(64,24) == border-ridge-simple.yaml border-ridge-simple-ref.yaml platform(linux,mac) fuzzy(1,3) == degenerate-curve.yaml degenerate-curve.png platform(linux,mac) == border-image.yaml border-image-ref.png == border-image-crash.yaml border-image-crash-ref.yaml -== border-image-fill.yaml border-image-fill-ref.png +skip_on(android) == border-image-fill.yaml border-image-fill-ref.png # fails on Android == border-no-bogus-line.yaml border-no-bogus-line-ref.png platform(linux,mac) == dotted-corner-small-radius.yaml dotted-corner-small-radius.png == overlapping.yaml overlapping.png diff --git a/gfx/wr/wrench/reftests/boxshadow/reftest.list b/gfx/wr/wrench/reftests/boxshadow/reftest.list index 5daaeee1df19..6887bd7c27ea 100644 --- a/gfx/wr/wrench/reftests/boxshadow/reftest.list +++ b/gfx/wr/wrench/reftests/boxshadow/reftest.list @@ -15,7 +15,7 @@ platform(linux,mac) == box-shadow-suite-blur.yaml box-shadow-suite-blur.png == box-shadow-large-blur-radius.yaml box-shadow-large-blur-radius-ref.yaml fuzzy(1,6388) == rounding.yaml rounding-ref.yaml platform(linux,mac) == box-shadow-border-radii.yaml box-shadow-border-radii.png -== box-shadow-spread.yaml box-shadow-spread.png +skip_on(android) == box-shadow-spread.yaml box-shadow-spread.png # Too wide for Android == box-shadow-spread-radii.yaml box-shadow-spread-radii-ref.yaml == invalid.yaml invalid-ref.yaml == inset-empty.yaml blank.yaml @@ -23,7 +23,7 @@ platform(linux,mac) == inset-subpx.yaml inset-subpx.png platform(linux,mac) fuzzy(1,4) == inset-downscale.yaml inset-downscale.png platform(linux,mac) fuzzy(1,50) == box-shadow-cache.yaml box-shadow-cache.png platform(linux,mac) fuzzy(1,685) == overlap1.yaml overlap1.png -fuzzy(1,61) == overlap2.yaml overlap2.png +fuzzy(1,134) == overlap2.yaml overlap2.png platform(linux,mac) fuzzy(1,48) == no-stretch.yaml no-stretch.png platform(linux,mac) fuzzy(1,9) == box-shadow-stretch-mode-x.yaml box-shadow-stretch-mode-x.png platform(linux,mac) fuzzy(1,41) == box-shadow-stretch-mode-y.yaml box-shadow-stretch-mode-y.png diff --git a/gfx/wr/wrench/reftests/clip/reftest.list b/gfx/wr/wrench/reftests/clip/reftest.list index 45aa3928c49c..34a65dbcc964 100644 --- a/gfx/wr/wrench/reftests/clip/reftest.list +++ b/gfx/wr/wrench/reftests/clip/reftest.list @@ -11,4 +11,4 @@ platform(linux,mac) == segmentation-with-other-coordinate-system-clip.yaml segme == segmentation-across-rotation.yaml segmentation-across-rotation-ref.yaml == color_targets(2) alpha_targets(1) stacking-context-clip.yaml stacking-context-clip-ref.yaml == snapping.yaml snapping-ref.yaml -fuzzy(65,2400) == clip-and-filter-with-rotation.yaml clip-and-filter-with-rotation-ref.yaml +fuzzy(70,2400) == clip-and-filter-with-rotation.yaml clip-and-filter-with-rotation-ref.yaml diff --git a/gfx/wr/wrench/reftests/filters/reftest.list b/gfx/wr/wrench/reftests/filters/reftest.list index 00d43d4b37a4..c8a6bd5deecd 100644 --- a/gfx/wr/wrench/reftests/filters/reftest.list +++ b/gfx/wr/wrench/reftests/filters/reftest.list @@ -9,13 +9,13 @@ color_targets(1) alpha_targets(0) == opacity-combined.yaml opacity-combined-ref. == filter-brightness-2.yaml filter-brightness-2-ref.yaml == filter-brightness-3.yaml filter-brightness-3-ref.yaml == filter-brightness-4.yaml filter-brightness-4-ref.yaml -== filter-component-transfer.yaml filter-component-transfer-ref.yaml +skip_on(android) == filter-component-transfer.yaml filter-component-transfer-ref.yaml # fails on Android == filter-color-matrix.yaml filter-color-matrix-ref.yaml == filter-contrast-gray-alpha-1.yaml filter-contrast-gray-alpha-1-ref.yaml == filter-invert.yaml filter-invert-ref.yaml == filter-invert-2.yaml filter-invert-2-ref.yaml platform(linux,mac) fuzzy(1,133) == filter-large-blur-radius.yaml filter-large-blur-radius.png -== draw_calls(4) color_targets(4) alpha_targets(0) filter-small-blur-radius.yaml filter-small-blur-radius.png +skip_on(android) == draw_calls(4) color_targets(4) alpha_targets(0) filter-small-blur-radius.yaml filter-small-blur-radius.png # fails on Android == filter-saturate-red-1.yaml filter-saturate-red-1-ref.yaml == filter-saturate-red-2.yaml filter-saturate-red-2-ref.yaml == filter-saturate-red-3.yaml filter-saturate-red-3-ref.yaml @@ -30,17 +30,17 @@ platform(linux,mac) fuzzy(1,133) == filter-large-blur-radius.yaml filter-large-b == filter-saturate-blue-alpha-1.yaml filter-saturate-blue-alpha-1-ref.yaml == filter-hue-rotate-1.yaml filter-hue-rotate-1-ref.yaml == filter-hue-rotate-alpha-1.yaml filter-hue-rotate-alpha-1-ref.yaml -fuzzy(1,14) == filter-long-chain.yaml filter-long-chain.png +skip_on(android) fuzzy(1,14) == filter-long-chain.yaml filter-long-chain.png # fails on Android platform(linux,mac) == filter-drop-shadow.yaml filter-drop-shadow.png platform(linux,mac) == filter-drop-shadow-on-viewport-edge.yaml filter-drop-shadow-on-viewport-edge.png platform(linux,mac) == blend-clipped.yaml blend-clipped.png platform(linux,mac) == filter-drop-shadow-clip.yaml filter-drop-shadow-clip.png == filter-segments.yaml filter-segments-ref.yaml == iframe-dropshadow.yaml iframe-dropshadow-ref.yaml -== filter-mix-blend-mode.yaml filter-mix-blend-mode-ref.yaml +skip_on(android) == filter-mix-blend-mode.yaml filter-mix-blend-mode-ref.yaml # Android debug: GL error 502 at blit_framebuffer, Android opt: fails == fuzzy(3,20000) srgb-to-linear.yaml srgb-to-linear-ref.yaml != srgb-to-linear-2.yaml srgb-to-linear-ref.yaml != filter-blur-huge.yaml blank.yaml != filter-drop-shadow-huge.yaml blank.yaml == filter-blur-scaled.yaml filter-blur-scaled-ref.yaml -== filter-blur-scaled-xonly.yaml filter-blur-scaled-xonly.png +skip_on(android) == filter-blur-scaled-xonly.yaml filter-blur-scaled-xonly.png # fails on Android diff --git a/gfx/wr/wrench/reftests/gradient/reftest.list b/gfx/wr/wrench/reftests/gradient/reftest.list index 48b03a690600..aa9bb96c9c6b 100644 --- a/gfx/wr/wrench/reftests/gradient/reftest.list +++ b/gfx/wr/wrench/reftests/gradient/reftest.list @@ -18,7 +18,7 @@ platform(linux,mac) fuzzy(1,35000) == linear-stops.yaml linear-stops-ref.png fuzzy(1,20000) == linear.yaml linear-ref.yaml fuzzy(1,20000) == linear-reverse.yaml linear-ref.yaml -fuzzy(1,15200) == linear-aligned-clip.yaml linear-aligned-clip-ref.yaml +skip_on(android,debug) fuzzy(1,15200) == linear-aligned-clip.yaml linear-aligned-clip-ref.yaml # Android: GL error 502 at blit_framebuffer platform(linux,mac) fuzzy(1,80000) == radial-circle.yaml radial-circle-ref.png platform(linux,mac) fuzzy(1,80000) == radial-ellipse.yaml radial-ellipse-ref.png diff --git a/gfx/wr/wrench/reftests/image/reftest.list b/gfx/wr/wrench/reftests/image/reftest.list index 11fb99dd56f5..12d23f58b16c 100644 --- a/gfx/wr/wrench/reftests/image/reftest.list +++ b/gfx/wr/wrench/reftests/image/reftest.list @@ -1,10 +1,15 @@ -== tile-size.yaml tile-size-ref.yaml -== very-big.yaml very-big-ref.yaml +# Some tests in this file skipped on debug Android because of panic, +# GL error 502 at tex_sub_image_3d_pbo. These are marked with skip_on(android) +# or skip_on(android,debug). Additionally, the ones marked skip_on(android) +# fail in opt builds. + +skip_on(android) == tile-size.yaml tile-size-ref.yaml +skip_on(android) == very-big.yaml very-big-ref.yaml == very-big-tile-size.yaml very-big-tile-size-ref.yaml -== tile-with-spacing.yaml tile-with-spacing-ref.yaml -fuzzy(1,331264) == tile-repeat-prim-or-decompose.yaml tile-repeat-prim-or-decompose-ref.yaml +skip_on(android,debug) == tile-with-spacing.yaml tile-with-spacing-ref.yaml +skip_on(android) fuzzy(1,331264) == tile-repeat-prim-or-decompose.yaml tile-repeat-prim-or-decompose-ref.yaml platform(linux,mac) options(allow-mipmaps) == downscale.yaml downscale.png -== segments.yaml segments.png +skip_on(android) == segments.yaml segments.png platform(linux,mac) == yuv.yaml yuv.png -== tiled-clip-chain.yaml tiled-clip-chain-ref.yaml -== tiled-complex-clip.yaml tiled-complex-clip-ref.yaml +skip_on(android) == tiled-clip-chain.yaml tiled-clip-chain-ref.yaml +skip_on(android) == tiled-complex-clip.yaml tiled-complex-clip-ref.yaml diff --git a/gfx/wr/wrench/reftests/invalidation/reftest.list b/gfx/wr/wrench/reftests/invalidation/reftest.list index 44c7e4b0b89e..5f6fb3aa0d46 100644 --- a/gfx/wr/wrench/reftests/invalidation/reftest.list +++ b/gfx/wr/wrench/reftests/invalidation/reftest.list @@ -1,2 +1,5 @@ # Test that a red -> green -> red rect correctly invalidates each frame -dirty([(50,50):256x256]) one-rounded-rect.yaml dirty([(50,50):256x256]) one-rounded-rect-green.yaml dirty([(50,50):256x256]) one-rounded-rect.yaml == one-rounded-rect.yaml +# Skipped on debug Android debug because of panic, +# GL error 502 at blit_framebuffer. It also fails on +# Android release. +skip_on(android) dirty([(50,50):256x256]) one-rounded-rect.yaml dirty([(50,50):256x256]) one-rounded-rect-green.yaml dirty([(50,50):256x256]) one-rounded-rect.yaml == one-rounded-rect.yaml diff --git a/gfx/wr/wrench/reftests/mask/reftest.list b/gfx/wr/wrench/reftests/mask/reftest.list index 402dc8a37b32..e6b5b1b69c57 100644 --- a/gfx/wr/wrench/reftests/mask/reftest.list +++ b/gfx/wr/wrench/reftests/mask/reftest.list @@ -1,7 +1,7 @@ == mask.yaml mask-ref.yaml -== mask-tiling.yaml mask-ref.yaml +skip_on(android) == mask-tiling.yaml mask-ref.yaml # Android: GL error 502 at tex_sub_image_3d_pbo, fails on opt == nested-mask.yaml nested-mask-ref.yaml -== nested-mask-tiling.yaml nested-mask-ref.yaml +skip_on(android) == nested-mask-tiling.yaml nested-mask-ref.yaml # Android: GL error 502 at tex_sub_image_3d_pbo, fails on opt != mask.yaml green.yaml == aligned-layer-rect.yaml aligned-layer-rect-ref.yaml == mask-transformed-to-empty-rect.yaml mask-transformed-to-empty-rect-ref.yaml @@ -10,7 +10,7 @@ platform(linux,mac) == rounded-corners.yaml rounded-corners.png platform(linux,mac) fuzzy(1,8750) color_targets(2) alpha_targets(1) == mask-atomicity.yaml mask-atomicity-ref.yaml platform(linux,mac) fuzzy(1,8750) == mask-atomicity-tiling.yaml mask-atomicity-ref.yaml platform(linux,mac) == mask-perspective.yaml mask-perspective.png -== fuzzy(1,6) mask-perspective-tiling.yaml mask-perspective.yaml +skip_on(android) == fuzzy(1,6) mask-perspective-tiling.yaml mask-perspective.yaml # Android: GL error 502 at tex_sub_image_3d_pbo, fails on opt platform(linux,mac) == checkerboard.yaml checkerboard.png -== checkerboard.yaml checkerboard-tiling.yaml +skip_on(android) == checkerboard.yaml checkerboard-tiling.yaml # Android: GL error 502 at blit_framebuffer, fails on opt == missing-mask.yaml missing-mask-ref.yaml diff --git a/gfx/wr/wrench/reftests/performance/reftest.list b/gfx/wr/wrench/reftests/performance/reftest.list index 50bbbb25dcbf..ced74d4abd0b 100644 --- a/gfx/wr/wrench/reftests/performance/reftest.list +++ b/gfx/wr/wrench/reftests/performance/reftest.list @@ -1 +1 @@ -== color_targets(1) alpha_targets(0) no-clip-mask.yaml no-clip-mask.png +skip_on(android) == color_targets(1) alpha_targets(0) no-clip-mask.yaml no-clip-mask.png # Too wide for Android diff --git a/gfx/wr/wrench/reftests/split/reftest.list b/gfx/wr/wrench/reftests/split/reftest.list index 0d8138da304b..5e65d6c229e7 100644 --- a/gfx/wr/wrench/reftests/split/reftest.list +++ b/gfx/wr/wrench/reftests/split/reftest.list @@ -10,7 +10,7 @@ fuzzy(35,200) == nested-coord-systems.yaml nested-coord-systems-ref.yaml == intermediate-2.yaml intermediate-1-ref.yaml == split-intersect1.yaml split-intersect1-ref.yaml == ordering.yaml ordering-ref.yaml -fuzzy(1,20) == near-plane.yaml near-plane.png +skip_on(android) fuzzy(1,20) == near-plane.yaml near-plane.png # Fails on Android # Note: on windows the image is rendered at a slightly different spot. # similarly, a lot of tests in "transform" are non-windows. TODO: investigate platform(linux,mac) fuzzy(1,20) == same-plane.yaml same-plane.png diff --git a/gfx/wr/wrench/reftests/text/reftest.list b/gfx/wr/wrench/reftests/text/reftest.list index 42568d108e5b..1ca34337dff1 100644 --- a/gfx/wr/wrench/reftests/text/reftest.list +++ b/gfx/wr/wrench/reftests/text/reftest.list @@ -6,7 +6,7 @@ != shadow-cover-1.yaml blank.yaml != shadow-cover-2.yaml blank.yaml -== shadow.yaml shadow-ref.yaml +skip_on(android) == shadow.yaml shadow-ref.yaml # Fails on Android == shadow-huge.yaml shadow-huge-ref.yaml != shadow-cover-1.yaml shadow-cover-2.yaml != shadow-many.yaml shadow.yaml @@ -14,22 +14,24 @@ != shadow-clipped-text.yaml blank.yaml != non-opaque.yaml non-opaque-notref.yaml == decorations.yaml decorations-ref.yaml -fuzzy(1,173) == decorations-suite.yaml decorations-suite.png +skip_on(android) fuzzy(1,173) == decorations-suite.yaml decorations-suite.png # Fails on Android == 1658.yaml 1658-ref.yaml -fuzzy(1,5) == split-batch.yaml split-batch-ref.yaml -== shadow-red.yaml shadow-red-ref.yaml -fuzzy(1,735) == shadow-grey.yaml shadow-grey-ref.yaml -fuzzy(1,663) == shadow-grey-transparent.yaml shadow-grey-ref.yaml +skip_on(android,debug) fuzzy(1,5) == split-batch.yaml split-batch-ref.yaml # Android: GL error 502 at blit_framebuffer +# Next 3 tests affected by bug 1548099 on Android +skip_on(android) == shadow-red.yaml shadow-red-ref.yaml +skip_on(android) fuzzy(1,735) == shadow-grey.yaml shadow-grey-ref.yaml +skip_on(android) fuzzy(1,663) == shadow-grey-transparent.yaml shadow-grey-ref.yaml == subtle-shadow.yaml subtle-shadow-ref.yaml == shadow-atomic.yaml shadow-atomic-ref.yaml == shadow-clip-rect.yaml shadow-atomic-ref.yaml fuzzy(1,1) platform(linux) == shadow-ordering.yaml shadow-ordering-ref.yaml -!= synthetic-bold.yaml synthetic-bold-not-ref.yaml -fuzzy(1,1786) options(disable-subpixel) == synthetic-bold-transparent.yaml synthetic-bold-transparent-ref.yaml -!= synthetic-bold-transparent.yaml synthetic-bold.yaml -!= synthetic-italics.yaml synthetic-italics-ref.yaml -!= synthetic-italics-custom.yaml synthetic-italics-ref.yaml -!= synthetic-italics-custom.yaml synthetic-italics.yaml +# Next 6 tests affected by bug 1548099 on Android +skip_on(android) != synthetic-bold.yaml synthetic-bold-not-ref.yaml +skip_on(android) fuzzy(1,1786) options(disable-subpixel) == synthetic-bold-transparent.yaml synthetic-bold-transparent-ref.yaml +skip_on(android) != synthetic-bold-transparent.yaml synthetic-bold.yaml +skip_on(android) != synthetic-italics.yaml synthetic-italics-ref.yaml +skip_on(android) != synthetic-italics-custom.yaml synthetic-italics-ref.yaml +skip_on(android) != synthetic-italics-custom.yaml synthetic-italics.yaml options(disable-aa) == ahem.yaml ahem-ref.yaml platform(linux) == isolated-text.yaml isolated-text.png platform(mac) skip_on(mac,>=10.14) fuzzy(3,67) == white-opacity.yaml white-opacity.png @@ -56,16 +58,16 @@ platform(linux) == writing-modes.yaml writing-modes-ref.yaml platform(linux) == blurred-shadow-local-clip-rect.yaml blurred-shadow-local-clip-rect-ref.png fuzzy(1,1) platform(linux) == two-shadows.yaml two-shadows.png == shadow-clip.yaml shadow-clip-ref.yaml -== shadow-fast-clip.yaml shadow-fast-clip-ref.yaml -== shadow-partial-glyph.yaml shadow-partial-glyph-ref.yaml +skip_on(android) == shadow-fast-clip.yaml shadow-fast-clip-ref.yaml # Fails on Android +skip_on(android) == shadow-partial-glyph.yaml shadow-partial-glyph-ref.yaml # Fails on Android fuzzy(1,107) platform(linux) == shadow-transforms.yaml shadow-transforms.png fuzzy(1,113) platform(linux) == raster-space.yaml raster-space.png -skip_on(mac,>=10.14) != allow-subpixel.yaml allow-subpixel-ref.yaml +skip_on(android,debug) skip_on(mac,>=10.14) != allow-subpixel.yaml allow-subpixel-ref.yaml # Android: GL error 502 at blit_framebuffer == bg-color.yaml bg-color-ref.yaml != large-glyphs.yaml blank.yaml == snap-text-offset.yaml snap-text-offset-ref.yaml == shadow-border.yaml shadow-solid-ref.yaml -== shadow-image.yaml shadow-solid-ref.yaml -options(disable-aa) == snap-clip.yaml snap-clip-ref.yaml +skip_on(android) == shadow-image.yaml shadow-solid-ref.yaml # Fails on Android +skip_on(android) options(disable-aa) == snap-clip.yaml snap-clip-ref.yaml # Fails on Android platform(linux) == perspective-clip.yaml perspective-clip.png options(disable-subpixel) == raster-space-snap.yaml raster-space-snap-ref.yaml diff --git a/gfx/wr/wrench/reftests/transforms/reftest.list b/gfx/wr/wrench/reftests/transforms/reftest.list index c369025ca755..e71d9e456990 100644 --- a/gfx/wr/wrench/reftests/transforms/reftest.list +++ b/gfx/wr/wrench/reftests/transforms/reftest.list @@ -1,7 +1,7 @@ platform(linux,mac) == local-clip.yaml local-clip.png platform(linux,mac) == rotated-clip.yaml rotated-clip.png platform(linux,mac) == rotated-clip-large.yaml rotated-clip-large.png -== image-rotated-clip.yaml image-rotated-clip.png +skip_on(android) == image-rotated-clip.yaml image-rotated-clip.png # Fails on Android # Something leaks the state: the test passes if only run `reftest reftests/transform` # but fails when all the tests are run platform(linux,mac) fuzzy(1,4) == rotated-image.yaml rotated-image.png @@ -23,7 +23,7 @@ platform(linux,mac) == nested-preserve-3d.yaml nested-preserve-3d.png platform(linux,mac) fuzzy(1,283) == near-plane-clip.yaml near-plane-clip.png platform(linux,mac) == perspective-mask.yaml perspective-mask.png rotate-clip.yaml rotate-clip-ref.yaml -clip-translate.yaml clip-translate-ref.yaml +skip_on(android) == clip-translate.yaml clip-translate-ref.yaml # Fails on Android platform(linux,mac) == perspective-clip.yaml perspective-clip.png platform(linux,mac) fuzzy(1,2) == perspective-clip-1.yaml perspective-clip-1.png platform(linux,mac) fuzzy(1,2) == perspective-shadow.yaml perspective-shadow.png diff --git a/intl/l10n/DocumentL10n.cpp b/intl/l10n/DocumentL10n.cpp index b036a38a3a79..790323ff7dc0 100644 --- a/intl/l10n/DocumentL10n.cpp +++ b/intl/l10n/DocumentL10n.cpp @@ -8,12 +8,14 @@ #include "js/JSON.h" // JS_ParseJSON #include "mozilla/dom/DocumentL10n.h" #include "mozilla/dom/DocumentL10nBinding.h" -#include "mozilla/dom/Element.h" #include "mozilla/dom/L10nUtilsBinding.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/PromiseNativeHandler.h" +#include "mozilla/dom/ScriptSettings.h" #include "mozilla/dom/l10n/DOMOverlays.h" +#include "mozilla/intl/LocaleService.h" #include "nsQueryObject.h" +#include "nsIScriptError.h" #include "nsISupports.h" #include "nsImportModule.h" #include "nsContentUtils.h" @@ -27,6 +29,8 @@ static const char* kObservedPrefs[] = {L10N_PSEUDO_PREF, INTL_UI_DIRECTION_PREF, nullptr}; +using namespace mozilla::intl; + namespace mozilla { namespace dom { @@ -51,8 +55,26 @@ void PromiseResolver::RejectedCallback(JSContext* aCx, PromiseResolver::~PromiseResolver() { mPromise = nullptr; } -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DocumentL10n, mDocument, mDOMLocalization, - mContentSink, mReady) +NS_IMPL_CYCLE_COLLECTION_CLASS(DocumentL10n) +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DocumentL10n) + tmp->DisconnectMutations(); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mMutations) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalization) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mContentSink) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mReady) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoots) + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_UNLINK_END +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DocumentL10n) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMutations) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalization) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContentSink) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReady) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoots) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END +NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(DocumentL10n) NS_IMPL_CYCLE_COLLECTING_ADDREF(DocumentL10n) NS_IMPL_CYCLE_COLLECTING_RELEASE(DocumentL10n) @@ -66,6 +88,7 @@ NS_INTERFACE_MAP_END DocumentL10n::DocumentL10n(Document* aDocument) : mDocument(aDocument), mState(DocumentL10nState::Initialized) { mContentSink = do_QueryInterface(aDocument->GetCurrentContentSink()); + mMutations = new mozilla::dom::l10n::Mutations(this); } DocumentL10n::~DocumentL10n() { @@ -75,15 +98,24 @@ DocumentL10n::~DocumentL10n() { } Preferences::RemoveObservers(this, kObservedPrefs); + + DisconnectMutations(); +} + +void DocumentL10n::DisconnectMutations() { + if (mMutations) { + mMutations->Disconnect(); + DisconnectRoots(); + } } bool DocumentL10n::Init(nsTArray& aResourceIds) { - nsCOMPtr jsm = - do_ImportModule("resource://gre/modules/DOMLocalization.jsm"); + nsCOMPtr jsm = + do_ImportModule("resource://gre/modules/Localization.jsm"); MOZ_RELEASE_ASSERT(jsm); - Unused << jsm->GetDOMLocalization(getter_AddRefs(mDOMLocalization)); - MOZ_RELEASE_ASSERT(mDOMLocalization); + Unused << jsm->GetLocalization(getter_AddRefs(mLocalization)); + MOZ_RELEASE_ASSERT(mLocalization); nsIGlobalObject* global = mDocument->GetScopeObject(); if (!global) { @@ -101,7 +133,7 @@ bool DocumentL10n::Init(nsTArray& aResourceIds) { // resources will be ready by the time the document // is ready for localization. uint32_t ret; - if (NS_FAILED(mDOMLocalization->AddResourceIds(aResourceIds, true, &ret))) { + if (NS_FAILED(mLocalization->AddResourceIds(aResourceIds, true, &ret))) { return false; } @@ -128,32 +160,19 @@ NS_IMETHODIMP DocumentL10n::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) { if (!strcmp(aTopic, INTL_APP_LOCALES_CHANGED)) { - if (mDOMLocalization) { - mDOMLocalization->OnChange(); - } + OnChange(); } else { MOZ_ASSERT(!strcmp("nsPref:changed", aTopic)); nsDependentString pref(aData); if (pref.EqualsLiteral(L10N_PSEUDO_PREF) || pref.EqualsLiteral(INTL_UI_DIRECTION_PREF)) { - if (mDOMLocalization) { - mDOMLocalization->OnChange(); - } + OnChange(); } } return NS_OK; } -void DocumentL10n::Destroy() { - if (mDOMLocalization) { - Element* elem = mDocument->GetDocumentElement(); - if (elem) { - mDOMLocalization->DisconnectRoot(elem); - } - } -} - JSObject* DocumentL10n::WrapObject(JSContext* aCx, JS::Handle aGivenProto) { return DocumentL10n_Binding::Wrap(aCx, this, aGivenProto); @@ -185,19 +204,19 @@ already_AddRefed DocumentL10n::MaybeWrapPromise( uint32_t DocumentL10n::AddResourceIds(nsTArray& aResourceIds) { uint32_t ret = 0; - mDOMLocalization->AddResourceIds(aResourceIds, false, &ret); + mLocalization->AddResourceIds(aResourceIds, false, &ret); return ret; } uint32_t DocumentL10n::RemoveResourceIds(nsTArray& aResourceIds) { // We need to guard against a scenario where the - // mDOMLocalization has been unlinked, but the elements + // mLocalization has been unlinked, but the elements // are only now removed from DOM. - if (!mDOMLocalization) { + if (!mLocalization) { return 0; } uint32_t ret = 0; - mDOMLocalization->RemoveResourceIds(aResourceIds, &ret); + mLocalization->RemoveResourceIds(aResourceIds, &ret); return ret; } @@ -215,7 +234,7 @@ already_AddRefed DocumentL10n::FormatMessages( } RefPtr promise; - aRv = mDOMLocalization->FormatMessages(jsKeys, getter_AddRefs(promise)); + aRv = mLocalization->FormatMessages(jsKeys, getter_AddRefs(promise)); if (aRv.Failed()) { return nullptr; } @@ -237,7 +256,7 @@ already_AddRefed DocumentL10n::FormatValues( } RefPtr promise; - aRv = mDOMLocalization->FormatValues(jsKeys, getter_AddRefs(promise)); + aRv = mLocalization->FormatValues(jsKeys, getter_AddRefs(promise)); if (aRv.Failed()) { return nullptr; } @@ -257,8 +276,7 @@ already_AddRefed DocumentL10n::FormatValue( } RefPtr promise; - nsresult rv = - mDOMLocalization->FormatValue(aId, args, getter_AddRefs(promise)); + nsresult rv = mLocalization->FormatValue(aId, args, getter_AddRefs(promise)); if (NS_FAILED(rv)) { aRv.Throw(rv); return nullptr; @@ -316,7 +334,9 @@ void DocumentL10n::GetAttributes(JSContext* aCx, Element& aElement, class LocalizationHandler : public PromiseNativeHandler { public: - explicit LocalizationHandler(nsINode* aNode) { mNode = aNode; }; + explicit LocalizationHandler(DocumentL10n* aDocumentL10n) { + mDocumentL10n = aDocumentL10n; + }; NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_CLASS(LocalizationHandler) @@ -331,8 +351,6 @@ class LocalizationHandler : public PromiseNativeHandler { JS::Handle aValue) override { ErrorResult rv; - RefPtr docL10n = mNode->OwnerDoc()->GetL10n(); - nsTArray l10nData; if (aValue.isObject()) { JS::ForOfIterator iter(aCx); @@ -375,12 +393,10 @@ class LocalizationHandler : public PromiseNativeHandler { return; } - if (docL10n) { - docL10n->PauseObserving(rv); - if (NS_WARN_IF(rv.Failed())) { - mReturnValuePromise->MaybeRejectWithUndefined(); - return; - } + mDocumentL10n->PauseObserving(rv); + if (NS_WARN_IF(rv.Failed())) { + mReturnValuePromise->MaybeRejectWithUndefined(); + return; } nsTArray errors; @@ -394,26 +410,15 @@ class LocalizationHandler : public PromiseNativeHandler { } } - if (docL10n) { - docL10n->ResumeObserving(rv); - if (NS_WARN_IF(rv.Failed())) { - mReturnValuePromise->MaybeRejectWithUndefined(); - return; - } + mDocumentL10n->ResumeObserving(rv); + if (NS_WARN_IF(rv.Failed())) { + mReturnValuePromise->MaybeRejectWithUndefined(); + return; } - nsTArray jsErrors; - SequenceRooter rooter(aCx, &jsErrors); - for (auto& error : errors) { - JS::RootedValue jsError(aCx); - if (!ToJSValue(aCx, error, &jsError)) { - mReturnValuePromise->MaybeRejectWithUndefined(); - return; - } - jsErrors.AppendElement(jsError); - } + DocumentL10n::ReportDOMOverlaysErrors(mDocumentL10n->GetDocument(), errors); - mReturnValuePromise->MaybeResolve(jsErrors); + mReturnValuePromise->MaybeResolveWithUndefined(); } virtual void RejectedCallback(JSContext* aCx, @@ -425,7 +430,7 @@ class LocalizationHandler : public PromiseNativeHandler { ~LocalizationHandler() = default; nsTArray> mElements; - RefPtr mNode; + RefPtr mDocumentL10n; RefPtr mReturnValuePromise; }; @@ -440,23 +445,28 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(LocalizationHandler) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(LocalizationHandler) NS_IMPL_CYCLE_COLLECTION_UNLINK(mElements) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mNode) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentL10n) NS_IMPL_CYCLE_COLLECTION_UNLINK(mReturnValuePromise) NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(LocalizationHandler) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElements) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNode) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentL10n) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReturnValuePromise) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END -already_AddRefed DocumentL10n::TranslateFragment(JSContext* aCx, - nsINode& aNode, +already_AddRefed DocumentL10n::TranslateFragment(nsINode& aNode, ErrorResult& aRv) { - Sequence l10nKeys; - SequenceRooter rooter(aCx, &l10nKeys); - RefPtr nativeHandler = new LocalizationHandler(&aNode); - nsTArray>& domElements = nativeHandler->Elements(); + Sequence> elements; + + GetTranslatables(aNode, elements, aRv); + + return TranslateElements(elements, aRv); +} + +void DocumentL10n::GetTranslatables(nsINode& aNode, + Sequence>& aElements, + ErrorResult& aRv) { nsIContent* node = aNode.IsContent() ? aNode.AsContent() : aNode.GetFirstChild(); for (; node; node = node->GetNextNode(&aNode)) { @@ -470,13 +480,42 @@ already_AddRefed DocumentL10n::TranslateFragment(JSContext* aCx, continue; } + if (!aElements.AppendElement(*domElement, fallible)) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + } +} + +already_AddRefed DocumentL10n::TranslateElements( + const Sequence>& aElements, ErrorResult& aRv) { + JS::RootingContext* rcx = RootingCx(); + Sequence l10nKeys; + SequenceRooter rooter(rcx, &l10nKeys); + RefPtr nativeHandler = new LocalizationHandler(this); + nsTArray>& domElements = nativeHandler->Elements(); + domElements.SetCapacity(aElements.Length()); + + nsIGlobalObject* global = mDocument->GetScopeObject(); + if (!global) { + return nullptr; + } + + AutoEntryScript aes(global, "DocumentL10n GetAttributes"); + JSContext* cx = aes.cx(); + + for (auto& domElement : aElements) { + if (!domElement->HasAttr(kNameSpaceID_None, nsGkAtoms::datal10nid)) { + continue; + } + L10nKey* key = l10nKeys.AppendElement(fallible); if (!key) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); return nullptr; } - GetAttributes(aCx, *domElement, *key, aRv); + GetAttributes(cx, *domElement, *key, aRv); if (aRv.Failed()) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); return nullptr; @@ -488,17 +527,12 @@ already_AddRefed DocumentL10n::TranslateFragment(JSContext* aCx, } } - nsIGlobalObject* global = mDocument->GetScopeObject(); - if (!global) { - return nullptr; - } - RefPtr promise = Promise::Create(global, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } - RefPtr callbackResult = FormatMessages(aCx, l10nKeys, aRv); + RefPtr callbackResult = FormatMessages(cx, l10nKeys, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } @@ -509,27 +543,98 @@ already_AddRefed DocumentL10n::TranslateFragment(JSContext* aCx, return MaybeWrapPromise(promise); } -already_AddRefed DocumentL10n::TranslateElements( - const Sequence>& aElements, ErrorResult& aRv) { - AutoTArray, 10> elements; - elements.SetCapacity(aElements.Length()); - for (auto& element : aElements) { - elements.AppendElement(element); +class L10nRootTranslationHandler final : public PromiseNativeHandler { + public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_CLASS(L10nRootTranslationHandler) + + explicit L10nRootTranslationHandler(Element* aRoot) : mRoot(aRoot) {} + + void ResolvedCallback(JSContext* aCx, JS::Handle aValue) override { + DocumentL10n::SetRootInfo(mRoot); } - RefPtr promise; - aRv = mDOMLocalization->TranslateElements(elements, getter_AddRefs(promise)); - if (aRv.Failed()) { - return nullptr; + + void RejectedCallback(JSContext* aCx, JS::Handle aValue) override { + } + + private: + ~L10nRootTranslationHandler() = default; + + RefPtr mRoot; +}; + +NS_IMPL_CYCLE_COLLECTION(L10nRootTranslationHandler, mRoot) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(L10nRootTranslationHandler) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(L10nRootTranslationHandler) +NS_IMPL_CYCLE_COLLECTING_RELEASE(L10nRootTranslationHandler) + +void DocumentL10n::TranslateRoots() { + ErrorResult rv; + + for (auto iter = mRoots.ConstIter(); !iter.Done(); iter.Next()) { + Element* root = iter.Get()->GetKey(); + + RefPtr promise = TranslateFragment(*root, rv); + RefPtr nativeHandler = + new L10nRootTranslationHandler(root); + promise->AppendNativeHandler(nativeHandler); } - return MaybeWrapPromise(promise); } void DocumentL10n::PauseObserving(ErrorResult& aRv) { - aRv = mDOMLocalization->PauseObserving(); + mMutations->PauseObserving(); } void DocumentL10n::ResumeObserving(ErrorResult& aRv) { - aRv = mDOMLocalization->ResumeObserving(); + mMutations->ResumeObserving(); +} + +/* static */ +void DocumentL10n::ReportDOMOverlaysErrors( + Document* aDocument, nsTArray& aErrors) { + nsAutoString msg; + + for (auto& error : aErrors) { + if (error.mCode.WasPassed()) { + msg = NS_LITERAL_STRING("[fluent-dom] "); + switch (error.mCode.Value()) { + case DOMOverlays_Binding::ERROR_FORBIDDEN_TYPE: + msg += NS_LITERAL_STRING("An element of forbidden type \"") + + error.mTranslatedElementName.Value() + + NS_LITERAL_STRING( + "\" was found in the translation. Only safe text-level " + "elements and elements with data-l10n-name are allowed."); + break; + case DOMOverlays_Binding::ERROR_NAMED_ELEMENT_MISSING: + msg += NS_LITERAL_STRING("An element named \"") + + error.mL10nName.Value() + + NS_LITERAL_STRING("\" wasn't found in the source."); + break; + case DOMOverlays_Binding::ERROR_NAMED_ELEMENT_TYPE_MISMATCH: + msg += NS_LITERAL_STRING("An element named \"") + + error.mL10nName.Value() + + NS_LITERAL_STRING( + "\" was found in the translation but its type ") + + error.mTranslatedElementName.Value() + + NS_LITERAL_STRING( + " didn't match the element found in the source ") + + error.mSourceElementName.Value() + NS_LITERAL_STRING("."); + break; + case DOMOverlays_Binding::ERROR_UNKNOWN: + default: + msg += NS_LITERAL_STRING( + "Unknown error happened while translation of an element."); + break; + } + nsContentUtils::ReportToConsoleNonLocalized( + msg, nsIScriptError::warningFlag, NS_LITERAL_CSTRING("DOM"), + aDocument); + } + } } class L10nReadyHandler final : public PromiseNativeHandler { @@ -542,12 +647,12 @@ class L10nReadyHandler final : public PromiseNativeHandler { void ResolvedCallback(JSContext* aCx, JS::Handle aValue) override { mDocumentL10n->InitialDocumentTranslationCompleted(); - mPromise->MaybeResolveWithClone(aCx, aValue); + mPromise->MaybeResolveWithUndefined(); } void RejectedCallback(JSContext* aCx, JS::Handle aValue) override { mDocumentL10n->InitialDocumentTranslationCompleted(); - mPromise->MaybeRejectWithClone(aCx, aValue); + mPromise->MaybeRejectWithUndefined(); } private: @@ -574,12 +679,18 @@ void DocumentL10n::TriggerInitialDocumentTranslation() { mState = DocumentL10nState::InitialTranslationTriggered; Element* elem = mDocument->GetDocumentElement(); - if (elem) { - mDOMLocalization->ConnectRoot(elem); + if (!elem) { + return; } - RefPtr promise; - mDOMLocalization->TranslateRoots(getter_AddRefs(promise)); + Sequence> elements; + ErrorResult rv; + + GetTranslatables(*elem, elements, rv); + + ConnectRoot(elem); + + RefPtr promise = TranslateElements(elements, rv); if (!promise) { return; } @@ -594,6 +705,11 @@ void DocumentL10n::InitialDocumentTranslationCompleted() { return; } + Element* documentElement = mDocument->GetDocumentElement(); + if (documentElement) { + SetRootInfo(documentElement); + } + mState = DocumentL10nState::InitialTranslationCompleted; mDocument->InitialDocumentTranslationCompleted(); @@ -607,5 +723,70 @@ void DocumentL10n::InitialDocumentTranslationCompleted() { Promise* DocumentL10n::Ready() { return mReady; } +void DocumentL10n::OnChange() { + if (mLocalization) { + mLocalization->OnChange(); + TranslateRoots(); + } +} + +void DocumentL10n::ConnectRoot(Element* aNode) { + nsCOMPtr global = aNode->GetOwnerGlobal(); + if (!global) { + return; + } + +#ifdef DEBUG + for (auto iter = mRoots.ConstIter(); !iter.Done(); iter.Next()) { + Element* root = iter.Get()->GetKey(); + + MOZ_ASSERT( + root != aNode && !root->Contains(aNode) && !aNode->Contains(root), + "Cannot add a root that overlaps with existing root."); + } +#endif + + mRoots.PutEntry(aNode); + + aNode->AddMutationObserverUnlessExists(mMutations); +} + +void DocumentL10n::DisconnectRoot(Element* aNode) { + if (mRoots.Contains(aNode)) { + aNode->RemoveMutationObserver(mMutations); + mRoots.RemoveEntry(aNode); + } +} + +void DocumentL10n::DisconnectRoots() { + for (auto iter = mRoots.ConstIter(); !iter.Done(); iter.Next()) { + Element* elem = iter.Get()->GetKey(); + + elem->RemoveMutationObserver(mMutations); + } + mRoots.Clear(); +} + +/* static */ +void DocumentL10n::SetRootInfo(Element* aElement) { + nsAutoCString primaryLocale; + LocaleService::GetInstance()->GetAppLocaleAsBCP47(primaryLocale); + aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::lang, + NS_ConvertUTF8toUTF16(primaryLocale), true); + + nsAutoString dir; + if (LocaleService::GetInstance()->IsAppLocaleRTL()) { + nsGkAtoms::rtl->ToString(dir); + } else { + nsGkAtoms::ltr->ToString(dir); + } + + uint32_t nameSpace = aElement->GetNameSpaceID(); + nsAtom* dirAtom = + nameSpace == kNameSpaceID_XUL ? nsGkAtoms::localedir : nsGkAtoms::dir; + + aElement->SetAttr(kNameSpaceID_None, dirAtom, dir, true); +} + } // namespace dom } // namespace mozilla diff --git a/intl/l10n/DocumentL10n.h b/intl/l10n/DocumentL10n.h index 37139a53e3bf..38568b8022a0 100644 --- a/intl/l10n/DocumentL10n.h +++ b/intl/l10n/DocumentL10n.h @@ -7,7 +7,7 @@ #ifndef mozilla_dom_DocumentL10n_h #define mozilla_dom_DocumentL10n_h -#include "mozIDOMLocalization.h" +#include "mozILocalization.h" #include "nsCycleCollectionParticipant.h" #include "nsIContentSink.h" #include "nsINode.h" @@ -19,14 +19,18 @@ #include "mozilla/ErrorResult.h" #include "mozilla/dom/BindingDeclarations.h" #include "mozilla/dom/Document.h" +#include "mozilla/dom/Element.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/PromiseNativeHandler.h" +#include "mozilla/dom/l10n/Mutations.h" +#include "mozilla/dom/DOMOverlaysBinding.h" namespace mozilla { namespace dom { -class Document; -class Element; +namespace l10n { +class Mutations; +} struct L10nKey; class PromiseResolver final : public PromiseNativeHandler { @@ -56,7 +60,7 @@ enum class DocumentL10nState { * resource is added to the document. * * Once initialized, DocumentL10n relays all API methods to an - * instance of mozIDOMLocalization and maintains a single promise + * instance of mozILocalization and maintains a single promise * which gets resolved the first time the document gets translated. */ class DocumentL10n final : public nsIObserver, @@ -71,7 +75,6 @@ class DocumentL10n final : public nsIObserver, public: explicit DocumentL10n(Document* aDocument); bool Init(nsTArray& aResourceIds); - void Destroy(); protected: virtual ~DocumentL10n(); @@ -79,11 +82,14 @@ class DocumentL10n final : public nsIObserver, RefPtr mDocument; RefPtr mReady; DocumentL10nState mState; - nsCOMPtr mDOMLocalization; + nsCOMPtr mLocalization; nsCOMPtr mContentSink; + RefPtr mMutations; + nsTHashtable> mRoots; already_AddRefed MaybeWrapPromise(Promise* aPromise); void RegisterObservers(); + void DisconnectMutations(); public: Document* GetParentObject() const { return mDocument; }; @@ -121,19 +127,46 @@ class DocumentL10n final : public nsIObserver, void GetAttributes(JSContext* aCx, Element& aElement, L10nKey& aResult, ErrorResult& aRv); - already_AddRefed TranslateFragment(JSContext* aCx, nsINode& aNode, - ErrorResult& aRv); + already_AddRefed TranslateFragment(nsINode& aNode, ErrorResult& aRv); + + void GetTranslatables(nsINode& aNode, + Sequence>& aElements, + ErrorResult& aRv); + already_AddRefed TranslateElements( const Sequence>& aElements, ErrorResult& aRv); void PauseObserving(ErrorResult& aRv); void ResumeObserving(ErrorResult& aRv); + static void ReportDOMOverlaysErrors( + Document* aDocument, nsTArray& aErrors); Promise* Ready(); + /** + * Add node to nodes observed for localization + * related changes. + */ + void ConnectRoot(Element* aNode); + + /** + * Remove node from nodes observed for localization + * related changes. + */ + void DisconnectRoot(Element* aNode); + void TriggerInitialDocumentTranslation(); void InitialDocumentTranslationCompleted(); + + Document* GetDocument() { return mDocument; }; + + void OnChange(); + static void SetRootInfo(Element* aElement); + + protected: + void DisconnectRoots(); + void TranslateRoots(); }; } // namespace dom diff --git a/intl/l10n/Localization.jsm b/intl/l10n/Localization.jsm index 24a2f4df67a2..91ac999aee79 100644 --- a/intl/l10n/Localization.jsm +++ b/intl/l10n/Localization.jsm @@ -582,6 +582,12 @@ function keysFromBundle(method, bundle, keys, translations) { return missingIds; } +/** + * Helper function which allows us to construct a new + * Localization from DocumentL10n. + */ +var getLocalization = () => new Localization(); + this.Localization = Localization; this.LocalizationSync = LocalizationSync; -var EXPORTED_SYMBOLS = ["Localization", "LocalizationSync"]; +var EXPORTED_SYMBOLS = ["Localization", "LocalizationSync", "getLocalization"]; diff --git a/intl/l10n/moz.build b/intl/l10n/moz.build index 205acc6d8af5..59def9491683 100644 --- a/intl/l10n/moz.build +++ b/intl/l10n/moz.build @@ -17,6 +17,7 @@ TESTING_JS_MODULES += [ XPIDL_SOURCES += [ 'mozIDOMLocalization.idl', + 'mozILocalization.idl', ] XPIDL_MODULE = 'locale' diff --git a/intl/l10n/mozILocalization.idl b/intl/l10n/mozILocalization.idl new file mode 100644 index 000000000000..d5c5266a3bb1 --- /dev/null +++ b/intl/l10n/mozILocalization.idl @@ -0,0 +1,24 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +[scriptable, uuid(7d468600-551f-4fe0-98c9-92a53b63ec8d)] +interface mozILocalization : nsISupports +{ + unsigned long addResourceIds(in Array resourceIds, in bool aEager); + unsigned long removeResourceIds(in Array resourceIds); + void onChange(); + + Promise formatMessages(in Array aKeys); + Promise formatValues(in Array aKeys); + Promise formatValue(in AString aId, [optional] in jsval aArgs); +}; + +[scriptable, uuid(96632d26-1422-12e9-b1ce-9bb586acd241)] +interface mozILocalizationJSM : nsISupports +{ + mozILocalization getLocalization(); +}; diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 2cedbbd48853..52441b211160 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -2478,8 +2478,7 @@ bool BytecodeEmitter::emitFunctionScript(FunctionNode* funNode, parser->errorReporter(), funbox); MOZ_ASSERT((fieldInitializers_.valid) == - (funbox->kind() == - JSFunction::FunctionKind::ClassConstructor)); + (funbox->kind() == JSFunction::FunctionKind::ClassConstructor)); setScriptStartOffsetIfUnset(paramsBody->pn_pos.begin); @@ -2752,7 +2751,7 @@ bool BytecodeEmitter::emitSetOrInitializeDestructuring( if (!eoe.skipObjAndKeyAndRhs()) { return false; } - if (!eoe.emitAssignment()) { + if (!eoe.emitAssignment(ElemOpEmitter::EmitSetFunctionName::No)) { // [stack] VAL return false; } @@ -4045,19 +4044,28 @@ bool BytecodeEmitter::emitSingleDeclaration(ListNode* declList, NameNode* decl, return true; } -static bool EmitAssignmentRhs(BytecodeEmitter* bce, ParseNode* rhs, - uint8_t offset) { - // If there is a RHS tree, emit the tree. - if (rhs) { - return bce->emitTree(rhs); +bool BytecodeEmitter::emitAssignmentRhs(ParseNode* rhs, + HandleAtom anonFunctionName, + bool* emitSetFunName) { + *emitSetFunName = false; + if (rhs->isDirectRHSAnonFunction()) { + if (anonFunctionName) { + return emitAnonymousFunctionWithName(rhs, anonFunctionName); + } + // If anonFunctionName is null, that means we don't have a compiletime + // name, and should emit JSOP_SETFUNNAME (which happens later). + *emitSetFunName = true; } + return emitTree(rhs); +} - // Otherwise the RHS value to assign is already on the stack, i.e., the - // next enumeration value in a for-in or for-of loop. Depending on how - // many other values have been pushed on the stack, we need to get the - // already-pushed RHS value. - if (offset != 1 && !bce->emit2(JSOP_PICK, offset - 1)) { - return false; +// The RHS value to assign is already on the stack, i.e., the next enumeration +// value in a for-in or for-of loop. Offset is the location in the stack of the +// already-emitted rhs. If we emitted a BIND[G]NAME, then the scope is on the +// top of the stack and we need to dig one deeper to get the right RHS value. +bool BytecodeEmitter::emitAssignmentRhs(uint8_t offset) { + if (offset != 1) { + return emit2(JSOP_PICK, offset - 1); } return true; @@ -4103,6 +4111,8 @@ bool BytecodeEmitter::emitAssignmentOrInit(ParseNodeKind kind, ParseNode* lhs, JSOp compoundOp = CompoundAssignmentParseNodeKindToJSOp(kind); bool isCompound = compoundOp != JSOP_NOP; bool isInit = kind == ParseNodeKind::InitExpr; + ElemOpEmitter::EmitSetFunctionName emitSetFunName = + ElemOpEmitter::EmitSetFunctionName::No; MOZ_ASSERT_IF(isInit, lhs->isKind(ParseNodeKind::DotExpr) || lhs->isKind(ParseNodeKind::ElemExpr)); @@ -4120,18 +4130,19 @@ bool BytecodeEmitter::emitAssignmentOrInit(ParseNodeKind kind, ParseNode* lhs, return false; } - if (rhs && rhs->isDirectRHSAnonFunction()) { - MOZ_ASSERT(!nameNode->isInParens()); - MOZ_ASSERT(!isCompound); - if (!emitAnonymousFunctionWithName(rhs, name)) { + if (rhs) { + bool emitSetFunctionName; + if (!emitAssignmentRhs(rhs, name, &emitSetFunctionName)) { // [stack] ENV? VAL? RHS return false; } + // We should always have the name, so we should never need to emit + // JSOP_SETFUNNAME. + MOZ_ASSERT(!emitSetFunctionName); } else { - // Emit the RHS. If we emitted a BIND[G]NAME, then the scope is on - // the top of the stack and we need to pick the right RHS value. uint8_t offset = noe.emittedBindOp() ? 2 : 1; - if (!EmitAssignmentRhs(this, rhs, offset)) { + // Assumption: Things with pre-emitted RHS values never need to be named. + if (!emitAssignmentRhs(offset)) { // [stack] ENV? VAL? RHS return false; } @@ -4158,6 +4169,7 @@ bool BytecodeEmitter::emitAssignmentOrInit(ParseNodeKind kind, ParseNode* lhs, // Deal with non-name assignments. uint8_t offset = 1; + RootedAtom anonFunctionName(cx); switch (lhs->getKind()) { case ParseNodeKind::DotExpr: { PropertyAccess* prop = &lhs->as(); @@ -4171,6 +4183,7 @@ bool BytecodeEmitter::emitAssignmentOrInit(ParseNodeKind kind, ParseNode* lhs, if (!poe->prepareForObj()) { return false; } + anonFunctionName = &prop->name(); if (isSuper) { UnaryNode* base = &prop->expression().as(); if (!emitGetThisForSuperBase(base)) { @@ -4301,9 +4314,29 @@ bool BytecodeEmitter::emitAssignmentOrInit(ParseNodeKind kind, ParseNode* lhs, break; } - if (!EmitAssignmentRhs(this, rhs, offset)) { - // [stack] ... VAL? RHS - return false; + // Purpose of anonFunctionName: + // In normal property assignments (`obj.x = function(){}`), the anonymous + // function does not have a computed name, and rhs->isDirectRHSAnonFunction() + // will be false (and anonFunctionName will not be used). However, in field + // initializers (`class C { x = function(){} }`), field initialization is + // implemented via a property or elem assignment (where we are now), and + // rhs->isDirectRHSAnonFunction() is set - so we'll assign the name of the + // function. + if (rhs) { + bool emitSetFunctionName; + if (!emitAssignmentRhs(rhs, anonFunctionName, &emitSetFunctionName)) { + // [stack] ... VAL? RHS + return false; + } + emitSetFunName = emitSetFunctionName + ? ElemOpEmitter::EmitSetFunctionName::Yes + : ElemOpEmitter::EmitSetFunctionName::No; + } else { + // Assumption: Things with pre-emitted RHS values never need to be named. + if (!emitAssignmentRhs(offset)) { + // [stack] ... VAL? RHS + return false; + } } /* If += etc., emit the binary operator with a source note. */ @@ -4334,7 +4367,9 @@ bool BytecodeEmitter::emitAssignmentOrInit(ParseNodeKind kind, ParseNode* lhs, // We threw above, so nothing to do here. break; case ParseNodeKind::ElemExpr: { - if (!eoe->emitAssignment()) { + MOZ_ASSERT((!anonFunctionName && rhs && rhs->isDirectRHSAnonFunction()) == + (emitSetFunName == ElemOpEmitter::EmitSetFunctionName::Yes)); + if (!eoe->emitAssignment(emitSetFunName)) { // [stack] VAL return false; } @@ -5632,8 +5667,7 @@ MOZ_NEVER_INLINE bool BytecodeEmitter::emitFunction( RootedFunction fun(cx, funbox->function()); MOZ_ASSERT((classContentsIfConstructor != nullptr) == - (funbox->kind() == - JSFunction::FunctionKind::ClassConstructor)); + (funbox->kind() == JSFunction::FunctionKind::ClassConstructor)); // [stack] @@ -8080,8 +8114,7 @@ const FieldInitializers& BytecodeEmitter::findFieldInitializersForCall() { for (BytecodeEmitter* current = this; current; current = current->parent) { if (current->sc->isFunctionBox()) { FunctionBox* box = current->sc->asFunctionBox(); - if (box->kind() == - JSFunction::FunctionKind::ClassConstructor) { + if (box->kind() == JSFunction::FunctionKind::ClassConstructor) { const FieldInitializers& fieldInitializers = current->getFieldInitializers(); MOZ_ASSERT(fieldInitializers.valid); diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 3a76e901e1ec..f128c0047c68 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -507,6 +507,12 @@ struct MOZ_STACK_CLASS BytecodeEmitter { MOZ_MUST_USE bool emitDeclarationList(ListNode* declList); MOZ_MUST_USE bool emitSingleDeclaration(ListNode* declList, NameNode* decl, ParseNode* initializer); + // emitSetFunName should be of type ElemOpEmitter::EmitSetFunctionName, but + // that's not possible due to C++ declaration order. + MOZ_MUST_USE bool emitAssignmentRhs(ParseNode* rhs, + HandleAtom anonFunctionName, + bool* emitSetFunName); + MOZ_MUST_USE bool emitAssignmentRhs(uint8_t offset); MOZ_MUST_USE bool emitNewInit(); MOZ_MUST_USE bool emitSingletonInitialiser(ListNode* objOrArray); diff --git a/js/src/frontend/ElemOpEmitter.cpp b/js/src/frontend/ElemOpEmitter.cpp index c6a3700c1c21..01f76f48a585 100644 --- a/js/src/frontend/ElemOpEmitter.cpp +++ b/js/src/frontend/ElemOpEmitter.cpp @@ -195,10 +195,24 @@ bool ElemOpEmitter::emitDelete() { return true; } -bool ElemOpEmitter::emitAssignment() { +bool ElemOpEmitter::emitAssignment(EmitSetFunctionName emitSetFunName) { MOZ_ASSERT(isSimpleAssignment() || isPropInit() || isCompoundAssignment()); MOZ_ASSERT(state_ == State::Rhs); + if (emitSetFunName == EmitSetFunctionName::Yes) { + // JSOP_*SETELEM_SUPER has a different stack ordering. + MOZ_ASSERT(!isSuper()); + // [stack] obj, id, val + if (!bce_->emitDupAt(1)) { + // [stack] obj, id, val, id + return false; + } + if (!bce_->emit2(JSOP_SETFUNNAME, uint8_t(FunctionPrefixKind::None))) { + // [stack] obj, id, val + return false; + } + } + MOZ_ASSERT_IF(isPropInit(), !isSuper()); JSOp setOp = diff --git a/js/src/frontend/ElemOpEmitter.h b/js/src/frontend/ElemOpEmitter.h index 95e62e240bc7..944eef555bf8 100644 --- a/js/src/frontend/ElemOpEmitter.h +++ b/js/src/frontend/ElemOpEmitter.h @@ -256,7 +256,8 @@ class MOZ_STACK_CLASS ElemOpEmitter { MOZ_MUST_USE bool emitDelete(); - MOZ_MUST_USE bool emitAssignment(); + enum class EmitSetFunctionName : bool { No, Yes }; + MOZ_MUST_USE bool emitAssignment(EmitSetFunctionName emitSetFunName); MOZ_MUST_USE bool emitIncDec(); }; diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index 0057bd1d21e0..477a923c9be6 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -755,14 +755,12 @@ class FullParseHandler { inline MOZ_MUST_USE bool setLastFunctionFormalParameterDefault( FunctionNodeType funNode, Node defaultValue); - private: void checkAndSetIsDirectRHSAnonFunction(Node pn) { if (IsAnonymousFunctionDefinition(pn)) { pn->setDirectRHSAnonFunction(true); } } - public: FunctionNodeType newFunction(FunctionSyntaxKind syntaxKind, const TokenPos& pos) { return new_(syntaxKind, pos); diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 7e3dd6818a4f..69ee03b1e66f 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -7385,6 +7385,9 @@ GeneralParser::fieldInitializerOpt( if (!initializerExpr) { return null(); } + + handler_.checkAndSetIsDirectRHSAnonFunction(initializerExpr); + wholeInitializerPos = pos(); wholeInitializerPos.begin = firstTokenPos.begin; } else { diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index 1491173dcd9c..66145c3501c7 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -491,6 +491,8 @@ class SyntaxParseHandler { return true; } + void checkAndSetIsDirectRHSAnonFunction(Node pn) {} + FunctionNodeType newFunction(FunctionSyntaxKind syntaxKind, const TokenPos& pos) { switch (syntaxKind) { diff --git a/js/src/jit-test/tests/fields/bug1552875.js b/js/src/jit-test/tests/fields/bug1552875.js new file mode 100644 index 000000000000..42b15ad9451e --- /dev/null +++ b/js/src/jit-test/tests/fields/bug1552875.js @@ -0,0 +1,15 @@ +// |jit-test| --enable-experimental-fields + +class C { + x = function(){}; + 0 = function(){}; + ["y" + 0] = function(){}; +} + +let c = new C(); +assertEq(c["x"].name, "x"); +assertEq(c[0].name, "0"); +assertEq(c["y0"].name, "y0"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index 0e963a5548c1..45a4fc6b289e 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -917,7 +917,7 @@ bool DebugEpilogue(JSContext* cx, BaselineFrame* frame, jsbytecode* pc, EnvironmentIter ei(cx, frame, pc); UnwindAllEnvironmentsInFrame(cx, ei); JSScript* script = frame->script(); - frame->setOverridePc(script->lastPC()); + frame->setOverridePc(script->offsetToPC(0)); if (!ok) { // Pop this frame by updating packedExitFP, so that the exception diff --git a/js/src/jit/arm64/Assembler-arm64.cpp b/js/src/jit/arm64/Assembler-arm64.cpp index 970224e207a7..e1a5a908fcdf 100644 --- a/js/src/jit/arm64/Assembler-arm64.cpp +++ b/js/src/jit/arm64/Assembler-arm64.cpp @@ -391,7 +391,7 @@ void Assembler::PatchWrite_NearCall(CodeLocationLabel start, ptrdiff_t relTarget = (Instruction*)toCall.raw() - dest; ptrdiff_t relTarget00 = relTarget >> 2; MOZ_RELEASE_ASSERT((relTarget & 0x3) == 0); - MOZ_RELEASE_ASSERT(vixl::is_int26(relTarget00)); + MOZ_RELEASE_ASSERT(vixl::IsInt26(relTarget00)); // printf("patching %p with call to %p\n", start.raw(), toCall.raw()); bl(dest, relTarget00); @@ -420,7 +420,7 @@ void Assembler::ToggleToJmp(CodeLocationLabel inst_) { // Refer to instruction layout in ToggleToCmp(). int imm19 = (int)i->Bits(23, 5); - MOZ_ASSERT(vixl::is_int19(imm19)); + MOZ_ASSERT(vixl::IsInt19(imm19)); b(i, imm19, Always); @@ -434,7 +434,7 @@ void Assembler::ToggleToCmp(CodeLocationLabel inst_) { int imm19 = i->ImmCondBranch(); // bit 23 is reserved, and the simulator throws an assertion when this happens // It'll be messy to decode, but we can steal bit 30 or bit 31. - MOZ_ASSERT(vixl::is_int18(imm19)); + MOZ_ASSERT(vixl::IsInt18(imm19)); // 31 - 64-bit if set, 32-bit if unset. (OK!) // 30 - sub if set, add if unset. (OK!) @@ -497,7 +497,7 @@ void Assembler::ToggleCall(CodeLocationLabel inst_, bool enabled) { // ldr x17, [pc, offset] // blr x17 int32_t offset = (int)load->ImmPCRawOffset(); - MOZ_ASSERT(vixl::is_int19(offset)); + MOZ_ASSERT(vixl::IsInt19(offset)); ldr(load, ScratchReg2_64, int32_t(offset)); blr(call, ScratchReg2_64); } diff --git a/js/src/jit/arm64/MacroAssembler-arm64.cpp b/js/src/jit/arm64/MacroAssembler-arm64.cpp index f0fc4af6f000..a18e6a3b66ce 100644 --- a/js/src/jit/arm64/MacroAssembler-arm64.cpp +++ b/js/src/jit/arm64/MacroAssembler-arm64.cpp @@ -675,7 +675,7 @@ void MacroAssembler::patchCall(uint32_t callerOffset, uint32_t calleeOffset) { ptrdiff_t relTarget = (int)calleeOffset - ((int)callerOffset - 4); ptrdiff_t relTarget00 = relTarget >> 2; MOZ_RELEASE_ASSERT((relTarget & 0x3) == 0); - MOZ_RELEASE_ASSERT(vixl::is_int26(relTarget00)); + MOZ_RELEASE_ASSERT(vixl::IsInt26(relTarget00)); bl(inst, relTarget00); AutoFlushICache::flush(uintptr_t(inst), 4); } diff --git a/js/src/jit/arm64/vixl/Assembler-vixl.cpp b/js/src/jit/arm64/vixl/Assembler-vixl.cpp index 5e60d7591a19..f3a876112e69 100644 --- a/js/src/jit/arm64/vixl/Assembler-vixl.cpp +++ b/js/src/jit/arm64/vixl/Assembler-vixl.cpp @@ -3083,11 +3083,11 @@ void Assembler::movi(const VRegister& vd, Emit(q | NEONModImmOp(1) | NEONModifiedImmediate_MOVI | ImmNEONabcdefgh(imm8) | NEONCmode(0xe) | Rd(vd)); } else if (shift == LSL) { - VIXL_ASSERT(is_uint8(imm)); + VIXL_ASSERT(IsUint8(imm)); NEONModifiedImmShiftLsl(vd, static_cast(imm), shift_amount, NEONModifiedImmediate_MOVI); } else { - VIXL_ASSERT(is_uint8(imm)); + VIXL_ASSERT(IsUint8(imm)); NEONModifiedImmShiftMsl(vd, static_cast(imm), shift_amount, NEONModifiedImmediate_MOVI); } @@ -4198,7 +4198,7 @@ void Assembler::uqrshrn2(const VRegister& vd, uint32_t Assembler::FP32ToImm8(float imm) { VIXL_ASSERT(IsImmFP32(imm)); // bits: aBbb.bbbc.defg.h000.0000.0000.0000.0000 - uint32_t bits = float_to_rawbits(imm); + uint32_t bits = FloatToRawbits(imm); // bit7: a000.0000 uint32_t bit7 = ((bits >> 31) & 0x1) << 7; // bit6: 0b00.0000 @@ -4219,7 +4219,7 @@ uint32_t Assembler::FP64ToImm8(double imm) { VIXL_ASSERT(IsImmFP64(imm)); // bits: aBbb.bbbb.bbcd.efgh.0000.0000.0000.0000 // 0000.0000.0000.0000.0000.0000.0000.0000 - uint64_t bits = double_to_rawbits(imm); + uint64_t bits = DoubleToRawbits(imm); // bit7: a000.0000 uint64_t bit7 = ((bits >> 63) & 0x1) << 7; // bit6: 0b00.0000 @@ -4276,7 +4276,7 @@ void Assembler::MoveWide(const Register& rd, } } - VIXL_ASSERT(is_uint16(imm)); + VIXL_ASSERT(IsUint16(imm)); Emit(SF(rd) | MoveWideImmediateFixed | mov_op | Rd(rd) | ImmMoveWide(imm) | ShiftMoveWide(shift)); @@ -4333,13 +4333,13 @@ void Assembler::AddSubWithCarry(const Register& rd, void Assembler::hlt(int code) { - VIXL_ASSERT(is_uint16(code)); + VIXL_ASSERT(IsUint16(code)); Emit(HLT | ImmException(code)); } void Assembler::brk(int code) { - VIXL_ASSERT(is_uint16(code)); + VIXL_ASSERT(IsUint16(code)); Emit(BRK | ImmException(code)); } @@ -4403,7 +4403,7 @@ void Assembler::NEONModifiedImmShiftLsl(const VRegister& vd, vd.Is2S() || vd.Is4S()); VIXL_ASSERT((left_shift == 0) || (left_shift == 8) || (left_shift == 16) || (left_shift == 24)); - VIXL_ASSERT(is_uint8(imm8)); + VIXL_ASSERT(IsUint8(imm8)); int cmode_1, cmode_2, cmode_3; if (vd.Is8B() || vd.Is16B()) { @@ -4434,7 +4434,7 @@ void Assembler::NEONModifiedImmShiftMsl(const VRegister& vd, NEONModifiedImmediateOp op) { VIXL_ASSERT(vd.Is2S() || vd.Is4S()); VIXL_ASSERT((shift_amount == 8) || (shift_amount == 16)); - VIXL_ASSERT(is_uint8(imm8)); + VIXL_ASSERT(IsUint8(imm8)); int cmode_0 = (shift_amount >> 4) & 1; int cmode = 0xc | cmode_0; @@ -4601,20 +4601,20 @@ void Assembler::Prefetch(PrefetchOperation op, bool Assembler::IsImmAddSub(int64_t immediate) { - return is_uint12(immediate) || - (is_uint12(immediate >> 12) && ((immediate & 0xfff) == 0)); + return IsUint12(immediate) || + (IsUint12(immediate >> 12) && ((immediate & 0xfff) == 0)); } bool Assembler::IsImmConditionalCompare(int64_t immediate) { - return is_uint5(immediate); + return IsUint5(immediate); } bool Assembler::IsImmFP32(float imm) { // Valid values will have the form: // aBbb.bbbc.defg.h000.0000.0000.0000.0000 - uint32_t bits = float_to_rawbits(imm); + uint32_t bits = FloatToRawbits(imm); // bits[19..0] are cleared. if ((bits & 0x7ffff) != 0) { return false; @@ -4639,7 +4639,7 @@ bool Assembler::IsImmFP64(double imm) { // Valid values will have the form: // aBbb.bbbb.bbcd.efgh.0000.0000.0000.0000 // 0000.0000.0000.0000.0000.0000.0000.0000 - uint64_t bits = double_to_rawbits(imm); + uint64_t bits = DoubleToRawbits(imm); // bits[47..0] are cleared. if ((bits & 0x0000ffffffffffff) != 0) { return false; @@ -4664,7 +4664,7 @@ bool Assembler::IsImmLSPair(int64_t offset, unsigned access_size) { VIXL_ASSERT(access_size <= kQRegSizeInBytesLog2); bool offset_is_size_multiple = (((offset >> access_size) << access_size) == offset); - return offset_is_size_multiple && is_int7(offset >> access_size); + return offset_is_size_multiple && IsInt7(offset >> access_size); } @@ -4672,12 +4672,12 @@ bool Assembler::IsImmLSScaled(int64_t offset, unsigned access_size) { VIXL_ASSERT(access_size <= kQRegSizeInBytesLog2); bool offset_is_size_multiple = (((offset >> access_size) << access_size) == offset); - return offset_is_size_multiple && is_uint12(offset >> access_size); + return offset_is_size_multiple && IsUint12(offset >> access_size); } bool Assembler::IsImmLSUnscaled(int64_t offset) { - return is_int9(offset); + return IsInt9(offset); } diff --git a/js/src/jit/arm64/vixl/Assembler-vixl.h b/js/src/jit/arm64/vixl/Assembler-vixl.h index 0ff0638b526b..8a8542b7967d 100644 --- a/js/src/jit/arm64/vixl/Assembler-vixl.h +++ b/js/src/jit/arm64/vixl/Assembler-vixl.h @@ -3628,8 +3628,8 @@ class Assembler : public MozBaseAssembler { // PC-relative address encoding. static Instr ImmPCRelAddress(int imm21) { - VIXL_ASSERT(is_int21(imm21)); - Instr imm = static_cast(truncate_to_int21(imm21)); + VIXL_ASSERT(IsInt21(imm21)); + Instr imm = static_cast(TruncateToUint21(imm21)); Instr immhi = (imm >> ImmPCRelLo_width) << ImmPCRelHi_offset; Instr immlo = imm << ImmPCRelLo_offset; return (immhi & ImmPCRelHi_mask) | (immlo & ImmPCRelLo_mask); @@ -3637,27 +3637,27 @@ class Assembler : public MozBaseAssembler { // Branch encoding. static Instr ImmUncondBranch(int imm26) { - VIXL_ASSERT(is_int26(imm26)); - return truncate_to_int26(imm26) << ImmUncondBranch_offset; + VIXL_ASSERT(IsInt26(imm26)); + return TruncateToUint26(imm26) << ImmUncondBranch_offset; } static Instr ImmCondBranch(int imm19) { - VIXL_ASSERT(is_int19(imm19)); - return truncate_to_int19(imm19) << ImmCondBranch_offset; + VIXL_ASSERT(IsInt19(imm19)); + return TruncateToUint19(imm19) << ImmCondBranch_offset; } static Instr ImmCmpBranch(int imm19) { - VIXL_ASSERT(is_int19(imm19)); - return truncate_to_int19(imm19) << ImmCmpBranch_offset; + VIXL_ASSERT(IsInt19(imm19)); + return TruncateToUint19(imm19) << ImmCmpBranch_offset; } static Instr ImmTestBranch(int imm14) { - VIXL_ASSERT(is_int14(imm14)); - return truncate_to_int14(imm14) << ImmTestBranch_offset; + VIXL_ASSERT(IsInt14(imm14)); + return TruncateToUint14(imm14) << ImmTestBranch_offset; } static Instr ImmTestBranchBit(unsigned bit_pos) { - VIXL_ASSERT(is_uint6(bit_pos)); + VIXL_ASSERT(IsUint6(bit_pos)); // Subtract five from the shift offset, as we need bit 5 from bit_pos. unsigned b5 = bit_pos << (ImmTestBranchBit5_offset - 5); unsigned b40 = bit_pos << ImmTestBranchBit40_offset; @@ -3673,7 +3673,7 @@ class Assembler : public MozBaseAssembler { static Instr ImmAddSub(int imm) { VIXL_ASSERT(IsImmAddSub(imm)); - if (is_uint12(imm)) { // No shift required. + if (IsUint12(imm)) { // No shift required. imm <<= ImmAddSub_offset; } else { imm = ((imm >> 12) << ImmAddSub_offset) | (1 << ShiftAddSub_offset); @@ -3682,39 +3682,39 @@ class Assembler : public MozBaseAssembler { } static Instr ImmS(unsigned imms, unsigned reg_size) { - VIXL_ASSERT(((reg_size == kXRegSize) && is_uint6(imms)) || - ((reg_size == kWRegSize) && is_uint5(imms))); + VIXL_ASSERT(((reg_size == kXRegSize) && IsUint6(imms)) || + ((reg_size == kWRegSize) && IsUint5(imms))); USE(reg_size); return imms << ImmS_offset; } static Instr ImmR(unsigned immr, unsigned reg_size) { - VIXL_ASSERT(((reg_size == kXRegSize) && is_uint6(immr)) || - ((reg_size == kWRegSize) && is_uint5(immr))); + VIXL_ASSERT(((reg_size == kXRegSize) && IsUint6(immr)) || + ((reg_size == kWRegSize) && IsUint5(immr))); USE(reg_size); - VIXL_ASSERT(is_uint6(immr)); + VIXL_ASSERT(IsUint6(immr)); return immr << ImmR_offset; } static Instr ImmSetBits(unsigned imms, unsigned reg_size) { VIXL_ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); - VIXL_ASSERT(is_uint6(imms)); - VIXL_ASSERT((reg_size == kXRegSize) || is_uint6(imms + 3)); + VIXL_ASSERT(IsUint6(imms)); + VIXL_ASSERT((reg_size == kXRegSize) || IsUint6(imms + 3)); USE(reg_size); return imms << ImmSetBits_offset; } static Instr ImmRotate(unsigned immr, unsigned reg_size) { VIXL_ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); - VIXL_ASSERT(((reg_size == kXRegSize) && is_uint6(immr)) || - ((reg_size == kWRegSize) && is_uint5(immr))); + VIXL_ASSERT(((reg_size == kXRegSize) && IsUint6(immr)) || + ((reg_size == kWRegSize) && IsUint5(immr))); USE(reg_size); return immr << ImmRotate_offset; } static Instr ImmLLiteral(int imm19) { - VIXL_ASSERT(is_int19(imm19)); - return truncate_to_int19(imm19) << ImmLLiteral_offset; + VIXL_ASSERT(IsInt19(imm19)); + return TruncateToUint19(imm19) << ImmLLiteral_offset; } static Instr BitN(unsigned bitn, unsigned reg_size) { @@ -3730,7 +3730,7 @@ class Assembler : public MozBaseAssembler { } static Instr ImmDPShift(unsigned amount) { - VIXL_ASSERT(is_uint6(amount)); + VIXL_ASSERT(IsUint6(amount)); return amount << ImmDPShift_offset; } @@ -3744,7 +3744,7 @@ class Assembler : public MozBaseAssembler { } static Instr ImmCondCmp(unsigned imm) { - VIXL_ASSERT(is_uint5(imm)); + VIXL_ASSERT(IsUint5(imm)); return imm << ImmCondCmp_offset; } @@ -3754,90 +3754,90 @@ class Assembler : public MozBaseAssembler { // MemOperand offset encoding. static Instr ImmLSUnsigned(int imm12) { - VIXL_ASSERT(is_uint12(imm12)); + VIXL_ASSERT(IsUint12(imm12)); return imm12 << ImmLSUnsigned_offset; } static Instr ImmLS(int imm9) { - VIXL_ASSERT(is_int9(imm9)); - return truncate_to_int9(imm9) << ImmLS_offset; + VIXL_ASSERT(IsInt9(imm9)); + return TruncateToUint9(imm9) << ImmLS_offset; } static Instr ImmLSPair(int imm7, unsigned access_size) { VIXL_ASSERT(((imm7 >> access_size) << access_size) == imm7); int scaled_imm7 = imm7 >> access_size; - VIXL_ASSERT(is_int7(scaled_imm7)); - return truncate_to_int7(scaled_imm7) << ImmLSPair_offset; + VIXL_ASSERT(IsInt7(scaled_imm7)); + return TruncateToUint7(scaled_imm7) << ImmLSPair_offset; } static Instr ImmShiftLS(unsigned shift_amount) { - VIXL_ASSERT(is_uint1(shift_amount)); + VIXL_ASSERT(IsUint1(shift_amount)); return shift_amount << ImmShiftLS_offset; } static Instr ImmPrefetchOperation(int imm5) { - VIXL_ASSERT(is_uint5(imm5)); + VIXL_ASSERT(IsUint5(imm5)); return imm5 << ImmPrefetchOperation_offset; } static Instr ImmException(int imm16) { - VIXL_ASSERT(is_uint16(imm16)); + VIXL_ASSERT(IsUint16(imm16)); return imm16 << ImmException_offset; } static Instr ImmSystemRegister(int imm15) { - VIXL_ASSERT(is_uint15(imm15)); + VIXL_ASSERT(IsUint15(imm15)); return imm15 << ImmSystemRegister_offset; } static Instr ImmHint(int imm7) { - VIXL_ASSERT(is_uint7(imm7)); + VIXL_ASSERT(IsUint7(imm7)); return imm7 << ImmHint_offset; } static Instr CRm(int imm4) { - VIXL_ASSERT(is_uint4(imm4)); + VIXL_ASSERT(IsUint4(imm4)); return imm4 << CRm_offset; } static Instr CRn(int imm4) { - VIXL_ASSERT(is_uint4(imm4)); + VIXL_ASSERT(IsUint4(imm4)); return imm4 << CRn_offset; } static Instr SysOp(int imm14) { - VIXL_ASSERT(is_uint14(imm14)); + VIXL_ASSERT(IsUint14(imm14)); return imm14 << SysOp_offset; } static Instr ImmSysOp1(int imm3) { - VIXL_ASSERT(is_uint3(imm3)); + VIXL_ASSERT(IsUint3(imm3)); return imm3 << SysOp1_offset; } static Instr ImmSysOp2(int imm3) { - VIXL_ASSERT(is_uint3(imm3)); + VIXL_ASSERT(IsUint3(imm3)); return imm3 << SysOp2_offset; } static Instr ImmBarrierDomain(int imm2) { - VIXL_ASSERT(is_uint2(imm2)); + VIXL_ASSERT(IsUint2(imm2)); return imm2 << ImmBarrierDomain_offset; } static Instr ImmBarrierType(int imm2) { - VIXL_ASSERT(is_uint2(imm2)); + VIXL_ASSERT(IsUint2(imm2)); return imm2 << ImmBarrierType_offset; } // Move immediates encoding. static Instr ImmMoveWide(uint64_t imm) { - VIXL_ASSERT(is_uint16(imm)); + VIXL_ASSERT(IsUint16(imm)); return static_cast(imm << ImmMoveWide_offset); } static Instr ShiftMoveWide(int64_t shift) { - VIXL_ASSERT(is_uint2(shift)); + VIXL_ASSERT(IsUint2(shift)); return static_cast(shift << ShiftMoveWide_offset); } @@ -3851,7 +3851,7 @@ class Assembler : public MozBaseAssembler { } static Instr FPScale(unsigned scale) { - VIXL_ASSERT(is_uint6(scale)); + VIXL_ASSERT(IsUint6(scale)); return scale << FPScale_offset; } @@ -3949,17 +3949,17 @@ class Assembler : public MozBaseAssembler { static Instr ImmNEONHLM(int index, int num_bits) { int h, l, m; if (num_bits == 3) { - VIXL_ASSERT(is_uint3(index)); + VIXL_ASSERT(IsUint3(index)); h = (index >> 2) & 1; l = (index >> 1) & 1; m = (index >> 0) & 1; } else if (num_bits == 2) { - VIXL_ASSERT(is_uint2(index)); + VIXL_ASSERT(IsUint2(index)); h = (index >> 1) & 1; l = (index >> 0) & 1; m = 0; } else { - VIXL_ASSERT(is_uint1(index) && (num_bits == 1)); + VIXL_ASSERT(IsUint1(index) && (num_bits == 1)); h = (index >> 0) & 1; l = 0; m = 0; @@ -3968,26 +3968,26 @@ class Assembler : public MozBaseAssembler { } static Instr ImmNEONExt(int imm4) { - VIXL_ASSERT(is_uint4(imm4)); + VIXL_ASSERT(IsUint4(imm4)); return imm4 << ImmNEONExt_offset; } static Instr ImmNEON5(Instr format, int index) { - VIXL_ASSERT(is_uint4(index)); + VIXL_ASSERT(IsUint4(index)); int s = LaneSizeInBytesLog2FromFormat(static_cast(format)); int imm5 = (index << (s + 1)) | (1 << s); return imm5 << ImmNEON5_offset; } static Instr ImmNEON4(Instr format, int index) { - VIXL_ASSERT(is_uint4(index)); + VIXL_ASSERT(IsUint4(index)); int s = LaneSizeInBytesLog2FromFormat(static_cast(format)); int imm4 = index << s; return imm4 << ImmNEON4_offset; } static Instr ImmNEONabcdefgh(int imm8) { - VIXL_ASSERT(is_uint8(imm8)); + VIXL_ASSERT(IsUint8(imm8)); Instr instr; instr = ((imm8 >> 5) & 7) << ImmNEONabc_offset; instr |= (imm8 & 0x1f) << ImmNEONdefgh_offset; @@ -3995,12 +3995,12 @@ class Assembler : public MozBaseAssembler { } static Instr NEONCmode(int cmode) { - VIXL_ASSERT(is_uint4(cmode)); + VIXL_ASSERT(IsUint4(cmode)); return cmode << NEONCmode_offset; } static Instr NEONModImmOp(int op) { - VIXL_ASSERT(is_uint1(op)); + VIXL_ASSERT(IsUint1(op)); return op << NEONModImmOp_offset; } diff --git a/js/src/jit/arm64/vixl/Cpu-vixl.h b/js/src/jit/arm64/vixl/Cpu-vixl.h index 57ac65f61a9d..72805a94edab 100644 --- a/js/src/jit/arm64/vixl/Cpu-vixl.h +++ b/js/src/jit/arm64/vixl/Cpu-vixl.h @@ -46,7 +46,7 @@ class CPU { // Handle tagged pointers. template static T SetPointerTag(T pointer, uint64_t tag) { - VIXL_ASSERT(is_uintn(kAddressTagWidth, tag)); + VIXL_ASSERT(IsUintN(kAddressTagWidth, tag)); // Use C-style casts to get static_cast behaviour for integral types (T), // and reinterpret_cast behaviour for other types. diff --git a/js/src/jit/arm64/vixl/Globals-vixl.h b/js/src/jit/arm64/vixl/Globals-vixl.h index 00668e99aa86..7712fd942dc4 100644 --- a/js/src/jit/arm64/vixl/Globals-vixl.h +++ b/js/src/jit/arm64/vixl/Globals-vixl.h @@ -1,4 +1,4 @@ -// Copyright 2015, ARM Limited +// Copyright 2015, VIXL authors // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -42,80 +42,227 @@ #include "mozilla/Assertions.h" +#include +#include +#include +#include + +extern "C" { #include -#include -#include #include -#include -#include +} #include "js-config.h" #include "jit/arm64/vixl/Platform-vixl.h" #include "js/Utility.h" +#ifdef VIXL_NEGATIVE_TESTING +#include +#include +#include +#endif + +namespace vixl { typedef uint8_t byte; -// Type for half-precision (16 bit) floating point numbers. -typedef uint16_t float16; - const int KBytes = 1024; const int MBytes = 1024 * KBytes; -#define VIXL_ABORT() \ - do { printf("in %s, line %i", __FILE__, __LINE__); abort(); } while (false) -#ifdef DEBUG - #define VIXL_ASSERT(condition) MOZ_ASSERT(condition) - #define VIXL_CHECK(condition) VIXL_ASSERT(condition) - #define VIXL_UNIMPLEMENTED() \ - do { fprintf(stderr, "UNIMPLEMENTED\t"); VIXL_ABORT(); } while (false) - #define VIXL_UNREACHABLE() \ - do { fprintf(stderr, "UNREACHABLE\t"); VIXL_ABORT(); } while (false) +const int kBitsPerByte = 8; + +template +struct Unsigned; + +template <> +struct Unsigned<32> { + typedef uint32_t type; +}; + +template <> +struct Unsigned<64> { + typedef uint64_t type; +}; + +} // namespace vixl + +// Detect the host's pointer size. +#if (UINTPTR_MAX == UINT32_MAX) +#define VIXL_HOST_POINTER_32 +#elif (UINTPTR_MAX == UINT64_MAX) +#define VIXL_HOST_POINTER_64 #else - #define VIXL_ASSERT(condition) ((void) 0) - #define VIXL_CHECK(condition) ((void) 0) - #define VIXL_UNIMPLEMENTED() ((void) 0) - #define VIXL_UNREACHABLE() MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("vixl unreachable") +#error "Unsupported host pointer size." +#endif + +#ifdef VIXL_NEGATIVE_TESTING +#define VIXL_ABORT() \ + do { \ + std::ostringstream oss; \ + oss << "Aborting in " << __FILE__ << ", line " << __LINE__ << std::endl; \ + throw std::runtime_error(oss.str()); \ + } while (false) +#define VIXL_ABORT_WITH_MSG(msg) \ + do { \ + std::ostringstream oss; \ + oss << (msg) << "in " << __FILE__ << ", line " << __LINE__ << std::endl; \ + throw std::runtime_error(oss.str()); \ + } while (false) +#define VIXL_CHECK(condition) \ + do { \ + if (!(condition)) { \ + std::ostringstream oss; \ + oss << "Assertion failed (" #condition ")\nin "; \ + oss << __FILE__ << ", line " << __LINE__ << std::endl; \ + throw std::runtime_error(oss.str()); \ + } \ + } while (false) +#else +#define VIXL_ABORT() \ + do { \ + MOZ_CRASH(); \ + } while (false) +#define VIXL_ABORT_WITH_MSG(msg) \ + do { \ + MOZ_CRASH(msg); \ + } while (false) +#define VIXL_CHECK(condition) \ + do { \ + if (!(condition)) { \ + MOZ_CRASH(); \ + } \ + } while (false) +#endif +#ifdef DEBUG +#define VIXL_ASSERT(condition) MOZ_ASSERT(condition) +#define VIXL_UNIMPLEMENTED() \ + do { \ + VIXL_ABORT_WITH_MSG("UNIMPLEMENTED "); \ + } while (false) +#define VIXL_UNREACHABLE() \ + do { \ + VIXL_ABORT_WITH_MSG("UNREACHABLE "); \ + } while (false) +#else +#define VIXL_ASSERT(condition) ((void)0) +#define VIXL_UNIMPLEMENTED() ((void)0) +#define VIXL_UNREACHABLE() MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("vixl unreachable") #endif // This is not as powerful as template based assertions, but it is simple. // It assumes that the descriptions are unique. If this starts being a problem, // we can switch to a different implemention. -#define VIXL_S(x) #x -#define VIXL_STATIC_ASSERT_LINE(line, condition) \ - static_assert(condition, "STATIC_ASSERT_LINE_" VIXL_S(line)) +#define VIXL_CONCAT(a, b) a##b +#if __cplusplus >= 201103L +#define VIXL_STATIC_ASSERT_LINE(line_unused, condition, message) \ + static_assert(condition, message) +#else +#define VIXL_STATIC_ASSERT_LINE(line, condition, message_unused) \ + typedef char VIXL_CONCAT(STATIC_ASSERT_LINE_, line)[(condition) ? 1 : -1] \ + __attribute__((unused)) +#endif #define VIXL_STATIC_ASSERT(condition) \ - VIXL_STATIC_ASSERT_LINE(__LINE__, condition) + VIXL_STATIC_ASSERT_LINE(__LINE__, condition, "") +#define VIXL_STATIC_ASSERT_MESSAGE(condition, message) \ + VIXL_STATIC_ASSERT_LINE(__LINE__, condition, message) + +#define VIXL_WARNING(message) \ + do { \ + printf("WARNING in %s, line %i: %s", __FILE__, __LINE__, message); \ + } while (false) template -inline void USE(T1) {} +inline void USE(const T1&) {} template -inline void USE(T1, T2) {} +inline void USE(const T1&, const T2&) {} template -inline void USE(T1, T2, T3) {} +inline void USE(const T1&, const T2&, const T3&) {} template -inline void USE(T1, T2, T3, T4) {} +inline void USE(const T1&, const T2&, const T3&, const T4&) {} -#define VIXL_ALIGNMENT_EXCEPTION() \ - do { fprintf(stderr, "ALIGNMENT EXCEPTION\t"); VIXL_ABORT(); } while (0) +#define VIXL_ALIGNMENT_EXCEPTION() \ + do { \ + VIXL_ABORT_WITH_MSG("ALIGNMENT EXCEPTION\t"); \ + } while (0) // The clang::fallthrough attribute is used along with the Wimplicit-fallthrough // argument to annotate intentional fall-through between switch labels. // For more information please refer to: // http://clang.llvm.org/docs/AttributeReference.html#fallthrough-clang-fallthrough #ifndef __has_warning - #define __has_warning(x) 0 +#define __has_warning(x) 0 #endif -// Note: This option is only available for Clang. And will only be enabled for -// C++11(201103L). +// Fallthrough annotation for Clang and C++11(201103L). #if __has_warning("-Wimplicit-fallthrough") && __cplusplus >= 201103L - #define VIXL_FALLTHROUGH() [[clang::fallthrough]] //NOLINT +#define VIXL_FALLTHROUGH() [[clang::fallthrough]] +// Fallthrough annotation for GCC >= 7. +#elif __GNUC__ >= 7 +#define VIXL_FALLTHROUGH() __attribute__((fallthrough)) #else - #define VIXL_FALLTHROUGH() do {} while (0) +#define VIXL_FALLTHROUGH() \ + do { \ + } while (0) #endif +#if __cplusplus >= 201103L +#define VIXL_NO_RETURN [[noreturn]] +#else +#define VIXL_NO_RETURN __attribute__((noreturn)) +#endif +#ifdef VIXL_DEBUG +#define VIXL_NO_RETURN_IN_DEBUG_MODE VIXL_NO_RETURN +#else +#define VIXL_NO_RETURN_IN_DEBUG_MODE +#endif + +#if __cplusplus >= 201103L +#define VIXL_OVERRIDE override +#else +#define VIXL_OVERRIDE +#endif + +#ifdef VIXL_INCLUDE_SIMULATOR_AARCH64 +#ifndef VIXL_AARCH64_GENERATE_SIMULATOR_CODE +#define VIXL_AARCH64_GENERATE_SIMULATOR_CODE 1 +#endif +#else +#ifndef VIXL_AARCH64_GENERATE_SIMULATOR_CODE +#define VIXL_AARCH64_GENERATE_SIMULATOR_CODE 0 +#endif +#if VIXL_AARCH64_GENERATE_SIMULATOR_CODE +#warning "Generating Simulator instructions without Simulator support." +#endif +#endif + +// We do not have a simulator for AArch32, although we can pretend we do so that +// tests that require running natively can be skipped. +#ifndef __arm__ +#define VIXL_INCLUDE_SIMULATOR_AARCH32 +#ifndef VIXL_AARCH32_GENERATE_SIMULATOR_CODE +#define VIXL_AARCH32_GENERATE_SIMULATOR_CODE 1 +#endif +#else +#ifndef VIXL_AARCH32_GENERATE_SIMULATOR_CODE +#define VIXL_AARCH32_GENERATE_SIMULATOR_CODE 0 +#endif +#endif + +// Target Architecture/ISA +#ifdef VIXL_INCLUDE_TARGET_A64 +#define VIXL_INCLUDE_TARGET_AARCH64 +#endif + +#if defined(VIXL_INCLUDE_TARGET_A32) && defined(VIXL_INCLUDE_TARGET_T32) +#define VIXL_INCLUDE_TARGET_AARCH32 +#elif defined(VIXL_INCLUDE_TARGET_A32) +#define VIXL_INCLUDE_TARGET_A32_ONLY +#else +#define VIXL_INCLUDE_TARGET_T32_ONLY +#endif + + #endif // VIXL_GLOBALS_H diff --git a/js/src/jit/arm64/vixl/Instructions-vixl.cpp b/js/src/jit/arm64/vixl/Instructions-vixl.cpp index dcbed023e646..887f12d0f217 100644 --- a/js/src/jit/arm64/vixl/Instructions-vixl.cpp +++ b/js/src/jit/arm64/vixl/Instructions-vixl.cpp @@ -30,34 +30,6 @@ namespace vixl { - -// Floating-point infinity values. -const float16 kFP16PositiveInfinity = 0x7c00; -const float16 kFP16NegativeInfinity = 0xfc00; -const float kFP32PositiveInfinity = rawbits_to_float(0x7f800000); -const float kFP32NegativeInfinity = rawbits_to_float(0xff800000); -const double kFP64PositiveInfinity = - rawbits_to_double(UINT64_C(0x7ff0000000000000)); -const double kFP64NegativeInfinity = - rawbits_to_double(UINT64_C(0xfff0000000000000)); - - -// The default NaN values (for FPCR.DN=1). -const double kFP64DefaultNaN = rawbits_to_double(UINT64_C(0x7ff8000000000000)); -const float kFP32DefaultNaN = rawbits_to_float(0x7fc00000); -const float16 kFP16DefaultNaN = 0x7e00; - - -static uint64_t RotateRight(uint64_t value, - unsigned int rotate, - unsigned int width) { - VIXL_ASSERT(width <= 64); - rotate &= 63; - return ((value & ((UINT64_C(1) << rotate) - 1)) << - (width - rotate)) | (value >> rotate); -} - - static uint64_t RepeatBitsAcrossReg(unsigned reg_size, uint64_t value, unsigned width) { @@ -196,7 +168,7 @@ float Instruction::Imm8ToFP32(uint32_t imm8) { uint32_t bit5_to_0 = bits & 0x3f; uint32_t result = (bit7 << 31) | ((32 - bit6) << 25) | (bit5_to_0 << 19); - return rawbits_to_float(result); + return RawbitsToFloat(result); } @@ -216,7 +188,7 @@ double Instruction::Imm8ToFP64(uint32_t imm8) { uint64_t bit5_to_0 = bits & 0x3f; uint64_t result = (bit7 << 63) | ((256 - bit6) << 54) | (bit5_to_0 << 48); - return rawbits_to_double(result); + return RawbitsToDouble(result); } @@ -289,7 +261,7 @@ int32_t Instruction::ImmBranchForwardRange(ImmBranchType branch_type) { bool Instruction::IsValidImmPCOffset(ImmBranchType branch_type, int64_t offset) { - return is_intn(ImmBranchRangeBitwidth(branch_type), offset); + return IsIntN(ImmBranchRangeBitwidth(branch_type), offset); } ImmBranchRangeType Instruction::ImmBranchTypeToRange(ImmBranchType branch_type) diff --git a/js/src/jit/arm64/vixl/Instructions-vixl.h b/js/src/jit/arm64/vixl/Instructions-vixl.h index 844eb2a0dc98..a9d2e03a1a50 100644 --- a/js/src/jit/arm64/vixl/Instructions-vixl.h +++ b/js/src/jit/arm64/vixl/Instructions-vixl.h @@ -102,27 +102,6 @@ const uint64_t kAddressTagMask = ((UINT64_C(1) << kAddressTagWidth) - 1) << kAddressTagOffset; VIXL_STATIC_ASSERT(kAddressTagMask == UINT64_C(0xff00000000000000)); -// AArch64 floating-point specifics. These match IEEE-754. -const unsigned kDoubleMantissaBits = 52; -const unsigned kDoubleExponentBits = 11; -const unsigned kFloatMantissaBits = 23; -const unsigned kFloatExponentBits = 8; -const unsigned kFloat16MantissaBits = 10; -const unsigned kFloat16ExponentBits = 5; - -// Floating-point infinity values. -extern const float16 kFP16PositiveInfinity; -extern const float16 kFP16NegativeInfinity; -extern const float kFP32PositiveInfinity; -extern const float kFP32NegativeInfinity; -extern const double kFP64PositiveInfinity; -extern const double kFP64NegativeInfinity; - -// The default NaN values (for FPCR.DN=1). -extern const float16 kFP16DefaultNaN; -extern const float kFP32DefaultNaN; -extern const double kFP64DefaultNaN; - unsigned CalcLSDataSize(LoadStoreOp op); unsigned CalcLSPairDataSize(LoadStorePairOp op); @@ -153,19 +132,6 @@ enum AddrMode { PostIndex }; -enum FPRounding { - // The first four values are encodable directly by FPCR. - FPTieEven = 0x0, - FPPositiveInfinity = 0x1, - FPNegativeInfinity = 0x2, - FPZero = 0x3, - - // The final rounding modes are only available when explicitly specified by - // the instruction (such as with fcvta). It cannot be set in FPCR. - FPTieAway, - FPRoundOdd -}; - enum Reg31Mode { Reg31IsStackPointer, Reg31IsZeroRegister @@ -188,12 +154,12 @@ class Instruction { } uint32_t Bits(int msb, int lsb) const { - return unsigned_bitextract_32(msb, lsb, InstructionBits()); + return ExtractUnsignedBitfield32(msb, lsb, InstructionBits()); } int32_t SignedBits(int msb, int lsb) const { int32_t bits = *(reinterpret_cast(this)); - return signed_bitextract_32(msb, lsb, bits); + return ExtractSignedBitfield32(msb, lsb, bits); } Instr Mask(uint32_t mask) const { @@ -216,7 +182,7 @@ class Instruction { int offset = static_cast((ImmPCRelHi() << ImmPCRelLo_width) | ImmPCRelLo()); int width = ImmPCRelLo_width + ImmPCRelHi_width; - return signed_bitextract_32(width - 1, 0, offset); + return ExtractSignedBitfield32(width - 1, 0, offset); } uint64_t ImmLogical() const; @@ -467,11 +433,11 @@ class Instruction { } float LiteralFP32() const { - return rawbits_to_float(Literal32()); + return RawbitsToFloat(Literal32()); } double LiteralFP64() const { - return rawbits_to_double(Literal64()); + return RawbitsToDouble(Literal64()); } const Instruction* NextInstruction() const { diff --git a/js/src/jit/arm64/vixl/Logic-vixl.cpp b/js/src/jit/arm64/vixl/Logic-vixl.cpp index 539e145ec6c2..d0305c44f7b5 100644 --- a/js/src/jit/arm64/vixl/Logic-vixl.cpp +++ b/js/src/jit/arm64/vixl/Logic-vixl.cpp @@ -41,39 +41,6 @@ template<> float Simulator::FPDefaultNaN() { return kFP32DefaultNaN; } -// See FPRound for a description of this function. -static inline double FPRoundToDouble(int64_t sign, int64_t exponent, - uint64_t mantissa, FPRounding round_mode) { - int64_t bits = - FPRound(sign, - exponent, - mantissa, - round_mode); - return rawbits_to_double(bits); -} - - -// See FPRound for a description of this function. -static inline float FPRoundToFloat(int64_t sign, int64_t exponent, - uint64_t mantissa, FPRounding round_mode) { - int32_t bits = - FPRound(sign, - exponent, - mantissa, - round_mode); - return rawbits_to_float(bits); -} - - -// See FPRound for a description of this function. -static inline float16 FPRoundToFloat16(int64_t sign, - int64_t exponent, - uint64_t mantissa, - FPRounding round_mode) { - return FPRound( - sign, exponent, mantissa, round_mode); -} - double Simulator::FixedToDouble(int64_t src, int fbits, FPRounding round) { if (src >= 0) { @@ -127,267 +94,6 @@ float Simulator::UFixedToFloat(uint64_t src, int fbits, FPRounding round) { } -double Simulator::FPToDouble(float value) { - switch (std::fpclassify(value)) { - case FP_NAN: { - if (IsSignallingNaN(value)) { - FPProcessException(); - } - if (DN()) return kFP64DefaultNaN; - - // Convert NaNs as the processor would: - // - The sign is propagated. - // - The payload (mantissa) is transferred entirely, except that the top - // bit is forced to '1', making the result a quiet NaN. The unused - // (low-order) payload bits are set to 0. - uint32_t raw = float_to_rawbits(value); - - uint64_t sign = raw >> 31; - uint64_t exponent = (1 << 11) - 1; - uint64_t payload = unsigned_bitextract_64(21, 0, raw); - payload <<= (52 - 23); // The unused low-order bits should be 0. - payload |= (UINT64_C(1) << 51); // Force a quiet NaN. - - return rawbits_to_double((sign << 63) | (exponent << 52) | payload); - } - - case FP_ZERO: - case FP_NORMAL: - case FP_SUBNORMAL: - case FP_INFINITE: { - // All other inputs are preserved in a standard cast, because every value - // representable using an IEEE-754 float is also representable using an - // IEEE-754 double. - return static_cast(value); - } - } - - VIXL_UNREACHABLE(); - return static_cast(value); -} - - -float Simulator::FPToFloat(float16 value) { - uint32_t sign = value >> 15; - uint32_t exponent = unsigned_bitextract_32( - kFloat16MantissaBits + kFloat16ExponentBits - 1, kFloat16MantissaBits, - value); - uint32_t mantissa = unsigned_bitextract_32( - kFloat16MantissaBits - 1, 0, value); - - switch (float16classify(value)) { - case FP_ZERO: - return (sign == 0) ? 0.0f : -0.0f; - - case FP_INFINITE: - return (sign == 0) ? kFP32PositiveInfinity : kFP32NegativeInfinity; - - case FP_SUBNORMAL: { - // Calculate shift required to put mantissa into the most-significant bits - // of the destination mantissa. - int shift = CountLeadingZeros(mantissa << (32 - 10)); - - // Shift mantissa and discard implicit '1'. - mantissa <<= (kFloatMantissaBits - kFloat16MantissaBits) + shift + 1; - mantissa &= (1 << kFloatMantissaBits) - 1; - - // Adjust the exponent for the shift applied, and rebias. - exponent = exponent - shift + (-15 + 127); - break; - } - - case FP_NAN: - if (IsSignallingNaN(value)) { - FPProcessException(); - } - if (DN()) return kFP32DefaultNaN; - - // Convert NaNs as the processor would: - // - The sign is propagated. - // - The payload (mantissa) is transferred entirely, except that the top - // bit is forced to '1', making the result a quiet NaN. The unused - // (low-order) payload bits are set to 0. - exponent = (1 << kFloatExponentBits) - 1; - - // Increase bits in mantissa, making low-order bits 0. - mantissa <<= (kFloatMantissaBits - kFloat16MantissaBits); - mantissa |= 1 << 22; // Force a quiet NaN. - break; - - case FP_NORMAL: - // Increase bits in mantissa, making low-order bits 0. - mantissa <<= (kFloatMantissaBits - kFloat16MantissaBits); - - // Change exponent bias. - exponent += (-15 + 127); - break; - - default: VIXL_UNREACHABLE(); - } - return rawbits_to_float((sign << 31) | - (exponent << kFloatMantissaBits) | - mantissa); -} - - -float16 Simulator::FPToFloat16(float value, FPRounding round_mode) { - // Only the FPTieEven rounding mode is implemented. - VIXL_ASSERT(round_mode == FPTieEven); - USE(round_mode); - - uint32_t raw = float_to_rawbits(value); - int32_t sign = raw >> 31; - int32_t exponent = unsigned_bitextract_32(30, 23, raw) - 127; - uint32_t mantissa = unsigned_bitextract_32(22, 0, raw); - - switch (std::fpclassify(value)) { - case FP_NAN: { - if (IsSignallingNaN(value)) { - FPProcessException(); - } - if (DN()) return kFP16DefaultNaN; - - // Convert NaNs as the processor would: - // - The sign is propagated. - // - The payload (mantissa) is transferred as much as possible, except - // that the top bit is forced to '1', making the result a quiet NaN. - float16 result = (sign == 0) ? kFP16PositiveInfinity - : kFP16NegativeInfinity; - result |= mantissa >> (kFloatMantissaBits - kFloat16MantissaBits); - result |= (1 << 9); // Force a quiet NaN; - return result; - } - - case FP_ZERO: - return (sign == 0) ? 0 : 0x8000; - - case FP_INFINITE: - return (sign == 0) ? kFP16PositiveInfinity : kFP16NegativeInfinity; - - case FP_NORMAL: - case FP_SUBNORMAL: { - // Convert float-to-half as the processor would, assuming that FPCR.FZ - // (flush-to-zero) is not set. - - // Add the implicit '1' bit to the mantissa. - mantissa += (1 << 23); - return FPRoundToFloat16(sign, exponent, mantissa, round_mode); - } - } - - VIXL_UNREACHABLE(); - return 0; -} - - -float16 Simulator::FPToFloat16(double value, FPRounding round_mode) { - // Only the FPTieEven rounding mode is implemented. - VIXL_ASSERT(round_mode == FPTieEven); - USE(round_mode); - - uint64_t raw = double_to_rawbits(value); - int32_t sign = raw >> 63; - int64_t exponent = unsigned_bitextract_64(62, 52, raw) - 1023; - uint64_t mantissa = unsigned_bitextract_64(51, 0, raw); - - switch (std::fpclassify(value)) { - case FP_NAN: { - if (IsSignallingNaN(value)) { - FPProcessException(); - } - if (DN()) return kFP16DefaultNaN; - - // Convert NaNs as the processor would: - // - The sign is propagated. - // - The payload (mantissa) is transferred as much as possible, except - // that the top bit is forced to '1', making the result a quiet NaN. - float16 result = (sign == 0) ? kFP16PositiveInfinity - : kFP16NegativeInfinity; - result |= mantissa >> (kDoubleMantissaBits - kFloat16MantissaBits); - result |= (1 << 9); // Force a quiet NaN; - return result; - } - - case FP_ZERO: - return (sign == 0) ? 0 : 0x8000; - - case FP_INFINITE: - return (sign == 0) ? kFP16PositiveInfinity : kFP16NegativeInfinity; - - case FP_NORMAL: - case FP_SUBNORMAL: { - // Convert double-to-half as the processor would, assuming that FPCR.FZ - // (flush-to-zero) is not set. - - // Add the implicit '1' bit to the mantissa. - mantissa += (UINT64_C(1) << 52); - return FPRoundToFloat16(sign, exponent, mantissa, round_mode); - } - } - - VIXL_UNREACHABLE(); - return 0; -} - - -float Simulator::FPToFloat(double value, FPRounding round_mode) { - // Only the FPTieEven rounding mode is implemented. - VIXL_ASSERT((round_mode == FPTieEven) || (round_mode == FPRoundOdd)); - USE(round_mode); - - switch (std::fpclassify(value)) { - case FP_NAN: { - if (IsSignallingNaN(value)) { - FPProcessException(); - } - if (DN()) return kFP32DefaultNaN; - - // Convert NaNs as the processor would: - // - The sign is propagated. - // - The payload (mantissa) is transferred as much as possible, except - // that the top bit is forced to '1', making the result a quiet NaN. - uint64_t raw = double_to_rawbits(value); - - uint32_t sign = raw >> 63; - uint32_t exponent = (1 << 8) - 1; - uint32_t payload = - static_cast(unsigned_bitextract_64(50, 52 - 23, raw)); - payload |= (1 << 22); // Force a quiet NaN. - - return rawbits_to_float((sign << 31) | (exponent << 23) | payload); - } - - case FP_ZERO: - case FP_INFINITE: { - // In a C++ cast, any value representable in the target type will be - // unchanged. This is always the case for +/-0.0 and infinities. - return static_cast(value); - } - - case FP_NORMAL: - case FP_SUBNORMAL: { - // Convert double-to-float as the processor would, assuming that FPCR.FZ - // (flush-to-zero) is not set. - uint64_t raw = double_to_rawbits(value); - // Extract the IEEE-754 double components. - uint32_t sign = raw >> 63; - // Extract the exponent and remove the IEEE-754 encoding bias. - int32_t exponent = - static_cast(unsigned_bitextract_64(62, 52, raw)) - 1023; - // Extract the mantissa and add the implicit '1' bit. - uint64_t mantissa = unsigned_bitextract_64(51, 0, raw); - if (std::fpclassify(value) == FP_NORMAL) { - mantissa |= (UINT64_C(1) << 52); - } - return FPRoundToFloat(sign, exponent, mantissa, round_mode); - } - } - - VIXL_UNREACHABLE(); - return value; -} - - void Simulator::ld1(VectorFormat vform, LogicVRegister dst, uint64_t addr) { @@ -4020,12 +3726,12 @@ LogicVRegister Simulator::fcmp_zero(VectorFormat vform, Condition cond) { SimVRegister temp; if (LaneSizeInBitsFromFormat(vform) == kSRegSize) { - LogicVRegister zero_reg = dup_immediate(vform, temp, float_to_rawbits(0.0)); + LogicVRegister zero_reg = dup_immediate(vform, temp, FloatToRawbits(0.0)); fcmp(vform, dst, src, zero_reg, cond); } else { VIXL_ASSERT(LaneSizeInBitsFromFormat(vform) == kDRegSize); LogicVRegister zero_reg = dup_immediate(vform, temp, - double_to_rawbits(0.0)); + DoubleToRawbits(0.0)); fcmp(vform, dst, src, zero_reg, cond); } return dst; @@ -4433,12 +4139,15 @@ LogicVRegister Simulator::fcvtl(VectorFormat vform, const LogicVRegister& src) { if (LaneSizeInBitsFromFormat(vform) == kSRegSize) { for (int i = LaneCountFromFormat(vform) - 1; i >= 0; i--) { - dst.SetFloat(i, FPToFloat(src.Float(i))); + // TODO: Full support for SimFloat16 in SimRegister(s). + dst.SetFloat(i, + FPToFloat(RawbitsToFloat16(src.Float(i)), + ReadDN())); } } else { VIXL_ASSERT(LaneSizeInBitsFromFormat(vform) == kDRegSize); for (int i = LaneCountFromFormat(vform) - 1; i >= 0; i--) { - dst.SetFloat(i, FPToDouble(src.Float(i))); + dst.SetFloat(i, FPToDouble(src.Float(i), ReadDN())); } } return dst; @@ -4451,12 +4160,16 @@ LogicVRegister Simulator::fcvtl2(VectorFormat vform, int lane_count = LaneCountFromFormat(vform); if (LaneSizeInBitsFromFormat(vform) == kSRegSize) { for (int i = 0; i < lane_count; i++) { - dst.SetFloat(i, FPToFloat(src.Float(i + lane_count))); + // TODO: Full support for SimFloat16 in SimRegister(s). + dst.SetFloat(i, + FPToFloat(RawbitsToFloat16( + src.Float(i + lane_count)), + ReadDN())); } } else { VIXL_ASSERT(LaneSizeInBitsFromFormat(vform) == kDRegSize); for (int i = 0; i < lane_count; i++) { - dst.SetFloat(i, FPToDouble(src.Float(i + lane_count))); + dst.SetFloat(i, FPToDouble(src.Float(i + lane_count), ReadDN())); } } return dst; @@ -4468,12 +4181,14 @@ LogicVRegister Simulator::fcvtn(VectorFormat vform, const LogicVRegister& src) { if (LaneSizeInBitsFromFormat(vform) == kHRegSize) { for (int i = 0; i < LaneCountFromFormat(vform); i++) { - dst.SetFloat(i, FPToFloat16(src.Float(i), FPTieEven)); + dst.SetFloat(i, + Float16ToRawbits( + FPToFloat16(src.Float(i), FPTieEven, ReadDN()))); } } else { VIXL_ASSERT(LaneSizeInBitsFromFormat(vform) == kSRegSize); for (int i = 0; i < LaneCountFromFormat(vform); i++) { - dst.SetFloat(i, FPToFloat(src.Float(i), FPTieEven)); + dst.SetFloat(i, FPToFloat(src.Float(i), FPTieEven, ReadDN())); } } return dst; @@ -4486,12 +4201,15 @@ LogicVRegister Simulator::fcvtn2(VectorFormat vform, int lane_count = LaneCountFromFormat(vform) / 2; if (LaneSizeInBitsFromFormat(vform) == kHRegSize) { for (int i = lane_count - 1; i >= 0; i--) { - dst.SetFloat(i + lane_count, FPToFloat16(src.Float(i), FPTieEven)); + dst.SetFloat(i + lane_count, + Float16ToRawbits( + FPToFloat16(src.Float(i), FPTieEven, ReadDN()))); } } else { VIXL_ASSERT(LaneSizeInBitsFromFormat(vform) == kSRegSize); for (int i = lane_count - 1; i >= 0; i--) { - dst.SetFloat(i + lane_count, FPToFloat(src.Float(i), FPTieEven)); + dst.SetFloat(i + lane_count, + FPToFloat(src.Float(i), FPTieEven, ReadDN())); } } return dst; @@ -4504,7 +4222,7 @@ LogicVRegister Simulator::fcvtxn(VectorFormat vform, dst.ClearForWrite(vform); VIXL_ASSERT(LaneSizeInBitsFromFormat(vform) == kSRegSize); for (int i = 0; i < LaneCountFromFormat(vform); i++) { - dst.SetFloat(i, FPToFloat(src.Float(i), FPRoundOdd)); + dst.SetFloat(i, FPToFloat(src.Float(i), FPRoundOdd, ReadDN())); } return dst; } @@ -4516,7 +4234,8 @@ LogicVRegister Simulator::fcvtxn2(VectorFormat vform, VIXL_ASSERT(LaneSizeInBitsFromFormat(vform) == kSRegSize); int lane_count = LaneCountFromFormat(vform) / 2; for (int i = lane_count - 1; i >= 0; i--) { - dst.SetFloat(i + lane_count, FPToFloat(src.Float(i), FPRoundOdd)); + dst.SetFloat(i + lane_count, + FPToFloat(src.Float(i), FPRoundOdd, ReadDN())); } return dst; } @@ -4539,7 +4258,7 @@ double Simulator::recip_sqrt_estimate(double a) { static inline uint64_t Bits(uint64_t val, int start_bit, int end_bit) { - return unsigned_bitextract_64(start_bit, end_bit, val); + return ExtractUnsignedBitfield64(start_bit, end_bit, val); } @@ -4563,12 +4282,12 @@ T Simulator::FPRecipSqrtEstimate(T op) { int exp, result_exp; if (sizeof(T) == sizeof(float)) { // NOLINT(runtime/sizeof) - exp = float_exp(op); - fraction = float_mantissa(op); + exp = FloatExp(op); + fraction = FloatMantissa(op); fraction <<= 29; } else { - exp = double_exp(op); - fraction = double_mantissa(op); + exp = DoubleExp(op); + fraction = DoubleMantissa(op); } if (exp == 0) { @@ -4581,9 +4300,9 @@ T Simulator::FPRecipSqrtEstimate(T op) { double scaled; if (Bits(exp, 0, 0) == 0) { - scaled = double_pack(0, 1022, Bits(fraction, 51, 44) << 44); + scaled = DoublePack(0, 1022, Bits(fraction, 51, 44) << 44); } else { - scaled = double_pack(0, 1021, Bits(fraction, 51, 44) << 44); + scaled = DoublePack(0, 1021, Bits(fraction, 51, 44) << 44); } if (sizeof(T) == sizeof(float)) { // NOLINT(runtime/sizeof) @@ -4592,14 +4311,14 @@ T Simulator::FPRecipSqrtEstimate(T op) { result_exp = (3068 - exp) / 2; } - uint64_t estimate = double_to_rawbits(recip_sqrt_estimate(scaled)); + uint64_t estimate = DoubleToRawbits(recip_sqrt_estimate(scaled)); if (sizeof(T) == sizeof(float)) { // NOLINT(runtime/sizeof) uint32_t exp_bits = static_cast(Bits(result_exp, 7, 0)); uint32_t est_bits = static_cast(Bits(estimate, 51, 29)); - return float_pack(0, exp_bits, est_bits); + return FloatPack(0, exp_bits, est_bits); } else { - return double_pack(0, Bits(result_exp, 10, 0), Bits(estimate, 51, 0)); + return DoublePack(0, Bits(result_exp, 10, 0), Bits(estimate, 51, 0)); } } } @@ -4629,9 +4348,9 @@ T Simulator::FPRecipEstimate(T op, FPRounding rounding) { uint32_t sign; if (sizeof(T) == sizeof(float)) { // NOLINT(runtime/sizeof) - sign = float_sign(op); + sign = FloatSign(op); } else { - sign = double_sign(op); + sign = DoubleSign(op); } if (std::isnan(op)) { @@ -4659,9 +4378,9 @@ T Simulator::FPRecipEstimate(T op, FPRounding rounding) { } else { // Return FPMaxNormal(sign). if (sizeof(T) == sizeof(float)) { // NOLINT(runtime/sizeof) - return float_pack(sign, 0xfe, 0x07fffff); + return FloatPack(sign, 0xfe, 0x07fffff); } else { - return double_pack(sign, 0x7fe, 0x0fffffffffffffl); + return DoublePack(sign, 0x7fe, 0x0fffffffffffffl); } } } else { @@ -4670,14 +4389,14 @@ T Simulator::FPRecipEstimate(T op, FPRounding rounding) { uint32_t sign; if (sizeof(T) == sizeof(float)) { // NOLINT(runtime/sizeof) - sign = float_sign(op); - exp = float_exp(op); - fraction = float_mantissa(op); + sign = FloatSign(op); + exp = FloatExp(op); + fraction = FloatMantissa(op); fraction <<= 29; } else { - sign = double_sign(op); - exp = double_exp(op); - fraction = double_mantissa(op); + sign = DoubleSign(op); + exp = DoubleExp(op); + fraction = DoubleMantissa(op); } if (exp == 0) { @@ -4689,7 +4408,7 @@ T Simulator::FPRecipEstimate(T op, FPRounding rounding) { } } - double scaled = double_pack(0, 1022, Bits(fraction, 51, 44) << 44); + double scaled = DoublePack(0, 1022, Bits(fraction, 51, 44) << 44); if (sizeof(T) == sizeof(float)) { // NOLINT(runtime/sizeof) result_exp = (253 - exp); // In range 253-254 = -1 to 253+1 = 254. @@ -4699,7 +4418,7 @@ T Simulator::FPRecipEstimate(T op, FPRounding rounding) { double estimate = recip_estimate(scaled); - fraction = double_mantissa(estimate); + fraction = DoubleMantissa(estimate); if (result_exp == 0) { fraction = (UINT64_C(1) << 51) | Bits(fraction, 51, 1); } else if (result_exp == -1) { @@ -4709,9 +4428,9 @@ T Simulator::FPRecipEstimate(T op, FPRounding rounding) { if (sizeof(T) == sizeof(float)) { // NOLINT(runtime/sizeof) uint32_t exp_bits = static_cast(Bits(result_exp, 7, 0)); uint32_t frac_bits = static_cast(Bits(fraction, 51, 29)); - return float_pack(sign, exp_bits, frac_bits); + return FloatPack(sign, exp_bits, frac_bits); } else { - return double_pack(sign, Bits(result_exp, 10, 0), Bits(fraction, 51, 0)); + return DoublePack(sign, Bits(result_exp, 10, 0), Bits(fraction, 51, 0)); } } } @@ -4806,15 +4525,15 @@ LogicVRegister Simulator::frecpx(VectorFormat vform, int exp; uint32_t sign; if (sizeof(T) == sizeof(float)) { // NOLINT(runtime/sizeof) - sign = float_sign(op); - exp = float_exp(op); + sign = FloatSign(op); + exp = FloatExp(op); exp = (exp == 0) ? (0xFF - 1) : static_cast(Bits(~exp, 7, 0)); - result = float_pack(sign, exp, 0); + result = FloatPack(sign, exp, 0); } else { - sign = double_sign(op); - exp = double_exp(op); + sign = DoubleSign(op); + exp = DoubleExp(op); exp = (exp == 0) ? (0x7FF - 1) : static_cast(Bits(~exp, 10, 0)); - result = double_pack(sign, exp, 0); + result = DoublePack(sign, exp, 0); } } dst.SetFloat(i, result); diff --git a/js/src/jit/arm64/vixl/MacroAssembler-vixl.cpp b/js/src/jit/arm64/vixl/MacroAssembler-vixl.cpp index 6cf6574cbc80..aa8f85d519b7 100644 --- a/js/src/jit/arm64/vixl/MacroAssembler-vixl.cpp +++ b/js/src/jit/arm64/vixl/MacroAssembler-vixl.cpp @@ -48,7 +48,7 @@ int MacroAssembler::MoveImmediateHelper(MacroAssembler* masm, const Register &rd, uint64_t imm) { bool emit_code = (masm != NULL); - VIXL_ASSERT(is_uint32(imm) || is_int32(imm) || rd.Is64Bits()); + VIXL_ASSERT(IsUint32(imm) || IsInt32(imm) || rd.Is64Bits()); // The worst case for size is mov 64-bit immediate to sp: // * up to 4 instructions to materialise the constant // * 1 instruction to move to sp @@ -364,7 +364,7 @@ void MacroAssembler::LogicalMacro(const Register& rd, immediate &= kWRegMask; } - VIXL_ASSERT(rd.Is64Bits() || is_uint32(immediate)); + VIXL_ASSERT(rd.Is64Bits() || IsUint32(immediate)); // Special cases for all set or all clear immediates. if (immediate == 0) { @@ -495,7 +495,7 @@ void MacroAssembler::Mov(const Register& rd, void MacroAssembler::Movi16bitHelper(const VRegister& vd, uint64_t imm) { - VIXL_ASSERT(is_uint16(imm)); + VIXL_ASSERT(IsUint16(imm)); int byte1 = (imm & 0xff); int byte2 = ((imm >> 8) & 0xff); if (byte1 == byte2) { @@ -518,7 +518,7 @@ void MacroAssembler::Movi16bitHelper(const VRegister& vd, uint64_t imm) { void MacroAssembler::Movi32bitHelper(const VRegister& vd, uint64_t imm) { - VIXL_ASSERT(is_uint32(imm)); + VIXL_ASSERT(IsUint32(imm)); uint8_t bytes[sizeof(imm)]; memcpy(bytes, &imm, sizeof(imm)); @@ -641,7 +641,7 @@ void MacroAssembler::Movi(const VRegister& vd, movi(vd, imm, shift, shift_amount); } else if (vd.Is8B() || vd.Is16B()) { // 8-bit immediate. - VIXL_ASSERT(is_uint8(imm)); + VIXL_ASSERT(IsUint8(imm)); movi(vd, imm); } else if (vd.Is4H() || vd.Is8H()) { // 16-bit immediate. @@ -887,7 +887,7 @@ void MacroAssembler::Fmov(VRegister vd, double imm) { if (IsImmFP64(imm)) { fmov(vd, imm); } else { - uint64_t rawbits = double_to_rawbits(imm); + uint64_t rawbits = DoubleToRawbits(imm); if (vd.IsScalar()) { if (rawbits == 0) { fmov(vd, xzr); @@ -915,7 +915,7 @@ void MacroAssembler::Fmov(VRegister vd, float imm) { if (IsImmFP32(imm)) { fmov(vd, imm); } else { - uint32_t rawbits = float_to_rawbits(imm); + uint32_t rawbits = FloatToRawbits(imm); if (vd.IsScalar()) { if (rawbits == 0) { fmov(vd, wzr); @@ -1131,7 +1131,7 @@ void MacroAssembler::AddSubWithCarryMacro(const Register& rd, // Add/sub with carry (shifted register). VIXL_ASSERT(operand.reg().size() == rd.size()); VIXL_ASSERT(operand.shift() != ROR); - VIXL_ASSERT(is_uintn(rd.size() == kXRegSize ? kXRegSizeLog2 : kWRegSizeLog2, + VIXL_ASSERT(IsUintN(rd.size() == kXRegSize ? kXRegSizeLog2 : kWRegSizeLog2, operand.shift_amount())); temps.Exclude(operand.reg()); Register temp = temps.AcquireSameSizeAs(rn); diff --git a/js/src/jit/arm64/vixl/MozAssembler-vixl.cpp b/js/src/jit/arm64/vixl/MozAssembler-vixl.cpp index 451479fbb993..bbff3183f848 100644 --- a/js/src/jit/arm64/vixl/MozAssembler-vixl.cpp +++ b/js/src/jit/arm64/vixl/MozAssembler-vixl.cpp @@ -399,7 +399,7 @@ void Assembler::hint(Instruction* at, SystemHint code) { void Assembler::svc(Instruction* at, int code) { - VIXL_ASSERT(is_uint16(code)); + VIXL_ASSERT(IsUint16(code)); Emit(at, SVC | ImmException(code)); } @@ -424,7 +424,7 @@ BufferOffset Assembler::Logical(const Register& rd, const Register& rn, VIXL_ASSERT(immediate != 0); VIXL_ASSERT(immediate != -1); - VIXL_ASSERT(rd.Is64Bits() || is_uint32(immediate)); + VIXL_ASSERT(rd.Is64Bits() || IsUint32(immediate)); // If the operation is NOT, invert the operation and immediate. if ((op & NOT) == NOT) { @@ -463,7 +463,7 @@ BufferOffset Assembler::DataProcShiftedRegister(const Register& rd, const Regist const Operand& operand, FlagsUpdate S, Instr op) { VIXL_ASSERT(operand.IsShiftedRegister()); - VIXL_ASSERT(rn.Is64Bits() || (rn.Is32Bits() && is_uint5(operand.shift_amount()))); + VIXL_ASSERT(rn.Is64Bits() || (rn.Is32Bits() && IsUint5(operand.shift_amount()))); return Emit(SF(rd) | op | Flags(S) | ShiftDP(operand.shift()) | ImmDPShift(operand.shift_amount()) | Rm(operand.reg()) | Rn(rn) | Rd(rd)); @@ -685,7 +685,7 @@ void MozBaseAssembler::RetargetNearBranch(Instruction* i, int byteOffset, bool f VIXL_ASSERT(byteOffset % kInstructionSize == 0); // Opposite of ImmTestBranchBit(): MSB in bit 5, 0:5 at bit 40. unsigned bit_pos = (i->ImmTestBranchBit5() << 5) | (i->ImmTestBranchBit40()); - VIXL_ASSERT(is_uint6(bit_pos)); + VIXL_ASSERT(IsUint6(bit_pos)); // Register size doesn't matter for the encoding. Register rt = Register::XRegFromCode(i->Rt()); diff --git a/js/src/jit/arm64/vixl/MozCpu-vixl.cpp b/js/src/jit/arm64/vixl/MozCpu-vixl.cpp index 1939e7c28f4a..66eabd457d05 100644 --- a/js/src/jit/arm64/vixl/MozCpu-vixl.cpp +++ b/js/src/jit/arm64/vixl/MozCpu-vixl.cpp @@ -37,7 +37,7 @@ uint32_t CPU::GetCacheType() { // Copy the content of the cache type register to a core register. __asm__ __volatile__ ("mrs %[ctr], ctr_el0" // NOLINT : [ctr] "=r" (cache_type_register)); - VIXL_ASSERT(is_uint32(cache_type_register)); + VIXL_ASSERT(IsUint32(cache_type_register)); return cache_type_register; #else // This will lead to a cache with 1 byte long lines, which is fine since diff --git a/js/src/jit/arm64/vixl/MozInstructions-vixl.cpp b/js/src/jit/arm64/vixl/MozInstructions-vixl.cpp index 032890caef21..398f864493cc 100644 --- a/js/src/jit/arm64/vixl/MozInstructions-vixl.cpp +++ b/js/src/jit/arm64/vixl/MozInstructions-vixl.cpp @@ -121,13 +121,13 @@ bool Instruction::IsTargetReachable(const Instruction* target) const { int offset = (target - this) >> kInstructionSizeLog2; switch (BranchType()) { case CondBranchType: - return is_int19(offset); + return IsInt19(offset); case UncondBranchType: - return is_int26(offset); + return IsInt26(offset); case CompareBranchType: - return is_int19(offset); + return IsInt19(offset); case TestBranchType: - return is_int14(offset); + return IsInt14(offset); default: VIXL_UNREACHABLE(); } diff --git a/js/src/jit/arm64/vixl/MozSimulator-vixl.cpp b/js/src/jit/arm64/vixl/MozSimulator-vixl.cpp index 7d588d5e3787..11de9b85479e 100644 --- a/js/src/jit/arm64/vixl/MozSimulator-vixl.cpp +++ b/js/src/jit/arm64/vixl/MozSimulator-vixl.cpp @@ -89,8 +89,8 @@ void Simulator::ResetState() { } // Set FP registers to a value that is a NaN in both 32-bit and 64-bit FP. uint64_t nan_bits = UINT64_C(0x7ff0dead7f8beef1); - VIXL_ASSERT(IsSignallingNaN(rawbits_to_double(nan_bits & kDRegMask))); - VIXL_ASSERT(IsSignallingNaN(rawbits_to_float(nan_bits & kSRegMask))); + VIXL_ASSERT(IsSignallingNaN(RawbitsToDouble(nan_bits & kDRegMask))); + VIXL_ASSERT(IsSignallingNaN(RawbitsToFloat(nan_bits & kSRegMask))); for (unsigned i = 0; i < kNumberOfFPRegisters; i++) { set_dreg_bits(i, nan_bits); } diff --git a/js/src/jit/arm64/vixl/Simulator-vixl.cpp b/js/src/jit/arm64/vixl/Simulator-vixl.cpp index 6d59781074dd..13a4b34c6874 100644 --- a/js/src/jit/arm64/vixl/Simulator-vixl.cpp +++ b/js/src/jit/arm64/vixl/Simulator-vixl.cpp @@ -41,7 +41,7 @@ const Instruction* Simulator::kEndOfSimAddress = NULL; void SimSystemRegister::SetBits(int msb, int lsb, uint32_t bits) { int width = msb - lsb + 1; - VIXL_ASSERT(is_uintn(width, bits) || is_intn(width, bits)); + VIXL_ASSERT(IsUintN(width, bits) || IsIntN(width, bits)); bits <<= lsb; uint32_t mask = ((1 << width) - 1) << lsb; @@ -2152,12 +2152,24 @@ void Simulator::VisitFPDataProcessing1Source(const Instruction* instr) { case FABS_d: fabs_(kFormatD, vreg(fd), vreg(fn)); return; case FNEG_s: fneg(kFormatS, vreg(fd), vreg(fn)); return; case FNEG_d: fneg(kFormatD, vreg(fd), vreg(fn)); return; - case FCVT_ds: set_dreg(fd, FPToDouble(sreg(fn))); return; - case FCVT_sd: set_sreg(fd, FPToFloat(dreg(fn), FPTieEven)); return; - case FCVT_hs: set_hreg(fd, FPToFloat16(sreg(fn), FPTieEven)); return; - case FCVT_sh: set_sreg(fd, FPToFloat(hreg(fn))); return; - case FCVT_dh: set_dreg(fd, FPToDouble(FPToFloat(hreg(fn)))); return; - case FCVT_hd: set_hreg(fd, FPToFloat16(dreg(fn), FPTieEven)); return; + case FCVT_ds: + set_dreg(fd, FPToDouble(sreg(fn), ReadDN())); + return; + case FCVT_sd: + set_sreg(fd, FPToFloat(dreg(fn), FPTieEven, ReadDN())); + return; + case FCVT_hs: + set_hreg(fd, Float16ToRawbits(FPToFloat16(sreg(fn), FPTieEven, ReadDN()))); + return; + case FCVT_sh: + set_sreg(fd, FPToFloat(RawbitsToFloat16(hreg(fn)), ReadDN())); + return; + case FCVT_dh: + set_dreg(fd, FPToDouble(hreg(fn), ReadDN())); + return; + case FCVT_hd: + set_hreg(fd, Float16ToRawbits(FPToFloat16(dreg(fn), FPTieEven, ReadDN()))); + return; case FSQRT_s: case FSQRT_d: fsqrt(vform, rd, rn); return; case FRINTI_s: @@ -3289,10 +3301,10 @@ void Simulator::VisitNEONModifiedImmediate(const Instruction* instr) { } else { // cmode_0 == 1, cmode == 0xf. if (op_bit == 0) { vform = q ? kFormat4S : kFormat2S; - imm = float_to_rawbits(instr->ImmNEONFP32()); + imm = FloatToRawbits(instr->ImmNEONFP32()); } else if (q == 1) { vform = kFormat2D; - imm = double_to_rawbits(instr->ImmNEONFP64()); + imm = DoubleToRawbits(instr->ImmNEONFP64()); } else { VIXL_ASSERT((q == 0) && (op_bit == 1) && (cmode == 0xf)); VisitUnallocated(instr); diff --git a/js/src/jit/arm64/vixl/Simulator-vixl.h b/js/src/jit/arm64/vixl/Simulator-vixl.h index 54c4da5301bd..776871508d12 100644 --- a/js/src/jit/arm64/vixl/Simulator-vixl.h +++ b/js/src/jit/arm64/vixl/Simulator-vixl.h @@ -49,202 +49,6 @@ namespace vixl { -// Assemble the specified IEEE-754 components into the target type and apply -// appropriate rounding. -// sign: 0 = positive, 1 = negative -// exponent: Unbiased IEEE-754 exponent. -// mantissa: The mantissa of the input. The top bit (which is not encoded for -// normal IEEE-754 values) must not be omitted. This bit has the -// value 'pow(2, exponent)'. -// -// The input value is assumed to be a normalized value. That is, the input may -// not be infinity or NaN. If the source value is subnormal, it must be -// normalized before calling this function such that the highest set bit in the -// mantissa has the value 'pow(2, exponent)'. -// -// Callers should use FPRoundToFloat or FPRoundToDouble directly, rather than -// calling a templated FPRound. -template -T FPRound(int64_t sign, int64_t exponent, uint64_t mantissa, - FPRounding round_mode) { - VIXL_ASSERT((sign == 0) || (sign == 1)); - - // Only FPTieEven and FPRoundOdd rounding modes are implemented. - VIXL_ASSERT((round_mode == FPTieEven) || (round_mode == FPRoundOdd)); - - // Rounding can promote subnormals to normals, and normals to infinities. For - // example, a double with exponent 127 (FLT_MAX_EXP) would appear to be - // encodable as a float, but rounding based on the low-order mantissa bits - // could make it overflow. With ties-to-even rounding, this value would become - // an infinity. - - // ---- Rounding Method ---- - // - // The exponent is irrelevant in the rounding operation, so we treat the - // lowest-order bit that will fit into the result ('onebit') as having - // the value '1'. Similarly, the highest-order bit that won't fit into - // the result ('halfbit') has the value '0.5'. The 'point' sits between - // 'onebit' and 'halfbit': - // - // These bits fit into the result. - // |---------------------| - // mantissa = 0bxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - // || - // / | - // / halfbit - // onebit - // - // For subnormal outputs, the range of representable bits is smaller and - // the position of onebit and halfbit depends on the exponent of the - // input, but the method is otherwise similar. - // - // onebit(frac) - // | - // | halfbit(frac) halfbit(adjusted) - // | / / - // | | | - // 0b00.0 (exact) -> 0b00.0 (exact) -> 0b00 - // 0b00.0... -> 0b00.0... -> 0b00 - // 0b00.1 (exact) -> 0b00.0111..111 -> 0b00 - // 0b00.1... -> 0b00.1... -> 0b01 - // 0b01.0 (exact) -> 0b01.0 (exact) -> 0b01 - // 0b01.0... -> 0b01.0... -> 0b01 - // 0b01.1 (exact) -> 0b01.1 (exact) -> 0b10 - // 0b01.1... -> 0b01.1... -> 0b10 - // 0b10.0 (exact) -> 0b10.0 (exact) -> 0b10 - // 0b10.0... -> 0b10.0... -> 0b10 - // 0b10.1 (exact) -> 0b10.0111..111 -> 0b10 - // 0b10.1... -> 0b10.1... -> 0b11 - // 0b11.0 (exact) -> 0b11.0 (exact) -> 0b11 - // ... / | / | - // / | / | - // / | - // adjusted = frac - (halfbit(mantissa) & ~onebit(frac)); / | - // - // mantissa = (mantissa >> shift) + halfbit(adjusted); - - static const int mantissa_offset = 0; - static const int exponent_offset = mantissa_offset + mbits; - static const int sign_offset = exponent_offset + ebits; - VIXL_ASSERT(sign_offset == (sizeof(T) * 8 - 1)); - - // Bail out early for zero inputs. - if (mantissa == 0) { - return static_cast(sign << sign_offset); - } - - // If all bits in the exponent are set, the value is infinite or NaN. - // This is true for all binary IEEE-754 formats. - static const int infinite_exponent = (1 << ebits) - 1; - static const int max_normal_exponent = infinite_exponent - 1; - - // Apply the exponent bias to encode it for the result. Doing this early makes - // it easy to detect values that will be infinite or subnormal. - exponent += max_normal_exponent >> 1; - - if (exponent > max_normal_exponent) { - // Overflow: the input is too large for the result type to represent. - if (round_mode == FPTieEven) { - // FPTieEven rounding mode handles overflows using infinities. - exponent = infinite_exponent; - mantissa = 0; - } else { - VIXL_ASSERT(round_mode == FPRoundOdd); - // FPRoundOdd rounding mode handles overflows using the largest magnitude - // normal number. - exponent = max_normal_exponent; - mantissa = (UINT64_C(1) << exponent_offset) - 1; - } - return static_cast((sign << sign_offset) | - (exponent << exponent_offset) | - (mantissa << mantissa_offset)); - } - - // Calculate the shift required to move the top mantissa bit to the proper - // place in the destination type. - const int highest_significant_bit = 63 - CountLeadingZeros(mantissa); - int shift = highest_significant_bit - mbits; - - if (exponent <= 0) { - // The output will be subnormal (before rounding). - // For subnormal outputs, the shift must be adjusted by the exponent. The +1 - // is necessary because the exponent of a subnormal value (encoded as 0) is - // the same as the exponent of the smallest normal value (encoded as 1). - shift += -exponent + 1; - - // Handle inputs that would produce a zero output. - // - // Shifts higher than highest_significant_bit+1 will always produce a zero - // result. A shift of exactly highest_significant_bit+1 might produce a - // non-zero result after rounding. - if (shift > (highest_significant_bit + 1)) { - if (round_mode == FPTieEven) { - // The result will always be +/-0.0. - return static_cast(sign << sign_offset); - } else { - VIXL_ASSERT(round_mode == FPRoundOdd); - VIXL_ASSERT(mantissa != 0); - // For FPRoundOdd, if the mantissa is too small to represent and - // non-zero return the next "odd" value. - return static_cast((sign << sign_offset) | 1); - } - } - - // Properly encode the exponent for a subnormal output. - exponent = 0; - } else { - // Clear the topmost mantissa bit, since this is not encoded in IEEE-754 - // normal values. - mantissa &= ~(UINT64_C(1) << highest_significant_bit); - } - - if (shift > 0) { - if (round_mode == FPTieEven) { - // We have to shift the mantissa to the right. Some precision is lost, so - // we need to apply rounding. - uint64_t onebit_mantissa = (mantissa >> (shift)) & 1; - uint64_t halfbit_mantissa = (mantissa >> (shift-1)) & 1; - uint64_t adjustment = (halfbit_mantissa & ~onebit_mantissa); - uint64_t adjusted = mantissa - adjustment; - T halfbit_adjusted = (adjusted >> (shift-1)) & 1; - - T result = static_cast((sign << sign_offset) | - (exponent << exponent_offset) | - ((mantissa >> shift) << mantissa_offset)); - - // A very large mantissa can overflow during rounding. If this happens, - // the exponent should be incremented and the mantissa set to 1.0 - // (encoded as 0). Applying halfbit_adjusted after assembling the float - // has the nice side-effect that this case is handled for free. - // - // This also handles cases where a very large finite value overflows to - // infinity, or where a very large subnormal value overflows to become - // normal. - return result + halfbit_adjusted; - } else { - VIXL_ASSERT(round_mode == FPRoundOdd); - // If any bits at position halfbit or below are set, onebit (ie. the - // bottom bit of the resulting mantissa) must be set. - uint64_t fractional_bits = mantissa & ((UINT64_C(1) << shift) - 1); - if (fractional_bits != 0) { - mantissa |= UINT64_C(1) << shift; - } - - return static_cast((sign << sign_offset) | - (exponent << exponent_offset) | - ((mantissa >> shift) << mantissa_offset)); - } - } else { - // We have to shift the mantissa to the left (or not at all). The input - // mantissa is exactly representable in the output mantissa, so apply no - // rounding correction. - return static_cast((sign << sign_offset) | - (exponent << exponent_offset) | - ((mantissa << -shift) << mantissa_offset)); - } -} - - // Representation of memory, with typed getters and setters for access. class Memory { public: @@ -593,11 +397,11 @@ class SimSystemRegister { } uint32_t Bits(int msb, int lsb) const { - return unsigned_bitextract_32(msb, lsb, value_); + return ExtractUnsignedBitfield32(msb, lsb, value_); } int32_t SignedBits(int msb, int lsb) const { - return signed_bitextract_32(msb, lsb, value_); + return ExtractSignedBitfield32(msb, lsb, value_); } void SetBits(int msb, int lsb, uint32_t bits); @@ -1053,6 +857,10 @@ class Simulator : public DecoderVisitor { bool DN() { return fpcr_.DN() != 0; } SimSystemRegister& fpcr() { return fpcr_; } + UseDefaultNaN ReadDN() const { + return fpcr_.DN() != 0 ? kUseDefaultNaN : kIgnoreDefaultNaN; + } + // Specify relevant register formats for Print(V)Register and related helpers. enum PrintRegisterFormat { // The lane size. @@ -2471,11 +2279,6 @@ class Simulator : public DecoderVisitor { void FPCompare(double val0, double val1, FPTrapFlags trap); double FPRoundInt(double value, FPRounding round_mode); - double FPToDouble(float value); - float FPToFloat(double value, FPRounding round_mode); - float FPToFloat(float16 value); - float16 FPToFloat16(float value, FPRounding round_mode); - float16 FPToFloat16(double value, FPRounding round_mode); double recip_sqrt_estimate(double a); double recip_estimate(double a); double FPRecipSqrtEstimate(double a); diff --git a/js/src/jit/arm64/vixl/Utils-vixl.cpp b/js/src/jit/arm64/vixl/Utils-vixl.cpp index 7af311beef2e..381c3501d128 100644 --- a/js/src/jit/arm64/vixl/Utils-vixl.cpp +++ b/js/src/jit/arm64/vixl/Utils-vixl.cpp @@ -1,4 +1,4 @@ -// Copyright 2015, ARM Limited +// Copyright 2015, VIXL authors // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -26,95 +26,153 @@ #include "jit/arm64/vixl/Utils-vixl.h" -#include "mozilla/MathAlgorithms.h" - -#include +#include namespace vixl { -uint32_t float_to_rawbits(float value) { +// The default NaN values (for FPCR.DN=1). +const double kFP64DefaultNaN = RawbitsToDouble(UINT64_C(0x7ff8000000000000)); +const float kFP32DefaultNaN = RawbitsToFloat(0x7fc00000); +const Float16 kFP16DefaultNaN = RawbitsToFloat16(0x7e00); + +// Floating-point zero values. +const Float16 kFP16PositiveZero = RawbitsToFloat16(0x0); +const Float16 kFP16NegativeZero = RawbitsToFloat16(0x8000); + +// Floating-point infinity values. +const Float16 kFP16PositiveInfinity = RawbitsToFloat16(0x7c00); +const Float16 kFP16NegativeInfinity = RawbitsToFloat16(0xfc00); +const float kFP32PositiveInfinity = RawbitsToFloat(0x7f800000); +const float kFP32NegativeInfinity = RawbitsToFloat(0xff800000); +const double kFP64PositiveInfinity = + RawbitsToDouble(UINT64_C(0x7ff0000000000000)); +const double kFP64NegativeInfinity = + RawbitsToDouble(UINT64_C(0xfff0000000000000)); + +bool IsZero(Float16 value) { + uint16_t bits = Float16ToRawbits(value); + return (bits == Float16ToRawbits(kFP16PositiveZero) || + bits == Float16ToRawbits(kFP16NegativeZero)); +} + +uint16_t Float16ToRawbits(Float16 value) { return value.rawbits_; } + +uint32_t FloatToRawbits(float value) { uint32_t bits = 0; memcpy(&bits, &value, 4); return bits; } -uint64_t double_to_rawbits(double value) { +uint64_t DoubleToRawbits(double value) { uint64_t bits = 0; memcpy(&bits, &value, 8); return bits; } -float rawbits_to_float(uint32_t bits) { +Float16 RawbitsToFloat16(uint16_t bits) { + Float16 f; + f.rawbits_ = bits; + return f; +} + + +float RawbitsToFloat(uint32_t bits) { float value = 0.0; memcpy(&value, &bits, 4); return value; } -double rawbits_to_double(uint64_t bits) { +double RawbitsToDouble(uint64_t bits) { double value = 0.0; memcpy(&value, &bits, 8); return value; } -uint32_t float_sign(float val) { - uint32_t rawbits = float_to_rawbits(val); - return unsigned_bitextract_32(31, 31, rawbits); +uint32_t Float16Sign(internal::SimFloat16 val) { + uint16_t rawbits = Float16ToRawbits(val); + return ExtractUnsignedBitfield32(15, 15, rawbits); } -uint32_t float_exp(float val) { - uint32_t rawbits = float_to_rawbits(val); - return unsigned_bitextract_32(30, 23, rawbits); +uint32_t Float16Exp(internal::SimFloat16 val) { + uint16_t rawbits = Float16ToRawbits(val); + return ExtractUnsignedBitfield32(14, 10, rawbits); +} + +uint32_t Float16Mantissa(internal::SimFloat16 val) { + uint16_t rawbits = Float16ToRawbits(val); + return ExtractUnsignedBitfield32(9, 0, rawbits); } -uint32_t float_mantissa(float val) { - uint32_t rawbits = float_to_rawbits(val); - return unsigned_bitextract_32(22, 0, rawbits); +uint32_t FloatSign(float val) { + uint32_t rawbits = FloatToRawbits(val); + return ExtractUnsignedBitfield32(31, 31, rawbits); } -uint32_t double_sign(double val) { - uint64_t rawbits = double_to_rawbits(val); - return static_cast(unsigned_bitextract_64(63, 63, rawbits)); +uint32_t FloatExp(float val) { + uint32_t rawbits = FloatToRawbits(val); + return ExtractUnsignedBitfield32(30, 23, rawbits); } -uint32_t double_exp(double val) { - uint64_t rawbits = double_to_rawbits(val); - return static_cast(unsigned_bitextract_64(62, 52, rawbits)); +uint32_t FloatMantissa(float val) { + uint32_t rawbits = FloatToRawbits(val); + return ExtractUnsignedBitfield32(22, 0, rawbits); } -uint64_t double_mantissa(double val) { - uint64_t rawbits = double_to_rawbits(val); - return unsigned_bitextract_64(51, 0, rawbits); +uint32_t DoubleSign(double val) { + uint64_t rawbits = DoubleToRawbits(val); + return static_cast(ExtractUnsignedBitfield64(63, 63, rawbits)); } -float float_pack(uint32_t sign, uint32_t exp, uint32_t mantissa) { +uint32_t DoubleExp(double val) { + uint64_t rawbits = DoubleToRawbits(val); + return static_cast(ExtractUnsignedBitfield64(62, 52, rawbits)); +} + + +uint64_t DoubleMantissa(double val) { + uint64_t rawbits = DoubleToRawbits(val); + return ExtractUnsignedBitfield64(51, 0, rawbits); +} + + +internal::SimFloat16 Float16Pack(uint16_t sign, + uint16_t exp, + uint16_t mantissa) { + uint16_t bits = (sign << 15) | (exp << 10) | mantissa; + return RawbitsToFloat16(bits); +} + + +float FloatPack(uint32_t sign, uint32_t exp, uint32_t mantissa) { uint32_t bits = (sign << 31) | (exp << 23) | mantissa; - return rawbits_to_float(bits); + return RawbitsToFloat(bits); } -double double_pack(uint64_t sign, uint64_t exp, uint64_t mantissa) { +double DoublePack(uint64_t sign, uint64_t exp, uint64_t mantissa) { uint64_t bits = (sign << 63) | (exp << 52) | mantissa; - return rawbits_to_double(bits); + return RawbitsToDouble(bits); } -int float16classify(float16 value) { +int Float16Classify(Float16 value) { + uint16_t bits = Float16ToRawbits(value); uint16_t exponent_max = (1 << 5) - 1; uint16_t exponent_mask = exponent_max << 10; uint16_t mantissa_mask = (1 << 10) - 1; - uint16_t exponent = (value & exponent_mask) >> 10; - uint16_t mantissa = value & mantissa_mask; + uint16_t exponent = (bits & exponent_mask) >> 10; + uint16_t mantissa = bits & mantissa_mask; if (exponent == 0) { if (mantissa == 0) { return FP_ZERO; @@ -142,4 +200,356 @@ unsigned CountClearHalfWords(uint64_t imm, unsigned reg_size) { return count; } + +int BitCount(uint64_t value) { return CountSetBits(value); } + +// Float16 definitions. + +Float16::Float16(double dvalue) { + rawbits_ = + Float16ToRawbits(FPToFloat16(dvalue, FPTieEven, kIgnoreDefaultNaN)); +} + +namespace internal { + +SimFloat16 SimFloat16::operator-() const { + return RawbitsToFloat16(rawbits_ ^ 0x8000); +} + +// SimFloat16 definitions. +SimFloat16 SimFloat16::operator+(SimFloat16 rhs) const { + return static_cast(*this) + static_cast(rhs); +} + +SimFloat16 SimFloat16::operator-(SimFloat16 rhs) const { + return static_cast(*this) - static_cast(rhs); +} + +SimFloat16 SimFloat16::operator*(SimFloat16 rhs) const { + return static_cast(*this) * static_cast(rhs); +} + +SimFloat16 SimFloat16::operator/(SimFloat16 rhs) const { + return static_cast(*this) / static_cast(rhs); +} + +bool SimFloat16::operator<(SimFloat16 rhs) const { + return static_cast(*this) < static_cast(rhs); +} + +bool SimFloat16::operator>(SimFloat16 rhs) const { + return static_cast(*this) > static_cast(rhs); +} + +bool SimFloat16::operator==(SimFloat16 rhs) const { + if (IsNaN(*this) || IsNaN(rhs)) { + return false; + } else if (IsZero(rhs) && IsZero(*this)) { + // +0 and -0 should be treated as equal. + return true; + } + return this->rawbits_ == rhs.rawbits_; +} + +bool SimFloat16::operator!=(SimFloat16 rhs) const { return !(*this == rhs); } + +bool SimFloat16::operator==(double rhs) const { + return static_cast(*this) == static_cast(rhs); +} + +SimFloat16::operator double() const { + return FPToDouble(*this, kIgnoreDefaultNaN); +} + +Int64 BitCount(Uint32 value) { return CountSetBits(value.Get()); } + +} // namespace internal + +float FPToFloat(Float16 value, UseDefaultNaN DN, bool* exception) { + uint16_t bits = Float16ToRawbits(value); + uint32_t sign = bits >> 15; + uint32_t exponent = + ExtractUnsignedBitfield32(kFloat16MantissaBits + kFloat16ExponentBits - 1, + kFloat16MantissaBits, + bits); + uint32_t mantissa = + ExtractUnsignedBitfield32(kFloat16MantissaBits - 1, 0, bits); + + switch (Float16Classify(value)) { + case FP_ZERO: + return (sign == 0) ? 0.0f : -0.0f; + + case FP_INFINITE: + return (sign == 0) ? kFP32PositiveInfinity : kFP32NegativeInfinity; + + case FP_SUBNORMAL: { + // Calculate shift required to put mantissa into the most-significant bits + // of the destination mantissa. + int shift = CountLeadingZeros(mantissa << (32 - 10)); + + // Shift mantissa and discard implicit '1'. + mantissa <<= (kFloatMantissaBits - kFloat16MantissaBits) + shift + 1; + mantissa &= (1 << kFloatMantissaBits) - 1; + + // Adjust the exponent for the shift applied, and rebias. + exponent = exponent - shift + (-15 + 127); + break; + } + + case FP_NAN: + if (IsSignallingNaN(value)) { + if (exception != NULL) { + *exception = true; + } + } + if (DN == kUseDefaultNaN) return kFP32DefaultNaN; + + // Convert NaNs as the processor would: + // - The sign is propagated. + // - The payload (mantissa) is transferred entirely, except that the top + // bit is forced to '1', making the result a quiet NaN. The unused + // (low-order) payload bits are set to 0. + exponent = (1 << kFloatExponentBits) - 1; + + // Increase bits in mantissa, making low-order bits 0. + mantissa <<= (kFloatMantissaBits - kFloat16MantissaBits); + mantissa |= 1 << 22; // Force a quiet NaN. + break; + + case FP_NORMAL: + // Increase bits in mantissa, making low-order bits 0. + mantissa <<= (kFloatMantissaBits - kFloat16MantissaBits); + + // Change exponent bias. + exponent += (-15 + 127); + break; + + default: + VIXL_UNREACHABLE(); + } + return RawbitsToFloat((sign << 31) | (exponent << kFloatMantissaBits) | + mantissa); +} + + +float FPToFloat(double value, + FPRounding round_mode, + UseDefaultNaN DN, + bool* exception) { + // Only the FPTieEven rounding mode is implemented. + VIXL_ASSERT((round_mode == FPTieEven) || (round_mode == FPRoundOdd)); + USE(round_mode); + + switch (std::fpclassify(value)) { + case FP_NAN: { + if (IsSignallingNaN(value)) { + if (exception != NULL) { + *exception = true; + } + } + if (DN == kUseDefaultNaN) return kFP32DefaultNaN; + + // Convert NaNs as the processor would: + // - The sign is propagated. + // - The payload (mantissa) is transferred as much as possible, except + // that the top bit is forced to '1', making the result a quiet NaN. + uint64_t raw = DoubleToRawbits(value); + + uint32_t sign = raw >> 63; + uint32_t exponent = (1 << 8) - 1; + uint32_t payload = + static_cast(ExtractUnsignedBitfield64(50, 52 - 23, raw)); + payload |= (1 << 22); // Force a quiet NaN. + + return RawbitsToFloat((sign << 31) | (exponent << 23) | payload); + } + + case FP_ZERO: + case FP_INFINITE: { + // In a C++ cast, any value representable in the target type will be + // unchanged. This is always the case for +/-0.0 and infinities. + return static_cast(value); + } + + case FP_NORMAL: + case FP_SUBNORMAL: { + // Convert double-to-float as the processor would, assuming that FPCR.FZ + // (flush-to-zero) is not set. + uint64_t raw = DoubleToRawbits(value); + // Extract the IEEE-754 double components. + uint32_t sign = raw >> 63; + // Extract the exponent and remove the IEEE-754 encoding bias. + int32_t exponent = + static_cast(ExtractUnsignedBitfield64(62, 52, raw)) - 1023; + // Extract the mantissa and add the implicit '1' bit. + uint64_t mantissa = ExtractUnsignedBitfield64(51, 0, raw); + if (std::fpclassify(value) == FP_NORMAL) { + mantissa |= (UINT64_C(1) << 52); + } + return FPRoundToFloat(sign, exponent, mantissa, round_mode); + } + } + + VIXL_UNREACHABLE(); + return value; +} + +// TODO: We should consider implementing a full FPToDouble(Float16) +// conversion function (for performance reasons). +double FPToDouble(Float16 value, UseDefaultNaN DN, bool* exception) { + // We can rely on implicit float to double conversion here. + return FPToFloat(value, DN, exception); +} + + +double FPToDouble(float value, UseDefaultNaN DN, bool* exception) { + switch (std::fpclassify(value)) { + case FP_NAN: { + if (IsSignallingNaN(value)) { + if (exception != NULL) { + *exception = true; + } + } + if (DN == kUseDefaultNaN) return kFP64DefaultNaN; + + // Convert NaNs as the processor would: + // - The sign is propagated. + // - The payload (mantissa) is transferred entirely, except that the top + // bit is forced to '1', making the result a quiet NaN. The unused + // (low-order) payload bits are set to 0. + uint32_t raw = FloatToRawbits(value); + + uint64_t sign = raw >> 31; + uint64_t exponent = (1 << 11) - 1; + uint64_t payload = ExtractUnsignedBitfield64(21, 0, raw); + payload <<= (52 - 23); // The unused low-order bits should be 0. + payload |= (UINT64_C(1) << 51); // Force a quiet NaN. + + return RawbitsToDouble((sign << 63) | (exponent << 52) | payload); + } + + case FP_ZERO: + case FP_NORMAL: + case FP_SUBNORMAL: + case FP_INFINITE: { + // All other inputs are preserved in a standard cast, because every value + // representable using an IEEE-754 float is also representable using an + // IEEE-754 double. + return static_cast(value); + } + } + + VIXL_UNREACHABLE(); + return static_cast(value); +} + + +Float16 FPToFloat16(float value, + FPRounding round_mode, + UseDefaultNaN DN, + bool* exception) { + // Only the FPTieEven rounding mode is implemented. + VIXL_ASSERT(round_mode == FPTieEven); + USE(round_mode); + + uint32_t raw = FloatToRawbits(value); + int32_t sign = raw >> 31; + int32_t exponent = ExtractUnsignedBitfield32(30, 23, raw) - 127; + uint32_t mantissa = ExtractUnsignedBitfield32(22, 0, raw); + + switch (std::fpclassify(value)) { + case FP_NAN: { + if (IsSignallingNaN(value)) { + if (exception != NULL) { + *exception = true; + } + } + if (DN == kUseDefaultNaN) return kFP16DefaultNaN; + + // Convert NaNs as the processor would: + // - The sign is propagated. + // - The payload (mantissa) is transferred as much as possible, except + // that the top bit is forced to '1', making the result a quiet NaN. + uint16_t result = (sign == 0) ? Float16ToRawbits(kFP16PositiveInfinity) + : Float16ToRawbits(kFP16NegativeInfinity); + result |= mantissa >> (kFloatMantissaBits - kFloat16MantissaBits); + result |= (1 << 9); // Force a quiet NaN; + return RawbitsToFloat16(result); + } + + case FP_ZERO: + return (sign == 0) ? kFP16PositiveZero : kFP16NegativeZero; + + case FP_INFINITE: + return (sign == 0) ? kFP16PositiveInfinity : kFP16NegativeInfinity; + + case FP_NORMAL: + case FP_SUBNORMAL: { + // Convert float-to-half as the processor would, assuming that FPCR.FZ + // (flush-to-zero) is not set. + + // Add the implicit '1' bit to the mantissa. + mantissa += (1 << 23); + return FPRoundToFloat16(sign, exponent, mantissa, round_mode); + } + } + + VIXL_UNREACHABLE(); + return kFP16PositiveZero; +} + + +Float16 FPToFloat16(double value, + FPRounding round_mode, + UseDefaultNaN DN, + bool* exception) { + // Only the FPTieEven rounding mode is implemented. + VIXL_ASSERT(round_mode == FPTieEven); + USE(round_mode); + + uint64_t raw = DoubleToRawbits(value); + int32_t sign = raw >> 63; + int64_t exponent = ExtractUnsignedBitfield64(62, 52, raw) - 1023; + uint64_t mantissa = ExtractUnsignedBitfield64(51, 0, raw); + + switch (std::fpclassify(value)) { + case FP_NAN: { + if (IsSignallingNaN(value)) { + if (exception != NULL) { + *exception = true; + } + } + if (DN == kUseDefaultNaN) return kFP16DefaultNaN; + + // Convert NaNs as the processor would: + // - The sign is propagated. + // - The payload (mantissa) is transferred as much as possible, except + // that the top bit is forced to '1', making the result a quiet NaN. + uint16_t result = (sign == 0) ? Float16ToRawbits(kFP16PositiveInfinity) + : Float16ToRawbits(kFP16NegativeInfinity); + result |= mantissa >> (kDoubleMantissaBits - kFloat16MantissaBits); + result |= (1 << 9); // Force a quiet NaN; + return RawbitsToFloat16(result); + } + + case FP_ZERO: + return (sign == 0) ? kFP16PositiveZero : kFP16NegativeZero; + + case FP_INFINITE: + return (sign == 0) ? kFP16PositiveInfinity : kFP16NegativeInfinity; + case FP_NORMAL: + case FP_SUBNORMAL: { + // Convert double-to-half as the processor would, assuming that FPCR.FZ + // (flush-to-zero) is not set. + + // Add the implicit '1' bit to the mantissa. + mantissa += (UINT64_C(1) << 52); + return FPRoundToFloat16(sign, exponent, mantissa, round_mode); + } + } + + VIXL_UNREACHABLE(); + return kFP16PositiveZero; +} + } // namespace vixl diff --git a/js/src/jit/arm64/vixl/Utils-vixl.h b/js/src/jit/arm64/vixl/Utils-vixl.h index add66f624e28..d1f6a835f854 100644 --- a/js/src/jit/arm64/vixl/Utils-vixl.h +++ b/js/src/jit/arm64/vixl/Utils-vixl.h @@ -1,4 +1,4 @@ -// Copyright 2015, ARM Limited +// Copyright 2015, VIXL authors // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -29,100 +29,364 @@ #include "mozilla/FloatingPoint.h" +#include +#include +#include +#include + #include "jit/arm64/vixl/CompilerIntrinsics-vixl.h" #include "jit/arm64/vixl/Globals-vixl.h" namespace vixl { // Macros for compile-time format checking. -#if defined(__GNUC__) +#if GCC_VERSION_OR_NEWER(4, 4, 0) #define PRINTF_CHECK(format_index, varargs_index) \ - __attribute__((format(printf, format_index, varargs_index))) + __attribute__((format(gnu_printf, format_index, varargs_index))) #else #define PRINTF_CHECK(format_index, varargs_index) #endif +#ifdef __GNUC__ +#define VIXL_HAS_DEPRECATED_WITH_MSG +#elif defined(__clang__) +#ifdef __has_extension +#define VIXL_HAS_DEPRECATED_WITH_MSG +#endif +#endif + +#ifdef VIXL_HAS_DEPRECATED_WITH_MSG +#define VIXL_DEPRECATED(replaced_by, declarator) \ + __attribute__((deprecated("Use \"" replaced_by "\" instead"))) declarator +#else +#define VIXL_DEPRECATED(replaced_by, declarator) declarator +#endif + +#ifdef VIXL_DEBUG +#define VIXL_UNREACHABLE_OR_FALLTHROUGH() VIXL_UNREACHABLE() +#else +#define VIXL_UNREACHABLE_OR_FALLTHROUGH() VIXL_FALLTHROUGH() +#endif + +template +size_t ArrayLength(const T (&)[n]) { + return n; +} + // Check number width. -inline bool is_intn(unsigned n, int64_t x) { +// TODO: Refactor these using templates. +inline bool IsIntN(unsigned n, uint32_t x) { + VIXL_ASSERT((0 < n) && (n < 32)); + uint32_t limit = UINT32_C(1) << (n - 1); + return x < limit; +} +inline bool IsIntN(unsigned n, int32_t x) { + VIXL_ASSERT((0 < n) && (n < 32)); + int32_t limit = INT32_C(1) << (n - 1); + return (-limit <= x) && (x < limit); +} +inline bool IsIntN(unsigned n, uint64_t x) { + VIXL_ASSERT((0 < n) && (n < 64)); + uint64_t limit = UINT64_C(1) << (n - 1); + return x < limit; +} +inline bool IsIntN(unsigned n, int64_t x) { VIXL_ASSERT((0 < n) && (n < 64)); int64_t limit = INT64_C(1) << (n - 1); return (-limit <= x) && (x < limit); } +VIXL_DEPRECATED("IsIntN", inline bool is_intn(unsigned n, int64_t x)) { + return IsIntN(n, x); +} -inline bool is_uintn(unsigned n, int64_t x) { +inline bool IsUintN(unsigned n, uint32_t x) { + VIXL_ASSERT((0 < n) && (n < 32)); + return !(x >> n); +} +inline bool IsUintN(unsigned n, int32_t x) { + VIXL_ASSERT((0 < n) && (n < 32)); + // Convert to an unsigned integer to avoid implementation-defined behavior. + return !(static_cast(x) >> n); +} +inline bool IsUintN(unsigned n, uint64_t x) { VIXL_ASSERT((0 < n) && (n < 64)); return !(x >> n); } - -inline uint32_t truncate_to_intn(unsigned n, int64_t x) { +inline bool IsUintN(unsigned n, int64_t x) { VIXL_ASSERT((0 < n) && (n < 64)); - return static_cast(x & ((INT64_C(1) << n) - 1)); + // Convert to an unsigned integer to avoid implementation-defined behavior. + return !(static_cast(x) >> n); +} +VIXL_DEPRECATED("IsUintN", inline bool is_uintn(unsigned n, int64_t x)) { + return IsUintN(n, x); } -#define INT_1_TO_63_LIST(V) \ +inline uint64_t TruncateToUintN(unsigned n, uint64_t x) { + VIXL_ASSERT((0 < n) && (n < 64)); + return static_cast(x) & ((UINT64_C(1) << n) - 1); +} +VIXL_DEPRECATED("TruncateToUintN", + inline uint64_t truncate_to_intn(unsigned n, int64_t x)) { + return TruncateToUintN(n, x); +} + +// clang-format off +#define INT_1_TO_32_LIST(V) \ V(1) V(2) V(3) V(4) V(5) V(6) V(7) V(8) \ V(9) V(10) V(11) V(12) V(13) V(14) V(15) V(16) \ V(17) V(18) V(19) V(20) V(21) V(22) V(23) V(24) \ -V(25) V(26) V(27) V(28) V(29) V(30) V(31) V(32) \ +V(25) V(26) V(27) V(28) V(29) V(30) V(31) V(32) + +#define INT_33_TO_63_LIST(V) \ V(33) V(34) V(35) V(36) V(37) V(38) V(39) V(40) \ V(41) V(42) V(43) V(44) V(45) V(46) V(47) V(48) \ V(49) V(50) V(51) V(52) V(53) V(54) V(55) V(56) \ V(57) V(58) V(59) V(60) V(61) V(62) V(63) -#define DECLARE_IS_INT_N(N) \ -inline bool is_int##N(int64_t x) { return is_intn(N, x); } -#define DECLARE_IS_UINT_N(N) \ -inline bool is_uint##N(int64_t x) { return is_uintn(N, x); } -#define DECLARE_TRUNCATE_TO_INT_N(N) \ -inline uint32_t truncate_to_int##N(int x) { return truncate_to_intn(N, x); } +#define INT_1_TO_63_LIST(V) INT_1_TO_32_LIST(V) INT_33_TO_63_LIST(V) + +// clang-format on + +#define DECLARE_IS_INT_N(N) \ + inline bool IsInt##N(int64_t x) { return IsIntN(N, x); } \ + VIXL_DEPRECATED("IsInt" #N, inline bool is_int##N(int64_t x)) { \ + return IsIntN(N, x); \ + } + +#define DECLARE_IS_UINT_N(N) \ + inline bool IsUint##N(int64_t x) { return IsUintN(N, x); } \ + VIXL_DEPRECATED("IsUint" #N, inline bool is_uint##N(int64_t x)) { \ + return IsUintN(N, x); \ + } + +#define DECLARE_TRUNCATE_TO_UINT_32(N) \ + inline uint32_t TruncateToUint##N(uint64_t x) { \ + return static_cast(TruncateToUintN(N, x)); \ + } \ + VIXL_DEPRECATED("TruncateToUint" #N, \ + inline uint32_t truncate_to_int##N(int64_t x)) { \ + return TruncateToUint##N(x); \ + } + INT_1_TO_63_LIST(DECLARE_IS_INT_N) INT_1_TO_63_LIST(DECLARE_IS_UINT_N) -INT_1_TO_63_LIST(DECLARE_TRUNCATE_TO_INT_N) +INT_1_TO_32_LIST(DECLARE_TRUNCATE_TO_UINT_32) + #undef DECLARE_IS_INT_N #undef DECLARE_IS_UINT_N #undef DECLARE_TRUNCATE_TO_INT_N // Bit field extraction. -inline uint32_t unsigned_bitextract_32(int msb, int lsb, uint32_t x) { - return (x >> lsb) & ((1 << (1 + msb - lsb)) - 1); -} - -inline uint64_t unsigned_bitextract_64(int msb, int lsb, uint64_t x) { +inline uint64_t ExtractUnsignedBitfield64(int msb, int lsb, uint64_t x) { + VIXL_ASSERT((static_cast(msb) < sizeof(x) * 8) && (lsb >= 0) && + (msb >= lsb)); + if ((msb == 63) && (lsb == 0)) return x; return (x >> lsb) & ((static_cast(1) << (1 + msb - lsb)) - 1); } -inline int32_t signed_bitextract_32(int msb, int lsb, int32_t x) { - return (x << (31 - msb)) >> (lsb + 31 - msb); + +inline uint32_t ExtractUnsignedBitfield32(int msb, int lsb, uint32_t x) { + VIXL_ASSERT((static_cast(msb) < sizeof(x) * 8) && (lsb >= 0) && + (msb >= lsb)); + return TruncateToUint32(ExtractUnsignedBitfield64(msb, lsb, x)); } -inline int64_t signed_bitextract_64(int msb, int lsb, int64_t x) { - return (x << (63 - msb)) >> (lsb + 63 - msb); + +inline int64_t ExtractSignedBitfield64(int msb, int lsb, int64_t x) { + VIXL_ASSERT((static_cast(msb) < sizeof(x) * 8) && (lsb >= 0) && + (msb >= lsb)); + uint64_t temp = ExtractUnsignedBitfield64(msb, lsb, x); + // If the highest extracted bit is set, sign extend. + if ((temp >> (msb - lsb)) == 1) { + temp |= ~UINT64_C(0) << (msb - lsb); + } + int64_t result; + memcpy(&result, &temp, sizeof(result)); + return result; } + +inline int32_t ExtractSignedBitfield32(int msb, int lsb, int32_t x) { + VIXL_ASSERT((static_cast(msb) < sizeof(x) * 8) && (lsb >= 0) && + (msb >= lsb)); + uint32_t temp = TruncateToUint32(ExtractSignedBitfield64(msb, lsb, x)); + int32_t result; + memcpy(&result, &temp, sizeof(result)); + return result; +} + + +inline uint64_t RotateRight(uint64_t value, + unsigned int rotate, + unsigned int width) { + VIXL_ASSERT((width > 0) && (width <= 64)); + uint64_t width_mask = ~UINT64_C(0) >> (64 - width); + rotate &= 63; + if (rotate > 0) { + value &= width_mask; + value = (value << (width - rotate)) | (value >> rotate); + } + return value & width_mask; +} + + +// Wrapper class for passing FP16 values through the assembler. +// This is purely to aid with type checking/casting. +class Float16 { + public: + explicit Float16(double dvalue); + Float16() : rawbits_(0x0) {} + friend uint16_t Float16ToRawbits(Float16 value); + friend Float16 RawbitsToFloat16(uint16_t bits); + + protected: + uint16_t rawbits_; +}; + // Floating point representation. -uint32_t float_to_rawbits(float value); -uint64_t double_to_rawbits(double value); -float rawbits_to_float(uint32_t bits); -double rawbits_to_double(uint64_t bits); +uint16_t Float16ToRawbits(Float16 value); -uint32_t float_sign(float val); -uint32_t float_exp(float val); -uint32_t float_mantissa(float val); -uint32_t double_sign(double val); -uint32_t double_exp(double val); -uint64_t double_mantissa(double val); -float float_pack(uint32_t sign, uint32_t exp, uint32_t mantissa); -double double_pack(uint64_t sign, uint64_t exp, uint64_t mantissa); +uint32_t FloatToRawbits(float value); +VIXL_DEPRECATED("FloatToRawbits", + inline uint32_t float_to_rawbits(float value)) { + return FloatToRawbits(value); +} + +uint64_t DoubleToRawbits(double value); +VIXL_DEPRECATED("DoubleToRawbits", + inline uint64_t double_to_rawbits(double value)) { + return DoubleToRawbits(value); +} + +Float16 RawbitsToFloat16(uint16_t bits); + +float RawbitsToFloat(uint32_t bits); +VIXL_DEPRECATED("RawbitsToFloat", + inline float rawbits_to_float(uint32_t bits)) { + return RawbitsToFloat(bits); +} + +double RawbitsToDouble(uint64_t bits); +VIXL_DEPRECATED("RawbitsToDouble", + inline double rawbits_to_double(uint64_t bits)) { + return RawbitsToDouble(bits); +} + +namespace internal { + +// Internal simulation class used solely by the simulator to +// provide an abstraction layer for any half-precision arithmetic. +class SimFloat16 : public Float16 { + public: + // TODO: We should investigate making this constructor explicit. + // This is currently difficult to do due to a number of templated + // functions in the simulator which rely on returning double values. + SimFloat16(double dvalue) : Float16(dvalue) {} // NOLINT(runtime/explicit) + SimFloat16(Float16 f) { // NOLINT(runtime/explicit) + this->rawbits_ = Float16ToRawbits(f); + } + SimFloat16() : Float16() {} + SimFloat16 operator-() const; + SimFloat16 operator+(SimFloat16 rhs) const; + SimFloat16 operator-(SimFloat16 rhs) const; + SimFloat16 operator*(SimFloat16 rhs) const; + SimFloat16 operator/(SimFloat16 rhs) const; + bool operator<(SimFloat16 rhs) const; + bool operator>(SimFloat16 rhs) const; + bool operator==(SimFloat16 rhs) const; + bool operator!=(SimFloat16 rhs) const; + // This is necessary for conversions peformed in (macro asm) Fmov. + bool operator==(double rhs) const; + operator double() const; +}; +} // namespace internal + +uint32_t Float16Sign(internal::SimFloat16 value); + +uint32_t Float16Exp(internal::SimFloat16 value); + +uint32_t Float16Mantissa(internal::SimFloat16 value); + +uint32_t FloatSign(float value); +VIXL_DEPRECATED("FloatSign", inline uint32_t float_sign(float value)) { + return FloatSign(value); +} + +uint32_t FloatExp(float value); +VIXL_DEPRECATED("FloatExp", inline uint32_t float_exp(float value)) { + return FloatExp(value); +} + +uint32_t FloatMantissa(float value); +VIXL_DEPRECATED("FloatMantissa", inline uint32_t float_mantissa(float value)) { + return FloatMantissa(value); +} + +uint32_t DoubleSign(double value); +VIXL_DEPRECATED("DoubleSign", inline uint32_t double_sign(double value)) { + return DoubleSign(value); +} + +uint32_t DoubleExp(double value); +VIXL_DEPRECATED("DoubleExp", inline uint32_t double_exp(double value)) { + return DoubleExp(value); +} + +uint64_t DoubleMantissa(double value); +VIXL_DEPRECATED("DoubleMantissa", + inline uint64_t double_mantissa(double value)) { + return DoubleMantissa(value); +} + +internal::SimFloat16 Float16Pack(uint16_t sign, + uint16_t exp, + uint16_t mantissa); + +float FloatPack(uint32_t sign, uint32_t exp, uint32_t mantissa); +VIXL_DEPRECATED("FloatPack", + inline float float_pack(uint32_t sign, + uint32_t exp, + uint32_t mantissa)) { + return FloatPack(sign, exp, mantissa); +} + +double DoublePack(uint64_t sign, uint64_t exp, uint64_t mantissa); +VIXL_DEPRECATED("DoublePack", + inline double double_pack(uint32_t sign, + uint32_t exp, + uint64_t mantissa)) { + return DoublePack(sign, exp, mantissa); +} // An fpclassify() function for 16-bit half-precision floats. -int float16classify(float16 value); +int Float16Classify(Float16 value); +VIXL_DEPRECATED("Float16Classify", inline int float16classify(uint16_t value)) { + return Float16Classify(RawbitsToFloat16(value)); +} + +bool IsZero(Float16 value); + +inline bool IsNaN(float value) { return std::isnan(value); } + +inline bool IsNaN(double value) { return std::isnan(value); } + +inline bool IsNaN(Float16 value) { return Float16Classify(value) == FP_NAN; } + +inline bool IsInf(float value) { return std::isinf(value); } + +inline bool IsInf(double value) { return std::isinf(value); } + +inline bool IsInf(Float16 value) { + return Float16Classify(value) == FP_INFINITE; +} + // NaN tests. inline bool IsSignallingNaN(double num) { const uint64_t kFP64QuietNaNMask = UINT64_C(0x0008000000000000); - uint64_t raw = double_to_rawbits(num); - if (mozilla::IsNaN(num) && ((raw & kFP64QuietNaNMask) == 0)) { + uint64_t raw = DoubleToRawbits(num); + if (IsNaN(num) && ((raw & kFP64QuietNaNMask) == 0)) { return true; } return false; @@ -131,39 +395,46 @@ inline bool IsSignallingNaN(double num) { inline bool IsSignallingNaN(float num) { const uint32_t kFP32QuietNaNMask = 0x00400000; - uint32_t raw = float_to_rawbits(num); - if (mozilla::IsNaN(num) && ((raw & kFP32QuietNaNMask) == 0)) { + uint32_t raw = FloatToRawbits(num); + if (IsNaN(num) && ((raw & kFP32QuietNaNMask) == 0)) { return true; } return false; } -inline bool IsSignallingNaN(float16 num) { +inline bool IsSignallingNaN(Float16 num) { const uint16_t kFP16QuietNaNMask = 0x0200; - return (float16classify(num) == FP_NAN) && - ((num & kFP16QuietNaNMask) == 0); + return IsNaN(num) && ((Float16ToRawbits(num) & kFP16QuietNaNMask) == 0); } template inline bool IsQuietNaN(T num) { - return mozilla::IsNaN(num) && !IsSignallingNaN(num); + return IsNaN(num) && !IsSignallingNaN(num); } // Convert the NaN in 'num' to a quiet NaN. inline double ToQuietNaN(double num) { const uint64_t kFP64QuietNaNMask = UINT64_C(0x0008000000000000); - VIXL_ASSERT(mozilla::IsNaN(num)); - return rawbits_to_double(double_to_rawbits(num) | kFP64QuietNaNMask); + VIXL_ASSERT(IsNaN(num)); + return RawbitsToDouble(DoubleToRawbits(num) | kFP64QuietNaNMask); } inline float ToQuietNaN(float num) { const uint32_t kFP32QuietNaNMask = 0x00400000; - VIXL_ASSERT(mozilla::IsNaN(num)); - return rawbits_to_float(float_to_rawbits(num) | kFP32QuietNaNMask); + VIXL_ASSERT(IsNaN(num)); + return RawbitsToFloat(FloatToRawbits(num) | kFP32QuietNaNMask); +} + + +inline internal::SimFloat16 ToQuietNaN(internal::SimFloat16 num) { + const uint16_t kFP16QuietNaNMask = 0x0200; + VIXL_ASSERT(IsNaN(num)); + return internal::SimFloat16( + RawbitsToFloat16(Float16ToRawbits(num) | kFP16QuietNaNMask)); } @@ -178,19 +449,17 @@ inline float FusedMultiplyAdd(float op1, float op2, float a) { } -inline uint64_t LowestSetBit(uint64_t value) { - return value & (0 - value); -} +inline uint64_t LowestSetBit(uint64_t value) { return value & -value; } -template +template inline int HighestSetBitPosition(T value) { VIXL_ASSERT(value != 0); return (sizeof(value) * 8 - 1) - CountLeadingZeros(value); } -template +template inline int WhichPowerOf2(V value) { VIXL_ASSERT(IsPowerOf2(value)); return CountTrailingZeros(value); @@ -200,6 +469,9 @@ inline int WhichPowerOf2(V value) { unsigned CountClearHalfWords(uint64_t imm, unsigned reg_size); +int BitCount(uint64_t value); + + template T ReverseBits(T value) { VIXL_ASSERT((sizeof(value) == 1) || (sizeof(value) == 2) || @@ -213,14 +485,25 @@ T ReverseBits(T value) { } +template +inline T SignExtend(T val, int bitSize) { + VIXL_ASSERT(bitSize > 0); + T mask = (T(2) << (bitSize - 1)) - T(1); + val &= mask; + T sign_bits = -((val >> (bitSize - 1)) << bitSize); + val |= sign_bits; + return val; +} + + template T ReverseBytes(T value, int block_bytes_log2) { VIXL_ASSERT((sizeof(value) == 4) || (sizeof(value) == 8)); - VIXL_ASSERT((1ULL << block_bytes_log2) <= sizeof(value)); + VIXL_ASSERT((1U << block_bytes_log2) <= sizeof(value)); // Split the 64-bit value into an 8-bit array, where b[0] is the least // significant byte, and b[7] is the most significant. uint8_t bytes[8]; - uint64_t mask = 0xff00000000000000; + uint64_t mask = UINT64_C(0xff00000000000000); for (int i = 7; i >= 0; i--) { bytes[i] = (static_cast(value) & mask) >> (i * 8); mask >>= 8; @@ -231,56 +514,770 @@ T ReverseBytes(T value, int block_bytes_log2) { // permute_table[1] is used by REV32_x, REV_w // permute_table[2] is used by REV_x VIXL_ASSERT((0 < block_bytes_log2) && (block_bytes_log2 < 4)); - static const uint8_t permute_table[3][8] = { {6, 7, 4, 5, 2, 3, 0, 1}, - {4, 5, 6, 7, 0, 1, 2, 3}, - {0, 1, 2, 3, 4, 5, 6, 7} }; - T result = 0; + static const uint8_t permute_table[3][8] = {{6, 7, 4, 5, 2, 3, 0, 1}, + {4, 5, 6, 7, 0, 1, 2, 3}, + {0, 1, 2, 3, 4, 5, 6, 7}}; + uint64_t temp = 0; for (int i = 0; i < 8; i++) { - result <<= 8; - result |= bytes[permute_table[block_bytes_log2 - 1][i]]; + temp <<= 8; + temp |= bytes[permute_table[block_bytes_log2 - 1][i]]; } + + T result; + VIXL_STATIC_ASSERT(sizeof(result) <= sizeof(temp)); + memcpy(&result, &temp, sizeof(result)); return result; } +template +inline bool IsMultiple(T value) { + VIXL_ASSERT(IsPowerOf2(MULTIPLE)); + return (value & (MULTIPLE - 1)) == 0; +} + +template +inline bool IsMultiple(T value, unsigned multiple) { + VIXL_ASSERT(IsPowerOf2(multiple)); + return (value & (multiple - 1)) == 0; +} + +template +inline bool IsAligned(T pointer, int alignment) { + VIXL_ASSERT(IsPowerOf2(alignment)); + return (pointer & (alignment - 1)) == 0; +} // Pointer alignment // TODO: rename/refactor to make it specific to instructions. -template +template +inline bool IsAligned(T pointer) { + VIXL_ASSERT(sizeof(pointer) == sizeof(intptr_t)); // NOLINT(runtime/sizeof) + // Use C-style casts to get static_cast behaviour for integral types (T), and + // reinterpret_cast behaviour for other types. + return IsAligned((intptr_t)(pointer), ALIGN); +} + +template bool IsWordAligned(T pointer) { - VIXL_ASSERT(sizeof(pointer) == sizeof(intptr_t)); // NOLINT(runtime/sizeof) - return ((intptr_t)(pointer) & 3) == 0; + return IsAligned<4>(pointer); } -// Increment a pointer (up to 64 bits) until it has the specified alignment. -template -T AlignUp(T pointer, size_t alignment) { +// Increment a pointer until it has the specified alignment. The alignment must +// be a power of two. +template +T AlignUp(T pointer, + typename Unsigned::type alignment) { + VIXL_ASSERT(IsPowerOf2(alignment)); // Use C-style casts to get static_cast behaviour for integral types (T), and // reinterpret_cast behaviour for other types. - uint64_t pointer_raw = (uint64_t)pointer; + typename Unsigned::type pointer_raw = + (typename Unsigned::type)pointer; VIXL_STATIC_ASSERT(sizeof(pointer) <= sizeof(pointer_raw)); - size_t align_step = (alignment - pointer_raw) % alignment; - VIXL_ASSERT((pointer_raw + align_step) % alignment == 0); + size_t mask = alignment - 1; + T result = (T)((pointer_raw + mask) & ~mask); + VIXL_ASSERT(result >= pointer); - return (T)(pointer_raw + align_step); + return result; } -// Decrement a pointer (up to 64 bits) until it has the specified alignment. -template -T AlignDown(T pointer, size_t alignment) { +// Decrement a pointer until it has the specified alignment. The alignment must +// be a power of two. +template +T AlignDown(T pointer, + typename Unsigned::type alignment) { + VIXL_ASSERT(IsPowerOf2(alignment)); // Use C-style casts to get static_cast behaviour for integral types (T), and // reinterpret_cast behaviour for other types. - uint64_t pointer_raw = (uint64_t)pointer; + typename Unsigned::type pointer_raw = + (typename Unsigned::type)pointer; VIXL_STATIC_ASSERT(sizeof(pointer) <= sizeof(pointer_raw)); - size_t align_step = pointer_raw % alignment; - VIXL_ASSERT((pointer_raw - align_step) % alignment == 0); - - return (T)(pointer_raw - align_step); + size_t mask = alignment - 1; + return (T)(pointer_raw & ~mask); } + +template +inline T ExtractBit(T value, unsigned bit) { + return (value >> bit) & T(1); +} + +template +inline Td ExtractBits(Ts value, int least_significant_bit, Td mask) { + return Td((value >> least_significant_bit) & Ts(mask)); +} + +template +inline void AssignBit(Td& dst, // NOLINT(runtime/references) + int bit, + Ts value) { + VIXL_ASSERT((value == Ts(0)) || (value == Ts(1))); + VIXL_ASSERT(bit >= 0); + VIXL_ASSERT(bit < static_cast(sizeof(Td) * 8)); + Td mask(1); + dst &= ~(mask << bit); + dst |= Td(value) << bit; +} + +template +inline void AssignBits(Td& dst, // NOLINT(runtime/references) + int least_significant_bit, + Ts mask, + Ts value) { + VIXL_ASSERT(least_significant_bit >= 0); + VIXL_ASSERT(least_significant_bit < static_cast(sizeof(Td) * 8)); + VIXL_ASSERT(((Td(mask) << least_significant_bit) >> least_significant_bit) == + Td(mask)); + VIXL_ASSERT((value & mask) == value); + dst &= ~(Td(mask) << least_significant_bit); + dst |= Td(value) << least_significant_bit; +} + +class VFP { + public: + static uint32_t FP32ToImm8(float imm) { + // bits: aBbb.bbbc.defg.h000.0000.0000.0000.0000 + uint32_t bits = FloatToRawbits(imm); + // bit7: a000.0000 + uint32_t bit7 = ((bits >> 31) & 0x1) << 7; + // bit6: 0b00.0000 + uint32_t bit6 = ((bits >> 29) & 0x1) << 6; + // bit5_to_0: 00cd.efgh + uint32_t bit5_to_0 = (bits >> 19) & 0x3f; + return static_cast(bit7 | bit6 | bit5_to_0); + } + static uint32_t FP64ToImm8(double imm) { + // bits: aBbb.bbbb.bbcd.efgh.0000.0000.0000.0000 + // 0000.0000.0000.0000.0000.0000.0000.0000 + uint64_t bits = DoubleToRawbits(imm); + // bit7: a000.0000 + uint64_t bit7 = ((bits >> 63) & 0x1) << 7; + // bit6: 0b00.0000 + uint64_t bit6 = ((bits >> 61) & 0x1) << 6; + // bit5_to_0: 00cd.efgh + uint64_t bit5_to_0 = (bits >> 48) & 0x3f; + + return static_cast(bit7 | bit6 | bit5_to_0); + } + static float Imm8ToFP32(uint32_t imm8) { + // Imm8: abcdefgh (8 bits) + // Single: aBbb.bbbc.defg.h000.0000.0000.0000.0000 (32 bits) + // where B is b ^ 1 + uint32_t bits = imm8; + uint32_t bit7 = (bits >> 7) & 0x1; + uint32_t bit6 = (bits >> 6) & 0x1; + uint32_t bit5_to_0 = bits & 0x3f; + uint32_t result = (bit7 << 31) | ((32 - bit6) << 25) | (bit5_to_0 << 19); + + return RawbitsToFloat(result); + } + static double Imm8ToFP64(uint32_t imm8) { + // Imm8: abcdefgh (8 bits) + // Double: aBbb.bbbb.bbcd.efgh.0000.0000.0000.0000 + // 0000.0000.0000.0000.0000.0000.0000.0000 (64 bits) + // where B is b ^ 1 + uint32_t bits = imm8; + uint64_t bit7 = (bits >> 7) & 0x1; + uint64_t bit6 = (bits >> 6) & 0x1; + uint64_t bit5_to_0 = bits & 0x3f; + uint64_t result = (bit7 << 63) | ((256 - bit6) << 54) | (bit5_to_0 << 48); + return RawbitsToDouble(result); + } + static bool IsImmFP32(float imm) { + // Valid values will have the form: + // aBbb.bbbc.defg.h000.0000.0000.0000.0000 + uint32_t bits = FloatToRawbits(imm); + // bits[19..0] are cleared. + if ((bits & 0x7ffff) != 0) { + return false; + } + + + // bits[29..25] are all set or all cleared. + uint32_t b_pattern = (bits >> 16) & 0x3e00; + if (b_pattern != 0 && b_pattern != 0x3e00) { + return false; + } + // bit[30] and bit[29] are opposite. + if (((bits ^ (bits << 1)) & 0x40000000) == 0) { + return false; + } + return true; + } + static bool IsImmFP64(double imm) { + // Valid values will have the form: + // aBbb.bbbb.bbcd.efgh.0000.0000.0000.0000 + // 0000.0000.0000.0000.0000.0000.0000.0000 + uint64_t bits = DoubleToRawbits(imm); + // bits[47..0] are cleared. + if ((bits & 0x0000ffffffffffff) != 0) { + return false; + } + // bits[61..54] are all set or all cleared. + uint32_t b_pattern = (bits >> 48) & 0x3fc0; + if ((b_pattern != 0) && (b_pattern != 0x3fc0)) { + return false; + } + // bit[62] and bit[61] are opposite. + if (((bits ^ (bits << 1)) & (UINT64_C(1) << 62)) == 0) { + return false; + } + return true; + } +}; + +class BitField { + // ForEachBitHelper is a functor that will call + // bool ForEachBitHelper::execute(ElementType id) const + // and expects a boolean in return whether to continue (if true) + // or stop (if false) + // check_set will check if the bits are on (true) or off(false) + template + bool ForEachBit(const ForEachBitHelper& helper) { + for (int i = 0; static_cast(i) < bitfield_.size(); i++) { + if (bitfield_[i] == check_set) + if (!helper.execute(i)) return false; + } + return true; + } + + public: + explicit BitField(unsigned size) : bitfield_(size, 0) {} + + void Set(int i) { + VIXL_ASSERT((i >= 0) && (static_cast(i) < bitfield_.size())); + bitfield_[i] = true; + } + + void Unset(int i) { + VIXL_ASSERT((i >= 0) && (static_cast(i) < bitfield_.size())); + bitfield_[i] = true; + } + + bool IsSet(int i) const { return bitfield_[i]; } + + // For each bit not set in the bitfield call the execute functor + // execute. + // ForEachBitSetHelper::execute returns true if the iteration through + // the bits can continue, otherwise it will stop. + // struct ForEachBitSetHelper { + // bool execute(int /*id*/) { return false; } + // }; + template + bool ForEachBitNotSet(const ForEachBitNotSetHelper& helper) { + return ForEachBit(helper); + } + + // For each bit set in the bitfield call the execute functor + // execute. + template + bool ForEachBitSet(const ForEachBitSetHelper& helper) { + return ForEachBit(helper); + } + + private: + std::vector bitfield_; +}; + +namespace internal { + +typedef int64_t Int64; +class Uint64; +class Uint128; + +class Uint32 { + uint32_t data_; + + public: + // Unlike uint32_t, Uint32 has a default constructor. + Uint32() { data_ = 0; } + explicit Uint32(uint32_t data) : data_(data) {} + inline explicit Uint32(Uint64 data); + uint32_t Get() const { return data_; } + template + int32_t GetSigned() const { + return ExtractSignedBitfield32(N - 1, 0, data_); + } + int32_t GetSigned() const { return data_; } + Uint32 operator~() const { return Uint32(~data_); } + Uint32 operator-() const { return Uint32(-data_); } + bool operator==(Uint32 value) const { return data_ == value.data_; } + bool operator!=(Uint32 value) const { return data_ != value.data_; } + bool operator>(Uint32 value) const { return data_ > value.data_; } + Uint32 operator+(Uint32 value) const { return Uint32(data_ + value.data_); } + Uint32 operator-(Uint32 value) const { return Uint32(data_ - value.data_); } + Uint32 operator&(Uint32 value) const { return Uint32(data_ & value.data_); } + Uint32 operator&=(Uint32 value) { + data_ &= value.data_; + return *this; + } + Uint32 operator^(Uint32 value) const { return Uint32(data_ ^ value.data_); } + Uint32 operator^=(Uint32 value) { + data_ ^= value.data_; + return *this; + } + Uint32 operator|(Uint32 value) const { return Uint32(data_ | value.data_); } + Uint32 operator|=(Uint32 value) { + data_ |= value.data_; + return *this; + } + // Unlike uint32_t, the shift functions can accept negative shift and + // return 0 when the shift is too big. + Uint32 operator>>(int shift) const { + if (shift == 0) return *this; + if (shift < 0) { + int tmp = -shift; + if (tmp >= 32) return Uint32(0); + return Uint32(data_ << tmp); + } + int tmp = shift; + if (tmp >= 32) return Uint32(0); + return Uint32(data_ >> tmp); + } + Uint32 operator<<(int shift) const { + if (shift == 0) return *this; + if (shift < 0) { + int tmp = -shift; + if (tmp >= 32) return Uint32(0); + return Uint32(data_ >> tmp); + } + int tmp = shift; + if (tmp >= 32) return Uint32(0); + return Uint32(data_ << tmp); + } +}; + +class Uint64 { + uint64_t data_; + + public: + // Unlike uint64_t, Uint64 has a default constructor. + Uint64() { data_ = 0; } + explicit Uint64(uint64_t data) : data_(data) {} + explicit Uint64(Uint32 data) : data_(data.Get()) {} + inline explicit Uint64(Uint128 data); + uint64_t Get() const { return data_; } + int64_t GetSigned(int N) const { + return ExtractSignedBitfield64(N - 1, 0, data_); + } + int64_t GetSigned() const { return data_; } + Uint32 ToUint32() const { + VIXL_ASSERT((data_ >> 32) == 0); + return Uint32(static_cast(data_)); + } + Uint32 GetHigh32() const { return Uint32(data_ >> 32); } + Uint32 GetLow32() const { return Uint32(data_ & 0xffffffff); } + Uint64 operator~() const { return Uint64(~data_); } + Uint64 operator-() const { return Uint64(-data_); } + bool operator==(Uint64 value) const { return data_ == value.data_; } + bool operator!=(Uint64 value) const { return data_ != value.data_; } + Uint64 operator+(Uint64 value) const { return Uint64(data_ + value.data_); } + Uint64 operator-(Uint64 value) const { return Uint64(data_ - value.data_); } + Uint64 operator&(Uint64 value) const { return Uint64(data_ & value.data_); } + Uint64 operator&=(Uint64 value) { + data_ &= value.data_; + return *this; + } + Uint64 operator^(Uint64 value) const { return Uint64(data_ ^ value.data_); } + Uint64 operator^=(Uint64 value) { + data_ ^= value.data_; + return *this; + } + Uint64 operator|(Uint64 value) const { return Uint64(data_ | value.data_); } + Uint64 operator|=(Uint64 value) { + data_ |= value.data_; + return *this; + } + // Unlike uint64_t, the shift functions can accept negative shift and + // return 0 when the shift is too big. + Uint64 operator>>(int shift) const { + if (shift == 0) return *this; + if (shift < 0) { + int tmp = -shift; + if (tmp >= 64) return Uint64(0); + return Uint64(data_ << tmp); + } + int tmp = shift; + if (tmp >= 64) return Uint64(0); + return Uint64(data_ >> tmp); + } + Uint64 operator<<(int shift) const { + if (shift == 0) return *this; + if (shift < 0) { + int tmp = -shift; + if (tmp >= 64) return Uint64(0); + return Uint64(data_ >> tmp); + } + int tmp = shift; + if (tmp >= 64) return Uint64(0); + return Uint64(data_ << tmp); + } +}; + +class Uint128 { + uint64_t data_high_; + uint64_t data_low_; + + public: + Uint128() : data_high_(0), data_low_(0) {} + explicit Uint128(uint64_t data_low) : data_high_(0), data_low_(data_low) {} + explicit Uint128(Uint64 data_low) + : data_high_(0), data_low_(data_low.Get()) {} + Uint128(uint64_t data_high, uint64_t data_low) + : data_high_(data_high), data_low_(data_low) {} + Uint64 ToUint64() const { + VIXL_ASSERT(data_high_ == 0); + return Uint64(data_low_); + } + Uint64 GetHigh64() const { return Uint64(data_high_); } + Uint64 GetLow64() const { return Uint64(data_low_); } + Uint128 operator~() const { return Uint128(~data_high_, ~data_low_); } + bool operator==(Uint128 value) const { + return (data_high_ == value.data_high_) && (data_low_ == value.data_low_); + } + Uint128 operator&(Uint128 value) const { + return Uint128(data_high_ & value.data_high_, data_low_ & value.data_low_); + } + Uint128 operator&=(Uint128 value) { + data_high_ &= value.data_high_; + data_low_ &= value.data_low_; + return *this; + } + Uint128 operator|=(Uint128 value) { + data_high_ |= value.data_high_; + data_low_ |= value.data_low_; + return *this; + } + Uint128 operator>>(int shift) const { + VIXL_ASSERT((shift >= 0) && (shift < 128)); + if (shift == 0) return *this; + if (shift >= 64) { + return Uint128(0, data_high_ >> (shift - 64)); + } + uint64_t tmp = (data_high_ << (64 - shift)) | (data_low_ >> shift); + return Uint128(data_high_ >> shift, tmp); + } + Uint128 operator<<(int shift) const { + VIXL_ASSERT((shift >= 0) && (shift < 128)); + if (shift == 0) return *this; + if (shift >= 64) { + return Uint128(data_low_ << (shift - 64), 0); + } + uint64_t tmp = (data_high_ << shift) | (data_low_ >> (64 - shift)); + return Uint128(tmp, data_low_ << shift); + } +}; + +Uint32::Uint32(Uint64 data) : data_(data.ToUint32().Get()) {} +Uint64::Uint64(Uint128 data) : data_(data.ToUint64().Get()) {} + +Int64 BitCount(Uint32 value); + +} // namespace internal + +// The default NaN values (for FPCR.DN=1). +extern const double kFP64DefaultNaN; +extern const float kFP32DefaultNaN; +extern const Float16 kFP16DefaultNaN; + +// Floating-point infinity values. +extern const Float16 kFP16PositiveInfinity; +extern const Float16 kFP16NegativeInfinity; +extern const float kFP32PositiveInfinity; +extern const float kFP32NegativeInfinity; +extern const double kFP64PositiveInfinity; +extern const double kFP64NegativeInfinity; + +// Floating-point zero values. +extern const Float16 kFP16PositiveZero; +extern const Float16 kFP16NegativeZero; + +// AArch64 floating-point specifics. These match IEEE-754. +const unsigned kDoubleMantissaBits = 52; +const unsigned kDoubleExponentBits = 11; +const unsigned kFloatMantissaBits = 23; +const unsigned kFloatExponentBits = 8; +const unsigned kFloat16MantissaBits = 10; +const unsigned kFloat16ExponentBits = 5; + +enum FPRounding { + // The first four values are encodable directly by FPCR. + FPTieEven = 0x0, + FPPositiveInfinity = 0x1, + FPNegativeInfinity = 0x2, + FPZero = 0x3, + + // The final rounding modes are only available when explicitly specified by + // the instruction (such as with fcvta). It cannot be set in FPCR. + FPTieAway, + FPRoundOdd +}; + +enum UseDefaultNaN { kUseDefaultNaN, kIgnoreDefaultNaN }; + +// Assemble the specified IEEE-754 components into the target type and apply +// appropriate rounding. +// sign: 0 = positive, 1 = negative +// exponent: Unbiased IEEE-754 exponent. +// mantissa: The mantissa of the input. The top bit (which is not encoded for +// normal IEEE-754 values) must not be omitted. This bit has the +// value 'pow(2, exponent)'. +// +// The input value is assumed to be a normalized value. That is, the input may +// not be infinity or NaN. If the source value is subnormal, it must be +// normalized before calling this function such that the highest set bit in the +// mantissa has the value 'pow(2, exponent)'. +// +// Callers should use FPRoundToFloat or FPRoundToDouble directly, rather than +// calling a templated FPRound. +template +T FPRound(int64_t sign, + int64_t exponent, + uint64_t mantissa, + FPRounding round_mode) { + VIXL_ASSERT((sign == 0) || (sign == 1)); + + // Only FPTieEven and FPRoundOdd rounding modes are implemented. + VIXL_ASSERT((round_mode == FPTieEven) || (round_mode == FPRoundOdd)); + + // Rounding can promote subnormals to normals, and normals to infinities. For + // example, a double with exponent 127 (FLT_MAX_EXP) would appear to be + // encodable as a float, but rounding based on the low-order mantissa bits + // could make it overflow. With ties-to-even rounding, this value would become + // an infinity. + + // ---- Rounding Method ---- + // + // The exponent is irrelevant in the rounding operation, so we treat the + // lowest-order bit that will fit into the result ('onebit') as having + // the value '1'. Similarly, the highest-order bit that won't fit into + // the result ('halfbit') has the value '0.5'. The 'point' sits between + // 'onebit' and 'halfbit': + // + // These bits fit into the result. + // |---------------------| + // mantissa = 0bxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + // || + // / | + // / halfbit + // onebit + // + // For subnormal outputs, the range of representable bits is smaller and + // the position of onebit and halfbit depends on the exponent of the + // input, but the method is otherwise similar. + // + // onebit(frac) + // | + // | halfbit(frac) halfbit(adjusted) + // | / / + // | | | + // 0b00.0 (exact) -> 0b00.0 (exact) -> 0b00 + // 0b00.0... -> 0b00.0... -> 0b00 + // 0b00.1 (exact) -> 0b00.0111..111 -> 0b00 + // 0b00.1... -> 0b00.1... -> 0b01 + // 0b01.0 (exact) -> 0b01.0 (exact) -> 0b01 + // 0b01.0... -> 0b01.0... -> 0b01 + // 0b01.1 (exact) -> 0b01.1 (exact) -> 0b10 + // 0b01.1... -> 0b01.1... -> 0b10 + // 0b10.0 (exact) -> 0b10.0 (exact) -> 0b10 + // 0b10.0... -> 0b10.0... -> 0b10 + // 0b10.1 (exact) -> 0b10.0111..111 -> 0b10 + // 0b10.1... -> 0b10.1... -> 0b11 + // 0b11.0 (exact) -> 0b11.0 (exact) -> 0b11 + // ... / | / | + // / | / | + // / | + // adjusted = frac - (halfbit(mantissa) & ~onebit(frac)); / | + // + // mantissa = (mantissa >> shift) + halfbit(adjusted); + + static const int mantissa_offset = 0; + static const int exponent_offset = mantissa_offset + mbits; + static const int sign_offset = exponent_offset + ebits; + VIXL_ASSERT(sign_offset == (sizeof(T) * 8 - 1)); + + // Bail out early for zero inputs. + if (mantissa == 0) { + return static_cast(sign << sign_offset); + } + + // If all bits in the exponent are set, the value is infinite or NaN. + // This is true for all binary IEEE-754 formats. + static const int infinite_exponent = (1 << ebits) - 1; + static const int max_normal_exponent = infinite_exponent - 1; + + // Apply the exponent bias to encode it for the result. Doing this early makes + // it easy to detect values that will be infinite or subnormal. + exponent += max_normal_exponent >> 1; + + if (exponent > max_normal_exponent) { + // Overflow: the input is too large for the result type to represent. + if (round_mode == FPTieEven) { + // FPTieEven rounding mode handles overflows using infinities. + exponent = infinite_exponent; + mantissa = 0; + } else { + VIXL_ASSERT(round_mode == FPRoundOdd); + // FPRoundOdd rounding mode handles overflows using the largest magnitude + // normal number. + exponent = max_normal_exponent; + mantissa = (UINT64_C(1) << exponent_offset) - 1; + } + return static_cast((sign << sign_offset) | + (exponent << exponent_offset) | + (mantissa << mantissa_offset)); + } + + // Calculate the shift required to move the top mantissa bit to the proper + // place in the destination type. + const int highest_significant_bit = 63 - CountLeadingZeros(mantissa); + int shift = highest_significant_bit - mbits; + + if (exponent <= 0) { + // The output will be subnormal (before rounding). + // For subnormal outputs, the shift must be adjusted by the exponent. The +1 + // is necessary because the exponent of a subnormal value (encoded as 0) is + // the same as the exponent of the smallest normal value (encoded as 1). + shift += -exponent + 1; + + // Handle inputs that would produce a zero output. + // + // Shifts higher than highest_significant_bit+1 will always produce a zero + // result. A shift of exactly highest_significant_bit+1 might produce a + // non-zero result after rounding. + if (shift > (highest_significant_bit + 1)) { + if (round_mode == FPTieEven) { + // The result will always be +/-0.0. + return static_cast(sign << sign_offset); + } else { + VIXL_ASSERT(round_mode == FPRoundOdd); + VIXL_ASSERT(mantissa != 0); + // For FPRoundOdd, if the mantissa is too small to represent and + // non-zero return the next "odd" value. + return static_cast((sign << sign_offset) | 1); + } + } + + // Properly encode the exponent for a subnormal output. + exponent = 0; + } else { + // Clear the topmost mantissa bit, since this is not encoded in IEEE-754 + // normal values. + mantissa &= ~(UINT64_C(1) << highest_significant_bit); + } + + // The casts below are only well-defined for unsigned integers. + VIXL_STATIC_ASSERT(std::numeric_limits::is_integer); + VIXL_STATIC_ASSERT(!std::numeric_limits::is_signed); + + if (shift > 0) { + if (round_mode == FPTieEven) { + // We have to shift the mantissa to the right. Some precision is lost, so + // we need to apply rounding. + uint64_t onebit_mantissa = (mantissa >> (shift)) & 1; + uint64_t halfbit_mantissa = (mantissa >> (shift - 1)) & 1; + uint64_t adjustment = (halfbit_mantissa & ~onebit_mantissa); + uint64_t adjusted = mantissa - adjustment; + T halfbit_adjusted = (adjusted >> (shift - 1)) & 1; + + T result = + static_cast((sign << sign_offset) | (exponent << exponent_offset) | + ((mantissa >> shift) << mantissa_offset)); + + // A very large mantissa can overflow during rounding. If this happens, + // the exponent should be incremented and the mantissa set to 1.0 + // (encoded as 0). Applying halfbit_adjusted after assembling the float + // has the nice side-effect that this case is handled for free. + // + // This also handles cases where a very large finite value overflows to + // infinity, or where a very large subnormal value overflows to become + // normal. + return result + halfbit_adjusted; + } else { + VIXL_ASSERT(round_mode == FPRoundOdd); + // If any bits at position halfbit or below are set, onebit (ie. the + // bottom bit of the resulting mantissa) must be set. + uint64_t fractional_bits = mantissa & ((UINT64_C(1) << shift) - 1); + if (fractional_bits != 0) { + mantissa |= UINT64_C(1) << shift; + } + + return static_cast((sign << sign_offset) | + (exponent << exponent_offset) | + ((mantissa >> shift) << mantissa_offset)); + } + } else { + // We have to shift the mantissa to the left (or not at all). The input + // mantissa is exactly representable in the output mantissa, so apply no + // rounding correction. + return static_cast((sign << sign_offset) | + (exponent << exponent_offset) | + ((mantissa << -shift) << mantissa_offset)); + } +} + + +// See FPRound for a description of this function. +inline double FPRoundToDouble(int64_t sign, + int64_t exponent, + uint64_t mantissa, + FPRounding round_mode) { + uint64_t bits = + FPRound(sign, + exponent, + mantissa, + round_mode); + return RawbitsToDouble(bits); +} + + +// See FPRound for a description of this function. +inline Float16 FPRoundToFloat16(int64_t sign, + int64_t exponent, + uint64_t mantissa, + FPRounding round_mode) { + return RawbitsToFloat16( + FPRound(sign, exponent, mantissa, round_mode)); +} + + +// See FPRound for a description of this function. +static inline float FPRoundToFloat(int64_t sign, + int64_t exponent, + uint64_t mantissa, + FPRounding round_mode) { + uint32_t bits = + FPRound(sign, + exponent, + mantissa, + round_mode); + return RawbitsToFloat(bits); +} + + +float FPToFloat(Float16 value, UseDefaultNaN DN, bool* exception = NULL); +float FPToFloat(double value, + FPRounding round_mode, + UseDefaultNaN DN, + bool* exception = NULL); + +double FPToDouble(Float16 value, UseDefaultNaN DN, bool* exception = NULL); +double FPToDouble(float value, UseDefaultNaN DN, bool* exception = NULL); + +Float16 FPToFloat16(float value, + FPRounding round_mode, + UseDefaultNaN DN, + bool* exception = NULL); + +Float16 FPToFloat16(double value, + FPRounding round_mode, + UseDefaultNaN DN, + bool* exception = NULL); } // namespace vixl #endif // VIXL_UTILS_H diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 765ed8b2e130..7264c76fc378 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -10718,11 +10718,18 @@ static int Shell(JSContext* cx, OptionParser* op, char** envp) { if (JS::TraceLoggerSupported()) { JS::StartTraceLogger(cx); } - auto stopTraceLogger = MakeScopeExit([&] { +#ifdef JS_STRUCTURED_SPEW + cx->spewer().enableSpewing(); +#endif + + auto exitShell = MakeScopeExit([&] { if (JS::TraceLoggerSupported()) { JS::SpewTraceLoggerForCurrentProcess(); JS::StopTraceLogger(cx); } +#ifdef JS_STRUCTURED_SPEW + cx->spewer().disableSpewing(); +#endif }); if (op->getBoolOption("wasm-compile-and-serialize")) { diff --git a/js/src/util/StructuredSpewer.cpp b/js/src/util/StructuredSpewer.cpp index 1006c4c04406..916f1a247c98 100644 --- a/js/src/util/StructuredSpewer.cpp +++ b/js/src/util/StructuredSpewer.cpp @@ -73,7 +73,7 @@ void StructuredSpewer::tryToInitializeOutput(const char* path) { // Returning here before we've emplaced the JSONPrinter // means this is effectively disabled, but fail earlier // we also disable all the bits - selectedChannels_.disableAll(); + selectedChannels_.disableAllChannels(); return; } @@ -102,8 +102,11 @@ static bool MatchJSScript(JSScript* script, const char* pattern) { return result != nullptr; } -/* static */ bool StructuredSpewer::enabled(JSScript* script) { + if (!spewingEnabled_) { + return false; + } + // We cannot call getenv under record/replay. if (mozilla::recordreplay::IsRecordingOrReplaying()) { return false; @@ -120,7 +123,7 @@ bool StructuredSpewer::enabled(JSContext* cx, const JSScript* script, if (script && !script->spewEnabled()) { return false; } - return cx->spewer().filter().enabled(channel); + return cx->spewer().enabled(channel); } // Attempt to setup a common header for objects based on script/channel. @@ -148,7 +151,7 @@ void StructuredSpewer::spew(JSContext* cx, SpewChannel channel, const char* fmt, ...) { // Because we don't have a script here, use the singleton's // filter to determine if the channel is active. - if (!cx->spewer().filter().enabled(channel)) { + if (!cx->spewer().enabled(channel)) { return; } @@ -184,6 +187,10 @@ void StructuredSpewer::parseSpewFlags(const char* flags) { # undef CHECK_CHANNEL + if (ContainsFlag(flags, "AtStartup")) { + enableSpewing(); + } + if (ContainsFlag(flags, "help")) { printf( "\n" @@ -192,11 +199,16 @@ void StructuredSpewer::parseSpewFlags(const char* flags) { " help Dump this help message\n" " all|* Enable all the below channels\n" " channel[,channel] Enable the selected channels from below\n" + " AtStartup Enable spewing at browser startup instead\n" + " of when gecko profiling starts." "\n" " Channels: \n" "\n" // List Channels " BaselineICStats Dump the IC Entry counters during Ion analysis\n" + " ScriptStats Dump statistics collected by tracelogger that\n" + " is aggregated by script. Requires\n" + " JS_TRACE_LOGGING=1\n" // End Channel list "\n\n" "By default output goes to a file called spew_output.$PID.$THREAD\n" diff --git a/js/src/util/StructuredSpewer.h b/js/src/util/StructuredSpewer.h index 9992e5d0ec0f..bec2a4e5d8d6 100644 --- a/js/src/util/StructuredSpewer.h +++ b/js/src/util/StructuredSpewer.h @@ -39,7 +39,11 @@ // profiler.firefox.com. // // The spewer has four main control knobs, all currently set as -// environment variables. All but the first are optional. +// environment variables. All but the first are optional. When the spewer is +// activated through the browser, it is synchronized with the gecko profiler +// start and stop routines. Setting SPEW=AtStartup activates the spewer at +// startup instead of profiler start, but profiler stop will still deactivate +// the spewer. // // SPEW: Activates the spewer. The value provided is interpreted as a comma // separated list that selects channels by name. Currently there's no @@ -97,13 +101,14 @@ class StructuredSpewFilter { void enableChannel(SpewChannel x) { bits_ += x; } - void disableAll() { bits_.clear(); } + void disableAllChannels() { bits_.clear(); } }; class StructuredSpewer { public: StructuredSpewer() : outputInitializationAttempted_(false), + spewingEnabled_(false), json_(mozilla::Nothing()), selectedChannels_() { // If we are recording or replaying, we cannot use getenv @@ -115,17 +120,29 @@ class StructuredSpewer { } } - ~StructuredSpewer() { + ~StructuredSpewer() { disableSpewing(); } + + void enableSpewing() { spewingEnabled_ = true; } + + void disableSpewing() { + if (!spewingEnabled_) { + return; + } + if (json_.isSome()) { json_->endList(); output_.flush(); output_.finish(); + json_.reset(); } + + spewingEnabled_ = false; + outputInitializationAttempted_ = false; } // Check if the spewer is enabled for a particular script, used to power // script level filtering. - static bool enabled(JSScript* script); + bool enabled(JSScript* script); // A generic printf like spewer that logs the formatted string. static void spew(JSContext* cx, SpewChannel channel, const char* fmt, ...) @@ -149,6 +166,8 @@ class StructuredSpewer { // attemped in the right place. bool outputInitializationAttempted_; + bool spewingEnabled_; + Fprinter output_; mozilla::Maybe json_; @@ -178,6 +197,11 @@ class StructuredSpewer { // Using flags, choose the enabled channels for this spewer. void parseSpewFlags(const char* flags); + // Returns true iff the channels is enabled + bool enabled(SpewChannel channel) { + return (spewingEnabled_ && filter().enabled(channel)); + } + // Returns true iff the channels is enabled for the given script. bool enabled(JSContext* cx, const JSScript* script, SpewChannel channel) const; diff --git a/js/src/vm/GeckoProfiler.cpp b/js/src/vm/GeckoProfiler.cpp index 5bf591df759a..40c59d46b905 100644 --- a/js/src/vm/GeckoProfiler.cpp +++ b/js/src/vm/GeckoProfiler.cpp @@ -151,6 +151,15 @@ void GeckoProfilerRuntime::enable(bool enabled) { for (RealmsIter r(rt); !r.done(); r.next()) { r->wasm.ensureProfilingLabels(enabled); } + +#ifdef JS_STRUCTURED_SPEW + // Enable the structured spewer if the environment variable is set. + if (enabled) { + cx->spewer().enableSpewing(); + } else { + cx->spewer().disableSpewing(); + } +#endif } /* Lookup the string for the function/script, creating one if necessary */ diff --git a/js/src/vm/JSScript.cpp b/js/src/vm/JSScript.cpp index 2cc5ef0003ba..6e80ffd004fc 100644 --- a/js/src/vm/JSScript.cpp +++ b/js/src/vm/JSScript.cpp @@ -3962,7 +3962,7 @@ bool JSScript::fullyInitFromEmitter(JSContext* cx, HandleScript script, #ifdef JS_STRUCTURED_SPEW // We want this to happen after line number initialization to allow filtering // to work. - script->setSpewEnabled(StructuredSpewer::enabled(script)); + script->setSpewEnabled(cx->spewer().enabled(script)); #endif #ifdef DEBUG diff --git a/netwerk/base/mozurl/Cargo.toml b/netwerk/base/mozurl/Cargo.toml index d9ef2db5d16e..2e7678a20207 100644 --- a/netwerk/base/mozurl/Cargo.toml +++ b/netwerk/base/mozurl/Cargo.toml @@ -8,4 +8,4 @@ url = "1.7.2" nserror = { path = "../../../xpcom/rust/nserror" } nsstring = { path = "../../../xpcom/rust/nsstring" } xpcom = { path = "../../../xpcom/rust/xpcom" } -uuid = { version = "0.6", features = ["v4"] } +uuid = { version = "0.7.2", features = ["v4"] } diff --git a/python/mozbuild/mozbuild/vendor_rust.py b/python/mozbuild/mozbuild/vendor_rust.py index fd6982c00a16..f7332fe9e85d 100644 --- a/python/mozbuild/mozbuild/vendor_rust.py +++ b/python/mozbuild/mozbuild/vendor_rust.py @@ -187,6 +187,8 @@ Please commit or stash these changes before vendoring, or re-run with `--ignore- RUNTIME_LICENSE_FILE_PACKAGE_WHITELIST = { # MIT 'deque': '6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb', + # we're whitelisting this fuchsia crate because it doesn't get built in the final product but has a license-file that needs ignoring + 'fuchsia-cprng' : '03b114f53e6587a398931762ee11e2395bfdba252a329940e2c8c9e81813845b', } @staticmethod diff --git a/security/manager/ssl/LibSecret.cpp b/security/manager/ssl/LibSecret.cpp index 930940f4c490..027c47a021a7 100644 --- a/security/manager/ssl/LibSecret.cpp +++ b/security/manager/ssl/LibSecret.cpp @@ -84,6 +84,8 @@ typedef SecretService* (*secret_service_get_sync_fn)(SecretServiceFlags, GCancellable*, GError**); typedef gint (*secret_service_lock_sync_fn)(SecretService*, GList*, GCancellable*, GList**, GError**); +typedef gint (*secret_service_unlock_sync_fn)(SecretService*, GList*, + GCancellable*, GList**, GError**); typedef gboolean (*secret_password_clear_sync_fn)(const SecretSchema*, GCancellable*, GError**, ...); typedef gchar* (*secret_password_lookup_sync_fn)(const SecretSchema*, @@ -99,6 +101,7 @@ static secret_collection_for_alias_sync_fn secret_collection_for_alias_sync = nullptr; static secret_service_get_sync_fn secret_service_get_sync = nullptr; static secret_service_lock_sync_fn secret_service_lock_sync = nullptr; +static secret_service_unlock_sync_fn secret_service_unlock_sync = nullptr; static secret_password_clear_sync_fn secret_password_clear_sync = nullptr; static secret_password_lookup_sync_fn secret_password_lookup_sync = nullptr; static secret_password_store_sync_fn secret_password_store_sync = nullptr; @@ -127,6 +130,7 @@ nsresult MaybeLoadLibSecret() { FIND_FUNCTION_SYMBOL(secret_collection_for_alias_sync); FIND_FUNCTION_SYMBOL(secret_service_get_sync); FIND_FUNCTION_SYMBOL(secret_service_lock_sync); + FIND_FUNCTION_SYMBOL(secret_service_unlock_sync); FIND_FUNCTION_SYMBOL(secret_password_clear_sync); FIND_FUNCTION_SYMBOL(secret_password_lookup_sync); FIND_FUNCTION_SYMBOL(secret_password_store_sync); @@ -185,6 +189,7 @@ LibSecret::~LibSecret() { secret_collection_for_alias_sync = nullptr; secret_service_get_sync = nullptr; secret_service_lock_sync = nullptr; + secret_service_unlock_sync = nullptr; secret_password_clear_sync = nullptr; secret_password_lookup_sync = nullptr; secret_password_store_sync = nullptr; @@ -260,13 +265,29 @@ nsresult LibSecret::Lock() { } nsresult LibSecret::Unlock() { - // Accessing the secret service unlocks it. So calling this separately isn't - // actually necessary. + MOZ_ASSERT(secret_service_unlock_sync); + if (!secret_service_unlock_sync) { + return NS_ERROR_FAILURE; + } + // Accessing the secret service might unlock it. ScopedSecretService ss; ScopedSecretCollection sc; if (NS_FAILED(GetScopedServices(ss, sc))) { return NS_ERROR_FAILURE; } + GError* raw_error = nullptr; + GList* collections = nullptr; + ScopedGList collectionList(g_list_append(collections, sc.get())); + int numLocked = secret_service_unlock_sync(ss.get(), collectionList.get(), + nullptr, // GCancellable + nullptr, // list of unlocked items + &raw_error); + ScopedGError error(raw_error); + if (numLocked != 1) { + MOZ_LOG(gLibSecretLog, LogLevel::Debug, + ("Couldn't unlock secret collection")); + return NS_ERROR_FAILURE; + } return NS_OK; } diff --git a/security/manager/ssl/NSSKeyStore.cpp b/security/manager/ssl/NSSKeyStore.cpp index 8c5c09712037..69797f31d05f 100644 --- a/security/manager/ssl/NSSKeyStore.cpp +++ b/security/manager/ssl/NSSKeyStore.cpp @@ -73,7 +73,7 @@ nsresult NSSKeyStore::Lock() { nsresult NSSKeyStoreMainThreadUnlock(PK11SlotInfo* aSlot) { nsCOMPtr token = new nsPK11Token(aSlot); - return token->Login(false /* force */); + return NS_FAILED(token->Login(false /* force */)) ? NS_ERROR_FAILURE : NS_OK; } nsresult NSSKeyStore::Unlock() { @@ -87,13 +87,15 @@ nsresult NSSKeyStore::Unlock() { } // Forward to the main thread synchronously. + nsresult result = NS_ERROR_FAILURE; SyncRunnable::DispatchToThread( mainThread, new SyncRunnable(NS_NewRunnableFunction( - "NSSKeyStoreMainThreadUnlock", [slot = mSlot.get()]() { - NSSKeyStoreMainThreadUnlock(slot); + "NSSKeyStoreMainThreadUnlock", + [slot = mSlot.get(), result = &result]() { + *result = NSSKeyStoreMainThreadUnlock(slot); }))); - return NS_OK; + return result; } return NSSKeyStoreMainThreadUnlock(mSlot.get()); diff --git a/security/manager/ssl/tests/unit/test_oskeystore.js b/security/manager/ssl/tests/unit/test_oskeystore.js index e3f3413deca9..4d3df360a71f 100644 --- a/security/manager/ssl/tests/unit/test_oskeystore.js +++ b/security/manager/ssl/tests/unit/test_oskeystore.js @@ -38,14 +38,15 @@ var gMockPrompter = { // how objects get wrapped when going across xpcom boundaries. promptPassword(dialogTitle, text, password, checkMsg, checkValue) { this.numPrompts++; - if (this.numPrompts > 1) { // don't keep retrying a bad password - return false; - } equal(text, "Please enter your master password.", "password prompt text should be as expected"); equal(checkMsg, null, "checkMsg should be null"); ok(this.passwordToTry, "passwordToTry should be non-null"); + if (this.passwordToTry == "DontTryThisPassword") { + // Cancel the prompt in this case. + return false; + } password.value = this.passwordToTry; return true; }, @@ -136,6 +137,31 @@ add_task(async function() { ok(gMockPrompter.numPrompts == 1, "There should've been one password prompt."); await delete_all_secrets(); } + + // Check lock/unlock behaviour. + // Unfortunately we can only test this automatically for the NSS key store. + // Uncomment the outer keystore.isNSSKeyStore to test other key stores manually. + if (keystore.isNSSKeyStore) { + await delete_all_secrets(); + await encrypt_decrypt_test(); + await keystore.asyncLock(); + info("Keystore should be locked. Cancel the login request."); + try { + if (keystore.isNSSKeyStore) { + gMockPrompter.passwordToTry = "DontTryThisPassword"; + } + await keystore.asyncUnlock(); + ok(false, "Unlock should've rejected."); + } catch (e) { + ok(e.result == Cr.NS_ERROR_FAILURE || e.result == Cr.NS_ERROR_ABORT, + "Rejected login prompt."); + } + // clean up + if (keystore.isNSSKeyStore) { + gMockPrompter.passwordToTry = "hunter2"; + } + await delete_all_secrets(); + } }); // Test that if we kick off a background operation and then call a synchronous function on the diff --git a/servo/components/style/Cargo.toml b/servo/components/style/Cargo.toml index 54493548b67a..9d1cec77fc7a 100644 --- a/servo/components/style/Cargo.toml +++ b/servo/components/style/Cargo.toml @@ -55,7 +55,7 @@ num-traits = "0.2" num-derive = "0.2" ordered-float = "1.0" owning_ref = "0.4" -parking_lot = "0.6" +parking_lot = "0.8" precomputed-hash = "0.1.1" rayon = "1" selectors = { path = "../selectors" } diff --git a/servo/ports/geckolib/Cargo.toml b/servo/ports/geckolib/Cargo.toml index e87a74ab9373..89ab1f00a5fc 100644 --- a/servo/ports/geckolib/Cargo.toml +++ b/servo/ports/geckolib/Cargo.toml @@ -22,7 +22,7 @@ log = {version = "0.4", features = ["release_max_level_info"]} malloc_size_of = {path = "../../components/malloc_size_of"} nsstring = {path = "../../../xpcom/rust/nsstring/"} num-traits = "0.2" -parking_lot = "0.6" +parking_lot = "0.8" selectors = {path = "../../components/selectors"} servo_arc = {path = "../../components/servo_arc"} smallvec = "0.6" diff --git a/servo/tests/unit/style/Cargo.toml b/servo/tests/unit/style/Cargo.toml index e67978da8547..e8aa474c4ac6 100644 --- a/servo/tests/unit/style/Cargo.toml +++ b/servo/tests/unit/style/Cargo.toml @@ -15,7 +15,7 @@ app_units = "0.7" cssparser = "0.25" euclid = "0.19" html5ever = "0.22" -parking_lot = "0.6" +parking_lot = "0.8" rayon = "1" serde_json = "1.0" selectors = {path = "../../../components/selectors"} diff --git a/taskcluster/ci/test/test-platforms.yml b/taskcluster/ci/test/test-platforms.yml index 68b2334fb08b..51c1034c5cf9 100644 --- a/taskcluster/ci/test/test-platforms.yml +++ b/taskcluster/ci/test/test-platforms.yml @@ -266,17 +266,17 @@ windows10-64-shippable-qr/opt: ## # MacOS X platforms (matching /macosx.*/) -# macosx1014-64-shippable/opt: -# build-platform: macosx64-shippable/opt -# test-sets: -# - macosx1014-64-tests -# - macosx64-talos -# - desktop-screenshot-capture -# - awsy -# - raptor-chromium -# - raptor-firefox -# - raptor-profiling -# - marionette-media-tests +macosx1014-64-shippable/opt: + build-platform: macosx64-shippable/opt + test-sets: + # - macosx1014-64-tests + - macosx64-talos + # - desktop-screenshot-capture + # - awsy + - raptor-chromium + - raptor-firefox + - raptor-profiling + # - marionette-media-tests # macosx1014-64/debug: # build-platform: macosx64-shippable/opt @@ -313,13 +313,9 @@ macosx1010-64/debug: macosx1010-64-shippable/opt: build-platform: macosx64-shippable/opt test-sets: - - macosx64-talos - macosx64-tests - desktop-screenshot-capture - awsy - - raptor-chromium - - raptor-firefox - - raptor-profiling - marionette-media-tests macosx1010-64-devedition/opt: diff --git a/taskcluster/ci/webrender/kind.yml b/taskcluster/ci/webrender/kind.yml index 84e54c9182dc..3da1ce1a02c8 100644 --- a/taskcluster/ci/webrender/kind.yml +++ b/taskcluster/ci/webrender/kind.yml @@ -247,7 +247,7 @@ jobs: artifacts: - type: file name: public/build/wrench-debug.apk - path: /builds/worker/checkouts/gecko/gfx/wr/target/android-artifacts/app/build/outputs/apk/app-debug.apk + path: /builds/worker/checkouts/gecko/gfx/wr/target/android-artifacts/app/build/outputs/apk/debug/app-debug.apk run: using: run-task command: '$GECKO_PATH/taskcluster/scripts/misc/wrench-android-build.sh debug' @@ -258,7 +258,7 @@ jobs: - linux64-rust-android - wrench-deps treeherder: - platform: android-4-0-armv7-api16/debug + platform: android-em-7-0-x86/debug symbol: WR(B) when: files-changed: @@ -285,10 +285,90 @@ jobs: - linux64-rust-android - wrench-deps treeherder: - platform: android-4-0-armv7-api16/opt + platform: android-em-7-0-x86/opt symbol: WR(B) when: files-changed: - 'gfx/wr/**' - 'taskcluster/scripts/misc/wrench-android-build.sh' - 'mobile/android/debug_sign_tool.py' + + android-debug: + description: Run debug wrench reftests on Android + worker-type: terraform-packet/gecko-t-linux # privileged:true doesn't work on b-linux + worker: + max-run-time: 5400 + docker-image: {in-tree: desktop1604-test} + privileged: true # for access to /dev/kvm for hw accel in the emulator + artifacts: + - type: directory + name: public/build/logs + path: /builds/worker/workspace/build/logs + env: + # need hard-coded URLs until bug 1551875 is fixed + TASKCLUSTER_ROOT_URL: 'https://taskcluster.net' + TASKCLUSTER_PROXY_URL: 'http://taskcluster' + MOZHARNESS_SCRIPT: android_emulator_wrench.py + MOZHARNESS_CONFIG: android/wrench.py + NEED_WINDOW_MANAGER: 'true' + dependencies: + webrender-wrench-android-debug: webrender-wrench-android-debug + fetches: + webrender-wrench-android-debug: + - 'wrench-debug.apk' + run: + using: run-task + tooltool-downloads: internal + command: >- + export WRENCH_APK=$MOZ_FETCHES_DIR/wrench-debug.apk && + export WRENCH_REFTESTS=$GECKO_PATH/gfx/wr/wrench/reftests && + export MOZHARNESS_PATH=$GECKO_PATH/testing/mozharness && + $GECKO_PATH/taskcluster/scripts/tester/test-linux.sh + treeherder: + platform: android-em-7-0-x86/debug + symbol: WR(wrench) + when: + files-changed: + - 'gfx/wr/**' + - 'testing/mozharness/scripts/android_emulator_wrench.py' + - 'testing/mozharness/configs/android/wrench.py' + + android-release: + description: Run release wrench reftests on Android + worker-type: terraform-packet/gecko-t-linux # privileged:true doesn't work on b-linux + worker: + max-run-time: 5400 + docker-image: {in-tree: desktop1604-test} + privileged: true # for access to /dev/kvm for hw accel in the emulator + artifacts: + - type: directory + name: public/build/logs + path: /builds/worker/workspace/build/logs + env: + # need hard-coded URLs until bug 1551875 is fixed + TASKCLUSTER_ROOT_URL: 'https://taskcluster.net' + TASKCLUSTER_PROXY_URL: 'http://taskcluster' + MOZHARNESS_SCRIPT: android_emulator_wrench.py + MOZHARNESS_CONFIG: android/wrench.py + NEED_WINDOW_MANAGER: 'true' + dependencies: + webrender-wrench-android-release: webrender-wrench-android-release + fetches: + webrender-wrench-android-release: + - 'wrench-release.apk' + run: + using: run-task + tooltool-downloads: internal + command: >- + export WRENCH_APK=$MOZ_FETCHES_DIR/wrench-release.apk && + export WRENCH_REFTESTS=$GECKO_PATH/gfx/wr/wrench/reftests && + export MOZHARNESS_PATH=$GECKO_PATH/testing/mozharness && + $GECKO_PATH/taskcluster/scripts/tester/test-linux.sh + treeherder: + platform: android-em-7-0-x86/opt + symbol: WR(wrench) + when: + files-changed: + - 'gfx/wr/**' + - 'testing/mozharness/scripts/android_emulator_wrench.py' + - 'testing/mozharness/configs/android/wrench.py' diff --git a/taskcluster/scripts/misc/build-clang-8-mingw.sh b/taskcluster/scripts/misc/build-clang-8-mingw.sh index 3bba68e3a895..3de5772bd172 100755 --- a/taskcluster/scripts/misc/build-clang-8-mingw.sh +++ b/taskcluster/scripts/misc/build-clang-8-mingw.sh @@ -36,10 +36,6 @@ mingw_version=164990461720e0ed6ea5ea9f359d78595b1a459a libunwind_version=6ee92fcc97350ae32db3172a269e9afcc2bab686 llvm_mingw_version=c3a16814bd26aa6702e1e5b482a3d9044bb0f725 -binutils_version=2.27 -binutils_ext=bz2 -binutils_sha=369737ce51587f92466041a97ab7d2358c6d9e1b6490b3940eb09fb0a9a6ac88 - # This is default value of _WIN32_WINNT. Gecko configure script explicitly sets this, # so this is not used to build Gecko itself. We default to 0x601, which is Windows 7. default_win32_winnt=0x601 @@ -72,14 +68,6 @@ prepare() { git checkout $llvm_mingw_version popd - wget -c --progress=dot:mega ftp://ftp.gnu.org/gnu/binutils/binutils-$binutils_version.tar.$binutils_ext - if [ "$(sha256sum binutils-$binutils_version.tar.$binutils_ext)" != "$binutils_sha binutils-$binutils_version.tar.$binutils_ext" ]; - then - echo Corrupted binutils archive - exit 1 - fi - tar -jxf binutils-$binutils_version.tar.$binutils_ext - popd } @@ -282,23 +270,13 @@ build_libcxx() { } build_utils() { - mkdir binutils - pushd binutils - $SRC_DIR/binutils-$binutils_version/configure --prefix=$INSTALL_DIR \ - --disable-multilib \ - --disable-nls \ - --target=$machine-w64-mingw32 - make $make_flags - - # Manually install only nm - cp binutils/nm-new $INSTALL_DIR/bin/$machine-w64-mingw32-nm - pushd $INSTALL_DIR/bin/ + ln -s llvm-nm $machine-w64-mingw32-nm + ln -s llvm-strip $machine-w64-mingw32-strip ln -s llvm-readobj $machine-w64-mingw32-readobj + ln -s llvm-objcopy $machine-w64-mingw32-objcopy ./clang $SRC_DIR/llvm-mingw/wrappers/windres-wrapper.c -O2 -Wl,-s -o $machine-w64-mingw32-windres popd - - popd } export PATH=$INSTALL_DIR/bin:$PATH diff --git a/taskcluster/scripts/tester/test-linux.sh b/taskcluster/scripts/tester/test-linux.sh index bc5dca30f145..f3ab40593ae5 100755 --- a/taskcluster/scripts/tester/test-linux.sh +++ b/taskcluster/scripts/tester/test-linux.sh @@ -6,11 +6,9 @@ echo "running as" $(id) # Detect release version. . /etc/lsb-release -if [ "${DISTRIB_RELEASE}" == "12.04" ]; then - echo "Ubuntu 12.04 not supported" +if [ "${DISTRIB_RELEASE}" != "16.04" ]; then + echo "Ubuntu 16.04 required" exit 1 -elif [ "${DISTRIB_RELEASE}" == "16.04" ]; then - UBUNTU_1604=1 fi #### @@ -27,6 +25,7 @@ fi : MOZHARNESS_OPTIONS ${MOZHARNESS_OPTIONS} : NEED_XVFB ${NEED_XVFB:=true} : NEED_WINDOW_MANAGER ${NEED_WINDOW_MANAGER:=false} +: NEED_COMPIZ ${NEED_COMPIZ} : NEED_PULSEAUDIO ${NEED_PULSEAUDIO:=false} : START_VNC ${START_VNC:=false} : TASKCLUSTER_INTERACTIVE ${TASKCLUSTER_INTERACTIVE:=false} @@ -151,18 +150,14 @@ if $NEED_WINDOW_MANAGER; then # credit card numbers. eval `dbus-launch --sh-syntax` eval `echo '' | /usr/bin/gnome-keyring-daemon -r -d --unlock --components=secrets` - - if [ "${UBUNTU_1604}" ]; then - # start compiz for our window manager - compiz 2>&1 & - #TODO: how to determine if compiz starts correctly? - fi fi -if [ "${UBUNTU_1604}" ]; then - maybe_start_pulse +if $NEED_COMPIZ; then + compiz 2>&1 & fi +maybe_start_pulse + # For telemetry purposes, the build process wants information about the # source it is running export MOZ_SOURCE_REPO="${GECKO_HEAD_REPOSITORY}" diff --git a/taskcluster/taskgraph/transforms/job/mozharness_test.py b/taskcluster/taskgraph/transforms/job/mozharness_test.py index 4fe27004e1ce..e467d27252f5 100644 --- a/taskcluster/taskgraph/transforms/job/mozharness_test.py +++ b/taskcluster/taskgraph/transforms/job/mozharness_test.py @@ -107,11 +107,35 @@ def mozharness_test_on_docker(config, job, taskdesc): 'MOZILLA_BUILD_URL': {'task-reference': installer_url}, 'NEED_PULSEAUDIO': 'true', 'NEED_WINDOW_MANAGER': 'true', + 'NEED_COMPIZ': 'true', 'ENABLE_E10S': str(bool(test.get('e10s'))).lower(), 'MOZ_AUTOMATION': '1', 'WORKING_DIR': '/builds/worker', }) + # by default, require compiz unless proven otherwise, hence a whitelist. + # See https://bugzilla.mozilla.org/show_bug.cgi?id=1552563 + # if using regex this list can be shortened greatly. + suites_not_need_compiz = [ + 'mochitest-webgl1-core', + 'mochitest-webgl1-ext', + 'mochitest-plain-gpu', + 'mochitest-browser-chrome-screenshots', + 'gtest', + 'cppunittest', + 'jsreftest', + 'crashtest', + 'reftest', + 'reftest-no-accel', + 'web-platform-tests', + 'web-platform-tests-reftests', + 'xpcshell' + ] + if job['run']['test']['suite'] in suites_not_need_compiz or ( + job['run']['test']['suite'] == 'mochitest-plain-chunked' and + job['run']['test']['try-name'] == 'mochitest-plain-headless'): + env['NEED_COMPIZ'] = 'false' + if mozharness.get('mochitest-flavor'): env['MOCHITEST_FLAVOR'] = mozharness['mochitest-flavor'] diff --git a/taskcluster/taskgraph/util/scriptworker.py b/taskcluster/taskgraph/util/scriptworker.py index aa5887d5ed81..5faf118404bc 100644 --- a/taskcluster/taskgraph/util/scriptworker.py +++ b/taskcluster/taskgraph/util/scriptworker.py @@ -141,16 +141,16 @@ BALROG_SCOPE_ALIAS_TO_PROJECT = [[ ], [ 'release', set([ 'mozilla-release', + 'comm-esr60', + 'comm-esr68', ]) ], [ 'esr60', set([ 'mozilla-esr60', - 'comm-esr60', ]) ], [ 'esr68', set([ 'mozilla-esr68', - 'comm-esr68', ]) ]] diff --git a/testing/mozharness/configs/android/wrench.py b/testing/mozharness/configs/android/wrench.py new file mode 100644 index 000000000000..48c20596b02e --- /dev/null +++ b/testing/mozharness/configs/android/wrench.py @@ -0,0 +1,30 @@ +# mozharness configuration for Android x86 7.0 unit tests +# +# This configuration should be combined with suite definitions and other +# mozharness configuration from android_common.py, or similar. + +config = { + "tooltool_manifest_path": "testing/config/tooltool-manifests/androidx86_7_0/releng.manifest", + "emulator_manifest": """ + [ + { + "algorithm": "sha512", + "visibility": "internal", + "filename": "android-sdk_r28.0.25.0-linux-x86emu.tar.gz", + "unpack": true, + "digest": "e62acc91f41ccef65a4937a2672fcb56362e9946b806bacc25854035b57d5bd2d525a9c7d660a643ab6381ae2e3b660be7fea70e302ed314c4b07880b2328e18", + "size": 241459387 + } + ] """, + "emulator_avd_name": "test-1", + "emulator_process_name": "emulator64-x86", + "emulator_extra_args": "-gpu swiftshader_indirect -skip-adb-auth -verbose -show-kernel -use-system-libs -ranchu -selinux permissive -memory 3072 -cores 4", + "exes": { + 'adb': '%(abs_work_dir)s/android-sdk-linux/platform-tools/adb', + }, + "env": { + "DISPLAY": ":0.0", + "PATH": "%(PATH)s:%(abs_work_dir)s/android-sdk-linux/emulator:%(abs_work_dir)s/android-sdk-linux/tools:%(abs_work_dir)s/android-sdk-linux/platform-tools", + "MINIDUMP_SAVEPATH": "%(abs_work_dir)s/minidumps", + }, +} diff --git a/testing/mozharness/mozharness/mozilla/testing/android.py b/testing/mozharness/mozharness/mozilla/testing/android.py index 09d56c12a09c..a48c94717920 100644 --- a/testing/mozharness/mozharness/mozilla/testing/android.py +++ b/testing/mozharness/mozharness/mozilla/testing/android.py @@ -302,6 +302,11 @@ class AndroidMixin(object): self.info("Found Android bogomips: %d" % bogomips) break + def logcat_path(self): + logcat_filename = 'logcat-%s.log' % self.device_serial + return os.path.join(self.query_abs_dirs()['abs_blob_upload_dir'], + logcat_filename) + def logcat_start(self): """ Start recording logcat. Writes logcat to the upload directory. @@ -310,10 +315,7 @@ class AndroidMixin(object): # corresponding device is stopped. Output is written directly to # the blobber upload directory so that it is uploaded automatically # at the end of the job. - logcat_filename = 'logcat-%s.log' % self.device_serial - logcat_path = os.path.join(self.abs_dirs['abs_blob_upload_dir'], - logcat_filename) - self.logcat_file = open(logcat_path, 'w') + self.logcat_file = open(self.logcat_path(), 'w') logcat_cmd = [self.adb_path, '-s', self.device_serial, 'logcat', '-v', 'threadtime', 'Trace:S', 'StrictMode:S', 'ExchangeService:S'] @@ -330,19 +332,19 @@ class AndroidMixin(object): self.logcat_proc.kill() self.logcat_file.close() - def install_apk(self, apk): + def install_apk(self, apk, replace=False): """ Install the specified apk. """ import mozdevice try: - self.device.install_app(apk) + self.device.install_app(apk, replace=replace) except (mozdevice.ADBError, mozdevice.ADBTimeoutError), e: self.info('Failed to install %s on %s: %s %s' % - (self.installer_path, self.device_name, + (apk, self.device_name, type(e).__name__, e)) self.fatal('INFRA-ERROR: Failed to install %s' % - os.path.basename(self.installer_path), + os.path.basename(apk), EXIT_STATUS_DICT[TBPL_RETRY]) def is_boot_completed(self): diff --git a/testing/mozharness/scripts/android_emulator_wrench.py b/testing/mozharness/scripts/android_emulator_wrench.py new file mode 100644 index 000000000000..967c36d85beb --- /dev/null +++ b/testing/mozharness/scripts/android_emulator_wrench.py @@ -0,0 +1,202 @@ +#!/usr/bin/env python +# ***** BEGIN LICENSE BLOCK ***** +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# ***** END LICENSE BLOCK ***** + +import datetime +import os +import sys +import time + +# load modules from parent dir +sys.path.insert(1, os.path.dirname(sys.path[0])) + +from mozharness.base.script import BaseScript +from mozharness.mozilla.automation import ( + EXIT_STATUS_DICT, + TBPL_FAILURE, +) +from mozharness.mozilla.mozbase import MozbaseMixin +from mozharness.mozilla.testing.android import AndroidMixin +from mozharness.mozilla.testing.testbase import TestingMixin + + +class AndroidEmulatorWrench(TestingMixin, BaseScript, MozbaseMixin, AndroidMixin): + def __init__(self, require_config_file=False): + super(AndroidEmulatorWrench, self).__init__() + self.device_serial = 'emulator-5554' + self.use_gles3 = True + self._errored = False + + def query_abs_dirs(self): + if self.abs_dirs: + return self.abs_dirs + + abs_dirs = {} + + abs_dirs['abs_work_dir'] = os.path.expanduser("~/.wrench") + if os.environ.get('MOZ_AUTOMATION', '0') == '1': + # In automation use the standard work dir if there is one + parent_abs_dirs = super(AndroidEmulatorWrench, self).query_abs_dirs() + if 'abs_work_dir' in parent_abs_dirs: + abs_dirs['abs_work_dir'] = parent_abs_dirs['abs_work_dir'] + + abs_dirs['abs_avds_dir'] = os.path.join( + abs_dirs['abs_work_dir'], 'avds') + abs_dirs['abs_blob_upload_dir'] = os.path.join( + abs_dirs['abs_work_dir'], 'logs') + abs_dirs['abs_apk_path'] = os.environ.get( + 'WRENCH_APK', + 'gfx/wr/target/android-artifacts/app/build/outputs/apk/app-debug.apk') + abs_dirs['abs_reftests_path'] = os.environ.get( + 'WRENCH_REFTESTS', + 'gfx/wr/wrench/reftests') + + self.abs_dirs = abs_dirs + return self.abs_dirs + + def wait_until_process_done(self, process_name, timeout): + """Waits until the specified process has exited. Polls the process list + every 5 seconds until the process disappears. + + :param process_name: string containing the package name of the + application. + :param timeout: integer specifying the maximum time in seconds + to wait for the application to finish. + :returns: boolean - True if the process exited within the indicated + timeout, False if the process had not exited by the timeout. + """ + end_time = datetime.datetime.now() + datetime.timedelta(seconds=timeout) + while self.device.process_exist(process_name, timeout=timeout): + if datetime.datetime.now() > end_time: + return False + time.sleep(5) + + return True + + def setup_sdcard(self): + # Note that we hard-code /sdcard/wrench as the path here, rather than + # using something like self.device.test_root, because it needs to be + # kept in sync with the path hard-coded inside the wrench source code. + self.device.rm('/sdcard/wrench', recursive=True, force=True) + self.device.mkdir('/sdcard/wrench', parents=True) + self.device.push(self.query_abs_dirs()['abs_reftests_path'], '/sdcard/wrench/reftests') + args_file = os.path.join( + self.query_abs_dirs()['abs_work_dir'], "wrench_args") + with open(args_file, 'w') as argfile: + argfile.write("reftest") + self.device.push(args_file, '/sdcard/wrench/args') + + def run_tests(self): + self.timed_screenshots(None) + self.device.launch_application( + app_name='org.mozilla.wrench', + activity_name='rust.wrench.MainActivity', + intent=None) + self.info("App launched") + done = self.wait_until_process_done('org.mozilla.wrench', timeout=60 * 30) + if not done: + self._errored = True + self.error("Wrench still running after timeout") + + def scrape_logcat(self): + """Wrench will dump the test output to logcat, but for convenience we + want it to show up in the main log. So we scrape it out of the logcat + and dump it to our own log. Note that all output from wrench goes + through the cargo-apk glue stuff, which uses the RustAndroidGlueStdouterr + tag on the output. Also it limits the line length to 512 bytes + (including the null terminator). For reftest unexpected-fail output + this means that the base64 image dump gets wrapped over multiple + lines, so part of what this function does is unwrap that so that the + resulting log is readable by the reftest analyzer.""" + + with open(self.logcat_path(), 'r') as f: + self.info("=== scraped logcat output ===") + tag = "RustAndroidGlueStdouterr: " + long_line = None + for line in f: + tag_index = line.find(tag) + if tag_index == -1: + # not a line we care about + continue + line = line[tag_index + len(tag):].rstrip() + if long_line is None and "REFTEST " not in line and "panicked" not in line: + # non-interesting line + continue + if long_line is not None: + # continuation of a wrapped line + long_line += line + if len(line) >= 511: + if long_line is None: + # start of a new long line + long_line = line + # else "middle" of a long line that keeps going to the next line + continue + # this line doesn't wrap over to the next, so we can + # print it + if long_line is not None: + line = long_line + long_line = None + if "UNEXPECTED-FAIL" in line or "panicked" in line: + self._errored = True + self.error(line) + else: + self.info(line) + self.info("=== end scraped logcat output ===") + self.info("(see logcat artifact for full logcat") + + def do_test(self): + # Running setup_avds will clobber the existing AVD and redownload it. + # For local testing that's kinda expensive, so we omit that if we + # already have that dir. + if not os.path.exists(self.query_abs_dirs()['abs_avds_dir']): + self.setup_avds() + + if os.environ.get('MOZ_AUTOMATION', '0') == '1': + self.start_emulator() + else: + # Can't use start_emulator because it tries to download a non-public + # artifact. Instead we just symlink the presumably already-existing + # SDK into the right place, and manually run the launch. + sdk_path = os.path.join( + self.query_abs_dirs()['abs_work_dir'], + 'android-sdk-linux') + if not os.path.exists(sdk_path): + mozbuild_path = os.environ.get( + 'MOZBUILD_STATE_PATH', + os.path.expanduser('~/.mozbuild')) + mozbuild_sdk = os.environ.get( + 'ANDROID_SDK_HOME', + os.path.join(mozbuild_path, 'android-sdk-linux')) + if os.path.exists(mozbuild_sdk): + os.symlink(mozbuild_sdk, sdk_path) + else: + self.error('Unable to find android SDK at %s' % mozbuild_sdk) + return + self._launch_emulator() + + self.verify_device() + + self.info('Installing APK...') + self.install_apk(self.query_abs_dirs()['abs_apk_path'], replace=True) + self.info('Setting up SD card...') + self.setup_sdcard() + self.info('Running tests...') + self.run_tests() + self.info('Tests done; parsing logcat...') + self.logcat_stop() + self.scrape_logcat() + self.info('All done!') + + def check_errors(self): + if self._errored: + self.info("Errors encountered, terminating with error code...") + exit(EXIT_STATUS_DICT[TBPL_FAILURE]) + + +if __name__ == '__main__': + test = AndroidEmulatorWrench() + test.do_test() + test.check_errors() diff --git a/testing/web-platform/meta/cookies/http-state/chromium-tests.html.ini b/testing/web-platform/meta/cookies/http-state/chromium-tests.html.ini index 8bc56f0d4f7f..9fa898c4eef5 100644 --- a/testing/web-platform/meta/cookies/http-state/chromium-tests.html.ini +++ b/testing/web-platform/meta/cookies/http-state/chromium-tests.html.ini @@ -2,9 +2,6 @@ [chromium0009 - chromium0009] expected: FAIL - [chromium0006 - chromium0006] - expected: FAIL - [chromium0012 - chromium0012] expected: FAIL @@ -14,6 +11,3 @@ [chromium0010 - chromium0010] expected: FAIL - [chromium0007 - chromium0007] - expected: FAIL - diff --git a/testing/web-platform/meta/cookies/http-state/value-tests.html.ini b/testing/web-platform/meta/cookies/http-state/value-tests.html.ini deleted file mode 100644 index e86d5186a615..000000000000 --- a/testing/web-platform/meta/cookies/http-state/value-tests.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[value-tests.html] - [value0003 - value0003] - expected: FAIL - diff --git a/testing/web-platform/tests/cookies/http-state/resources/iframe-expectation-doc.html.py-str b/testing/web-platform/tests/cookies/http-state/resources/iframe-expectation-doc.html.py-str index f2cf31312d7f..9b782a15f845 100644 --- a/testing/web-platform/tests/cookies/http-state/resources/iframe-expectation-doc.html.py-str +++ b/testing/web-platform/tests/cookies/http-state/resources/iframe-expectation-doc.html.py-str @@ -5,7 +5,7 @@ Cookie Test Expectation Document -
{data}
+
{data}
diff --git a/third_party/rust/lock_api/.cargo-checksum.json b/third_party/rust/lock_api/.cargo-checksum.json index b8be5a0c1a0a..f9c920d38c74 100644 --- a/third_party/rust/lock_api/.cargo-checksum.json +++ b/third_party/rust/lock_api/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"ab2a7a96105e15de46900fb0da37edbab44e5513a9818672153dae44ed318f7e","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"c9a75f18b9ab2927829a208fc6aa2cf4e63b8420887ba29cdb265d6619ae82d5","src/lib.rs":"4a16128f58e3380b22b26b137ee1096732995b7e401f3d227dd7b0738b6bd604","src/mutex.rs":"fee397f72325621812c5f78c7a6b9369ea7ec14e71bb0049678a50349519c0c7","src/remutex.rs":"ed76d7b93a56b6248d79676de2aaa66b607b64f1b773c9dd7326b8324e2bc71a","src/rwlock.rs":"5ab1aab614358cfdaf23e8ff8a0ac5e0c7656b777f385aca2e5422f0aa8f0985"},"package":"62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c"} \ No newline at end of file +{"files":{"Cargo.toml":"4e6804e66f9429156bfe15d0d796baceb73a3f06d358608afcbea95cdf0086ba","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"c9a75f18b9ab2927829a208fc6aa2cf4e63b8420887ba29cdb265d6619ae82d5","src/lib.rs":"d9ed1f911f058d066ebfd024940da8a5c1ebbab6cfd65a633dfbc613573dd823","src/mutex.rs":"eeaab6ce6e50aed906bebe598c1b151258327e101eec08b0ff9ccd9c87daddfb","src/remutex.rs":"24cbd5b5b77dd746b065c6d3494dcb2095e81a062341052003b96210a1297ba8","src/rwlock.rs":"a3789a7e820f5c22c8661c4c9e279510a3db50e24894fb380e49dde6b110ddb1"},"package":"ed946d4529956a20f2d63ebe1b69996d5a2137c91913fe3ebbeff957f5bca7ff"} \ No newline at end of file diff --git a/third_party/rust/lock_api/Cargo.toml b/third_party/rust/lock_api/Cargo.toml index ee39d857f697..4146912bfa7a 100644 --- a/third_party/rust/lock_api/Cargo.toml +++ b/third_party/rust/lock_api/Cargo.toml @@ -3,7 +3,7 @@ # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g. crates.io) dependencies +# to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're @@ -11,8 +11,9 @@ # will likely look very different (and much more reasonable) [package] +edition = "2018" name = "lock_api" -version = "0.1.5" +version = "0.2.0" authors = ["Amanieu d'Antras "] description = "Wrappers to create fully-featured Mutex and RwLock types. Compatible with no_std." keywords = ["mutex", "rwlock", "lock", "no_std"] @@ -24,7 +25,12 @@ version = "0.4" optional = true [dependencies.scopeguard] -version = "0.3" +version = "1.0" +default-features = false + +[dependencies.serde] +version = "1.0.90" +optional = true default-features = false [features] diff --git a/third_party/rust/lock_api/src/lib.rs b/third_party/rust/lock_api/src/lib.rs index 60271ed6cdcf..d388d8a3ced6 100644 --- a/third_party/rust/lock_api/src/lib.rs +++ b/third_party/rust/lock_api/src/lib.rs @@ -28,14 +28,14 @@ //! //! ``` //! use lock_api::{RawMutex, Mutex, GuardSend}; -//! use std::sync::atomic::{AtomicBool, Ordering, ATOMIC_BOOL_INIT}; +//! use std::sync::atomic::{AtomicBool, Ordering}; //! //! // 1. Define our raw lock type //! pub struct RawSpinlock(AtomicBool); //! //! // 2. Implement RawMutex for this type //! unsafe impl RawMutex for RawSpinlock { -//! const INIT: RawSpinlock = RawSpinlock(ATOMIC_BOOL_INIT); +//! const INIT: RawSpinlock = RawSpinlock(AtomicBool::new(false)); //! //! // A spinlock guard can be sent to another thread and unlocked there //! type GuardMarker = GuardSend; @@ -85,14 +85,12 @@ #![no_std] #![warn(missing_docs)] +#![warn(rust_2018_idioms)] #![cfg_attr(feature = "nightly", feature(const_fn))] #[macro_use] extern crate scopeguard; -#[cfg(feature = "owning_ref")] -extern crate owning_ref; - /// Marker type which indicates that the Guard type for a lock is `Send`. pub struct GuardSend(()); @@ -100,10 +98,10 @@ pub struct GuardSend(()); pub struct GuardNoSend(*mut ()); mod mutex; -pub use mutex::*; +pub use crate::mutex::*; mod remutex; -pub use remutex::*; +pub use crate::remutex::*; mod rwlock; -pub use rwlock::*; +pub use crate::rwlock::*; diff --git a/third_party/rust/lock_api/src/mutex.rs b/third_party/rust/lock_api/src/mutex.rs index b9cf49bb045e..12fb36895226 100644 --- a/third_party/rust/lock_api/src/mutex.rs +++ b/third_party/rust/lock_api/src/mutex.rs @@ -14,6 +14,9 @@ use core::ops::{Deref, DerefMut}; #[cfg(feature = "owning_ref")] use owning_ref::StableAddress; +#[cfg(feature = "serde")] +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + /// Basic operations for a mutex. /// /// Types implementing this trait can be used by `Mutex` to form a safe and @@ -93,6 +96,35 @@ pub struct Mutex { data: UnsafeCell, } +// Copied and modified from serde +#[cfg(feature = "serde")] +impl Serialize for Mutex +where + R: RawMutex, + T: Serialize + ?Sized, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.lock().serialize(serializer) + } +} + +#[cfg(feature = "serde")] +impl<'de, R, T> Deserialize<'de> for Mutex +where + R: RawMutex, + T: Deserialize<'de> + ?Sized, +{ + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Deserialize::deserialize(deserializer).map(Mutex::new) + } +} + unsafe impl Send for Mutex {} unsafe impl Sync for Mutex {} @@ -127,7 +159,7 @@ impl Mutex { impl Mutex { #[inline] - fn guard(&self) -> MutexGuard { + fn guard(&self) -> MutexGuard<'_, R, T> { MutexGuard { mutex: self, marker: PhantomData, @@ -144,7 +176,7 @@ impl Mutex { /// Attempts to lock a mutex in the thread which already holds the lock will /// result in a deadlock. #[inline] - pub fn lock(&self) -> MutexGuard { + pub fn lock(&self) -> MutexGuard<'_, R, T> { self.raw.lock(); self.guard() } @@ -157,7 +189,7 @@ impl Mutex { /// /// This function does not block. #[inline] - pub fn try_lock(&self) -> Option> { + pub fn try_lock(&self) -> Option> { if self.raw.try_lock() { Some(self.guard()) } else { @@ -230,7 +262,7 @@ impl Mutex { /// `None` is returned. Otherwise, an RAII guard is returned. The lock will /// be unlocked when the guard is dropped. #[inline] - pub fn try_lock_for(&self, timeout: R::Duration) -> Option> { + pub fn try_lock_for(&self, timeout: R::Duration) -> Option> { if self.raw.try_lock_for(timeout) { Some(self.guard()) } else { @@ -244,7 +276,7 @@ impl Mutex { /// `None` is returned. Otherwise, an RAII guard is returned. The lock will /// be unlocked when the guard is dropped. #[inline] - pub fn try_lock_until(&self, timeout: R::Instant) -> Option> { + pub fn try_lock_until(&self, timeout: R::Instant) -> Option> { if self.raw.try_lock_until(timeout) { Some(self.guard()) } else { @@ -268,10 +300,21 @@ impl From for Mutex { } impl fmt::Debug for Mutex { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.try_lock() { Some(guard) => f.debug_struct("Mutex").field("data", &&*guard).finish(), - None => f.pad("Mutex { }"), + None => { + struct LockedPlaceholder; + impl fmt::Debug for LockedPlaceholder { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("") + } + } + + f.debug_struct("Mutex") + .field("data", &LockedPlaceholder) + .finish() + } } } } @@ -281,8 +324,8 @@ impl fmt::Debug for Mutex { /// /// The data protected by the mutex can be accessed through this guard via its /// `Deref` and `DerefMut` implementations. -#[must_use] -pub struct MutexGuard<'a, R: RawMutex + 'a, T: ?Sized + 'a> { +#[must_use = "if unused the Mutex will immediately unlock"] +pub struct MutexGuard<'a, R: RawMutex, T: ?Sized> { mutex: &'a Mutex, marker: PhantomData<(&'a mut T, R::GuardMarker)>, } @@ -428,6 +471,18 @@ impl<'a, R: RawMutex + 'a, T: ?Sized + 'a> Drop for MutexGuard<'a, R, T> { } } +impl<'a, R: RawMutex + 'a, T: fmt::Debug + ?Sized + 'a> fmt::Debug for MutexGuard<'a, R, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +impl<'a, R: RawMutex + 'a, T: fmt::Display + ?Sized + 'a> fmt::Display for MutexGuard<'a, R, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + #[cfg(feature = "owning_ref")] unsafe impl<'a, R: RawMutex + 'a, T: ?Sized + 'a> StableAddress for MutexGuard<'a, R, T> {} @@ -438,8 +493,8 @@ unsafe impl<'a, R: RawMutex + 'a, T: ?Sized + 'a> StableAddress for MutexGuard<' /// former doesn't support temporarily unlocking and re-locking, since that /// could introduce soundness issues if the locked object is modified by another /// thread. -#[must_use] -pub struct MappedMutexGuard<'a, R: RawMutex + 'a, T: ?Sized + 'a> { +#[must_use = "if unused the Mutex will immediately unlock"] +pub struct MappedMutexGuard<'a, R: RawMutex, T: ?Sized> { raw: &'a R, data: *mut T, marker: PhantomData<&'a mut T>, @@ -447,10 +502,12 @@ pub struct MappedMutexGuard<'a, R: RawMutex + 'a, T: ?Sized + 'a> { unsafe impl<'a, R: RawMutex + Sync + 'a, T: ?Sized + Sync + 'a> Sync for MappedMutexGuard<'a, R, T> -{} +{ +} unsafe impl<'a, R: RawMutex + 'a, T: ?Sized + 'a> Send for MappedMutexGuard<'a, R, T> where R::GuardMarker: Send -{} +{ +} impl<'a, R: RawMutex + 'a, T: ?Sized + 'a> MappedMutexGuard<'a, R, T> { /// Makes a new `MappedMutexGuard` for a component of the locked data. @@ -546,5 +603,19 @@ impl<'a, R: RawMutex + 'a, T: ?Sized + 'a> Drop for MappedMutexGuard<'a, R, T> { } } +impl<'a, R: RawMutex + 'a, T: fmt::Debug + ?Sized + 'a> fmt::Debug for MappedMutexGuard<'a, R, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +impl<'a, R: RawMutex + 'a, T: fmt::Display + ?Sized + 'a> fmt::Display + for MappedMutexGuard<'a, R, T> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + #[cfg(feature = "owning_ref")] unsafe impl<'a, R: RawMutex + 'a, T: ?Sized + 'a> StableAddress for MappedMutexGuard<'a, R, T> {} diff --git a/third_party/rust/lock_api/src/remutex.rs b/third_party/rust/lock_api/src/remutex.rs index 3db9f516a487..fe419210243d 100644 --- a/third_party/rust/lock_api/src/remutex.rs +++ b/third_party/rust/lock_api/src/remutex.rs @@ -5,18 +5,21 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. +use crate::mutex::{RawMutex, RawMutexFair, RawMutexTimed}; +use crate::GuardNoSend; use core::cell::{Cell, UnsafeCell}; use core::fmt; use core::marker::PhantomData; use core::mem; use core::ops::Deref; use core::sync::atomic::{AtomicUsize, Ordering}; -use mutex::{RawMutex, RawMutexFair, RawMutexTimed}; -use GuardNoSend; #[cfg(feature = "owning_ref")] use owning_ref::StableAddress; +#[cfg(feature = "serde")] +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + /// Helper trait which returns a non-zero thread ID. /// /// The simplest way to implement this trait is to return the address of a @@ -140,12 +143,45 @@ pub struct ReentrantMutex { data: UnsafeCell, } +// Copied and modified from serde +#[cfg(feature = "serde")] +impl Serialize for ReentrantMutex +where + R: RawMutex, + G: GetThreadId, + T: Serialize + ?Sized, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.lock().serialize(serializer) + } +} + +#[cfg(feature = "serde")] +impl<'de, R, G, T> Deserialize<'de> for ReentrantMutex +where + R: RawMutex, + G: GetThreadId, + T: Deserialize<'de> + ?Sized, +{ + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Deserialize::deserialize(deserializer).map(ReentrantMutex::new) + } +} + unsafe impl Send for ReentrantMutex -{} +{ +} unsafe impl Sync for ReentrantMutex -{} +{ +} impl ReentrantMutex { /// Creates a new reentrant mutex in an unlocked state ready for use. @@ -188,7 +224,7 @@ impl ReentrantMutex { impl ReentrantMutex { #[inline] - fn guard(&self) -> ReentrantMutexGuard { + fn guard(&self) -> ReentrantMutexGuard<'_, R, G, T> { ReentrantMutexGuard { remutex: &self, marker: PhantomData, @@ -206,7 +242,7 @@ impl ReentrantMutex { /// returned to allow scoped unlock of the lock. When the guard goes out of /// scope, the mutex will be unlocked. #[inline] - pub fn lock(&self) -> ReentrantMutexGuard { + pub fn lock(&self) -> ReentrantMutexGuard<'_, R, G, T> { self.raw.lock(); self.guard() } @@ -219,7 +255,7 @@ impl ReentrantMutex { /// /// This function does not block. #[inline] - pub fn try_lock(&self) -> Option> { + pub fn try_lock(&self) -> Option> { if self.raw.try_lock() { Some(self.guard()) } else { @@ -292,7 +328,7 @@ impl ReentrantMutex { /// `None` is returned. Otherwise, an RAII guard is returned. The lock will /// be unlocked when the guard is dropped. #[inline] - pub fn try_lock_for(&self, timeout: R::Duration) -> Option> { + pub fn try_lock_for(&self, timeout: R::Duration) -> Option> { if self.raw.try_lock_for(timeout) { Some(self.guard()) } else { @@ -306,7 +342,7 @@ impl ReentrantMutex { /// `None` is returned. Otherwise, an RAII guard is returned. The lock will /// be unlocked when the guard is dropped. #[inline] - pub fn try_lock_until(&self, timeout: R::Instant) -> Option> { + pub fn try_lock_until(&self, timeout: R::Instant) -> Option> { if self.raw.try_lock_until(timeout) { Some(self.guard()) } else { @@ -330,13 +366,24 @@ impl From for ReentrantMutex { } impl fmt::Debug for ReentrantMutex { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.try_lock() { Some(guard) => f .debug_struct("ReentrantMutex") .field("data", &&*guard) .finish(), - None => f.pad("ReentrantMutex { }"), + None => { + struct LockedPlaceholder; + impl fmt::Debug for LockedPlaceholder { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("") + } + } + + f.debug_struct("ReentrantMutex") + .field("data", &LockedPlaceholder) + .finish() + } } } } @@ -346,15 +393,16 @@ impl fmt::Debug for Reentra /// /// The data protected by the mutex can be accessed through this guard via its /// `Deref` implementation. -#[must_use] -pub struct ReentrantMutexGuard<'a, R: RawMutex + 'a, G: GetThreadId + 'a, T: ?Sized + 'a> { +#[must_use = "if unused the ReentrantMutex will immediately unlock"] +pub struct ReentrantMutexGuard<'a, R: RawMutex, G: GetThreadId, T: ?Sized> { remutex: &'a ReentrantMutex, marker: PhantomData<(&'a T, GuardNoSend)>, } unsafe impl<'a, R: RawMutex + Sync + 'a, G: GetThreadId + Sync + 'a, T: ?Sized + Sync + 'a> Sync for ReentrantMutexGuard<'a, R, G, T> -{} +{ +} impl<'a, R: RawMutex + 'a, G: GetThreadId + 'a, T: ?Sized + 'a> ReentrantMutexGuard<'a, R, G, T> { /// Returns a reference to the original `ReentrantMutex` object. @@ -395,7 +443,10 @@ impl<'a, R: RawMutex + 'a, G: GetThreadId + 'a, T: ?Sized + 'a> ReentrantMutexGu /// used as `ReentrantMutexGuard::map(...)`. A method would interfere with methods of /// the same name on the contents of the locked data. #[inline] - pub fn try_map(s: Self, f: F) -> Result, Self> + pub fn try_map( + s: Self, + f: F, + ) -> Result, Self> where F: FnOnce(&mut T) -> Option<&mut U>, { @@ -494,10 +545,27 @@ impl<'a, R: RawMutex + 'a, G: GetThreadId + 'a, T: ?Sized + 'a> Drop } } +impl<'a, R: RawMutex + 'a, G: GetThreadId + 'a, T: fmt::Debug + ?Sized + 'a> fmt::Debug + for ReentrantMutexGuard<'a, R, G, T> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +impl<'a, R: RawMutex + 'a, G: GetThreadId + 'a, T: fmt::Display + ?Sized + 'a> fmt::Display + for ReentrantMutexGuard<'a, R, G, T> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + #[cfg(feature = "owning_ref")] unsafe impl<'a, R: RawMutex + 'a, G: GetThreadId + 'a, T: ?Sized + 'a> StableAddress for ReentrantMutexGuard<'a, R, G, T> -{} +{ +} /// An RAII mutex guard returned by `ReentrantMutexGuard::map`, which can point to a /// subfield of the protected data. @@ -506,8 +574,8 @@ unsafe impl<'a, R: RawMutex + 'a, G: GetThreadId + 'a, T: ?Sized + 'a> StableAdd /// former doesn't support temporarily unlocking and re-locking, since that /// could introduce soundness issues if the locked object is modified by another /// thread. -#[must_use] -pub struct MappedReentrantMutexGuard<'a, R: RawMutex + 'a, G: GetThreadId + 'a, T: ?Sized + 'a> { +#[must_use = "if unused the ReentrantMutex will immediately unlock"] +pub struct MappedReentrantMutexGuard<'a, R: RawMutex, G: GetThreadId, T: ?Sized> { raw: &'a RawReentrantMutex, data: *const T, marker: PhantomData<&'a T>, @@ -515,7 +583,8 @@ pub struct MappedReentrantMutexGuard<'a, R: RawMutex + 'a, G: GetThreadId + 'a, unsafe impl<'a, R: RawMutex + Sync + 'a, G: GetThreadId + Sync + 'a, T: ?Sized + Sync + 'a> Sync for MappedReentrantMutexGuard<'a, R, G, T> -{} +{ +} impl<'a, R: RawMutex + 'a, G: GetThreadId + 'a, T: ?Sized + 'a> MappedReentrantMutexGuard<'a, R, G, T> @@ -553,7 +622,10 @@ impl<'a, R: RawMutex + 'a, G: GetThreadId + 'a, T: ?Sized + 'a> /// used as `MappedReentrantMutexGuard::map(...)`. A method would interfere with methods of /// the same name on the contents of the locked data. #[inline] - pub fn try_map(s: Self, f: F) -> Result, Self> + pub fn try_map( + s: Self, + f: F, + ) -> Result, Self> where F: FnOnce(&T) -> Option<&U>, { @@ -612,7 +684,24 @@ impl<'a, R: RawMutex + 'a, G: GetThreadId + 'a, T: ?Sized + 'a> Drop } } +impl<'a, R: RawMutex + 'a, G: GetThreadId + 'a, T: fmt::Debug + ?Sized + 'a> fmt::Debug + for MappedReentrantMutexGuard<'a, R, G, T> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +impl<'a, R: RawMutex + 'a, G: GetThreadId + 'a, T: fmt::Display + ?Sized + 'a> fmt::Display + for MappedReentrantMutexGuard<'a, R, G, T> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + #[cfg(feature = "owning_ref")] unsafe impl<'a, R: RawMutex + 'a, G: GetThreadId + 'a, T: ?Sized + 'a> StableAddress for MappedReentrantMutexGuard<'a, R, G, T> -{} +{ +} diff --git a/third_party/rust/lock_api/src/rwlock.rs b/third_party/rust/lock_api/src/rwlock.rs index 4edb9179e2a4..900b1d00b568 100644 --- a/third_party/rust/lock_api/src/rwlock.rs +++ b/third_party/rust/lock_api/src/rwlock.rs @@ -14,6 +14,9 @@ use core::ops::{Deref, DerefMut}; #[cfg(feature = "owning_ref")] use owning_ref::StableAddress; +#[cfg(feature = "serde")] +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + /// Basic operations for a reader-writer lock. /// /// Types implementing this trait can be used by `RwLock` to form a safe and @@ -230,6 +233,35 @@ pub struct RwLock { data: UnsafeCell, } +// Copied and modified from serde +#[cfg(feature = "serde")] +impl Serialize for RwLock +where + R: RawRwLock, + T: Serialize + ?Sized, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.read().serialize(serializer) + } +} + +#[cfg(feature = "serde")] +impl<'de, R, T> Deserialize<'de> for RwLock +where + R: RawRwLock, + T: Deserialize<'de> + ?Sized, +{ + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Deserialize::deserialize(deserializer).map(RwLock::new) + } +} + unsafe impl Send for RwLock {} unsafe impl Sync for RwLock {} @@ -264,7 +296,7 @@ impl RwLock { impl RwLock { #[inline] - fn read_guard(&self) -> RwLockReadGuard { + fn read_guard(&self) -> RwLockReadGuard<'_, R, T> { RwLockReadGuard { rwlock: self, marker: PhantomData, @@ -272,7 +304,7 @@ impl RwLock { } #[inline] - fn write_guard(&self) -> RwLockWriteGuard { + fn write_guard(&self) -> RwLockWriteGuard<'_, R, T> { RwLockWriteGuard { rwlock: self, marker: PhantomData, @@ -292,7 +324,7 @@ impl RwLock { /// Returns an RAII guard which will release this thread's shared access /// once it is dropped. #[inline] - pub fn read(&self) -> RwLockReadGuard { + pub fn read(&self) -> RwLockReadGuard<'_, R, T> { self.raw.lock_shared(); self.read_guard() } @@ -305,7 +337,7 @@ impl RwLock { /// /// This function does not block. #[inline] - pub fn try_read(&self) -> Option> { + pub fn try_read(&self) -> Option> { if self.raw.try_lock_shared() { Some(self.read_guard()) } else { @@ -322,7 +354,7 @@ impl RwLock { /// Returns an RAII guard which will drop the write access of this `RwLock` /// when dropped. #[inline] - pub fn write(&self) -> RwLockWriteGuard { + pub fn write(&self) -> RwLockWriteGuard<'_, R, T> { self.raw.lock_exclusive(); self.write_guard() } @@ -335,7 +367,7 @@ impl RwLock { /// /// This function does not block. #[inline] - pub fn try_write(&self) -> Option> { + pub fn try_write(&self) -> Option> { if self.raw.try_lock_exclusive() { Some(self.write_guard()) } else { @@ -441,7 +473,7 @@ impl RwLock { /// `None` is returned. Otherwise, an RAII guard is returned which will /// release the shared access when it is dropped. #[inline] - pub fn try_read_for(&self, timeout: R::Duration) -> Option> { + pub fn try_read_for(&self, timeout: R::Duration) -> Option> { if self.raw.try_lock_shared_for(timeout) { Some(self.read_guard()) } else { @@ -456,7 +488,7 @@ impl RwLock { /// `None` is returned. Otherwise, an RAII guard is returned which will /// release the shared access when it is dropped. #[inline] - pub fn try_read_until(&self, timeout: R::Instant) -> Option> { + pub fn try_read_until(&self, timeout: R::Instant) -> Option> { if self.raw.try_lock_shared_until(timeout) { Some(self.read_guard()) } else { @@ -471,7 +503,7 @@ impl RwLock { /// `None` is returned. Otherwise, an RAII guard is returned which will /// release the exclusive access when it is dropped. #[inline] - pub fn try_write_for(&self, timeout: R::Duration) -> Option> { + pub fn try_write_for(&self, timeout: R::Duration) -> Option> { if self.raw.try_lock_exclusive_for(timeout) { Some(self.write_guard()) } else { @@ -486,7 +518,7 @@ impl RwLock { /// `None` is returned. Otherwise, an RAII guard is returned which will /// release the exclusive access when it is dropped. #[inline] - pub fn try_write_until(&self, timeout: R::Instant) -> Option> { + pub fn try_write_until(&self, timeout: R::Instant) -> Option> { if self.raw.try_lock_exclusive_until(timeout) { Some(self.write_guard()) } else { @@ -512,7 +544,7 @@ impl RwLock { /// Returns an RAII guard which will release this thread's shared access /// once it is dropped. #[inline] - pub fn read_recursive(&self) -> RwLockReadGuard { + pub fn read_recursive(&self) -> RwLockReadGuard<'_, R, T> { self.raw.lock_shared_recursive(); self.read_guard() } @@ -528,7 +560,7 @@ impl RwLock { /// /// This function does not block. #[inline] - pub fn try_read_recursive(&self) -> Option> { + pub fn try_read_recursive(&self) -> Option> { if self.raw.try_lock_shared_recursive() { Some(self.read_guard()) } else { @@ -549,7 +581,10 @@ impl RwLock { /// lock is held at the time of the call. See the documentation for /// `read_recursive` for details. #[inline] - pub fn try_read_recursive_for(&self, timeout: R::Duration) -> Option> { + pub fn try_read_recursive_for( + &self, + timeout: R::Duration, + ) -> Option> { if self.raw.try_lock_shared_recursive_for(timeout) { Some(self.read_guard()) } else { @@ -564,7 +599,10 @@ impl RwLock { /// `None` is returned. Otherwise, an RAII guard is returned which will /// release the shared access when it is dropped. #[inline] - pub fn try_read_recursive_until(&self, timeout: R::Instant) -> Option> { + pub fn try_read_recursive_until( + &self, + timeout: R::Instant, + ) -> Option> { if self.raw.try_lock_shared_recursive_until(timeout) { Some(self.read_guard()) } else { @@ -575,7 +613,7 @@ impl RwLock { impl RwLock { #[inline] - fn upgradable_guard(&self) -> RwLockUpgradableReadGuard { + fn upgradable_guard(&self) -> RwLockUpgradableReadGuard<'_, R, T> { RwLockUpgradableReadGuard { rwlock: self, marker: PhantomData, @@ -592,7 +630,7 @@ impl RwLock { /// Returns an RAII guard which will release this thread's shared access /// once it is dropped. #[inline] - pub fn upgradable_read(&self) -> RwLockUpgradableReadGuard { + pub fn upgradable_read(&self) -> RwLockUpgradableReadGuard<'_, R, T> { self.raw.lock_upgradable(); self.upgradable_guard() } @@ -605,7 +643,7 @@ impl RwLock { /// /// This function does not block. #[inline] - pub fn try_upgradable_read(&self) -> Option> { + pub fn try_upgradable_read(&self) -> Option> { if self.raw.try_lock_upgradable() { Some(self.upgradable_guard()) } else { @@ -625,7 +663,7 @@ impl RwLock { pub fn try_upgradable_read_for( &self, timeout: R::Duration, - ) -> Option> { + ) -> Option> { if self.raw.try_lock_upgradable_for(timeout) { Some(self.upgradable_guard()) } else { @@ -643,7 +681,7 @@ impl RwLock { pub fn try_upgradable_read_until( &self, timeout: R::Instant, - ) -> Option> { + ) -> Option> { if self.raw.try_lock_upgradable_until(timeout) { Some(self.upgradable_guard()) } else { @@ -667,18 +705,29 @@ impl From for RwLock { } impl fmt::Debug for RwLock { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.try_read() { Some(guard) => f.debug_struct("RwLock").field("data", &&*guard).finish(), - None => f.pad("RwLock { }"), + None => { + struct LockedPlaceholder; + impl fmt::Debug for LockedPlaceholder { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("") + } + } + + f.debug_struct("RwLock") + .field("data", &LockedPlaceholder) + .finish() + } } } } /// RAII structure used to release the shared read access of a lock when /// dropped. -#[must_use] -pub struct RwLockReadGuard<'a, R: RawRwLock + 'a, T: ?Sized + 'a> { +#[must_use = "if unused the RwLock will immediately unlock"] +pub struct RwLockReadGuard<'a, R: RawRwLock, T: ?Sized> { rwlock: &'a RwLock, marker: PhantomData<(&'a T, R::GuardMarker)>, } @@ -819,13 +868,27 @@ impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> Drop for RwLockReadGuard<'a, R, T> { } } +impl<'a, R: RawRwLock + 'a, T: fmt::Debug + ?Sized + 'a> fmt::Debug for RwLockReadGuard<'a, R, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +impl<'a, R: RawRwLock + 'a, T: fmt::Display + ?Sized + 'a> fmt::Display + for RwLockReadGuard<'a, R, T> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + #[cfg(feature = "owning_ref")] unsafe impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> StableAddress for RwLockReadGuard<'a, R, T> {} /// RAII structure used to release the exclusive write access of a lock when /// dropped. -#[must_use] -pub struct RwLockWriteGuard<'a, R: RawRwLock + 'a, T: ?Sized + 'a> { +#[must_use = "if unused the RwLock will immediately unlock"] +pub struct RwLockWriteGuard<'a, R: RawRwLock, T: ?Sized> { rwlock: &'a RwLock, marker: PhantomData<(&'a mut T, R::GuardMarker)>, } @@ -1007,20 +1070,35 @@ impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> Drop for RwLockWriteGuard<'a, R, T> } } +impl<'a, R: RawRwLock + 'a, T: fmt::Debug + ?Sized + 'a> fmt::Debug for RwLockWriteGuard<'a, R, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +impl<'a, R: RawRwLock + 'a, T: fmt::Display + ?Sized + 'a> fmt::Display + for RwLockWriteGuard<'a, R, T> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + #[cfg(feature = "owning_ref")] unsafe impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> StableAddress for RwLockWriteGuard<'a, R, T> {} /// RAII structure used to release the upgradable read access of a lock when /// dropped. -#[must_use] -pub struct RwLockUpgradableReadGuard<'a, R: RawRwLockUpgrade + 'a, T: ?Sized + 'a> { +#[must_use = "if unused the RwLock will immediately unlock"] +pub struct RwLockUpgradableReadGuard<'a, R: RawRwLockUpgrade, T: ?Sized> { rwlock: &'a RwLock, marker: PhantomData<(&'a T, R::GuardMarker)>, } unsafe impl<'a, R: RawRwLockUpgrade + 'a, T: ?Sized + Sync + 'a> Sync for RwLockUpgradableReadGuard<'a, R, T> -{} +{ +} impl<'a, R: RawRwLockUpgrade + 'a, T: ?Sized + 'a> RwLockUpgradableReadGuard<'a, R, T> { /// Returns a reference to the original reader-writer lock object. @@ -1196,10 +1274,27 @@ impl<'a, R: RawRwLockUpgrade + 'a, T: ?Sized + 'a> Drop for RwLockUpgradableRead } } +impl<'a, R: RawRwLockUpgrade + 'a, T: fmt::Debug + ?Sized + 'a> fmt::Debug + for RwLockUpgradableReadGuard<'a, R, T> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +impl<'a, R: RawRwLockUpgrade + 'a, T: fmt::Display + ?Sized + 'a> fmt::Display + for RwLockUpgradableReadGuard<'a, R, T> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + #[cfg(feature = "owning_ref")] unsafe impl<'a, R: RawRwLockUpgrade + 'a, T: ?Sized + 'a> StableAddress for RwLockUpgradableReadGuard<'a, R, T> -{} +{ +} /// An RAII read lock guard returned by `RwLockReadGuard::map`, which can point to a /// subfield of the protected data. @@ -1208,8 +1303,8 @@ unsafe impl<'a, R: RawRwLockUpgrade + 'a, T: ?Sized + 'a> StableAddress /// former doesn't support temporarily unlocking and re-locking, since that /// could introduce soundness issues if the locked object is modified by another /// thread. -#[must_use] -pub struct MappedRwLockReadGuard<'a, R: RawRwLock + 'a, T: ?Sized + 'a> { +#[must_use = "if unused the RwLock will immediately unlock"] +pub struct MappedRwLockReadGuard<'a, R: RawRwLock, T: ?Sized> { raw: &'a R, data: *const T, marker: PhantomData<&'a T>, @@ -1218,7 +1313,8 @@ pub struct MappedRwLockReadGuard<'a, R: RawRwLock + 'a, T: ?Sized + 'a> { unsafe impl<'a, R: RawRwLock + 'a, T: ?Sized + Sync + 'a> Sync for MappedRwLockReadGuard<'a, R, T> {} unsafe impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> Send for MappedRwLockReadGuard<'a, R, T> where R::GuardMarker: Send -{} +{ +} impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> MappedRwLockReadGuard<'a, R, T> { /// Make a new `MappedRwLockReadGuard` for a component of the locked data. @@ -1307,10 +1403,27 @@ impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> Drop for MappedRwLockReadGuard<'a, R } } +impl<'a, R: RawRwLock + 'a, T: fmt::Debug + ?Sized + 'a> fmt::Debug + for MappedRwLockReadGuard<'a, R, T> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +impl<'a, R: RawRwLock + 'a, T: fmt::Display + ?Sized + 'a> fmt::Display + for MappedRwLockReadGuard<'a, R, T> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + #[cfg(feature = "owning_ref")] unsafe impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> StableAddress for MappedRwLockReadGuard<'a, R, T> -{} +{ +} /// An RAII write lock guard returned by `RwLockWriteGuard::map`, which can point to a /// subfield of the protected data. @@ -1319,8 +1432,8 @@ unsafe impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> StableAddress /// former doesn't support temporarily unlocking and re-locking, since that /// could introduce soundness issues if the locked object is modified by another /// thread. -#[must_use] -pub struct MappedRwLockWriteGuard<'a, R: RawRwLock + 'a, T: ?Sized + 'a> { +#[must_use = "if unused the RwLock will immediately unlock"] +pub struct MappedRwLockWriteGuard<'a, R: RawRwLock, T: ?Sized> { raw: &'a R, data: *mut T, marker: PhantomData<&'a mut T>, @@ -1328,10 +1441,12 @@ pub struct MappedRwLockWriteGuard<'a, R: RawRwLock + 'a, T: ?Sized + 'a> { unsafe impl<'a, R: RawRwLock + 'a, T: ?Sized + Sync + 'a> Sync for MappedRwLockWriteGuard<'a, R, T> -{} +{ +} unsafe impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> Send for MappedRwLockWriteGuard<'a, R, T> where R::GuardMarker: Send -{} +{ +} impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> MappedRwLockWriteGuard<'a, R, T> { /// Make a new `MappedRwLockWriteGuard` for a component of the locked data. @@ -1447,7 +1562,24 @@ impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> Drop for MappedRwLockWriteGuard<'a, } } +impl<'a, R: RawRwLock + 'a, T: fmt::Debug + ?Sized + 'a> fmt::Debug + for MappedRwLockWriteGuard<'a, R, T> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +impl<'a, R: RawRwLock + 'a, T: fmt::Display + ?Sized + 'a> fmt::Display + for MappedRwLockWriteGuard<'a, R, T> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + #[cfg(feature = "owning_ref")] unsafe impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> StableAddress for MappedRwLockWriteGuard<'a, R, T> -{} +{ +} diff --git a/third_party/rust/parking_lot/.cargo-checksum.json b/third_party/rust/parking_lot/.cargo-checksum.json index 40cdd2ac7bc0..158379a9f3b8 100644 --- a/third_party/rust/parking_lot/.cargo-checksum.json +++ b/third_party/rust/parking_lot/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"CHANGELOG.md":"e254fac6600c725edb746f31f41b1b2ceeb9cfc85f4f9a3e6af874c70b020823","Cargo.toml":"215d5b3a2c18f556b5c66ac6d27eea71d7dd7e6b4857ecd6966c2e5cc03270ea","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"c9a75f18b9ab2927829a208fc6aa2cf4e63b8420887ba29cdb265d6619ae82d5","README.md":"a52cf38f796e7f12215662e8a3a23aa9802c170a09ecba0e4be766c88f95a9c5","appveyor.yml":"cb1d02316926d88e174976bfc6781194569ca27f386c50e3091d8e52587d30a2","src/condvar.rs":"ce127f75bad5c175abb8147aac4b5be78aabdb599c5f8f3aad77f6bc3705274d","src/deadlock.rs":"8916c2e2820bfd3a55860ddb9f1b907888406b68cdae2b7a2093c825d28f3b99","src/elision.rs":"89072fe0aca87d53abc0f56490ae77bcf9d77e28e291bd13e861b1924bbb079f","src/lib.rs":"3e259bf3421f10c3e920daca511a4880b2620145a1fcb070a37548835c4f429a","src/mutex.rs":"0ac3e654e4aa2c3078a6aa22c83428d604e7f3f8ed4c261c40d030d232ca7b64","src/once.rs":"606e0e88d6c1ff82b69bda56e7409ec3a1aefa66b45b7fa42b88cba07ae70598","src/raw_mutex.rs":"881e75a843d76399d01c4ae0f09cd23b93b137b5035a47bd7886505132e58165","src/raw_rwlock.rs":"2e3c13e80cd06be53118ae2bcc7bdec708dda8c139c371ee12885f48903cf69c","src/remutex.rs":"bad8022610344086010b0661998a416db4b458c222e671b67df03fc4795c0298","src/rwlock.rs":"fc826cbcf2d7862ecb184b657a82bb8794a9e26ac329c8f87b589fa09f15d245","src/util.rs":"2d07c0c010a857790ae2ed6a1215eeed8af76859e076797ea1ba8dec82169e84"},"package":"69376b761943787ebd5cc85a5bc95958651a22609c5c1c2b65de21786baec72b"} \ No newline at end of file +{"files":{"CHANGELOG.md":"f9a9c82373818d32816c42e0f127f6f14a64d37925f02041c10c66a528e0d454","Cargo.toml":"ef3558536eff060103a0c35e6e9ecfe723240c4a37429cf3d7d84d1eb4fda5e3","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"c9a75f18b9ab2927829a208fc6aa2cf4e63b8420887ba29cdb265d6619ae82d5","README.md":"61723e013019e0254522485795be4ff3f1cb4f580ebf4a8daa8fabeb4b9b9e6b","appveyor.yml":"fd584e381a2eb990c8d5eb44998d9c91ff4d538f4b9c62acc018a7bb94cb1fe7","build.rs":"4ed00d73d71057bcdf6c186559468927fc130fd65cfd806ee5d46d28540bc653","src/condvar.rs":"d7cf8af884d577a726f40ed043cbbf2a24424df6e20e1cc4718f4ae390cbb861","src/deadlock.rs":"081dbf009539b113f67ad0a1abd7af889dad684a47aa1a7dc00ae91f08975ef6","src/elision.rs":"00f7af80021fd602879fb7205befb6ff941cd8dc932a5c0a534b430fefe421ea","src/lib.rs":"acfb6cd0d6e69ab49325defc2d9dd624088d442c9c0dae71e20dd8eced84cae3","src/mutex.rs":"e3a48933b7e19d26eab4b5f44ed4e9bcb069b57cdd4a0569d1e65f6c3839b766","src/once.rs":"3b0c1254acbcff840048c722220066988df69f9d9487ac188356f64b7bcad54f","src/raw_mutex.rs":"9eeccbe797116f8c3f1a19e4803ac1bb57c6c5ec9b2d2770fb42ee5aee5a1002","src/raw_rwlock.rs":"5bb1d74a90a52f0f573d49776a2a68f00a2301c25c8400af2934d3e018728e79","src/remutex.rs":"85b3cff3aaa0ca4c644fcb7cd06447128e8e6065d6a632c436085841ac244022","src/rwlock.rs":"63be04f2af7eda7aa33f704846eb413a2ffd76135d248cb250dc91bd20d7dd66","src/util.rs":"8bd40151fea0a7ffb2fdcb751a5dfd868d8d4d275b0f1b04a7fc5d2a0ba41766"},"package":"fa7767817701cce701d5585b9c4db3cdd02086398322c1d7e8bf5094a96a2ce7"} \ No newline at end of file diff --git a/third_party/rust/parking_lot/CHANGELOG.md b/third_party/rust/parking_lot/CHANGELOG.md index 20651aa805af..ee8725180fd0 100644 --- a/third_party/rust/parking_lot/CHANGELOG.md +++ b/third_party/rust/parking_lot/CHANGELOG.md @@ -1,3 +1,14 @@ +0.7.1 (2019-01-01) +================== + +- Fixed potential deadlock when upgrading a RwLock. +- Fixed overflow panic on very long timeouts (#111). + +0.7.0 (2018-11-20) +================== + +- Return if or how many threads were notified from `Condvar::notify_*` + 0.6.3 (2018-07-18) ================== diff --git a/third_party/rust/parking_lot/Cargo.toml b/third_party/rust/parking_lot/Cargo.toml index 88923f30186d..508d133a1c01 100644 --- a/third_party/rust/parking_lot/Cargo.toml +++ b/third_party/rust/parking_lot/Cargo.toml @@ -3,7 +3,7 @@ # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g. crates.io) dependencies +# to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're @@ -11,8 +11,9 @@ # will likely look very different (and much more reasonable) [package] +edition = "2018" name = "parking_lot" -version = "0.6.3" +version = "0.8.0" authors = ["Amanieu d'Antras "] description = "More compact and efficient implementations of the standard synchronization primitives." readme = "README.md" @@ -21,15 +22,24 @@ categories = ["concurrency"] license = "Apache-2.0/MIT" repository = "https://github.com/Amanieu/parking_lot" [dependencies.lock_api] -version = "0.1" +version = "0.2" [dependencies.parking_lot_core] -version = "0.2" -[dev-dependencies.rand] version = "0.5" +[dev-dependencies.bincode] +version = "1.1.3" + +[dev-dependencies.lazy_static] +version = "1.0" + +[dev-dependencies.rand] +version = "0.6" +[build-dependencies.rustc_version] +version = "0.2" [features] deadlock_detection = ["parking_lot_core/deadlock_detection"] -default = ["owning_ref"] +default = [] nightly = ["parking_lot_core/nightly", "lock_api/nightly"] owning_ref = ["lock_api/owning_ref"] +serde = ["lock_api/serde"] diff --git a/third_party/rust/parking_lot/README.md b/third_party/rust/parking_lot/README.md index 4e9204cabdf7..5710c8b03f27 100644 --- a/third_party/rust/parking_lot/README.md +++ b/third_party/rust/parking_lot/README.md @@ -68,6 +68,9 @@ in the Rust standard library: can be enabled via the `deadlock_detection` feature. 17. `RwLock` supports atomically upgrading an "upgradable" read lock into a write lock. +18. Optional support for [serde](https://docs.serde.rs/serde/). Enable via the + feature `serde`. **NOTE!** this support is for `Mutex`, `ReentrantMutex`, + and `RwLock` only; `Condvar` and `Once` are not currently supported. ## The parking lot @@ -99,7 +102,7 @@ Add this to your `Cargo.toml`: ```toml [dependencies] -parking_lot = "0.6" +parking_lot = "0.8" ``` and this to your crate root: @@ -112,7 +115,7 @@ To enable nightly-only features, add this to your `Cargo.toml` instead: ```toml [dependencies] -parking_lot = {version = "0.6", features = ["nightly"]} +parking_lot = {version = "0.8", features = ["nightly"]} ``` The experimental deadlock detector can be enabled with the @@ -122,6 +125,11 @@ The core parking lot API is provided by the `parking_lot_core` crate. It is separate from the synchronization primitives in the `parking_lot` crate so that changes to the core API do not cause breaking changes for users of `parking_lot`. +## Minimum Rust version + +The current minimum required Rust version is 1.31. Any change to this is +considered a breaking change and will require a major version bump. + ## License Licensed under either of diff --git a/third_party/rust/parking_lot/appveyor.yml b/third_party/rust/parking_lot/appveyor.yml index fef6b57033ff..8a084a86eddc 100644 --- a/third_party/rust/parking_lot/appveyor.yml +++ b/third_party/rust/parking_lot/appveyor.yml @@ -6,10 +6,10 @@ environment: - TARGET: nightly-i686-pc-windows-msvc - TARGET: nightly-x86_64-pc-windows-gnu - TARGET: nightly-i686-pc-windows-gnu - - TARGET: 1.24.0-x86_64-pc-windows-msvc - - TARGET: 1.24.0-i686-pc-windows-msvc - - TARGET: 1.24.0-x86_64-pc-windows-gnu - - TARGET: 1.24.0-i686-pc-windows-gnu + - TARGET: 1.31.0-x86_64-pc-windows-msvc + - TARGET: 1.31.0-i686-pc-windows-msvc + - TARGET: 1.31.0-x86_64-pc-windows-gnu + - TARGET: 1.31.0-i686-pc-windows-gnu install: - SET PATH=C:\Python27;C:\Python27\Scripts;%PATH%;%APPDATA%\Python\Scripts @@ -25,5 +25,5 @@ build_script: test_script: - travis-cargo test - - travis-cargo test -- --features=deadlock_detection + - travis-cargo --only nightly test -- --features=deadlock_detection - travis-cargo doc diff --git a/third_party/rust/parking_lot/build.rs b/third_party/rust/parking_lot/build.rs new file mode 100644 index 000000000000..17e76f156280 --- /dev/null +++ b/third_party/rust/parking_lot/build.rs @@ -0,0 +1,8 @@ +use rustc_version::{version, Version}; + +fn main() { + if version().unwrap() >= Version::parse("1.34.0").unwrap() { + println!("cargo:rustc-cfg=has_sized_atomics"); + println!("cargo:rustc-cfg=has_checked_instant"); + } +} diff --git a/third_party/rust/parking_lot/src/condvar.rs b/third_party/rust/parking_lot/src/condvar.rs index 89ddac754c3d..59979671c89e 100644 --- a/third_party/rust/parking_lot/src/condvar.rs +++ b/third_party/rust/parking_lot/src/condvar.rs @@ -5,14 +5,16 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. -use deadlock; +use crate::mutex::MutexGuard; +use crate::raw_mutex::{RawMutex, TOKEN_HANDOFF, TOKEN_NORMAL}; +use crate::{deadlock, util}; +use core::{ + fmt, ptr, + sync::atomic::{AtomicPtr, Ordering}, +}; use lock_api::RawMutex as RawMutexTrait; -use mutex::MutexGuard; use parking_lot_core::{self, ParkResult, RequeueOp, UnparkResult, DEFAULT_PARK_TOKEN}; -use raw_mutex::{RawMutex, TOKEN_HANDOFF, TOKEN_NORMAL}; -use std::sync::atomic::{AtomicPtr, Ordering}; use std::time::{Duration, Instant}; -use std::{fmt, ptr}; /// A type indicating whether a timed wait on a condition variable returned /// due to a time out or not. @@ -87,7 +89,6 @@ pub struct Condvar { impl Condvar { /// Creates a new condition variable which is ready to be waited on and /// notified. - #[cfg(feature = "nightly")] #[inline] pub const fn new() -> Condvar { Condvar { @@ -95,71 +96,105 @@ impl Condvar { } } - /// Creates a new condition variable which is ready to be waited on and - /// notified. - #[cfg(not(feature = "nightly"))] - #[inline] - pub fn new() -> Condvar { - Condvar { - state: AtomicPtr::new(ptr::null_mut()), - } - } - /// Wakes up one blocked thread on this condvar. /// + /// Returns whether a thread was woken up. + /// /// If there is a blocked thread on this condition variable, then it will /// be woken up from its call to `wait` or `wait_timeout`. Calls to /// `notify_one` are not buffered in any way. /// /// To wake up all threads, see `notify_all()`. + /// + /// # Examples + /// + /// ``` + /// use parking_lot::Condvar; + /// + /// let condvar = Condvar::new(); + /// + /// // do something with condvar, share it with other threads + /// + /// if !condvar.notify_one() { + /// println!("Nobody was listening for this."); + /// } + /// ``` #[inline] - pub fn notify_one(&self) { + pub fn notify_one(&self) -> bool { // Nothing to do if there are no waiting threads - if self.state.load(Ordering::Relaxed).is_null() { - return; + let state = self.state.load(Ordering::Relaxed); + if state.is_null() { + return false; } - self.notify_one_slow(); + self.notify_one_slow(state) } #[cold] #[inline(never)] - fn notify_one_slow(&self) { + fn notify_one_slow(&self, mutex: *mut RawMutex) -> bool { unsafe { - // Unpark one thread - let addr = self as *const _ as usize; - let callback = |result: UnparkResult| { + // Unpark one thread and requeue the rest onto the mutex + let from = self as *const _ as usize; + let to = mutex as usize; + let validate = || { + // Make sure that our atomic state still points to the same + // mutex. If not then it means that all threads on the current + // mutex were woken up and a new waiting thread switched to a + // different mutex. In that case we can get away with doing + // nothing. + if self.state.load(Ordering::Relaxed) != mutex { + return RequeueOp::Abort; + } + + // Unpark one thread if the mutex is unlocked, otherwise just + // requeue everything to the mutex. This is safe to do here + // since unlocking the mutex when the parked bit is set requires + // locking the queue. There is the possibility of a race if the + // mutex gets locked after we check, but that doesn't matter in + // this case. + if (*mutex).mark_parked_if_locked() { + RequeueOp::RequeueOne + } else { + RequeueOp::UnparkOne + } + }; + let callback = |_op, result: UnparkResult| { // Clear our state if there are no more waiting threads if !result.have_more_threads { self.state.store(ptr::null_mut(), Ordering::Relaxed); } TOKEN_NORMAL }; - parking_lot_core::unpark_one(addr, callback); + let res = parking_lot_core::unpark_requeue(from, to, validate, callback); + + res.unparked_threads + res.requeued_threads != 0 } } /// Wakes up all blocked threads on this condvar. /// + /// Returns the number of threads woken up. + /// /// This method will ensure that any current waiters on the condition /// variable are awoken. Calls to `notify_all()` are not buffered in any /// way. /// /// To wake up only one thread, see `notify_one()`. #[inline] - pub fn notify_all(&self) { + pub fn notify_all(&self) -> usize { // Nothing to do if there are no waiting threads let state = self.state.load(Ordering::Relaxed); if state.is_null() { - return; + return 0; } - self.notify_all_slow(state); + self.notify_all_slow(state) } #[cold] #[inline(never)] - fn notify_all_slow(&self, mutex: *mut RawMutex) { + fn notify_all_slow(&self, mutex: *mut RawMutex) -> usize { unsafe { // Unpark one thread and requeue the rest onto the mutex let from = self as *const _ as usize; @@ -193,12 +228,14 @@ impl Condvar { let callback = |op, result: UnparkResult| { // If we requeued threads to the mutex, mark it as having // parked threads. The RequeueAll case is already handled above. - if op == RequeueOp::UnparkOneRequeueRest && result.have_more_threads { + if op == RequeueOp::UnparkOneRequeueRest && result.requeued_threads != 0 { (*mutex).mark_parked(); } TOKEN_NORMAL }; - parking_lot_core::unpark_requeue(from, to, validate, callback); + let res = parking_lot_core::unpark_requeue(from, to, validate, callback); + + res.unparked_threads + res.requeued_threads } } @@ -216,7 +253,7 @@ impl Condvar { /// This function will panic if another thread is waiting on the `Condvar` /// with a different `Mutex` object. #[inline] - pub fn wait(&self, mutex_guard: &mut MutexGuard) { + pub fn wait(&self, mutex_guard: &mut MutexGuard<'_, T>) { self.wait_until_internal(unsafe { MutexGuard::mutex(mutex_guard).raw() }, None); } @@ -246,7 +283,7 @@ impl Condvar { #[inline] pub fn wait_until( &self, - mutex_guard: &mut MutexGuard, + mutex_guard: &mut MutexGuard<'_, T>, timeout: Instant, ) -> WaitTimeoutResult { self.wait_until_internal( @@ -257,11 +294,7 @@ impl Condvar { // This is a non-generic function to reduce the monomorphization cost of // using `wait_until`. - fn wait_until_internal( - &self, - mutex: &RawMutex, - timeout: Option, - ) -> WaitTimeoutResult { + fn wait_until_internal(&self, mutex: &RawMutex, timeout: Option) -> WaitTimeoutResult { unsafe { let result; let mut bad_mutex = false; @@ -345,13 +378,20 @@ impl Condvar { /// /// Like `wait`, the lock specified will be re-acquired when this function /// returns, regardless of whether the timeout elapsed or not. + /// + /// # Panics + /// + /// Panics if the given `timeout` is so large that it can't be added to the current time. + /// This panic is not possible if the crate is built with the `nightly` feature, then a too + /// large `timeout` becomes equivalent to just calling `wait`. #[inline] pub fn wait_for( &self, - guard: &mut MutexGuard, + mutex_guard: &mut MutexGuard<'_, T>, timeout: Duration, ) -> WaitTimeoutResult { - self.wait_until(guard, Instant::now() + timeout) + let deadline = util::to_deadline(timeout); + self.wait_until_internal(unsafe { MutexGuard::mutex(mutex_guard).raw() }, deadline) } } @@ -363,18 +403,18 @@ impl Default for Condvar { } impl fmt::Debug for Condvar { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("Condvar { .. }") } } #[cfg(test)] mod tests { + use crate::{Condvar, Mutex, MutexGuard}; use std::sync::mpsc::channel; use std::sync::Arc; use std::thread; use std::time::{Duration, Instant}; - use {Condvar, Mutex}; #[test] fn smoke() { @@ -434,6 +474,70 @@ mod tests { } } + #[test] + fn notify_one_return_true() { + let m = Arc::new(Mutex::new(())); + let m2 = m.clone(); + let c = Arc::new(Condvar::new()); + let c2 = c.clone(); + + let mut g = m.lock(); + let _t = thread::spawn(move || { + let _g = m2.lock(); + assert!(c2.notify_one()); + }); + c.wait(&mut g); + } + + #[test] + fn notify_one_return_false() { + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); + + let _t = thread::spawn(move || { + let _g = m.lock(); + assert!(!c.notify_one()); + }); + } + + #[test] + fn notify_all_return() { + const N: usize = 10; + + let data = Arc::new((Mutex::new(0), Condvar::new())); + let (tx, rx) = channel(); + for _ in 0..N { + let data = data.clone(); + let tx = tx.clone(); + thread::spawn(move || { + let &(ref lock, ref cond) = &*data; + let mut cnt = lock.lock(); + *cnt += 1; + if *cnt == N { + tx.send(()).unwrap(); + } + while *cnt != 0 { + cond.wait(&mut cnt); + } + tx.send(()).unwrap(); + }); + } + drop(tx); + + let &(ref lock, ref cond) = &*data; + rx.recv().unwrap(); + let mut cnt = lock.lock(); + *cnt = 0; + assert_eq!(cond.notify_all(), N); + drop(cnt); + + for _ in 0..N { + rx.recv().unwrap(); + } + + assert_eq!(cond.notify_all(), 0); + } + #[test] fn wait_for() { let m = Arc::new(Mutex::new(())); @@ -444,12 +548,21 @@ mod tests { let mut g = m.lock(); let no_timeout = c.wait_for(&mut g, Duration::from_millis(1)); assert!(no_timeout.timed_out()); + let _t = thread::spawn(move || { let _g = m2.lock(); c2.notify_one(); }); - let timeout_res = c.wait_for(&mut g, Duration::from_millis(u32::max_value() as u64)); + // Non-nightly panics on too large timeouts. Nightly treats it as indefinite wait. + let very_long_timeout = if cfg!(feature = "nightly") { + Duration::from_secs(u64::max_value()) + } else { + Duration::from_millis(u32::max_value() as u64) + }; + + let timeout_res = c.wait_for(&mut g, very_long_timeout); assert!(!timeout_res.timed_out()); + drop(g); } @@ -530,4 +643,50 @@ mod tests { let c = Condvar::new(); assert_eq!(format!("{:?}", c), "Condvar { .. }"); } + + #[test] + fn test_condvar_requeue() { + let m = Arc::new(Mutex::new(())); + let m2 = m.clone(); + let c = Arc::new(Condvar::new()); + let c2 = c.clone(); + let t = thread::spawn(move || { + let mut g = m2.lock(); + c2.wait(&mut g); + }); + + let mut g = m.lock(); + while !c.notify_one() { + // Wait for the thread to get into wait() + MutexGuard::bump(&mut g); + } + // The thread should have been requeued to the mutex, which we wake up now. + drop(g); + t.join().unwrap(); + } + + #[test] + fn test_issue_129() { + let locks = Arc::new((Mutex::new(()), Condvar::new())); + + let (tx, rx) = channel(); + for _ in 0..4 { + let locks = locks.clone(); + let tx = tx.clone(); + thread::spawn(move || { + let mut guard = locks.0.lock(); + locks.1.wait(&mut guard); + locks.1.wait_for(&mut guard, Duration::from_millis(1)); + locks.1.notify_one(); + tx.send(()).unwrap(); + }); + } + + thread::sleep(Duration::from_millis(100)); + locks.1.notify_one(); + + for _ in 0..4 { + assert_eq!(rx.recv_timeout(Duration::from_millis(500)), Ok(())); + } + } } diff --git a/third_party/rust/parking_lot/src/deadlock.rs b/third_party/rust/parking_lot/src/deadlock.rs index ef7d6196f421..810edf1fde04 100644 --- a/third_party/rust/parking_lot/src/deadlock.rs +++ b/third_party/rust/parking_lot/src/deadlock.rs @@ -40,10 +40,15 @@ pub(crate) use parking_lot_core::deadlock::{acquire_resource, release_resource}; #[cfg(test)] #[cfg(feature = "deadlock_detection")] mod tests { + use crate::{Mutex, ReentrantMutex, RwLock}; use std::sync::{Arc, Barrier}; use std::thread::{self, sleep}; use std::time::Duration; - use {Mutex, ReentrantMutex, RwLock}; + + // We need to serialize these tests since deadlock detection uses global state + lazy_static::lazy_static! { + static ref DEADLOCK_DETECTION_LOCK: Mutex<()> = Mutex::new(()); + } fn check_deadlock() -> bool { use parking_lot_core::deadlock::check_deadlock; @@ -52,6 +57,8 @@ mod tests { #[test] fn test_mutex_deadlock() { + let _guard = DEADLOCK_DETECTION_LOCK.lock(); + let m1: Arc> = Default::default(); let m2: Arc> = Default::default(); let m3: Arc> = Default::default(); @@ -95,6 +102,8 @@ mod tests { #[test] fn test_mutex_deadlock_reentrant() { + let _guard = DEADLOCK_DETECTION_LOCK.lock(); + let m1: Arc> = Default::default(); assert!(!check_deadlock()); @@ -112,6 +121,8 @@ mod tests { #[test] fn test_remutex_deadlock() { + let _guard = DEADLOCK_DETECTION_LOCK.lock(); + let m1: Arc> = Default::default(); let m2: Arc> = Default::default(); let m3: Arc> = Default::default(); @@ -158,6 +169,8 @@ mod tests { #[test] fn test_rwlock_deadlock() { + let _guard = DEADLOCK_DETECTION_LOCK.lock(); + let m1: Arc> = Default::default(); let m2: Arc> = Default::default(); let m3: Arc> = Default::default(); @@ -199,8 +212,11 @@ mod tests { assert!(!check_deadlock()); } + #[cfg(rwlock_deadlock_detection_not_supported)] #[test] fn test_rwlock_deadlock_reentrant() { + let _guard = DEADLOCK_DETECTION_LOCK.lock(); + let m1: Arc> = Default::default(); assert!(!check_deadlock()); diff --git a/third_party/rust/parking_lot/src/elision.rs b/third_party/rust/parking_lot/src/elision.rs index 23895b157759..74ff5ffcc3bc 100644 --- a/third_party/rust/parking_lot/src/elision.rs +++ b/third_party/rust/parking_lot/src/elision.rs @@ -12,17 +12,14 @@ pub trait AtomicElisionExt { type IntType; // Perform a compare_exchange and start a transaction - fn elision_acquire( - &self, - current: Self::IntType, - new: Self::IntType, - ) -> Result; - // Perform a compare_exchange and end a transaction - fn elision_release( + fn elision_compare_exchange_acquire( &self, current: Self::IntType, new: Self::IntType, ) -> Result; + + // Perform a fetch_sub and end a transaction + fn elision_fetch_sub_release(&self, val: Self::IntType) -> Self::IntType; } // Indicates whether the target architecture supports lock elision @@ -41,22 +38,23 @@ impl AtomicElisionExt for AtomicUsize { type IntType = usize; #[inline] - fn elision_acquire(&self, _: usize, _: usize) -> Result { + fn elision_compare_exchange_acquire(&self, _: usize, _: usize) -> Result { unreachable!(); } #[inline] - fn elision_release(&self, _: usize, _: usize) -> Result { + fn elision_fetch_sub_release(&self, _: usize) -> usize { unreachable!(); } } -#[cfg(all(feature = "nightly", target_arch = "x86"))] +#[cfg(all(feature = "nightly", any(target_arch = "x86", target_arch = "x86_64")))] impl AtomicElisionExt for AtomicUsize { type IntType = usize; + #[cfg(target_pointer_width = "32")] #[inline] - fn elision_acquire(&self, current: usize, new: usize) -> Result { + fn elision_compare_exchange_acquire(&self, current: usize, new: usize) -> Result { unsafe { let prev: usize; asm!("xacquire; lock; cmpxchgl $2, $1" @@ -71,70 +69,9 @@ impl AtomicElisionExt for AtomicUsize { } } } - + #[cfg(target_pointer_width = "64")] #[inline] - fn elision_release(&self, current: usize, new: usize) -> Result { - unsafe { - let prev: usize; - asm!("xrelease; lock; cmpxchgl $2, $1" - : "={eax}" (prev), "+*m" (self) - : "r" (new), "{eax}" (current) - : "memory" - : "volatile"); - if prev == current { - Ok(prev) - } else { - Err(prev) - } - } - } -} - -#[cfg(all(feature = "nightly", target_arch = "x86_64", target_pointer_width = "32"))] -impl AtomicElisionExt for AtomicUsize { - type IntType = usize; - - #[inline] - fn elision_acquire(&self, current: usize, new: usize) -> Result { - unsafe { - let prev: usize; - asm!("xacquire; lock; cmpxchgl $2, $1" - : "={rax}" (prev), "+*m" (self) - : "r" (new), "{rax}" (current) - : "memory" - : "volatile"); - if prev == current { - Ok(prev) - } else { - Err(prev) - } - } - } - - #[inline] - fn elision_release(&self, current: usize, new: usize) -> Result { - unsafe { - let prev: usize; - asm!("xrelease; lock; cmpxchgl $2, $1" - : "={rax}" (prev), "+*m" (self) - : "r" (new), "{rax}" (current) - : "memory" - : "volatile"); - if prev == current { - Ok(prev) - } else { - Err(prev) - } - } - } -} - -#[cfg(all(feature = "nightly", target_arch = "x86_64", target_pointer_width = "64"))] -impl AtomicElisionExt for AtomicUsize { - type IntType = usize; - - #[inline] - fn elision_acquire(&self, current: usize, new: usize) -> Result { + fn elision_compare_exchange_acquire(&self, current: usize, new: usize) -> Result { unsafe { let prev: usize; asm!("xacquire; lock; cmpxchgq $2, $1" @@ -150,20 +87,30 @@ impl AtomicElisionExt for AtomicUsize { } } + #[cfg(target_pointer_width = "32")] #[inline] - fn elision_release(&self, current: usize, new: usize) -> Result { + fn elision_fetch_sub_release(&self, val: usize) -> usize { unsafe { let prev: usize; - asm!("xrelease; lock; cmpxchgq $2, $1" - : "={rax}" (prev), "+*m" (self) - : "r" (new), "{rax}" (current) + asm!("xrelease; lock; xaddl $2, $1" + : "=r" (prev), "+*m" (self) + : "0" (val.wrapping_neg()) : "memory" : "volatile"); - if prev == current { - Ok(prev) - } else { - Err(prev) - } + prev + } + } + #[cfg(target_pointer_width = "64")] + #[inline] + fn elision_fetch_sub_release(&self, val: usize) -> usize { + unsafe { + let prev: usize; + asm!("xrelease; lock; xaddq $2, $1" + : "=r" (prev), "+*m" (self) + : "0" (val.wrapping_neg()) + : "memory" + : "volatile"); + prev } } } diff --git a/third_party/rust/parking_lot/src/lib.rs b/third_party/rust/parking_lot/src/lib.rs index f16b821457ed..5e652897d668 100644 --- a/third_party/rust/parking_lot/src/lib.rs +++ b/third_party/rust/parking_lot/src/lib.rs @@ -10,13 +10,9 @@ //! standard library. It also provides a `ReentrantMutex` type. #![warn(missing_docs)] -#![cfg_attr(feature = "nightly", feature(const_fn))] -#![cfg_attr(feature = "nightly", feature(integer_atomics))] +#![warn(rust_2018_idioms)] #![cfg_attr(feature = "nightly", feature(asm))] -extern crate lock_api; -extern crate parking_lot_core; - mod condvar; mod elision; mod mutex; @@ -32,13 +28,15 @@ pub mod deadlock; #[cfg(not(feature = "deadlock_detection"))] mod deadlock; -pub use condvar::{Condvar, WaitTimeoutResult}; -pub use mutex::{MappedMutexGuard, Mutex, MutexGuard}; -pub use once::{Once, OnceState, ONCE_INIT}; -pub use raw_mutex::RawMutex; -pub use raw_rwlock::RawRwLock; -pub use remutex::{MappedReentrantMutexGuard, RawThreadId, ReentrantMutex, ReentrantMutexGuard}; -pub use rwlock::{ +pub use self::condvar::{Condvar, WaitTimeoutResult}; +pub use self::mutex::{MappedMutexGuard, Mutex, MutexGuard}; +pub use self::once::{Once, OnceState}; +pub use self::raw_mutex::RawMutex; +pub use self::raw_rwlock::RawRwLock; +pub use self::remutex::{ + MappedReentrantMutexGuard, RawThreadId, ReentrantMutex, ReentrantMutexGuard, +}; +pub use self::rwlock::{ MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLock, RwLockReadGuard, RwLockUpgradableReadGuard, RwLockWriteGuard, }; diff --git a/third_party/rust/parking_lot/src/mutex.rs b/third_party/rust/parking_lot/src/mutex.rs index d530400e3e26..4f88e58362aa 100644 --- a/third_party/rust/parking_lot/src/mutex.rs +++ b/third_party/rust/parking_lot/src/mutex.rs @@ -5,8 +5,8 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. +use crate::raw_mutex::RawMutex; use lock_api; -use raw_mutex::RawMutex; /// A mutual exclusion primitive useful for protecting shared data /// @@ -69,7 +69,7 @@ use raw_mutex::RawMutex; /// /// let (tx, rx) = channel(); /// for _ in 0..10 { -/// let (data, tx) = (data.clone(), tx.clone()); +/// let (data, tx) = (Arc::clone(&data), tx.clone()); /// thread::spawn(move || { /// // The shared state can only be accessed once the lock is held. /// // Our non-atomic increment is safe because we're the only thread @@ -105,11 +105,14 @@ pub type MappedMutexGuard<'a, T> = lock_api::MappedMutexGuard<'a, RawMutex, T>; #[cfg(test)] mod tests { + use crate::{Condvar, Mutex}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::mpsc::channel; use std::sync::Arc; use std::thread; - use {Condvar, Mutex}; + + #[cfg(feature = "serde")] + use bincode::{deserialize, serialize}; struct Packet(Arc<(Mutex, Condvar)>); @@ -253,7 +256,8 @@ mod tests { } let _u = Unwinder { i: arc2 }; panic!(); - }).join(); + }) + .join(); let lock = arc.lock(); assert_eq!(*lock, 2); } @@ -283,16 +287,20 @@ mod tests { let mutex = Mutex::new(vec![0u8, 10]); assert_eq!(format!("{:?}", mutex), "Mutex { data: [0, 10] }"); - assert_eq!( - format!("{:#?}", mutex), - "Mutex { - data: [ - 0, - 10 - ] -}" - ); let _lock = mutex.lock(); - assert_eq!(format!("{:?}", mutex), "Mutex { }"); + assert_eq!(format!("{:?}", mutex), "Mutex { data: }"); + } + + #[cfg(feature = "serde")] + #[test] + fn test_serde() { + let contents: Vec = vec![0, 1, 2]; + let mutex = Mutex::new(contents.clone()); + + let serialized = serialize(&mutex).unwrap(); + let deserialized: Mutex> = deserialize(&serialized).unwrap(); + + assert_eq!(*(mutex.lock()), *(deserialized.lock())); + assert_eq!(contents, *(deserialized.lock())); } } diff --git a/third_party/rust/parking_lot/src/once.rs b/third_party/rust/parking_lot/src/once.rs index f3e70bec06cb..e205e653959b 100644 --- a/third_party/rust/parking_lot/src/once.rs +++ b/third_party/rust/parking_lot/src/once.rs @@ -5,21 +5,21 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. -use std::sync::atomic::{fence, Ordering}; -#[cfg(feature = "nightly")] -use std::sync::atomic::{ATOMIC_U8_INIT, AtomicU8}; -#[cfg(feature = "nightly")] -type U8 = u8; -#[cfg(not(feature = "nightly"))] -use std::sync::atomic::AtomicUsize as AtomicU8; -#[cfg(not(feature = "nightly"))] -use std::sync::atomic::ATOMIC_USIZE_INIT as ATOMIC_U8_INIT; -#[cfg(not(feature = "nightly"))] -type U8 = usize; +use crate::util::UncheckedOptionExt; +#[cfg(has_sized_atomics)] +use core::sync::atomic::AtomicU8; +#[cfg(not(has_sized_atomics))] +use core::sync::atomic::AtomicUsize as AtomicU8; +use core::{ + fmt, mem, + sync::atomic::{fence, Ordering}, +}; use parking_lot_core::{self, SpinWait, DEFAULT_PARK_TOKEN, DEFAULT_UNPARK_TOKEN}; -use std::fmt; -use std::mem; -use util::UncheckedOptionExt; + +#[cfg(has_sized_atomics)] +type U8 = u8; +#[cfg(not(has_sized_atomics))] +type U8 = usize; const DONE_BIT: U8 = 1; const POISON_BIT: U8 = 2; @@ -38,14 +38,14 @@ pub enum OnceState { /// A thread is currently executing a closure. InProgress, - /// A closure has completed sucessfully. + /// A closure has completed successfully. Done, } impl OnceState { /// Returns whether the associated `Once` has been poisoned. /// - /// Once an initalization routine for a `Once` has panicked it will forever + /// Once an initialization routine for a `Once` has panicked it will forever /// indicate to future forced initialization routines that it is poisoned. #[inline] pub fn poisoned(&self) -> bool { @@ -55,7 +55,7 @@ impl OnceState { } } - /// Returns whether the associated `Once` has successfullly executed a + /// Returns whether the associated `Once` has successfully executed a /// closure. #[inline] pub fn done(&self) -> bool { @@ -81,9 +81,9 @@ impl OnceState { /// # Examples /// /// ``` -/// use parking_lot::{Once, ONCE_INIT}; +/// use parking_lot::Once; /// -/// static START: Once = ONCE_INIT; +/// static START: Once = Once::new(); /// /// START.call_once(|| { /// // run initialization here @@ -91,22 +91,11 @@ impl OnceState { /// ``` pub struct Once(AtomicU8); -/// Initialization value for static `Once` values. -pub const ONCE_INIT: Once = Once(ATOMIC_U8_INIT); - impl Once { /// Creates a new `Once` value. - #[cfg(feature = "nightly")] #[inline] pub const fn new() -> Once { - Once(ATOMIC_U8_INIT) - } - - /// Creates a new `Once` value. - #[cfg(not(feature = "nightly"))] - #[inline] - pub fn new() -> Once { - Once(ATOMIC_U8_INIT) + Once(AtomicU8::new(0)) } /// Returns the current state of this `Once`. @@ -141,10 +130,10 @@ impl Once { /// # Examples /// /// ``` - /// use parking_lot::{Once, ONCE_INIT}; + /// use parking_lot::Once; /// /// static mut VAL: usize = 0; - /// static INIT: Once = ONCE_INIT; + /// static INIT: Once = Once::new(); /// /// // Accessing a `static mut` is unsafe much of the time, but if we do so /// // in a synchronized fashion (e.g. write once or read all) then we're @@ -223,7 +212,7 @@ impl Once { // without some allocation overhead. #[cold] #[inline(never)] - fn call_once_slow(&self, ignore_poison: bool, f: &mut FnMut(OnceState)) { + fn call_once_slow(&self, ignore_poison: bool, f: &mut dyn FnMut(OnceState)) { let mut spinwait = SpinWait::new(); let mut state = self.0.load(Ordering::Relaxed); loop { @@ -344,7 +333,7 @@ impl Default for Once { } impl fmt::Debug for Once { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Once") .field("state", &self.state()) .finish() @@ -353,15 +342,14 @@ impl fmt::Debug for Once { #[cfg(test)] mod tests { - #[cfg(feature = "nightly")] + use crate::Once; use std::panic; use std::sync::mpsc::channel; use std::thread; - use {Once, ONCE_INIT}; #[test] fn smoke_once() { - static O: Once = ONCE_INIT; + static O: Once = Once::new(); let mut a = 0; O.call_once(|| a += 1); assert_eq!(a, 1); @@ -371,7 +359,7 @@ mod tests { #[test] fn stampede_once() { - static O: Once = ONCE_INIT; + static O: Once = Once::new(); static mut RUN: bool = false; let (tx, rx) = channel(); @@ -405,10 +393,9 @@ mod tests { } } - #[cfg(feature = "nightly")] #[test] fn poison_bad() { - static O: Once = ONCE_INIT; + static O: Once = Once::new(); // poison the once let t = panic::catch_unwind(|| { @@ -434,10 +421,9 @@ mod tests { O.call_once(|| {}); } - #[cfg(feature = "nightly")] #[test] fn wait_for_force_to_finish() { - static O: Once = ONCE_INIT; + static O: Once = Once::new(); // poison the once let t = panic::catch_unwind(|| { @@ -475,14 +461,8 @@ mod tests { #[test] fn test_once_debug() { - static O: Once = ONCE_INIT; + static O: Once = Once::new(); assert_eq!(format!("{:?}", O), "Once { state: New }"); - assert_eq!( - format!("{:#?}", O), - "Once { - state: New -}" - ); } } diff --git a/third_party/rust/parking_lot/src/raw_mutex.rs b/third_party/rust/parking_lot/src/raw_mutex.rs index 1ba5581e5c0f..be6eedb4aa09 100644 --- a/third_party/rust/parking_lot/src/raw_mutex.rs +++ b/third_party/rust/parking_lot/src/raw_mutex.rs @@ -5,21 +5,20 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. -use std::sync::atomic::Ordering; -#[cfg(feature = "nightly")] -use std::sync::atomic::{ATOMIC_U8_INIT, AtomicU8}; -#[cfg(feature = "nightly")] -type U8 = u8; -#[cfg(not(feature = "nightly"))] -use std::sync::atomic::AtomicUsize as AtomicU8; -#[cfg(not(feature = "nightly"))] -use std::sync::atomic::ATOMIC_USIZE_INIT as ATOMIC_U8_INIT; -#[cfg(not(feature = "nightly"))] -type U8 = usize; -use deadlock; +use crate::{deadlock, util}; +#[cfg(has_sized_atomics)] +use core::sync::atomic::AtomicU8; +#[cfg(not(has_sized_atomics))] +use core::sync::atomic::AtomicUsize as AtomicU8; +use core::{sync::atomic::Ordering, time::Duration}; use lock_api::{GuardNoSend, RawMutex as RawMutexTrait, RawMutexFair, RawMutexTimed}; use parking_lot_core::{self, ParkResult, SpinWait, UnparkResult, UnparkToken, DEFAULT_PARK_TOKEN}; -use std::time::{Duration, Instant}; +use std::time::Instant; + +#[cfg(has_sized_atomics)] +type U8 = u8; +#[cfg(not(has_sized_atomics))] +type U8 = usize; // UnparkToken used to indicate that that the target thread should attempt to // lock the mutex again as soon as it is unparked. @@ -39,7 +38,7 @@ pub struct RawMutex { unsafe impl RawMutexTrait for RawMutex { const INIT: RawMutex = RawMutex { - state: ATOMIC_U8_INIT, + state: AtomicU8::new(0), }; type GuardMarker = GuardNoSend; @@ -83,7 +82,7 @@ unsafe impl RawMutexTrait for RawMutex { unsafe { deadlock::release_resource(self as *const _ as usize) }; if self .state - .compare_exchange_weak(LOCKED_BIT, 0, Ordering::Release, Ordering::Relaxed) + .compare_exchange(LOCKED_BIT, 0, Ordering::Release, Ordering::Relaxed) .is_ok() { return; @@ -98,7 +97,7 @@ unsafe impl RawMutexFair for RawMutex { unsafe { deadlock::release_resource(self as *const _ as usize) }; if self .state - .compare_exchange_weak(LOCKED_BIT, 0, Ordering::Release, Ordering::Relaxed) + .compare_exchange(LOCKED_BIT, 0, Ordering::Release, Ordering::Relaxed) .is_ok() { return; @@ -144,7 +143,7 @@ unsafe impl RawMutexTimed for RawMutex { { true } else { - self.lock_slow(Some(Instant::now() + timeout)) + self.lock_slow(util::to_deadline(timeout)) }; if result { unsafe { deadlock::acquire_resource(self as *const _ as usize) }; @@ -264,15 +263,6 @@ impl RawMutex { #[cold] #[inline(never)] fn unlock_slow(&self, force_fair: bool) { - // Unlock directly if there are no parked threads - if self - .state - .compare_exchange(LOCKED_BIT, 0, Ordering::Release, Ordering::Relaxed) - .is_ok() - { - return; - } - // Unpark one thread and leave the parked bit set if there might // still be parked threads on this address. unsafe { diff --git a/third_party/rust/parking_lot/src/raw_rwlock.rs b/third_party/rust/parking_lot/src/raw_rwlock.rs index f59a6ac272a3..ccfe46168523 100644 --- a/third_party/rust/parking_lot/src/raw_rwlock.rs +++ b/third_party/rust/parking_lot/src/raw_rwlock.rs @@ -5,37 +5,55 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. -use deadlock; -use elision::{have_elision, AtomicElisionExt}; +use crate::elision::{have_elision, AtomicElisionExt}; +use crate::raw_mutex::{TOKEN_HANDOFF, TOKEN_NORMAL}; +use crate::util; +use core::{ + cell::Cell, + sync::atomic::{AtomicUsize, Ordering}, +}; use lock_api::{ GuardNoSend, RawRwLock as RawRwLockTrait, RawRwLockDowngrade, RawRwLockFair, RawRwLockRecursive, RawRwLockRecursiveTimed, RawRwLockTimed, RawRwLockUpgrade, RawRwLockUpgradeDowngrade, RawRwLockUpgradeFair, RawRwLockUpgradeTimed, }; -use parking_lot_core::{self, FilterOp, ParkResult, ParkToken, SpinWait, UnparkResult}; -use raw_mutex::{TOKEN_HANDOFF, TOKEN_NORMAL}; -use std::cell::Cell; -use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; +use parking_lot_core::{ + self, deadlock, FilterOp, ParkResult, ParkToken, SpinWait, UnparkResult, UnparkToken, +}; use std::time::{Duration, Instant}; -const PARKED_BIT: usize = 0b001; -const UPGRADING_BIT: usize = 0b010; -// A shared guard acquires a single guard resource -const SHARED_GUARD: usize = 0b100; -const GUARD_COUNT_MASK: usize = !(SHARED_GUARD - 1); -// An exclusive lock acquires all of guard resource (i.e. it is exclusive) -const EXCLUSIVE_GUARD: usize = GUARD_COUNT_MASK; -// An upgradable lock acquires just over half of the guard resource -// This should be (GUARD_COUNT_MASK + SHARED_GUARD) >> 1, however this might -// overflow, so we shift before adding (which is okay since the least -// significant bit is zero for both GUARD_COUNT_MASK and SHARED_GUARD) -const UPGRADABLE_GUARD: usize = (GUARD_COUNT_MASK >> 1) + (SHARED_GUARD >> 1); +// This reader-writer lock implementation is based on Boost's upgrade_mutex: +// https://github.com/boostorg/thread/blob/fc08c1fe2840baeeee143440fba31ef9e9a813c8/include/boost/thread/v2/shared_mutex.hpp#L432 +// +// This implementation uses 2 wait queues, one at key [addr] and one at key +// [addr + 1]. The primary queue is used for all new waiting threads, and the +// secondary queue is used by the thread which has acquired WRITER_BIT but is +// waiting for the remaining readers to exit the lock. +// +// This implementation is fair between readers and writers since it uses the +// order in which threads first started queuing to alternate between read phases +// and write phases. In particular is it not vulnerable to write starvation +// since readers will block if there is a pending writer. -// Token indicating what type of lock queued threads are trying to acquire -const TOKEN_SHARED: ParkToken = ParkToken(SHARED_GUARD); -const TOKEN_EXCLUSIVE: ParkToken = ParkToken(EXCLUSIVE_GUARD); -const TOKEN_UPGRADABLE: ParkToken = ParkToken(UPGRADABLE_GUARD); -const TOKEN_UPGRADING: ParkToken = ParkToken((EXCLUSIVE_GUARD - UPGRADABLE_GUARD) | UPGRADING_BIT); +// There is at least one thread in the main queue. +const PARKED_BIT: usize = 0b0001; +// There is a parked thread holding WRITER_BIT. WRITER_BIT must be set. +const WRITER_PARKED_BIT: usize = 0b0010; +// A reader is holding an upgradable lock. The reader count must be non-zero and +// WRITER_BIT must not be set. +const UPGRADABLE_BIT: usize = 0b0100; +// If the reader count is zero: a writer is currently holding an exclusive lock. +// Otherwise: a writer is waiting for the remaining readers to exit the lock. +const WRITER_BIT: usize = 0b1000; +// Mask of bits used to count readers. +const READERS_MASK: usize = !0b1111; +// Base unit for counting readers. +const ONE_READER: usize = 0b10000; + +// Token indicating what type of lock a queued thread is trying to acquire +const TOKEN_SHARED: ParkToken = ParkToken(ONE_READER); +const TOKEN_EXCLUSIVE: ParkToken = ParkToken(WRITER_BIT); +const TOKEN_UPGRADABLE: ParkToken = ParkToken(ONE_READER | UPGRADABLE_BIT); /// Raw reader-writer lock type backed by the parking lot. pub struct RawRwLock { @@ -44,7 +62,7 @@ pub struct RawRwLock { unsafe impl RawRwLockTrait for RawRwLock { const INIT: RawRwLock = RawRwLock { - state: ATOMIC_USIZE_INIT, + state: AtomicUsize::new(0), }; type GuardMarker = GuardNoSend; @@ -53,23 +71,23 @@ unsafe impl RawRwLockTrait for RawRwLock { fn lock_exclusive(&self) { if self .state - .compare_exchange_weak(0, EXCLUSIVE_GUARD, Ordering::Acquire, Ordering::Relaxed) + .compare_exchange_weak(0, WRITER_BIT, Ordering::Acquire, Ordering::Relaxed) .is_err() { let result = self.lock_exclusive_slow(None); debug_assert!(result); } - unsafe { deadlock::acquire_resource(self as *const _ as usize) }; + self.deadlock_acquire(); } #[inline] fn try_lock_exclusive(&self) -> bool { if self .state - .compare_exchange(0, EXCLUSIVE_GUARD, Ordering::Acquire, Ordering::Relaxed) + .compare_exchange(0, WRITER_BIT, Ordering::Acquire, Ordering::Relaxed) .is_ok() { - unsafe { deadlock::acquire_resource(self as *const _ as usize) }; + self.deadlock_acquire(); true } else { false @@ -78,10 +96,10 @@ unsafe impl RawRwLockTrait for RawRwLock { #[inline] fn unlock_exclusive(&self) { - unsafe { deadlock::release_resource(self as *const _ as usize) }; + self.deadlock_release(); if self .state - .compare_exchange_weak(EXCLUSIVE_GUARD, 0, Ordering::Release, Ordering::Relaxed) + .compare_exchange(WRITER_BIT, 0, Ordering::Release, Ordering::Relaxed) .is_ok() { return; @@ -95,7 +113,7 @@ unsafe impl RawRwLockTrait for RawRwLock { let result = self.lock_shared_slow(false, None); debug_assert!(result); } - unsafe { deadlock::acquire_resource(self as *const _ as usize) }; + self.deadlock_acquire(); } #[inline] @@ -106,85 +124,38 @@ unsafe impl RawRwLockTrait for RawRwLock { self.try_lock_shared_slow(false) }; if result { - unsafe { deadlock::acquire_resource(self as *const _ as usize) }; + self.deadlock_acquire(); } result } #[inline] fn unlock_shared(&self) { - unsafe { deadlock::release_resource(self as *const _ as usize) }; - let state = self.state.load(Ordering::Relaxed); - if state & PARKED_BIT == 0 - || (state & UPGRADING_BIT == 0 && state & GUARD_COUNT_MASK != SHARED_GUARD) - { - if have_elision() { - if self - .state - .elision_release(state, state - SHARED_GUARD) - .is_ok() - { - return; - } - } else { - if self - .state - .compare_exchange_weak( - state, - state - SHARED_GUARD, - Ordering::Release, - Ordering::Relaxed, - ) - .is_ok() - { - return; - } - } + self.deadlock_release(); + let state = if have_elision() { + self.state.elision_fetch_sub_release(ONE_READER) + } else { + self.state.fetch_sub(ONE_READER, Ordering::Release) + }; + if state & (READERS_MASK | WRITER_PARKED_BIT) == (ONE_READER | WRITER_PARKED_BIT) { + self.unlock_shared_slow(); } - self.unlock_shared_slow(false); } } unsafe impl RawRwLockFair for RawRwLock { #[inline] fn unlock_shared_fair(&self) { - unsafe { deadlock::release_resource(self as *const _ as usize) }; - let state = self.state.load(Ordering::Relaxed); - if state & PARKED_BIT == 0 - || (state & UPGRADING_BIT == 0 && state & GUARD_COUNT_MASK != SHARED_GUARD) - { - if have_elision() { - if self - .state - .elision_release(state, state - SHARED_GUARD) - .is_ok() - { - return; - } - } else { - if self - .state - .compare_exchange_weak( - state, - state - SHARED_GUARD, - Ordering::Release, - Ordering::Relaxed, - ) - .is_ok() - { - return; - } - } - } - self.unlock_shared_slow(true); + // Shared unlocking is always fair in this implementation. + self.unlock_shared(); } #[inline] fn unlock_exclusive_fair(&self) { - unsafe { deadlock::release_resource(self as *const _ as usize) }; + self.deadlock_release(); if self .state - .compare_exchange_weak(EXCLUSIVE_GUARD, 0, Ordering::Release, Ordering::Relaxed) + .compare_exchange(WRITER_BIT, 0, Ordering::Release, Ordering::Relaxed) .is_ok() { return; @@ -194,7 +165,9 @@ unsafe impl RawRwLockFair for RawRwLock { #[inline] fn bump_shared(&self) { - if self.state.load(Ordering::Relaxed) & PARKED_BIT != 0 { + if self.state.load(Ordering::Relaxed) & (READERS_MASK | WRITER_BIT) + == ONE_READER | WRITER_BIT + { self.bump_shared_slow(); } } @@ -212,7 +185,7 @@ unsafe impl RawRwLockDowngrade for RawRwLock { fn downgrade(&self) { let state = self .state - .fetch_sub(EXCLUSIVE_GUARD - SHARED_GUARD, Ordering::Release); + .fetch_add(ONE_READER - WRITER_BIT, Ordering::Release); // Wake up parked shared and upgradable threads if there are any if state & PARKED_BIT != 0 { @@ -230,10 +203,10 @@ unsafe impl RawRwLockTimed for RawRwLock { let result = if self.try_lock_shared_fast(false) { true } else { - self.lock_shared_slow(false, Some(Instant::now() + timeout)) + self.lock_shared_slow(false, util::to_deadline(timeout)) }; if result { - unsafe { deadlock::acquire_resource(self as *const _ as usize) }; + self.deadlock_acquire(); } result } @@ -246,7 +219,7 @@ unsafe impl RawRwLockTimed for RawRwLock { self.lock_shared_slow(false, Some(timeout)) }; if result { - unsafe { deadlock::acquire_resource(self as *const _ as usize) }; + self.deadlock_acquire(); } result } @@ -255,15 +228,15 @@ unsafe impl RawRwLockTimed for RawRwLock { fn try_lock_exclusive_for(&self, timeout: Duration) -> bool { let result = if self .state - .compare_exchange_weak(0, EXCLUSIVE_GUARD, Ordering::Acquire, Ordering::Relaxed) + .compare_exchange_weak(0, WRITER_BIT, Ordering::Acquire, Ordering::Relaxed) .is_ok() { true } else { - self.lock_exclusive_slow(Some(Instant::now() + timeout)) + self.lock_exclusive_slow(util::to_deadline(timeout)) }; if result { - unsafe { deadlock::acquire_resource(self as *const _ as usize) }; + self.deadlock_acquire(); } result } @@ -272,7 +245,7 @@ unsafe impl RawRwLockTimed for RawRwLock { fn try_lock_exclusive_until(&self, timeout: Instant) -> bool { let result = if self .state - .compare_exchange_weak(0, EXCLUSIVE_GUARD, Ordering::Acquire, Ordering::Relaxed) + .compare_exchange_weak(0, WRITER_BIT, Ordering::Acquire, Ordering::Relaxed) .is_ok() { true @@ -280,7 +253,7 @@ unsafe impl RawRwLockTimed for RawRwLock { self.lock_exclusive_slow(Some(timeout)) }; if result { - unsafe { deadlock::acquire_resource(self as *const _ as usize) }; + self.deadlock_acquire(); } result } @@ -293,7 +266,7 @@ unsafe impl RawRwLockRecursive for RawRwLock { let result = self.lock_shared_slow(true, None); debug_assert!(result); } - unsafe { deadlock::acquire_resource(self as *const _ as usize) }; + self.deadlock_acquire(); } #[inline] @@ -304,7 +277,7 @@ unsafe impl RawRwLockRecursive for RawRwLock { self.try_lock_shared_slow(true) }; if result { - unsafe { deadlock::acquire_resource(self as *const _ as usize) }; + self.deadlock_acquire(); } result } @@ -316,10 +289,10 @@ unsafe impl RawRwLockRecursiveTimed for RawRwLock { let result = if self.try_lock_shared_fast(true) { true } else { - self.lock_shared_slow(true, Some(Instant::now() + timeout)) + self.lock_shared_slow(true, util::to_deadline(timeout)) }; if result { - unsafe { deadlock::acquire_resource(self as *const _ as usize) }; + self.deadlock_acquire(); } result } @@ -332,7 +305,7 @@ unsafe impl RawRwLockRecursiveTimed for RawRwLock { self.lock_shared_slow(true, Some(timeout)) }; if result { - unsafe { deadlock::acquire_resource(self as *const _ as usize) }; + self.deadlock_acquire(); } result } @@ -345,7 +318,7 @@ unsafe impl RawRwLockUpgrade for RawRwLock { let result = self.lock_upgradable_slow(None); debug_assert!(result); } - unsafe { deadlock::acquire_resource(self as *const _ as usize) }; + self.deadlock_acquire(); } #[inline] @@ -356,47 +329,51 @@ unsafe impl RawRwLockUpgrade for RawRwLock { self.try_lock_upgradable_slow() }; if result { - unsafe { deadlock::acquire_resource(self as *const _ as usize) }; + self.deadlock_acquire(); } result } #[inline] fn unlock_upgradable(&self) { - unsafe { deadlock::release_resource(self as *const _ as usize) }; - if self - .state - .compare_exchange_weak(UPGRADABLE_GUARD, 0, Ordering::Release, Ordering::Relaxed) - .is_ok() - { - return; + self.deadlock_release(); + let state = self.state.load(Ordering::Relaxed); + if state & PARKED_BIT == 0 { + if self + .state + .compare_exchange_weak( + state, + state - (ONE_READER | UPGRADABLE_BIT), + Ordering::Release, + Ordering::Relaxed, + ) + .is_ok() + { + return; + } } self.unlock_upgradable_slow(false); } #[inline] fn upgrade(&self) { - if self - .state - .compare_exchange_weak( - UPGRADABLE_GUARD, - EXCLUSIVE_GUARD, - Ordering::Relaxed, - Ordering::Relaxed, - ) - .is_err() - { + let state = self.state.fetch_sub( + (ONE_READER | UPGRADABLE_BIT) - WRITER_BIT, + Ordering::Relaxed, + ); + if state & READERS_MASK != ONE_READER { let result = self.upgrade_slow(None); debug_assert!(result); } } + #[inline] fn try_upgrade(&self) -> bool { if self .state .compare_exchange_weak( - UPGRADABLE_GUARD, - EXCLUSIVE_GUARD, + ONE_READER | UPGRADABLE_BIT, + WRITER_BIT, Ordering::Relaxed, Ordering::Relaxed, ) @@ -412,20 +389,28 @@ unsafe impl RawRwLockUpgrade for RawRwLock { unsafe impl RawRwLockUpgradeFair for RawRwLock { #[inline] fn unlock_upgradable_fair(&self) { - unsafe { deadlock::release_resource(self as *const _ as usize) }; - if self - .state - .compare_exchange_weak(UPGRADABLE_GUARD, 0, Ordering::Release, Ordering::Relaxed) - .is_ok() - { - return; + self.deadlock_release(); + let state = self.state.load(Ordering::Relaxed); + if state & PARKED_BIT == 0 { + if self + .state + .compare_exchange_weak( + state, + state - (ONE_READER | UPGRADABLE_BIT), + Ordering::Release, + Ordering::Relaxed, + ) + .is_ok() + { + return; + } } - self.unlock_upgradable_slow(true); + self.unlock_upgradable_slow(false); } #[inline] fn bump_upgradable(&self) { - if self.state.load(Ordering::Relaxed) & PARKED_BIT != 0 { + if self.state.load(Ordering::Relaxed) == ONE_READER | UPGRADABLE_BIT | PARKED_BIT { self.bump_upgradable_slow(); } } @@ -434,21 +419,20 @@ unsafe impl RawRwLockUpgradeFair for RawRwLock { unsafe impl RawRwLockUpgradeDowngrade for RawRwLock { #[inline] fn downgrade_upgradable(&self) { - let state = self - .state - .fetch_sub(UPGRADABLE_GUARD - SHARED_GUARD, Ordering::Relaxed); + let state = self.state.fetch_sub(UPGRADABLE_BIT, Ordering::Relaxed); - // Wake up parked shared and upgradable threads if there are any + // Wake up parked upgradable threads if there are any if state & PARKED_BIT != 0 { - self.downgrade_upgradable_slow(state); + self.downgrade_slow(); } } #[inline] fn downgrade_to_upgradable(&self) { - let state = self - .state - .fetch_sub(EXCLUSIVE_GUARD - UPGRADABLE_GUARD, Ordering::Release); + let state = self.state.fetch_add( + (ONE_READER | UPGRADABLE_BIT) - WRITER_BIT, + Ordering::Release, + ); // Wake up parked shared threads if there are any if state & PARKED_BIT != 0 { @@ -466,7 +450,7 @@ unsafe impl RawRwLockUpgradeTimed for RawRwLock { self.lock_upgradable_slow(Some(timeout)) }; if result { - unsafe { deadlock::acquire_resource(self as *const _ as usize) }; + self.deadlock_acquire(); } result } @@ -476,26 +460,21 @@ unsafe impl RawRwLockUpgradeTimed for RawRwLock { let result = if self.try_lock_upgradable_fast() { true } else { - self.lock_upgradable_slow(Some(Instant::now() + timeout)) + self.lock_upgradable_slow(util::to_deadline(timeout)) }; if result { - unsafe { deadlock::acquire_resource(self as *const _ as usize) }; + self.deadlock_acquire(); } result } #[inline] fn try_upgrade_until(&self, timeout: Instant) -> bool { - if self - .state - .compare_exchange_weak( - UPGRADABLE_GUARD, - EXCLUSIVE_GUARD, - Ordering::Relaxed, - Ordering::Relaxed, - ) - .is_ok() - { + let state = self.state.fetch_sub( + (ONE_READER | UPGRADABLE_BIT) - WRITER_BIT, + Ordering::Relaxed, + ); + if state & READERS_MASK == ONE_READER { true } else { self.upgrade_slow(Some(timeout)) @@ -504,19 +483,14 @@ unsafe impl RawRwLockUpgradeTimed for RawRwLock { #[inline] fn try_upgrade_for(&self, timeout: Duration) -> bool { - if self - .state - .compare_exchange_weak( - UPGRADABLE_GUARD, - EXCLUSIVE_GUARD, - Ordering::Relaxed, - Ordering::Relaxed, - ) - .is_ok() - { + let state = self.state.fetch_sub( + (ONE_READER | UPGRADABLE_BIT) - WRITER_BIT, + Ordering::Relaxed, + ); + if state & READERS_MASK == ONE_READER { true } else { - self.upgrade_slow(Some(Instant::now() + timeout)) + self.upgrade_slow(util::to_deadline(timeout)) } } } @@ -526,18 +500,25 @@ impl RawRwLock { fn try_lock_shared_fast(&self, recursive: bool) -> bool { let state = self.state.load(Ordering::Relaxed); - // We can't allow grabbing a shared lock while there are parked threads - // since that could lead to writer starvation. - if !recursive && state & PARKED_BIT != 0 { - return false; + // We can't allow grabbing a shared lock if there is a writer, even if + // the writer is still waiting for the remaining readers to exit. + if state & WRITER_BIT != 0 { + // To allow recursive locks, we make an exception and allow readers + // to skip ahead of a pending writer to avoid deadlocking, at the + // cost of breaking the fairness guarantees. + if !recursive || state & READERS_MASK == 0 { + return false; + } } // Use hardware lock elision to avoid cache conflicts when multiple // readers try to acquire the lock. We only do this if the lock is // completely empty since elision handles conflicts poorly. if have_elision() && state == 0 { - self.state.elision_acquire(0, SHARED_GUARD).is_ok() - } else if let Some(new_state) = state.checked_add(SHARED_GUARD) { + self.state + .elision_compare_exchange_acquire(0, ONE_READER) + .is_ok() + } else if let Some(new_state) = state.checked_add(ONE_READER) { self.state .compare_exchange_weak(state, new_state, Ordering::Acquire, Ordering::Relaxed) .is_ok() @@ -546,501 +527,175 @@ impl RawRwLock { } } - #[inline(always)] - fn try_lock_upgradable_fast(&self) -> bool { - let state = self.state.load(Ordering::Relaxed); - - // We can't allow grabbing an upgradable lock while there are parked threads - // since that could lead to writer starvation. - if state & PARKED_BIT != 0 { - return false; - } - - if let Some(new_state) = state.checked_add(UPGRADABLE_GUARD) { - self.state - .compare_exchange_weak(state, new_state, Ordering::Acquire, Ordering::Relaxed) - .is_ok() - } else { - false - } - } - - #[cold] - #[inline(never)] - fn lock_exclusive_slow(&self, timeout: Option) -> bool { - let mut spinwait = SpinWait::new(); - let mut state = self.state.load(Ordering::Relaxed); - loop { - // Grab the lock if it isn't locked, even if there are other - // threads parked. - if let Some(new_state) = state.checked_add(EXCLUSIVE_GUARD) { - match self.state.compare_exchange_weak( - state, - new_state, - Ordering::Acquire, - Ordering::Relaxed, - ) { - Ok(_) => return true, - Err(x) => state = x, - } - continue; - } - - // If there are no parked threads and only one reader or writer, try - // spinning a few times. - if (state == EXCLUSIVE_GUARD || state == SHARED_GUARD || state == UPGRADABLE_GUARD) - && spinwait.spin() - { - state = self.state.load(Ordering::Relaxed); - continue; - } - - // Park our thread until we are woken up by an unlock - unsafe { - let addr = self as *const _ as usize; - let validate = || { - let mut state = self.state.load(Ordering::Relaxed); - loop { - // If the rwlock is free, abort the park and try to grab - // it immediately. - if state & GUARD_COUNT_MASK == 0 { - return false; - } - - // Nothing to do if the parked bit is already set - if state & PARKED_BIT != 0 { - return true; - } - - // Set the parked bit - match self.state.compare_exchange_weak( - state, - state | PARKED_BIT, - Ordering::Relaxed, - Ordering::Relaxed, - ) { - Ok(_) => return true, - Err(x) => state = x, - } - } - }; - let before_sleep = || {}; - let timed_out = |_, was_last_thread| { - // Clear the parked bit if we were the last parked thread - if was_last_thread { - self.state.fetch_and(!PARKED_BIT, Ordering::Relaxed); - } - }; - match parking_lot_core::park( - addr, - validate, - before_sleep, - timed_out, - TOKEN_EXCLUSIVE, - timeout, - ) { - // The thread that unparked us passed the lock on to us - // directly without unlocking it. - ParkResult::Unparked(TOKEN_HANDOFF) => return true, - - // We were unparked normally, try acquiring the lock again - ParkResult::Unparked(_) => (), - - // The validation function failed, try locking again - ParkResult::Invalid => (), - - // Timeout expired - ParkResult::TimedOut => return false, - } - } - - // Loop back and try locking again - spinwait.reset(); - state = self.state.load(Ordering::Relaxed); - } - } - - #[cold] - #[inline(never)] - fn unlock_exclusive_slow(&self, force_fair: bool) { - // Unlock directly if there are no parked threads - if self - .state - .compare_exchange(EXCLUSIVE_GUARD, 0, Ordering::Release, Ordering::Relaxed) - .is_ok() - { - return; - }; - - // There are threads to unpark. We unpark threads up to the guard capacity. - let guard_count = Cell::new(0usize); - unsafe { - let addr = self as *const _ as usize; - let filter = |ParkToken(token)| -> FilterOp { - match guard_count.get().checked_add(token) { - Some(new_guard_count) => { - guard_count.set(new_guard_count); - FilterOp::Unpark - } - None => FilterOp::Stop, - } - }; - let callback = |result: UnparkResult| { - // If we are using a fair unlock then we should keep the - // rwlock locked and hand it off to the unparked threads. - if result.unparked_threads != 0 && (force_fair || result.be_fair) { - // We need to set the guard count accordingly. - let mut new_state = guard_count.get(); - - if result.have_more_threads { - new_state |= PARKED_BIT; - } - - self.state.store(new_state, Ordering::Release); - TOKEN_HANDOFF - } else { - // Clear the parked bit if there are no more parked threads. - if result.have_more_threads { - self.state.store(PARKED_BIT, Ordering::Release); - } else { - self.state.store(0, Ordering::Release); - } - TOKEN_NORMAL - } - }; - parking_lot_core::unpark_filter(addr, filter, callback); - } - } - - #[cold] - #[inline(never)] - fn downgrade_slow(&self) { - unsafe { - let addr = self as *const _ as usize; - let mut guard_count = SHARED_GUARD; - let filter = |ParkToken(token)| -> FilterOp { - match guard_count.checked_add(token) { - Some(new_guard_count) => { - guard_count = new_guard_count; - FilterOp::Unpark - } - None => FilterOp::Stop, - } - }; - let callback = |result: UnparkResult| { - // Clear the parked bit if there no more parked threads - if !result.have_more_threads { - self.state.fetch_and(!PARKED_BIT, Ordering::Relaxed); - } - TOKEN_NORMAL - }; - parking_lot_core::unpark_filter(addr, filter, callback); - } - } - - #[cold] - #[inline(never)] - fn downgrade_to_upgradable_slow(&self) { - unsafe { - let addr = self as *const _ as usize; - let mut guard_count = UPGRADABLE_GUARD; - let filter = |ParkToken(token)| -> FilterOp { - match guard_count.checked_add(token) { - Some(new_guard_count) => { - guard_count = new_guard_count; - FilterOp::Unpark - } - None => FilterOp::Stop, - } - }; - let callback = |result: UnparkResult| { - // Clear the parked bit if there no more parked threads - if !result.have_more_threads { - self.state.fetch_and(!PARKED_BIT, Ordering::Relaxed); - } - TOKEN_NORMAL - }; - parking_lot_core::unpark_filter(addr, filter, callback); - } - } - - #[cold] - #[inline(never)] - fn lock_shared_slow(&self, recursive: bool, timeout: Option) -> bool { - let mut spinwait = SpinWait::new(); - let mut spinwait_shared = SpinWait::new(); - let mut state = self.state.load(Ordering::Relaxed); - let mut unparked = false; - loop { - // Use hardware lock elision to avoid cache conflicts when multiple - // readers try to acquire the lock. We only do this if the lock is - // completely empty since elision handles conflicts poorly. - if have_elision() && state == 0 { - match self.state.elision_acquire(0, SHARED_GUARD) { - Ok(_) => return true, - Err(x) => state = x, - } - } - - // Grab the lock if there are no exclusive threads locked or - // waiting. However if we were unparked then we are allowed to grab - // the lock even if there are pending exclusive threads. - if unparked || recursive || state & PARKED_BIT == 0 { - if let Some(new_state) = state.checked_add(SHARED_GUARD) { - if self - .state - .compare_exchange_weak( - state, - new_state, - Ordering::Acquire, - Ordering::Relaxed, - ) - .is_ok() - { - return true; - } - - // If there is high contention on the reader count then we want - // to leave some time between attempts to acquire the lock to - // let other threads make progress. - spinwait_shared.spin_no_yield(); - state = self.state.load(Ordering::Relaxed); - continue; - } else { - // We were unparked spuriously, reset unparked flag. - unparked = false; - } - } - - // If there are no parked threads, try spinning a few times - if state & PARKED_BIT == 0 && spinwait.spin() { - state = self.state.load(Ordering::Relaxed); - continue; - } - - // Park our thread until we are woken up by an unlock - unsafe { - let addr = self as *const _ as usize; - let validate = || { - let mut state = self.state.load(Ordering::Relaxed); - loop { - // Nothing to do if the parked bit is already set - if state & PARKED_BIT != 0 { - return true; - } - - // If the parked bit is not set then it means we are at - // the front of the queue. If there is space for another - // lock then we should abort the park and try acquiring - // the lock again. - if state & GUARD_COUNT_MASK != GUARD_COUNT_MASK { - return false; - } - - // Set the parked bit - match self.state.compare_exchange_weak( - state, - state | PARKED_BIT, - Ordering::Relaxed, - Ordering::Relaxed, - ) { - Ok(_) => return true, - Err(x) => state = x, - } - } - }; - let before_sleep = || {}; - let timed_out = |_, was_last_thread| { - // Clear the parked bit if we were the last parked thread - if was_last_thread { - self.state.fetch_and(!PARKED_BIT, Ordering::Relaxed); - } - }; - match parking_lot_core::park( - addr, - validate, - before_sleep, - timed_out, - TOKEN_SHARED, - timeout, - ) { - // The thread that unparked us passed the lock on to us - // directly without unlocking it. - ParkResult::Unparked(TOKEN_HANDOFF) => return true, - - // We were unparked normally, try acquiring the lock again - ParkResult::Unparked(_) => (), - - // The validation function failed, try locking again - ParkResult::Invalid => (), - - // Timeout expired - ParkResult::TimedOut => return false, - } - } - - // Loop back and try locking again - spinwait.reset(); - spinwait_shared.reset(); - state = self.state.load(Ordering::Relaxed); - unparked = true; - } - } - #[cold] #[inline(never)] fn try_lock_shared_slow(&self, recursive: bool) -> bool { let mut state = self.state.load(Ordering::Relaxed); loop { - if !recursive && state & PARKED_BIT != 0 { - return false; + // This mirrors the condition in try_lock_shared_fast + if state & WRITER_BIT != 0 { + if !recursive || state & READERS_MASK == 0 { + return false; + } } if have_elision() && state == 0 { - match self.state.elision_acquire(0, SHARED_GUARD) { + match self.state.elision_compare_exchange_acquire(0, ONE_READER) { Ok(_) => return true, Err(x) => state = x, } } else { - match state.checked_add(SHARED_GUARD) { - Some(new_state) => match self.state.compare_exchange_weak( - state, - new_state, + match self.state.compare_exchange_weak( + state, + state + .checked_add(ONE_READER) + .expect("RwLock reader count overflow"), + Ordering::Acquire, + Ordering::Relaxed, + ) { + Ok(_) => return true, + Err(x) => state = x, + } + } + } + } + + #[inline(always)] + fn try_lock_upgradable_fast(&self) -> bool { + let state = self.state.load(Ordering::Relaxed); + + // We can't grab an upgradable lock if there is already a writer or + // upgradable reader. + if state & (WRITER_BIT | UPGRADABLE_BIT) != 0 { + return false; + } + + if let Some(new_state) = state.checked_add(ONE_READER | UPGRADABLE_BIT) { + self.state + .compare_exchange_weak(state, new_state, Ordering::Acquire, Ordering::Relaxed) + .is_ok() + } else { + false + } + } + + #[cold] + #[inline(never)] + fn try_lock_upgradable_slow(&self) -> bool { + let mut state = self.state.load(Ordering::Relaxed); + loop { + // This mirrors the condition in try_lock_upgradable_fast + if state & (WRITER_BIT | UPGRADABLE_BIT) != 0 { + return false; + } + + match self.state.compare_exchange_weak( + state, + state + .checked_add(ONE_READER | UPGRADABLE_BIT) + .expect("RwLock reader count overflow"), + Ordering::Acquire, + Ordering::Relaxed, + ) { + Ok(_) => return true, + Err(x) => state = x, + } + } + } + + #[cold] + #[inline(never)] + fn lock_exclusive_slow(&self, timeout: Option) -> bool { + // Step 1: grab exclusive ownership of WRITER_BIT + let timed_out = !self.lock_common( + timeout, + TOKEN_EXCLUSIVE, + |state| { + loop { + if *state & (WRITER_BIT | UPGRADABLE_BIT) != 0 { + return false; + } + + // Grab WRITER_BIT if it isn't set, even if there are parked threads. + match self.state.compare_exchange_weak( + *state, + *state | WRITER_BIT, Ordering::Acquire, Ordering::Relaxed, ) { Ok(_) => return true, - Err(x) => state = x, - }, - None => return false, + Err(x) => *state = x, + } } - } + }, + |state| state & (WRITER_BIT | UPGRADABLE_BIT) != 0, + ); + if timed_out { + return false; } + + // Step 2: wait for all remaining readers to exit the lock. + self.wait_for_readers(timeout, 0) } #[cold] #[inline(never)] - fn unlock_shared_slow(&self, force_fair: bool) { - let mut state = self.state.load(Ordering::Relaxed); - loop { - // Just release the lock if there are no parked thread or if we are - // not the last shared thread. - if state & PARKED_BIT == 0 - || (state & UPGRADING_BIT == 0 && state & GUARD_COUNT_MASK != SHARED_GUARD) - || (state & UPGRADING_BIT != 0 - && state & GUARD_COUNT_MASK != UPGRADABLE_GUARD + SHARED_GUARD) - { - match self.state.compare_exchange_weak( - state, - state - SHARED_GUARD, - Ordering::Release, - Ordering::Relaxed, - ) { - Ok(_) => return, - Err(x) => state = x, + fn unlock_exclusive_slow(&self, force_fair: bool) { + // There are threads to unpark. Try to unpark as many as we can. + let callback = |mut new_state, result: UnparkResult| { + // If we are using a fair unlock then we should keep the + // rwlock locked and hand it off to the unparked threads. + if result.unparked_threads != 0 && (force_fair || result.be_fair) { + if result.have_more_threads { + new_state |= PARKED_BIT; } - continue; + self.state.store(new_state, Ordering::Release); + TOKEN_HANDOFF + } else { + // Clear the parked bit if there are no more parked threads. + if result.have_more_threads { + self.state.store(PARKED_BIT, Ordering::Release); + } else { + self.state.store(0, Ordering::Release); + } + TOKEN_NORMAL } + }; + self.wake_parked_threads(0, callback); + } - break; - } - - // There are threads to unpark. If there is a thread waiting to be - // upgraded, we find that thread and let it upgrade, otherwise we - // unpark threads up to the guard capacity. Note that there is a - // potential race condition here: another thread might grab a shared - // lock between now and when we actually release our lock. - let additional_guards = Cell::new(0usize); - let has_upgraded = Cell::new(if state & UPGRADING_BIT == 0 { - None - } else { - Some(false) - }); - unsafe { - let addr = self as *const _ as usize; - let filter = |ParkToken(token)| -> FilterOp { - match has_upgraded.get() { - None => match additional_guards.get().checked_add(token) { - Some(x) => { - additional_guards.set(x); - FilterOp::Unpark - } - None => FilterOp::Stop, - }, - Some(false) => if token & UPGRADING_BIT != 0 { - additional_guards.set(token & !UPGRADING_BIT); - has_upgraded.set(Some(true)); - FilterOp::Unpark - } else { - FilterOp::Skip - }, - Some(true) => FilterOp::Stop, - } - }; - let callback = |result: UnparkResult| { - let mut state = self.state.load(Ordering::Relaxed); + #[cold] + #[inline(never)] + fn lock_shared_slow(&self, recursive: bool, timeout: Option) -> bool { + self.lock_common( + timeout, + TOKEN_SHARED, + |state| { + let mut spinwait_shared = SpinWait::new(); loop { - // Release our shared lock - let mut new_state = state - SHARED_GUARD; - - // Clear the parked bit if there are no more threads in - // the queue. - if !result.have_more_threads { - new_state &= !PARKED_BIT; - } - - // Clear the upgrading bit if we are upgrading a thread. - if let Some(true) = has_upgraded.get() { - new_state &= !UPGRADING_BIT; - } - - // Consider using fair unlocking. If we are, then we should set - // the state to the new value and tell the threads that we are - // handing the lock directly. - let token = if result.unparked_threads != 0 && (force_fair || result.be_fair) { - match new_state.checked_add(additional_guards.get()) { - Some(x) => { - new_state = x; - TOKEN_HANDOFF - } - None => TOKEN_NORMAL, + // Use hardware lock elision to avoid cache conflicts when multiple + // readers try to acquire the lock. We only do this if the lock is + // completely empty since elision handles conflicts poorly. + if have_elision() && *state == 0 { + match self.state.elision_compare_exchange_acquire(0, ONE_READER) { + Ok(_) => return true, + Err(x) => *state = x, + } + } + + // This is the same condition as try_lock_shared_fast + if *state & WRITER_BIT != 0 { + if !recursive || *state & READERS_MASK == 0 { + return false; } - } else { - TOKEN_NORMAL - }; - - match self.state.compare_exchange_weak( - state, - new_state, - Ordering::Release, - Ordering::Relaxed, - ) { - Ok(_) => return token, - Err(x) => state = x, } - } - }; - parking_lot_core::unpark_filter(addr, filter, callback); - } - } - #[cold] - #[inline(never)] - fn lock_upgradable_slow(&self, timeout: Option) -> bool { - let mut spinwait = SpinWait::new(); - let mut spinwait_shared = SpinWait::new(); - let mut state = self.state.load(Ordering::Relaxed); - let mut unparked = false; - loop { - // Grab the lock if there are no exclusive or upgradable threads - // locked or waiting. However if we were unparked then we are - // allowed to grab the lock even if there are pending exclusive threads. - if unparked || state & PARKED_BIT == 0 { - if let Some(new_state) = state.checked_add(UPGRADABLE_GUARD) { if self .state .compare_exchange_weak( - state, - new_state, + *state, + state + .checked_add(ONE_READER) + .expect("RwLock reader count overflow"), Ordering::Acquire, Ordering::Relaxed, ) @@ -1053,50 +708,379 @@ impl RawRwLock { // to leave some time between attempts to acquire the lock to // let other threads make progress. spinwait_shared.spin_no_yield(); - state = self.state.load(Ordering::Relaxed); - continue; - } else { - // We were unparked spuriously, reset unparked flag. - unparked = false; + *state = self.state.load(Ordering::Relaxed); + } + }, + |state| state & WRITER_BIT != 0, + ) + } + + #[cold] + #[inline(never)] + fn unlock_shared_slow(&self) { + // At this point WRITER_PARKED_BIT is set and READER_MASK is empty. We + // just need to wake up a potentially sleeping pending writer. + unsafe { + // Using the 2nd key at addr + 1 + let addr = self as *const _ as usize + 1; + let callback = |result: UnparkResult| { + // Clear the WRITER_PARKED_BIT here since there can only be one + // parked writer thread. + debug_assert!(!result.have_more_threads); + self.state.fetch_and(!WRITER_PARKED_BIT, Ordering::Relaxed); + TOKEN_NORMAL + }; + parking_lot_core::unpark_one(addr, callback); + } + } + + #[cold] + #[inline(never)] + fn lock_upgradable_slow(&self, timeout: Option) -> bool { + self.lock_common( + timeout, + TOKEN_UPGRADABLE, + |state| { + let mut spinwait_shared = SpinWait::new(); + loop { + if *state & (WRITER_BIT | UPGRADABLE_BIT) != 0 { + return false; + } + + if self + .state + .compare_exchange_weak( + *state, + state + .checked_add(ONE_READER | UPGRADABLE_BIT) + .expect("RwLock reader count overflow"), + Ordering::Acquire, + Ordering::Relaxed, + ) + .is_ok() + { + return true; + } + + // If there is high contention on the reader count then we want + // to leave some time between attempts to acquire the lock to + // let other threads make progress. + spinwait_shared.spin_no_yield(); + *state = self.state.load(Ordering::Relaxed); + } + }, + |state| state & (WRITER_BIT | UPGRADABLE_BIT) != 0, + ) + } + + #[cold] + #[inline(never)] + fn unlock_upgradable_slow(&self, force_fair: bool) { + // Just release the lock if there are no parked threads. + let mut state = self.state.load(Ordering::Relaxed); + while state & PARKED_BIT == 0 { + match self.state.compare_exchange_weak( + state, + state - (ONE_READER | UPGRADABLE_BIT), + Ordering::Release, + Ordering::Relaxed, + ) { + Ok(_) => return, + Err(x) => state = x, + } + } + + // There are threads to unpark. Try to unpark as many as we can. + let callback = |new_state, result: UnparkResult| { + // If we are using a fair unlock then we should keep the + // rwlock locked and hand it off to the unparked threads. + let mut state = self.state.load(Ordering::Relaxed); + if force_fair || result.be_fair { + // Fall back to normal unpark on overflow. Panicking is + // not allowed in parking_lot callbacks. + while let Some(mut new_state) = + (state - (ONE_READER | UPGRADABLE_BIT)).checked_add(new_state) + { + if result.have_more_threads { + new_state |= PARKED_BIT; + } else { + new_state &= !PARKED_BIT; + } + match self.state.compare_exchange_weak( + state, + new_state, + Ordering::Relaxed, + Ordering::Relaxed, + ) { + Ok(_) => return TOKEN_HANDOFF, + Err(x) => state = x, + } } } - // If there are no parked threads, try spinning a few times - if state & PARKED_BIT == 0 && spinwait.spin() { + // Otherwise just release the upgradable lock and update PARKED_BIT. + loop { + let mut new_state = state - (ONE_READER | UPGRADABLE_BIT); + if result.have_more_threads { + new_state |= PARKED_BIT; + } else { + new_state &= !PARKED_BIT; + } + match self.state.compare_exchange_weak( + state, + new_state, + Ordering::Relaxed, + Ordering::Relaxed, + ) { + Ok(_) => return TOKEN_NORMAL, + Err(x) => state = x, + } + } + }; + self.wake_parked_threads(0, callback); + } + + #[cold] + #[inline(never)] + fn try_upgrade_slow(&self) -> bool { + let mut state = self.state.load(Ordering::Relaxed); + loop { + if state & READERS_MASK != ONE_READER { + return false; + } + match self.state.compare_exchange_weak( + state, + state - (ONE_READER | UPGRADABLE_BIT) + WRITER_BIT, + Ordering::Relaxed, + Ordering::Relaxed, + ) { + Ok(_) => return true, + Err(x) => state = x, + } + } + } + + #[cold] + #[inline(never)] + fn upgrade_slow(&self, timeout: Option) -> bool { + self.wait_for_readers(timeout, ONE_READER | UPGRADABLE_BIT) + } + + #[cold] + #[inline(never)] + fn downgrade_slow(&self) { + // We only reach this point if PARKED_BIT is set. + let callback = |_, result: UnparkResult| { + // Clear the parked bit if there no more parked threads + if !result.have_more_threads { + self.state.fetch_and(!PARKED_BIT, Ordering::Relaxed); + } + TOKEN_NORMAL + }; + self.wake_parked_threads(ONE_READER, callback); + } + + #[cold] + #[inline(never)] + fn downgrade_to_upgradable_slow(&self) { + // We only reach this point if PARKED_BIT is set. + let callback = |_, result: UnparkResult| { + // Clear the parked bit if there no more parked threads + if !result.have_more_threads { + self.state.fetch_and(!PARKED_BIT, Ordering::Relaxed); + } + TOKEN_NORMAL + }; + self.wake_parked_threads(ONE_READER | UPGRADABLE_BIT, callback); + } + + #[cold] + #[inline(never)] + fn bump_shared_slow(&self) { + self.unlock_shared(); + self.lock_shared(); + } + + #[cold] + #[inline(never)] + fn bump_exclusive_slow(&self) { + self.deadlock_release(); + self.unlock_exclusive_slow(true); + self.lock_exclusive(); + } + + #[cold] + #[inline(never)] + fn bump_upgradable_slow(&self) { + self.deadlock_release(); + self.unlock_upgradable_slow(true); + self.lock_upgradable(); + } + + // Common code for waking up parked threads after releasing WRITER_BIT or + // UPGRADABLE_BIT. + #[inline] + fn wake_parked_threads(&self, new_state: usize, callback: C) + where + C: FnOnce(usize, UnparkResult) -> UnparkToken, + { + // We must wake up at least one upgrader or writer if there is one, + // otherwise they may end up parked indefinitely since unlock_shared + // does not call wake_parked_threads. + let new_state = Cell::new(new_state); + unsafe { + let addr = self as *const _ as usize; + let filter = |ParkToken(token)| { + let s = new_state.get(); + + // If we are waking up a writer, don't wake anything else. + if s & WRITER_BIT != 0 { + return FilterOp::Stop; + } + + // Otherwise wake *all* readers and one upgrader/writer. + if token & (UPGRADABLE_BIT | WRITER_BIT) != 0 && s & UPGRADABLE_BIT != 0 { + // Skip writers and upgradable readers if we already have + // a writer/upgradable reader. + FilterOp::Skip + } else { + new_state.set(s + token); + FilterOp::Unpark + } + }; + parking_lot_core::unpark_filter(addr, filter, |result| { + callback(new_state.get(), result) + }); + } + } + + // Common code for waiting for readers to exit the lock after acquiring + // WRITER_BIT. + #[inline] + fn wait_for_readers(&self, timeout: Option, prev_value: usize) -> bool { + // At this point WRITER_BIT is already set, we just need to wait for the + // remaining readers to exit the lock. + let mut spinwait = SpinWait::new(); + let mut state = self.state.load(Ordering::Relaxed); + while state & READERS_MASK != 0 { + // Spin a few times to wait for readers to exit + if spinwait.spin() { state = self.state.load(Ordering::Relaxed); continue; } + // Set the parked bit + if state & WRITER_PARKED_BIT == 0 { + if let Err(x) = self.state.compare_exchange_weak( + state, + state | WRITER_PARKED_BIT, + Ordering::Relaxed, + Ordering::Relaxed, + ) { + state = x; + continue; + } + } + + // Park our thread until we are woken up by an unlock + unsafe { + // Using the 2nd key at addr + 1 + let addr = self as *const _ as usize + 1; + let validate = || { + let state = self.state.load(Ordering::Relaxed); + state & READERS_MASK != 0 && state & WRITER_PARKED_BIT != 0 + }; + let before_sleep = || {}; + let timed_out = |_, _| {}; + match parking_lot_core::park( + addr, + validate, + before_sleep, + timed_out, + TOKEN_EXCLUSIVE, + timeout, + ) { + // We still need to re-check the state if we are unparked + // since a previous writer timing-out could have allowed + // another reader to sneak in before we parked. + ParkResult::Unparked(_) | ParkResult::Invalid => { + state = self.state.load(Ordering::Relaxed); + continue; + } + + // Timeout expired + ParkResult::TimedOut => { + // We need to release WRITER_BIT and revert back to + // our previous value. We also wake up any threads that + // might be waiting on WRITER_BIT. + let state = self.state.fetch_add( + prev_value.wrapping_sub(WRITER_BIT | WRITER_PARKED_BIT), + Ordering::Relaxed, + ); + if state & PARKED_BIT != 0 { + let callback = |_, result: UnparkResult| { + // Clear the parked bit if there no more parked threads + if !result.have_more_threads { + self.state.fetch_and(!PARKED_BIT, Ordering::Relaxed); + } + TOKEN_NORMAL + }; + self.wake_parked_threads(ONE_READER | UPGRADABLE_BIT, callback); + } + return false; + } + } + } + } + true + } + + // Common code for acquiring a lock + #[inline] + fn lock_common( + &self, + timeout: Option, + token: ParkToken, + mut try_lock: F, + validate: V, + ) -> bool + where + F: FnMut(&mut usize) -> bool, + V: Fn(usize) -> bool, + { + let mut spinwait = SpinWait::new(); + let mut state = self.state.load(Ordering::Relaxed); + loop { + // Attempt to grab the lock + if try_lock(&mut state) { + return true; + } + + // If there are no parked threads, try spinning a few times. + if state & (PARKED_BIT | WRITER_PARKED_BIT) == 0 && spinwait.spin() { + state = self.state.load(Ordering::Relaxed); + continue; + } + + // Set the parked bit + if state & PARKED_BIT == 0 { + if let Err(x) = self.state.compare_exchange_weak( + state, + state | PARKED_BIT, + Ordering::Relaxed, + Ordering::Relaxed, + ) { + state = x; + continue; + } + } + // Park our thread until we are woken up by an unlock unsafe { let addr = self as *const _ as usize; let validate = || { - let mut state = self.state.load(Ordering::Relaxed); - loop { - // Nothing to do if the parked bit is already set - if state & PARKED_BIT != 0 { - return true; - } - - // If the parked bit is not set then it means we are at - // the front of the queue. If there is space for an - // upgradable lock then we should abort the park and try - // acquiring the lock again. - if state & UPGRADABLE_GUARD != UPGRADABLE_GUARD { - return false; - } - - // Set the parked bit - match self.state.compare_exchange_weak( - state, - state | PARKED_BIT, - Ordering::Relaxed, - Ordering::Relaxed, - ) { - Ok(_) => return true, - Err(x) => state = x, - } - } + let state = self.state.load(Ordering::Relaxed); + state & PARKED_BIT != 0 && validate(state) }; let before_sleep = || {}; let timed_out = |_, was_last_thread| { @@ -1110,249 +1094,7 @@ impl RawRwLock { validate, before_sleep, timed_out, - TOKEN_UPGRADABLE, - timeout, - ) { - // The thread that unparked us passed the lock on to us - // directly without unlocking it. - ParkResult::Unparked(TOKEN_HANDOFF) => return true, - - // We were unparked normally, try acquiring the lock again - ParkResult::Unparked(_) => (), - - // The validation function failed, try locking again - ParkResult::Invalid => (), - - // Timeout expired - ParkResult::TimedOut => return false, - } - } - - // Loop back and try locking again - spinwait.reset(); - spinwait_shared.reset(); - state = self.state.load(Ordering::Relaxed); - unparked = true; - } - } - - #[cold] - #[inline(never)] - fn try_lock_upgradable_slow(&self) -> bool { - let mut state = self.state.load(Ordering::Relaxed); - loop { - if state & PARKED_BIT != 0 { - return false; - } - - match state.checked_add(UPGRADABLE_GUARD) { - Some(new_state) => match self.state.compare_exchange_weak( - state, - new_state, - Ordering::Acquire, - Ordering::Relaxed, - ) { - Ok(_) => return true, - Err(x) => state = x, - }, - None => return false, - } - } - } - - #[cold] - #[inline(never)] - fn unlock_upgradable_slow(&self, force_fair: bool) { - let mut state = self.state.load(Ordering::Relaxed); - loop { - // Just release the lock if there are no parked threads. - if state & PARKED_BIT == 0 { - match self.state.compare_exchange_weak( - state, - state - UPGRADABLE_GUARD, - Ordering::Release, - Ordering::Relaxed, - ) { - Ok(_) => return, - Err(x) => state = x, - } - continue; - } - - break; - } - - // There are threads to unpark. We unpark threads up to the guard capacity. - let additional_guards = Cell::new(0usize); - unsafe { - let addr = self as *const _ as usize; - let filter = |ParkToken(token)| -> FilterOp { - match additional_guards.get().checked_add(token) { - Some(x) => { - additional_guards.set(x); - FilterOp::Unpark - } - None => FilterOp::Stop, - } - }; - let callback = |result: UnparkResult| { - let mut state = self.state.load(Ordering::Relaxed); - loop { - // Release our upgradable lock - let mut new_state = state - UPGRADABLE_GUARD; - - // Clear the parked bit if there are no more threads in - // the queue - if !result.have_more_threads { - new_state &= !PARKED_BIT; - } - - // Consider using fair unlocking. If we are, then we should set - // the state to the new value and tell the threads that we are - // handing the lock directly. - let token = if result.unparked_threads != 0 && (force_fair || result.be_fair) { - match new_state.checked_add(additional_guards.get()) { - Some(x) => { - new_state = x; - TOKEN_HANDOFF - } - None => TOKEN_NORMAL, - } - } else { - TOKEN_NORMAL - }; - - match self.state.compare_exchange_weak( - state, - new_state, - Ordering::Release, - Ordering::Relaxed, - ) { - Ok(_) => return token, - Err(x) => state = x, - } - } - }; - parking_lot_core::unpark_filter(addr, filter, callback); - } - } - - #[cold] - #[inline(never)] - fn downgrade_upgradable_slow(&self, state: usize) { - unsafe { - let addr = self as *const _ as usize; - let mut guard_count = (state & GUARD_COUNT_MASK) - UPGRADABLE_GUARD; - let filter = |ParkToken(token)| -> FilterOp { - match guard_count.checked_add(token) { - Some(x) => { - guard_count = x; - FilterOp::Unpark - } - None => FilterOp::Stop, - } - }; - let callback = |result: UnparkResult| { - // Clear the parked bit if there no more parked threads - if !result.have_more_threads { - self.state.fetch_and(!PARKED_BIT, Ordering::Relaxed); - } - TOKEN_NORMAL - }; - parking_lot_core::unpark_filter(addr, filter, callback); - } - } - - #[cold] - #[inline(never)] - fn try_upgrade_slow(&self) -> bool { - let mut state = self.state.load(Ordering::Relaxed); - loop { - match state.checked_add(EXCLUSIVE_GUARD - SHARED_GUARD) { - Some(new_state) => match self.state.compare_exchange_weak( - state, - new_state, - Ordering::Relaxed, - Ordering::Relaxed, - ) { - Ok(_) => return true, - Err(x) => state = x, - }, - None => return false, - } - } - } - - #[cold] - #[inline(never)] - fn upgrade_slow(&self, timeout: Option) -> bool { - let mut spinwait = SpinWait::new(); - let mut state = self.state.load(Ordering::Relaxed); - loop { - // Grab the lock if it isn't locked, even if there are other - // threads parked. - if let Some(new_state) = state.checked_add(EXCLUSIVE_GUARD - UPGRADABLE_GUARD) { - match self.state.compare_exchange_weak( - state, - new_state, - Ordering::Acquire, - Ordering::Relaxed, - ) { - Ok(_) => return true, - Err(x) => state = x, - } - continue; - } - - // If there are no parked threads and only one other reader, try - // spinning a few times. - if state == UPGRADABLE_GUARD | SHARED_GUARD && spinwait.spin() { - state = self.state.load(Ordering::Relaxed); - continue; - } - - // Park our thread until we are woken up by an unlock - unsafe { - let addr = self as *const _ as usize; - let validate = || { - let mut state = self.state.load(Ordering::Relaxed); - loop { - // If the rwlock is free, abort the park and try to grab - // it immediately. - if state & GUARD_COUNT_MASK == UPGRADABLE_GUARD { - return false; - } - - // Set the upgrading and parked bits - match self.state.compare_exchange_weak( - state, - state | (UPGRADING_BIT | PARKED_BIT), - Ordering::Relaxed, - Ordering::Relaxed, - ) { - Ok(_) => return true, - Err(x) => state = x, - } - } - }; - let before_sleep = || {}; - let timed_out = |_, was_last_thread| { - // Clear the upgrading bit - let mut flags = UPGRADING_BIT; - - // Clear the parked bit if we were the last parked thread - if was_last_thread { - flags |= PARKED_BIT; - } - - self.state.fetch_and(!flags, Ordering::Relaxed); - }; - match parking_lot_core::park( - addr, - validate, - before_sleep, - timed_out, - TOKEN_UPGRADING, + token, timeout, ) { // The thread that unparked us passed the lock on to us @@ -1376,27 +1118,15 @@ impl RawRwLock { } } - #[cold] - #[inline(never)] - fn bump_shared_slow(&self) { - unsafe { deadlock::release_resource(self as *const _ as usize) }; - self.unlock_shared_slow(true); - self.lock_shared(); + #[inline] + fn deadlock_acquire(&self) { + unsafe { deadlock::acquire_resource(self as *const _ as usize) }; + unsafe { deadlock::acquire_resource(self as *const _ as usize + 1) }; } - #[cold] - #[inline(never)] - fn bump_exclusive_slow(&self) { + #[inline] + fn deadlock_release(&self) { unsafe { deadlock::release_resource(self as *const _ as usize) }; - self.unlock_exclusive_slow(true); - self.lock_exclusive(); - } - - #[cold] - #[inline(never)] - fn bump_upgradable_slow(&self) { - unsafe { deadlock::release_resource(self as *const _ as usize) }; - self.unlock_upgradable_slow(true); - self.lock_upgradable(); + unsafe { deadlock::release_resource(self as *const _ as usize + 1) }; } } diff --git a/third_party/rust/parking_lot/src/remutex.rs b/third_party/rust/parking_lot/src/remutex.rs index ba9e004d7a69..c655a99a244e 100644 --- a/third_party/rust/parking_lot/src/remutex.rs +++ b/third_party/rust/parking_lot/src/remutex.rs @@ -5,8 +5,8 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. +use crate::raw_mutex::RawMutex; use lock_api::{self, GetThreadId}; -use raw_mutex::RawMutex; /// Implementation of the `GetThreadId` trait for `lock_api::ReentrantMutex`. pub struct RawThreadId; @@ -40,8 +40,7 @@ pub type ReentrantMutex = lock_api::ReentrantMutex; /// /// The data protected by the mutex can be accessed through this guard via its /// `Deref` implementation. -pub type ReentrantMutexGuard<'a, T> = - lock_api::ReentrantMutexGuard<'a, RawMutex, RawThreadId, T>; +pub type ReentrantMutexGuard<'a, T> = lock_api::ReentrantMutexGuard<'a, RawMutex, RawThreadId, T>; /// An RAII mutex guard returned by `ReentrantMutexGuard::map`, which can point to a /// subfield of the protected data. @@ -55,10 +54,13 @@ pub type MappedReentrantMutexGuard<'a, T> = #[cfg(test)] mod tests { + use crate::ReentrantMutex; use std::cell::RefCell; use std::sync::Arc; use std::thread; - use ReentrantMutex; + + #[cfg(feature = "serde")] + use bincode::{deserialize, serialize}; #[test] fn smoke() { @@ -103,8 +105,9 @@ mod tests { thread::spawn(move || { let lock = m2.try_lock(); assert!(lock.is_none()); - }).join() - .unwrap(); + }) + .join() + .unwrap(); let _lock3 = m.try_lock(); } @@ -113,14 +116,18 @@ mod tests { let mutex = ReentrantMutex::new(vec![0u8, 10]); assert_eq!(format!("{:?}", mutex), "ReentrantMutex { data: [0, 10] }"); - assert_eq!( - format!("{:#?}", mutex), - "ReentrantMutex { - data: [ - 0, - 10 - ] -}" - ); + } + + #[cfg(feature = "serde")] + #[test] + fn test_serde() { + let contents: Vec = vec![0, 1, 2]; + let mutex = ReentrantMutex::new(contents.clone()); + + let serialized = serialize(&mutex).unwrap(); + let deserialized: ReentrantMutex> = deserialize(&serialized).unwrap(); + + assert_eq!(*(mutex.lock()), *(deserialized.lock())); + assert_eq!(contents, *(deserialized.lock())); } } diff --git a/third_party/rust/parking_lot/src/rwlock.rs b/third_party/rust/parking_lot/src/rwlock.rs index 5a1d0cbf8dc0..fbfb39f2b817 100644 --- a/third_party/rust/parking_lot/src/rwlock.rs +++ b/third_party/rust/parking_lot/src/rwlock.rs @@ -5,8 +5,8 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. +use crate::raw_rwlock::RawRwLock; use lock_api; -use raw_rwlock::RawRwLock; /// A reader-writer lock /// @@ -116,19 +116,20 @@ pub type MappedRwLockWriteGuard<'a, T> = lock_api::MappedRwLockWriteGuard<'a, Ra /// RAII structure used to release the upgradable read access of a lock when /// dropped. -pub type RwLockUpgradableReadGuard<'a, T> = - lock_api::RwLockUpgradableReadGuard<'a, RawRwLock, T>; +pub type RwLockUpgradableReadGuard<'a, T> = lock_api::RwLockUpgradableReadGuard<'a, RawRwLock, T>; #[cfg(test)] mod tests { - extern crate rand; - use self::rand::Rng; + use crate::{RwLock, RwLockUpgradableReadGuard, RwLockWriteGuard}; + use rand::Rng; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::mpsc::channel; use std::sync::Arc; use std::thread; use std::time::Duration; - use {RwLock, RwLockUpgradableReadGuard, RwLockWriteGuard}; + + #[cfg(feature = "serde")] + use bincode::{deserialize, serialize}; #[derive(Eq, PartialEq, Debug)] struct NonCopy(i32); @@ -178,7 +179,8 @@ mod tests { let _: Result<(), _> = thread::spawn(move || { let _lock = arc2.write(); panic!(); - }).join(); + }) + .join(); let lock = arc.read(); assert_eq!(*lock, 1); } @@ -190,7 +192,8 @@ mod tests { let _: Result<(), _> = thread::spawn(move || { let _lock = arc2.write(); panic!(); - }).join(); + }) + .join(); let lock = arc.write(); assert_eq!(*lock, 1); } @@ -202,7 +205,8 @@ mod tests { let _: Result<(), _> = thread::spawn(move || { let _lock = arc2.read(); panic!(); - }).join(); + }) + .join(); let lock = arc.read(); assert_eq!(*lock, 1); } @@ -214,7 +218,8 @@ mod tests { let _: Result<(), _> = thread::spawn(move || { let _lock = arc2.read(); panic!() - }).join(); + }) + .join(); let lock = arc.write(); assert_eq!(*lock, 1); } @@ -329,7 +334,8 @@ mod tests { } let _u = Unwinder { i: arc2 }; panic!(); - }).join(); + }) + .join(); let lock = arc.read(); assert_eq!(*lock, 2); } @@ -530,7 +536,15 @@ mod tests { thread::spawn(move || { let _lock = arc2.write(); }); - thread::sleep(Duration::from_millis(100)); + + if cfg!(not(all(target_env = "sgx", target_vendor = "fortanix"))) { + thread::sleep(Duration::from_millis(100)); + } else { + // FIXME: https://github.com/fortanix/rust-sgx/issues/31 + for _ in 0..100 { + thread::yield_now(); + } + } // A normal read would block here since there is a pending writer let _lock2 = arc.read_recursive(); @@ -541,17 +555,8 @@ mod tests { let x = RwLock::new(vec![0u8, 10]); assert_eq!(format!("{:?}", x), "RwLock { data: [0, 10] }"); - assert_eq!( - format!("{:#?}", x), - "RwLock { - data: [ - 0, - 10 - ] -}" - ); let _lock = x.write(); - assert_eq!(format!("{:?}", x), "RwLock { }"); + assert_eq!(format!("{:?}", x), "RwLock { data: }"); } #[test] @@ -561,4 +566,17 @@ mod tests { let b = a.clone(); assert_eq!(Arc::strong_count(&b), 2); } + + #[cfg(feature = "serde")] + #[test] + fn test_serde() { + let contents: Vec = vec![0, 1, 2]; + let mutex = RwLock::new(contents.clone()); + + let serialized = serialize(&mutex).unwrap(); + let deserialized: RwLock> = deserialize(&serialized).unwrap(); + + assert_eq!(*(mutex.read()), *(deserialized.read())); + assert_eq!(contents, *(deserialized.read())); + } } diff --git a/third_party/rust/parking_lot/src/util.rs b/third_party/rust/parking_lot/src/util.rs index c7dfd322500c..159af7f07508 100644 --- a/third_party/rust/parking_lot/src/util.rs +++ b/third_party/rust/parking_lot/src/util.rs @@ -5,6 +5,8 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. +use std::time::{Duration, Instant}; + // Option::unchecked_unwrap pub trait UncheckedOptionExt { unsafe fn unchecked_unwrap(self) -> T; @@ -30,3 +32,13 @@ unsafe fn unreachable() -> ! { match *(1 as *const Void) {} } } + +#[inline] +pub fn to_deadline(timeout: Duration) -> Option { + #[cfg(has_checked_instant)] + let deadline = Instant::now().checked_add(timeout); + #[cfg(not(has_checked_instant))] + let deadline = Some(Instant::now() + timeout); + + deadline +} diff --git a/third_party/rust/parking_lot_core/.cargo-checksum.json b/third_party/rust/parking_lot_core/.cargo-checksum.json index 0dbf94dc5d67..ee3836d02ffb 100644 --- a/third_party/rust/parking_lot_core/.cargo-checksum.json +++ b/third_party/rust/parking_lot_core/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"220144666e4c0a4b3b3235e7d3b10f4f34cb3b8ca292ee19437f23c9a15758de","src/lib.rs":"e80f927665ef24660878e5e4a4ea3c26892c2849889d59aacee6beb59d02020d","src/parking_lot.rs":"2da388ff4c13003fc30531bb6110e4feedac30ad3ce905912e657711a6b0fdad","src/spinwait.rs":"cbd2d2464ef6fa5fb05109bdb3ca588467949dcd4ee9194deafef6004d10215e","src/thread_parker/generic.rs":"0c30db3d1c96bd5ef284a4761a829aba8d21fc813b3d1d70b2baf5f00744e006","src/thread_parker/linux.rs":"1c4c023ebb58fcc16451683c6c8b68311e87ab34537dc17a060ddf5aad02a215","src/thread_parker/unix.rs":"dc6f4af965618cc2d87d3bef6455ba78b44ffe5b38dff9d41fb86e1526cbbcd1","src/thread_parker/windows/keyed_event.rs":"efe64f7bcdfe03049a7b901d2573bc7db1bb73b8ab4a040245423d95c8f9514f","src/thread_parker/windows/mod.rs":"f31eed53f3e402477d80a70a7c6d474c01ba4c9ad952bbe562509448cd3cc1ad","src/thread_parker/windows/waitaddress.rs":"09d1e6a5a6c3f23f375ae4beee946290f7c66d183e69d476ce69b21a4a5aa7af","src/util.rs":"2d07c0c010a857790ae2ed6a1215eeed8af76859e076797ea1ba8dec82169e84","src/word_lock.rs":"692f443c52672c6e88c0cad259cf7c89dc2a1b54aa95eeeea582401b2a7d058d"},"package":"4db1a8ccf734a7bce794cc19b3df06ed87ab2f3907036b693c68f56b4d4537fa"} \ No newline at end of file +{"files":{"Cargo.toml":"99e468923e11bcd61cd9961fd5c0a8e0151fae5b6695c1aaaa802a9ee790b91b","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"c9a75f18b9ab2927829a208fc6aa2cf4e63b8420887ba29cdb265d6619ae82d5","build.rs":"d6aa24b67fdcacf238778c5efaf1f622ec7f7a7ec27fa051f415a1e2d31f3532","src/lib.rs":"4b754002784224bf94136139eadc30136c638feacdc0d25dab44834a29805e60","src/parking_lot.rs":"ecda8f1230f796d4f8014699f13436b42a3e3b02edf2694cff5d02497d0d8f19","src/spinwait.rs":"d568d8a81f9144ec4c4a139dc934d7d04ee1656a4a221eb548742fe7aba09ab1","src/thread_parker/cloudabi.rs":"8096eefdf3a7b6fe1af223b548eabae067e4a838e49f1834b3dbb92c6c10169f","src/thread_parker/generic.rs":"fb89e50fba40956e2322a4aa8bd409cf14186c757a6a525cca3e71215b814e59","src/thread_parker/linux.rs":"d52fc55e2c17e9111d5d5a00efe58a87d0e72def22f18f1f34f5364744c79ff6","src/thread_parker/redox.rs":"4fa0ac04dcc740ebab57653dc685853d9fb950af545bbba93dbe61d985788a8e","src/thread_parker/sgx.rs":"0e30172ecf48c56bc85e26d976661c493142eeb71bd7713e21465067256ded90","src/thread_parker/unix.rs":"09418fec4845d0d6cc3039c4196cec7c96d53e65d552eb1b0c0a88fb6f72dd3e","src/thread_parker/wasm.rs":"29f5c518184a73f83d00097c7f3747406b008dc112937520414e5d41e50f2779","src/thread_parker/windows/keyed_event.rs":"e0c2ed647e0550bffa003160405b5f4ddd40500134c2eb15c3eb598792c30e84","src/thread_parker/windows/mod.rs":"7252790b6d1126d773f17760692e3664c140abebea9930058c84113bedd3b48d","src/thread_parker/windows/waitaddress.rs":"06d994633006e237dc940f377432ea00cf1609e56096d69d46f7bb3b80eeb857","src/util.rs":"2d07c0c010a857790ae2ed6a1215eeed8af76859e076797ea1ba8dec82169e84","src/word_lock.rs":"471b4fdf7877da693d26f5b80c732120af752f6eaffc65d4ca316bca3601e44a"},"package":"cb88cb1cb3790baa6776844f968fea3be44956cf184fa1be5a03341f5491278c"} \ No newline at end of file diff --git a/third_party/rust/parking_lot_core/Cargo.toml b/third_party/rust/parking_lot_core/Cargo.toml index e5855ac05914..f71508c366f9 100644 --- a/third_party/rust/parking_lot_core/Cargo.toml +++ b/third_party/rust/parking_lot_core/Cargo.toml @@ -3,7 +3,7 @@ # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g. crates.io) dependencies +# to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're @@ -11,24 +11,28 @@ # will likely look very different (and much more reasonable) [package] +edition = "2018" name = "parking_lot_core" -version = "0.2.14" +version = "0.5.0" authors = ["Amanieu d'Antras "] description = "An advanced API for creating custom synchronization primitives." -documentation = "https://amanieu.github.io/parking_lot/parking_lot_core/index.html" keywords = ["mutex", "condvar", "rwlock", "once", "thread"] +categories = ["concurrency"] license = "Apache-2.0/MIT" repository = "https://github.com/Amanieu/parking_lot" [dependencies.backtrace] version = "0.3.2" optional = true +[dependencies.cfg-if] +version = "0.1" + [dependencies.petgraph] version = "0.4.5" optional = true [dependencies.rand] -version = "0.4" +version = "0.6" [dependencies.smallvec] version = "0.6" @@ -36,10 +40,16 @@ version = "0.6" [dependencies.thread-id] version = "3.2.0" optional = true +[build-dependencies.rustc_version] +version = "0.2" [features] deadlock_detection = ["petgraph", "thread-id", "backtrace"] nightly = [] +[target."cfg(target_os = \"cloudabi\")".dependencies.cloudabi] +version = "0.0.3" +[target."cfg(target_os = \"redox\")".dependencies.redox_syscall] +version = "0.1" [target."cfg(unix)".dependencies.libc] version = "0.2.27" [target."cfg(windows)".dependencies.winapi] diff --git a/third_party/rust/parking_lot_core/LICENSE-APACHE b/third_party/rust/parking_lot_core/LICENSE-APACHE new file mode 100644 index 000000000000..16fe87b06e80 --- /dev/null +++ b/third_party/rust/parking_lot_core/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/third_party/rust/parking_lot_core/LICENSE-MIT b/third_party/rust/parking_lot_core/LICENSE-MIT new file mode 100644 index 000000000000..40b8817a47be --- /dev/null +++ b/third_party/rust/parking_lot_core/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2016 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/third_party/rust/parking_lot_core/build.rs b/third_party/rust/parking_lot_core/build.rs new file mode 100644 index 000000000000..417a770ae809 --- /dev/null +++ b/third_party/rust/parking_lot_core/build.rs @@ -0,0 +1,7 @@ +use rustc_version::{version, Version}; + +fn main() { + if version().unwrap() >= Version::parse("1.34.0").unwrap() { + println!("cargo:rustc-cfg=has_sized_atomics"); + } +} diff --git a/third_party/rust/parking_lot_core/src/lib.rs b/third_party/rust/parking_lot_core/src/lib.rs index 4bf64148432e..1d599a31c734 100644 --- a/third_party/rust/parking_lot_core/src/lib.rs +++ b/third_party/rust/parking_lot_core/src/lib.rs @@ -38,44 +38,67 @@ //! reference count and the two mutex bits in the same atomic word. #![warn(missing_docs)] -#![cfg_attr(all(feature = "nightly", target_os = "linux"), feature(integer_atomics))] +#![warn(rust_2018_idioms)] +#![cfg_attr( + all(target_env = "sgx", target_vendor = "fortanix"), + feature(sgx_platform) +)] +#![cfg_attr( + all( + feature = "nightly", + target_arch = "wasm32", + target_feature = "atomics" + ), + feature(checked_duration_since, stdsimd) +)] +#![cfg_attr( + all(feature = "nightly", target_os = "cloudabi",), + feature(thread_local, checked_duration_since) +)] -extern crate rand; -extern crate smallvec; +use cfg_if::cfg_if; -#[cfg(feature = "deadlock_detection")] -extern crate backtrace; -#[cfg(feature = "deadlock_detection")] -extern crate petgraph; -#[cfg(feature = "deadlock_detection")] -extern crate thread_id; +cfg_if! { + if #[cfg(all(has_sized_atomics, target_os = "linux"))] { + #[path = "thread_parker/linux.rs"] + mod thread_parker; + } else if #[cfg(unix)] { + #[path = "thread_parker/unix.rs"] + mod thread_parker; + } else if #[cfg(windows)] { + #[path = "thread_parker/windows/mod.rs"] + mod thread_parker; + } else if #[cfg(all(has_sized_atomics, target_os = "redox"))] { + #[path = "thread_parker/redox.rs"] + mod thread_parker; + } else if #[cfg(all(target_env = "sgx", target_vendor = "fortanix"))] { + #[path = "thread_parker/sgx.rs"] + mod thread_parker; + } else if #[cfg(all( + feature = "nightly", + target_arch = "wasm32", + target_feature = "atomics" + ))] { + #[path = "thread_parker/wasm.rs"] + mod thread_parker; + } else if #[cfg(all(feature = "nightly", target_os = "cloudabi"))] { + #[path = "thread_parker/cloudabi.rs"] + mod thread_parker; + } else { + #[path = "thread_parker/generic.rs"] + mod thread_parker; + } +} -#[cfg(unix)] -extern crate libc; - -#[cfg(windows)] -extern crate winapi; - -#[cfg(all(feature = "nightly", target_os = "linux"))] -#[path = "thread_parker/linux.rs"] -mod thread_parker; -#[cfg(all(unix, not(all(feature = "nightly", target_os = "linux"))))] -#[path = "thread_parker/unix.rs"] -mod thread_parker; -#[cfg(windows)] -#[path = "thread_parker/windows/mod.rs"] -mod thread_parker; -#[cfg(not(any(windows, unix)))] -#[path = "thread_parker/generic.rs"] -mod thread_parker; - -mod util; -mod spinwait; -mod word_lock; mod parking_lot; +mod spinwait; +mod util; +mod word_lock; -pub use parking_lot::{FilterOp, ParkResult, ParkToken, RequeueOp, UnparkResult, UnparkToken}; -pub use parking_lot::{DEFAULT_PARK_TOKEN, DEFAULT_UNPARK_TOKEN}; -pub use parking_lot::{park, unpark_all, unpark_filter, unpark_one, unpark_requeue}; -pub use spinwait::SpinWait; -pub use parking_lot::deadlock; +pub use self::parking_lot::deadlock; +pub use self::parking_lot::{park, unpark_all, unpark_filter, unpark_one, unpark_requeue}; +pub use self::parking_lot::{ + FilterOp, ParkResult, ParkToken, RequeueOp, UnparkResult, UnparkToken, +}; +pub use self::parking_lot::{DEFAULT_PARK_TOKEN, DEFAULT_UNPARK_TOKEN}; +pub use self::spinwait::SpinWait; diff --git a/third_party/rust/parking_lot_core/src/parking_lot.rs b/third_party/rust/parking_lot_core/src/parking_lot.rs index ab01fdc75557..5779c318432a 100644 --- a/third_party/rust/parking_lot_core/src/parking_lot.rs +++ b/third_party/rust/parking_lot_core/src/parking_lot.rs @@ -5,22 +5,20 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. -use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; -use std::time::{Duration, Instant}; -use std::cell::{Cell, UnsafeCell}; -use std::ptr; -use std::mem; -use std::thread::LocalKey; -#[cfg(not(feature = "nightly"))] -use std::panic; +use crate::thread_parker::ThreadParker; +use crate::util::UncheckedOptionExt; +use crate::word_lock::WordLock; +use core::{ + cell::{Cell, UnsafeCell}, + ptr, + sync::atomic::{AtomicPtr, AtomicUsize, Ordering}, +}; +use rand::{rngs::SmallRng, FromEntropy, Rng}; use smallvec::SmallVec; -use rand::{self, Rng, XorShiftRng}; -use thread_parker::ThreadParker; -use word_lock::WordLock; -use util::UncheckedOptionExt; +use std::time::{Duration, Instant}; -static NUM_THREADS: AtomicUsize = ATOMIC_USIZE_INIT; -static HASHTABLE: AtomicUsize = ATOMIC_USIZE_INIT; +static NUM_THREADS: AtomicUsize = AtomicUsize::new(0); +static HASHTABLE: AtomicPtr = AtomicPtr::new(ptr::null_mut()); // Even with 3x more buckets than threads, the memory overhead per thread is // still only a few hundred bytes per thread. @@ -38,24 +36,26 @@ struct HashTable { } impl HashTable { + #[inline] fn new(num_threads: usize, prev: *const HashTable) -> Box { let new_size = (num_threads * LOAD_FACTOR).next_power_of_two(); let hash_bits = 0usize.leading_zeros() - new_size.leading_zeros() - 1; - let bucket = Bucket { - mutex: WordLock::new(), - queue_head: Cell::new(ptr::null()), - queue_tail: Cell::new(ptr::null()), - fair_timeout: UnsafeCell::new(FairTimeout::new()), - _padding: unsafe { mem::uninitialized() }, - }; + + let now = Instant::now(); + let mut entries = Vec::with_capacity(new_size); + for _ in 0..new_size { + entries.push(Bucket::new(now)); + } + Box::new(HashTable { - entries: vec![bucket; new_size].into_boxed_slice(), - hash_bits: hash_bits, + entries: entries.into_boxed_slice(), + hash_bits, _prev: prev, }) } } +#[repr(align(64))] struct Bucket { // Lock protecting the queue mutex: WordLock, @@ -66,22 +66,16 @@ struct Bucket { // Next time at which point be_fair should be set fair_timeout: UnsafeCell, - - // Padding to avoid false sharing between buckets. Ideally we would just - // align the bucket structure to 64 bytes, but Rust doesn't support that - // yet. - _padding: [u8; 64], } -// Implementation of Clone for Bucket, needed to make vec![] work -impl Clone for Bucket { - fn clone(&self) -> Bucket { - Bucket { - mutex: WordLock::new(), +impl Bucket { + #[inline] + pub fn new(timeout: Instant) -> Self { + Self { + mutex: WordLock::INIT, queue_head: Cell::new(ptr::null()), queue_tail: Cell::new(ptr::null()), - fair_timeout: UnsafeCell::new(FairTimeout::new()), - _padding: unsafe { mem::uninitialized() }, + fair_timeout: UnsafeCell::new(FairTimeout::new(timeout)), } } } @@ -91,18 +85,20 @@ struct FairTimeout { timeout: Instant, // Random number generator for calculating the next timeout - rng: XorShiftRng, + rng: SmallRng, } impl FairTimeout { - fn new() -> FairTimeout { + #[inline] + fn new(timeout: Instant) -> FairTimeout { FairTimeout { - timeout: Instant::now(), - rng: rand::weak_rng(), + timeout, + rng: SmallRng::from_entropy(), } } // Determine whether we should force a fair unlock, and update the timeout + #[inline] fn should_timeout(&mut self) -> bool { let now = Instant::now(); if now > self.timeout { @@ -134,8 +130,8 @@ struct ThreadData { parked_with_timeout: Cell, // Extra data for deadlock detection - // TODO: once supported in stable replace with #[cfg...] & remove dummy struct/impl - #[allow(dead_code)] deadlock_data: deadlock::DeadlockData, + #[cfg(feature = "deadlock_detection")] + deadlock_data: deadlock::DeadlockData, } impl ThreadData { @@ -154,34 +150,28 @@ impl ThreadData { unpark_token: Cell::new(DEFAULT_UNPARK_TOKEN), park_token: Cell::new(DEFAULT_PARK_TOKEN), parked_with_timeout: Cell::new(false), + #[cfg(feature = "deadlock_detection")] deadlock_data: deadlock::DeadlockData::new(), } } } -// Returns a ThreadData structure for the current thread -unsafe fn get_thread_data(local: &mut Option) -> &ThreadData { - // Try to read from thread-local storage, but return None if the TLS has - // already been destroyed. - #[cfg(feature = "nightly")] - fn try_get_tls(key: &'static LocalKey) -> Option<*const ThreadData> { - key.try_with(|x| x as *const ThreadData).ok() - } - #[cfg(not(feature = "nightly"))] - fn try_get_tls(key: &'static LocalKey) -> Option<*const ThreadData> { - panic::catch_unwind(|| key.with(|x| x as *const ThreadData)).ok() - } - +// Invokes the given closure with a reference to the current thread `ThreadData`. +#[inline(always)] +fn with_thread_data(f: F) -> T +where + F: FnOnce(&ThreadData) -> T, +{ // Unlike word_lock::ThreadData, parking_lot::ThreadData is always expensive - // to construct. Try to use a thread-local version if possible. + // to construct. Try to use a thread-local version if possible. Otherwise just + // create a ThreadData on the stack + let mut thread_data_storage = None; thread_local!(static THREAD_DATA: ThreadData = ThreadData::new()); - if let Some(tls) = try_get_tls(&THREAD_DATA) { - return &*tls; - } + let thread_data_ptr = THREAD_DATA + .try_with(|x| x as *const ThreadData) + .unwrap_or_else(|_| thread_data_storage.get_or_insert_with(ThreadData::new)); - // Otherwise just create a ThreadData on the stack - *local = Some(ThreadData::new()); - local.as_ref().unwrap() + f(unsafe { &*thread_data_ptr }) } impl Drop for ThreadData { @@ -191,30 +181,41 @@ impl Drop for ThreadData { } // Get a pointer to the latest hash table, creating one if it doesn't exist yet. -unsafe fn get_hashtable() -> *const HashTable { - let mut table = HASHTABLE.load(Ordering::Acquire); +#[inline] +fn get_hashtable() -> *mut HashTable { + let table = HASHTABLE.load(Ordering::Acquire); // If there is no table, create one - if table == 0 { - let new_table = Box::into_raw(HashTable::new(LOAD_FACTOR, ptr::null())); - - // If this fails then it means some other thread created the hash - // table first. - match HASHTABLE.compare_exchange( - 0, - new_table as usize, - Ordering::Release, - Ordering::Relaxed, - ) { - Ok(_) => return new_table, - Err(x) => table = x, - } - - // Free the table we created - Box::from_raw(new_table); + if table.is_null() { + create_hashtable() + } else { + table } +} - table as *const HashTable +// Get a pointer to the latest hash table, creating one if it doesn't exist yet. +#[cold] +#[inline(never)] +fn create_hashtable() -> *mut HashTable { + let new_table = Box::into_raw(HashTable::new(LOAD_FACTOR, ptr::null())); + + // If this fails then it means some other thread created the hash + // table first. + match HASHTABLE.compare_exchange( + ptr::null_mut(), + new_table, + Ordering::Release, + Ordering::Relaxed, + ) { + Ok(_) => new_table, + Err(old_table) => { + // Free the table we created + unsafe { + Box::from_raw(new_table); + } + old_table + } + } } // Grow the hash table so that it is big enough for the given number of threads. @@ -222,13 +223,18 @@ unsafe fn get_hashtable() -> *const HashTable { // created, which only happens once per thread. unsafe fn grow_hashtable(num_threads: usize) { // If there is no table, create one - if HASHTABLE.load(Ordering::Relaxed) == 0 { + if HASHTABLE.load(Ordering::Relaxed).is_null() { let new_table = Box::into_raw(HashTable::new(num_threads, ptr::null())); // If this fails then it means some other thread created the hash // table first. if HASHTABLE - .compare_exchange(0, new_table as usize, Ordering::Release, Ordering::Relaxed) + .compare_exchange( + ptr::null_mut(), + new_table, + Ordering::Release, + Ordering::Relaxed, + ) .is_ok() { return; @@ -240,7 +246,7 @@ unsafe fn grow_hashtable(num_threads: usize) { let mut old_table; loop { - old_table = HASHTABLE.load(Ordering::Acquire) as *mut HashTable; + old_table = HASHTABLE.load(Ordering::Acquire); // Check if we need to resize the existing table if (*old_table).entries.len() >= LOAD_FACTOR * num_threads { @@ -255,7 +261,7 @@ unsafe fn grow_hashtable(num_threads: usize) { // Now check if our table is still the latest one. Another thread could // have grown the hash table between us reading HASHTABLE and locking // the buckets. - if HASHTABLE.load(Ordering::Relaxed) == old_table as usize { + if HASHTABLE.load(Ordering::Relaxed) == old_table { break; } @@ -290,7 +296,7 @@ unsafe fn grow_hashtable(num_threads: usize) { // Publish the new table. No races are possible at this point because // any other thread trying to grow the hash table is blocked on the bucket // locks in the old table. - HASHTABLE.store(Box::into_raw(new_table) as usize, Ordering::Release); + HASHTABLE.store(Box::into_raw(new_table), Ordering::Release); // Unlock all buckets in the old table for b in &(*old_table).entries[..] { @@ -300,15 +306,18 @@ unsafe fn grow_hashtable(num_threads: usize) { // Hash function for addresses #[cfg(target_pointer_width = "32")] +#[inline] fn hash(key: usize, bits: u32) -> usize { key.wrapping_mul(0x9E3779B9) >> (32 - bits) } #[cfg(target_pointer_width = "64")] +#[inline] fn hash(key: usize, bits: u32) -> usize { key.wrapping_mul(0x9E3779B97F4A7C15) >> (64 - bits) } // Lock the bucket for the given key +#[inline] unsafe fn lock_bucket<'a>(key: usize) -> &'a Bucket { let mut bucket; loop { @@ -322,7 +331,7 @@ unsafe fn lock_bucket<'a>(key: usize) -> &'a Bucket { // If no other thread has rehashed the table before we grabbed the lock // then we are good to go! The lock we grabbed prevents any rehashes. - if HASHTABLE.load(Ordering::Relaxed) == hashtable as usize { + if HASHTABLE.load(Ordering::Relaxed) == hashtable { return bucket; } @@ -333,6 +342,7 @@ unsafe fn lock_bucket<'a>(key: usize) -> &'a Bucket { // Lock the bucket for the given key, but check that the key hasn't been changed // in the meantime due to a requeue. +#[inline] unsafe fn lock_bucket_checked<'a>(key: &AtomicUsize) -> (usize, &'a Bucket) { let mut bucket; loop { @@ -348,7 +358,7 @@ unsafe fn lock_bucket_checked<'a>(key: &AtomicUsize) -> (usize, &'a Bucket) { // Check that both the hash table and key are correct while the bucket // is locked. Note that the key can't change once we locked the proper // bucket for it, so we just keep trying until we have the correct key. - if HASHTABLE.load(Ordering::Relaxed) == hashtable as usize + if HASHTABLE.load(Ordering::Relaxed) == hashtable && key.load(Ordering::Relaxed) == current_key { return (current_key, bucket); @@ -360,6 +370,7 @@ unsafe fn lock_bucket_checked<'a>(key: &AtomicUsize) -> (usize, &'a Bucket) { } // Lock the two buckets for the given pair of keys +#[inline] unsafe fn lock_bucket_pair<'a>(key1: usize, key2: usize) -> (&'a Bucket, &'a Bucket) { let mut bucket1; loop { @@ -379,7 +390,7 @@ unsafe fn lock_bucket_pair<'a>(key1: usize, key2: usize) -> (&'a Bucket, &'a Buc // If no other thread has rehashed the table before we grabbed the lock // then we are good to go! The lock we grabbed prevents any rehashes. - if HASHTABLE.load(Ordering::Relaxed) == hashtable as usize { + if HASHTABLE.load(Ordering::Relaxed) == hashtable { // Now lock the second bucket and return the two buckets if hash1 == hash2 { return (bucket1, bucket1); @@ -400,6 +411,7 @@ unsafe fn lock_bucket_pair<'a>(key1: usize, key2: usize) -> (&'a Bucket, &'a Buc } // Unlock a pair of buckets +#[inline] unsafe fn unlock_bucket_pair(bucket1: &Bucket, bucket2: &Bucket) { if bucket1 as *const _ == bucket2 as *const _ { bucket1.mutex.unlock(); @@ -427,6 +439,7 @@ pub enum ParkResult { impl ParkResult { /// Returns true if we were unparked by another thread. + #[inline] pub fn is_unparked(self) -> bool { if let ParkResult::Unparked(_) = self { true @@ -437,11 +450,14 @@ impl ParkResult { } /// Result of an unpark operation. -#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[derive(Copy, Clone, Default, Eq, PartialEq, Debug)] pub struct UnparkResult { /// The number of threads that were unparked. pub unparked_threads: usize, + /// The number of threads that were requeued. + pub requeued_threads: usize, + /// Whether there are any threads remaining in the queue. This only returns /// true if a thread was unparked. pub have_more_threads: bool, @@ -450,6 +466,9 @@ pub struct UnparkResult { /// should be used to switch to a fair unlocking mechanism for a particular /// unlock. pub be_fair: bool, + + /// Private field so new fields can be added without breakage. + _sealed: (), } /// Operation that `unpark_requeue` should perform. @@ -463,6 +482,12 @@ pub enum RequeueOp { /// Requeue all threads onto the target queue. RequeueAll, + + /// Unpark one thread and leave the rest parked. No requeuing is done. + UnparkOne, + + /// Requeue one thread and leave the rest parked on the original queue. + RequeueOne, } /// Operation that `unpark_filter` should perform for each thread. @@ -534,129 +559,109 @@ where B: FnOnce(), T: FnOnce(usize, bool), { - let mut v = Some(validate); - let mut b = Some(before_sleep); - let mut t = Some(timed_out); - park_internal( - key, - &mut || v.take().unchecked_unwrap()(), - &mut || b.take().unchecked_unwrap()(), - &mut |key, was_last_thread| t.take().unchecked_unwrap()(key, was_last_thread), - park_token, - timeout, - ) -} - -// Non-generic version to reduce monomorphization cost -unsafe fn park_internal( - key: usize, - validate: &mut FnMut() -> bool, - before_sleep: &mut FnMut(), - timed_out: &mut FnMut(usize, bool), - park_token: ParkToken, - timeout: Option, -) -> ParkResult { // Grab our thread data, this also ensures that the hash table exists - let mut thread_data = None; - let thread_data = get_thread_data(&mut thread_data); + with_thread_data(|thread_data| { + // Lock the bucket for the given key + let bucket = lock_bucket(key); - // Lock the bucket for the given key - let bucket = lock_bucket(key); - - // If the validation function fails, just return - if !validate() { - bucket.mutex.unlock(); - return ParkResult::Invalid; - } - - // Append our thread data to the queue and unlock the bucket - thread_data.parked_with_timeout.set(timeout.is_some()); - thread_data.next_in_queue.set(ptr::null()); - thread_data.key.store(key, Ordering::Relaxed); - thread_data.park_token.set(park_token); - thread_data.parker.prepare_park(); - if !bucket.queue_head.get().is_null() { - (*bucket.queue_tail.get()).next_in_queue.set(thread_data); - } else { - bucket.queue_head.set(thread_data); - } - bucket.queue_tail.set(thread_data); - bucket.mutex.unlock(); - - // Invoke the pre-sleep callback - before_sleep(); - - // Park our thread and determine whether we were woken up by an unpark or by - // our timeout. Note that this isn't precise: we can still be unparked since - // we are still in the queue. - let unparked = match timeout { - Some(timeout) => thread_data.parker.park_until(timeout), - None => { - thread_data.parker.park(); - // call deadlock detection on_unpark hook - deadlock::on_unpark(thread_data); - true + // If the validation function fails, just return + if !validate() { + bucket.mutex.unlock(); + return ParkResult::Invalid; } - }; - // If we were unparked, return now - if unparked { - return ParkResult::Unparked(thread_data.unpark_token.get()); - } - - // Lock our bucket again. Note that the hashtable may have been rehashed in - // the meantime. Our key may also have changed if we were requeued. - let (key, bucket) = lock_bucket_checked(&thread_data.key); - - // Now we need to check again if we were unparked or timed out. Unlike the - // last check this is precise because we hold the bucket lock. - if !thread_data.parker.timed_out() { - bucket.mutex.unlock(); - return ParkResult::Unparked(thread_data.unpark_token.get()); - } - - // We timed out, so we now need to remove our thread from the queue - let mut link = &bucket.queue_head; - let mut current = bucket.queue_head.get(); - let mut previous = ptr::null(); - while !current.is_null() { - if current == thread_data { - let next = (*current).next_in_queue.get(); - link.set(next); - let mut was_last_thread = true; - if bucket.queue_tail.get() == current { - bucket.queue_tail.set(previous); - } else { - // Scan the rest of the queue to see if there are any other - // entries with the given key. - let mut scan = next; - while !scan.is_null() { - if (*scan).key.load(Ordering::Relaxed) == key { - was_last_thread = false; - break; - } - scan = (*scan).next_in_queue.get(); - } - } - - // Callback to indicate that we timed out, and whether we were the - // last thread on the queue. - timed_out(key, was_last_thread); - break; + // Append our thread data to the queue and unlock the bucket + thread_data.parked_with_timeout.set(timeout.is_some()); + thread_data.next_in_queue.set(ptr::null()); + thread_data.key.store(key, Ordering::Relaxed); + thread_data.park_token.set(park_token); + thread_data.parker.prepare_park(); + if !bucket.queue_head.get().is_null() { + (*bucket.queue_tail.get()).next_in_queue.set(thread_data); } else { - link = &(*current).next_in_queue; - previous = current; - current = link.get(); + bucket.queue_head.set(thread_data); } - } + bucket.queue_tail.set(thread_data); + bucket.mutex.unlock(); - // There should be no way for our thread to have been removed from the queue - // if we timed out. - debug_assert!(!current.is_null()); + // Invoke the pre-sleep callback + before_sleep(); - // Unlock the bucket, we are done - bucket.mutex.unlock(); - ParkResult::TimedOut + // Park our thread and determine whether we were woken up by an unpark or by + // our timeout. Note that this isn't precise: we can still be unparked since + // we are still in the queue. + let unparked = match timeout { + Some(timeout) => thread_data.parker.park_until(timeout), + None => { + thread_data.parker.park(); + // call deadlock detection on_unpark hook + deadlock::on_unpark(thread_data); + true + } + }; + + // If we were unparked, return now + if unparked { + return ParkResult::Unparked(thread_data.unpark_token.get()); + } + + // Lock our bucket again. Note that the hashtable may have been rehashed in + // the meantime. Our key may also have changed if we were requeued. + let (key, bucket) = lock_bucket_checked(&thread_data.key); + + // Now we need to check again if we were unparked or timed out. Unlike the + // last check this is precise because we hold the bucket lock. + if !thread_data.parker.timed_out() { + bucket.mutex.unlock(); + return ParkResult::Unparked(thread_data.unpark_token.get()); + } + + // We timed out, so we now need to remove our thread from the queue + let mut link = &bucket.queue_head; + let mut current = bucket.queue_head.get(); + let mut previous = ptr::null(); + let mut was_last_thread = true; + while !current.is_null() { + if current == thread_data { + let next = (*current).next_in_queue.get(); + link.set(next); + if bucket.queue_tail.get() == current { + bucket.queue_tail.set(previous); + } else { + // Scan the rest of the queue to see if there are any other + // entries with the given key. + let mut scan = next; + while !scan.is_null() { + if (*scan).key.load(Ordering::Relaxed) == key { + was_last_thread = false; + break; + } + scan = (*scan).next_in_queue.get(); + } + } + + // Callback to indicate that we timed out, and whether we were the + // last thread on the queue. + timed_out(key, was_last_thread); + break; + } else { + if (*current).key.load(Ordering::Relaxed) == key { + was_last_thread = false; + } + link = &(*current).next_in_queue; + previous = current; + current = link.get(); + } + } + + // There should be no way for our thread to have been removed from the queue + // if we timed out. + debug_assert!(!current.is_null()); + + // Unlock the bucket, we are done + bucket.mutex.unlock(); + ParkResult::TimedOut + }) } /// Unparks one thread from the queue associated with the given key. @@ -683,15 +688,6 @@ pub unsafe fn unpark_one(key: usize, callback: C) -> UnparkResult where C: FnOnce(UnparkResult) -> UnparkToken, { - let mut c = Some(callback); - unpark_one_internal(key, &mut |result| c.take().unchecked_unwrap()(result)) -} - -// Non-generic version to reduce monomorphization cost -unsafe fn unpark_one_internal( - key: usize, - callback: &mut FnMut(UnparkResult) -> UnparkToken, -) -> UnparkResult { // Lock the bucket for the given key let bucket = lock_bucket(key); @@ -699,11 +695,7 @@ unsafe fn unpark_one_internal( let mut link = &bucket.queue_head; let mut current = bucket.queue_head.get(); let mut previous = ptr::null(); - let mut result = UnparkResult { - unparked_threads: 0, - have_more_threads: false, - be_fair: false, - }; + let mut result = UnparkResult::default(); while !current.is_null() { if (*current).key.load(Ordering::Relaxed) == key { // Remove the thread from the queue @@ -766,6 +758,7 @@ unsafe fn unpark_one_internal( /// You should only call this function with an address that you control, since /// you could otherwise interfere with the operation of other synchronization /// primitives. +#[inline] pub unsafe fn unpark_all(key: usize, unpark_token: UnparkToken) -> usize { // Lock the bucket for the given key let bucket = lock_bucket(key); @@ -816,11 +809,10 @@ pub unsafe fn unpark_all(key: usize, unpark_token: UnparkToken) -> usize { /// unparks the first one and requeues the rest onto the queue associated with /// `key_to`. /// -/// The `validate` function is called while both queues are locked and can abort -/// the operation by returning `RequeueOp::Abort`. It can also choose to -/// unpark the first thread in the source queue while moving the rest by -/// returning `RequeueOp::UnparkFirstRequeueRest`. Returning -/// `RequeueOp::RequeueAll` will move all threads to the destination queue. +/// The `validate` function is called while both queues are locked. Its return +/// value will determine which operation is performed, or whether the operation +/// should be aborted. See `RequeueOp` for details about the different possible +/// return values. /// /// The `callback` function is also called while both queues are locked. It is /// passed the `RequeueOp` returned by `validate` and an `UnparkResult` @@ -851,32 +843,11 @@ where V: FnOnce() -> RequeueOp, C: FnOnce(RequeueOp, UnparkResult) -> UnparkToken, { - let mut v = Some(validate); - let mut c = Some(callback); - unpark_requeue_internal( - key_from, - key_to, - &mut || v.take().unchecked_unwrap()(), - &mut |op, r| c.take().unchecked_unwrap()(op, r), - ) -} - -// Non-generic version to reduce monomorphization cost -unsafe fn unpark_requeue_internal( - key_from: usize, - key_to: usize, - validate: &mut FnMut() -> RequeueOp, - callback: &mut FnMut(RequeueOp, UnparkResult) -> UnparkToken, -) -> UnparkResult { // Lock the two buckets for the given key let (bucket_from, bucket_to) = lock_bucket_pair(key_from, key_to); // If the validation function fails, just return - let mut result = UnparkResult { - unparked_threads: 0, - have_more_threads: false, - be_fair: false, - }; + let mut result = UnparkResult::default(); let op = validate(); if op == RequeueOp::Abort { unlock_bucket_pair(bucket_from, bucket_to); @@ -900,7 +871,9 @@ unsafe fn unpark_requeue_internal( } // Prepare the first thread for wakeup and requeue the rest. - if op == RequeueOp::UnparkOneRequeueRest && wakeup_thread.is_none() { + if (op == RequeueOp::UnparkOneRequeueRest || op == RequeueOp::UnparkOne) + && wakeup_thread.is_none() + { wakeup_thread = Some(current); result.unparked_threads = 1; } else { @@ -911,7 +884,20 @@ unsafe fn unpark_requeue_internal( } requeue_threads_tail = current; (*current).key.store(key_to, Ordering::Relaxed); - result.have_more_threads = true; + result.requeued_threads += 1; + } + if op == RequeueOp::UnparkOne || op == RequeueOp::RequeueOne { + // Scan the rest of the queue to see if there are any other + // entries with the given key. + let mut scan = next; + while !scan.is_null() { + if (*scan).key.load(Ordering::Relaxed) == key_from { + result.have_more_threads = true; + break; + } + scan = (*scan).next_in_queue.get(); + } + break; } current = next; } else { @@ -985,16 +971,6 @@ where F: FnMut(ParkToken) -> FilterOp, C: FnOnce(UnparkResult) -> UnparkToken, { - let mut c = Some(callback); - unpark_filter_internal(key, &mut filter, &mut |r| c.take().unchecked_unwrap()(r)) -} - -// Non-generic version to reduce monomorphization cost -unsafe fn unpark_filter_internal( - key: usize, - filter: &mut FnMut(ParkToken) -> FilterOp, - callback: &mut FnMut(UnparkResult) -> UnparkToken, -) -> UnparkResult { // Lock the bucket for the given key let bucket = lock_bucket(key); @@ -1003,11 +979,7 @@ unsafe fn unpark_filter_internal( let mut current = bucket.queue_head.get(); let mut previous = ptr::null(); let mut threads = SmallVec::<[_; 8]>::new(); - let mut result = UnparkResult { - unparked_threads: 0, - have_more_threads: false, - be_fair: false, - }; + let mut result = UnparkResult::default(); while !current.is_null() { if (*current).key.load(Ordering::Relaxed) == key { // Call the filter function with the thread's ParkToken @@ -1068,7 +1040,7 @@ unsafe fn unpark_filter_internal( result } -/// [Experimental] Deadlock detection +/// \[Experimental\] Deadlock detection /// /// Enabled via the `deadlock_detection` feature flag. pub mod deadlock { @@ -1078,16 +1050,6 @@ pub mod deadlock { #[cfg(feature = "deadlock_detection")] pub(super) use super::deadlock_impl::DeadlockData; - #[cfg(not(feature = "deadlock_detection"))] - pub(super) struct DeadlockData {} - - #[cfg(not(feature = "deadlock_detection"))] - impl DeadlockData { - pub(super) fn new() -> Self { - DeadlockData {} - } - } - /// Acquire a resource identified by key in the deadlock detector /// Noop if deadlock_detection feature isn't enabled. /// Note: Call after the resource is acquired @@ -1125,15 +1087,16 @@ pub mod deadlock { #[cfg(feature = "deadlock_detection")] mod deadlock_impl { - use super::{get_hashtable, get_thread_data, lock_bucket, ThreadData, NUM_THREADS}; - use std::cell::{Cell, UnsafeCell}; - use std::sync::mpsc; - use std::sync::atomic::Ordering; - use std::collections::HashSet; - use thread_id; + use super::{get_hashtable, lock_bucket, with_thread_data, ThreadData, NUM_THREADS}; + use crate::word_lock::WordLock; use backtrace::Backtrace; use petgraph; use petgraph::graphmap::DiGraphMap; + use std::cell::{Cell, UnsafeCell}; + use std::collections::HashSet; + use std::sync::atomic::Ordering; + use std::sync::mpsc; + use thread_id; /// Representation of a deadlocked thread pub struct DeadlockedThread { @@ -1198,19 +1161,19 @@ mod deadlock_impl { } pub unsafe fn acquire_resource(key: usize) { - let mut thread_data = None; - let thread_data = get_thread_data(&mut thread_data); - (*thread_data.deadlock_data.resources.get()).push(key); + with_thread_data(|thread_data| { + (*thread_data.deadlock_data.resources.get()).push(key); + }); } pub unsafe fn release_resource(key: usize) { - let mut thread_data = None; - let thread_data = get_thread_data(&mut thread_data); - let resources = &mut (*thread_data.deadlock_data.resources.get()); - match resources.iter().rposition(|x| *x == key) { - Some(p) => resources.swap_remove(p), - None => panic!("key {} not found in thread resources", key), - }; + with_thread_data(|thread_data| { + let resources = &mut (*thread_data.deadlock_data.resources.get()); + match resources.iter().rposition(|x| *x == key) { + Some(p) => resources.swap_remove(p), + None => panic!("key {} not found in thread resources", key), + }; + }); } pub fn check_deadlock() -> Vec> { @@ -1263,10 +1226,13 @@ mod deadlock_impl { use self::WaitGraphNode::*; - // Contrary to the _fast variant this locks the entrie table before looking for cycles. + // Contrary to the _fast variant this locks the entries table before looking for cycles. // Returns all detected thread wait cycles. // Note that once a cycle is reported it's never reported again. unsafe fn check_wait_graph_slow() -> Vec> { + static DEADLOCK_DETECTION_LOCK: WordLock = WordLock::INIT; + DEADLOCK_DETECTION_LOCK.lock(); + let mut table = get_hashtable(); loop { // Lock all buckets in the old table @@ -1340,6 +1306,8 @@ mod deadlock_impl { results.push(receiver.iter().collect()); } + DEADLOCK_DETECTION_LOCK.unlock(); + results } @@ -1362,14 +1330,15 @@ mod deadlock_impl { // returns all thread cycles in the wait graph fn graph_cycles(g: &DiGraphMap) -> Vec> { - use petgraph::visit::NodeIndexable; use petgraph::visit::depth_first_search; use petgraph::visit::DfsEvent; + use petgraph::visit::NodeIndexable; let mut cycles = HashSet::new(); let mut path = Vec::with_capacity(g.node_bound()); // start from threads to get the correct threads cycle - let threads = g.nodes() + let threads = g + .nodes() .filter(|n| if let &Thread(_) = n { true } else { false }); depth_first_search(g, threads, |e| match e { diff --git a/third_party/rust/parking_lot_core/src/spinwait.rs b/third_party/rust/parking_lot_core/src/spinwait.rs index 38128c5844d3..ad0327a3ae37 100644 --- a/third_party/rust/parking_lot_core/src/spinwait.rs +++ b/third_party/rust/parking_lot_core/src/spinwait.rs @@ -5,54 +5,9 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. -#[cfg(unix)] -use libc; -#[cfg(windows)] -use winapi; -#[cfg(not(any(windows, unix)))] -use std::thread; +use crate::thread_parker; use std::sync::atomic::spin_loop_hint; -// Yields the rest of the current timeslice to the OS -#[cfg(windows)] -#[inline] -fn thread_yield() { - // Note that this is manually defined here rather than using the definition - // through `winapi`. The `winapi` definition comes from the `synchapi` - // header which enables the "synchronization.lib" library. It turns out, - // however that `Sleep` comes from `kernel32.dll` so this activation isn't - // necessary. - // - // This was originally identified in rust-lang/rust where on MinGW the - // libsynchronization.a library pulls in a dependency on a newer DLL not - // present in older versions of Windows. (see rust-lang/rust#49438) - // - // This is a bit of a hack for now and ideally we'd fix MinGW's own import - // libraries, but that'll probably take a lot longer than patching this here - // and avoiding the `synchapi` feature entirely. - extern "system" { - fn Sleep(a: winapi::shared::minwindef::DWORD); - } - unsafe { - // We don't use SwitchToThread here because it doesn't consider all - // threads in the system and the thread we are waiting for may not get - // selected. - Sleep(0); - } -} -#[cfg(unix)] -#[inline] -fn thread_yield() { - unsafe { - libc::sched_yield(); - } -} -#[cfg(not(any(windows, unix)))] -#[inline] -fn thread_yield() { - thread::yield_now(); -} - // Wastes some CPU time for the given number of iterations, // using a hint to indicate to the CPU that we are spinning. #[inline] @@ -63,6 +18,7 @@ fn cpu_relax(iterations: u32) { } /// A counter used to perform exponential backoff in spin loops. +#[derive(Default)] pub struct SpinWait { counter: u32, } @@ -70,8 +26,8 @@ pub struct SpinWait { impl SpinWait { /// Creates a new `SpinWait`. #[inline] - pub fn new() -> SpinWait { - SpinWait { counter: 0 } + pub fn new() -> Self { + Self::default() } /// Resets a `SpinWait` to its initial state. @@ -90,14 +46,14 @@ impl SpinWait { /// to yielding the CPU to the OS after a few iterations. #[inline] pub fn spin(&mut self) -> bool { - if self.counter >= 20 { + if self.counter >= 10 { return false; } self.counter += 1; - if self.counter <= 10 { - cpu_relax(4 << self.counter); + if self.counter <= 3 { + cpu_relax(1 << self.counter); } else { - thread_yield(); + thread_parker::thread_yield(); } true } @@ -113,13 +69,6 @@ impl SpinWait { if self.counter > 10 { self.counter = 10; } - cpu_relax(4 << self.counter); - } -} - -impl Default for SpinWait { - #[inline] - fn default() -> SpinWait { - SpinWait::new() + cpu_relax(1 << self.counter); } } diff --git a/third_party/rust/parking_lot_core/src/thread_parker/cloudabi.rs b/third_party/rust/parking_lot_core/src/thread_parker/cloudabi.rs new file mode 100644 index 000000000000..d6d51438eef1 --- /dev/null +++ b/third_party/rust/parking_lot_core/src/thread_parker/cloudabi.rs @@ -0,0 +1,325 @@ +// Copyright 2016 Amanieu d'Antras +// +// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use cloudabi as abi; +use core::{ + cell::Cell, + mem, + sync::atomic::{AtomicU32, Ordering}, +}; +use std::{convert::TryFrom, thread, time::Instant}; + +extern "C" { + #[thread_local] + static __pthread_thread_id: abi::tid; +} + +struct Lock { + lock: AtomicU32, +} + +impl Lock { + #[inline] + pub fn new() -> Self { + Lock { + lock: AtomicU32::new(abi::LOCK_UNLOCKED.0), + } + } + + #[inline] + fn try_lock(&self) -> Option> { + // Attempt to acquire the lock. + if let Err(old) = self.lock.compare_exchange( + abi::LOCK_UNLOCKED.0, + unsafe { __pthread_thread_id.0 } | abi::LOCK_WRLOCKED.0, + Ordering::Acquire, + Ordering::Relaxed, + ) { + // Failure. Crash upon recursive acquisition. + debug_assert_ne!( + old & !abi::LOCK_KERNEL_MANAGED.0, + unsafe { __pthread_thread_id.0 } | abi::LOCK_WRLOCKED.0, + "Attempted to recursive write-lock a lock", + ); + None + } else { + Some(LockGuard { inner: &self }) + } + } + + #[inline] + pub fn lock(&self) -> LockGuard<'_> { + self.try_lock().unwrap_or_else(|| { + // Call into the kernel to acquire a write lock. + unsafe { + let subscription = abi::subscription { + type_: abi::eventtype::LOCK_WRLOCK, + union: abi::subscription_union { + lock: abi::subscription_lock { + lock: self.ptr(), + lock_scope: abi::scope::PRIVATE, + }, + }, + ..mem::zeroed() + }; + let mut event: abi::event = mem::uninitialized(); + let mut nevents: usize = mem::uninitialized(); + let ret = abi::poll(&subscription, &mut event, 1, &mut nevents); + debug_assert_eq!(ret, abi::errno::SUCCESS); + debug_assert_eq!(event.error, abi::errno::SUCCESS); + } + LockGuard { inner: &self } + }) + } + + #[inline] + fn ptr(&self) -> *mut abi::lock { + &self.lock as *const AtomicU32 as *mut abi::lock + } +} + +struct LockGuard<'a> { + inner: &'a Lock, +} + +impl LockGuard<'_> { + #[inline] + fn ptr(&self) -> *mut abi::lock { + &self.inner.lock as *const AtomicU32 as *mut abi::lock + } +} + +impl Drop for LockGuard<'_> { + fn drop(&mut self) { + debug_assert_eq!( + self.inner.lock.load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0, + unsafe { __pthread_thread_id.0 } | abi::LOCK_WRLOCKED.0, + "This lock is not write-locked by this thread" + ); + + if !self + .inner + .lock + .compare_exchange( + unsafe { __pthread_thread_id.0 } | abi::LOCK_WRLOCKED.0, + abi::LOCK_UNLOCKED.0, + Ordering::Release, + Ordering::Relaxed, + ) + .is_ok() + { + // Lock is managed by kernelspace. Call into the kernel + // to unblock waiting threads. + let ret = unsafe { abi::lock_unlock(self.ptr(), abi::scope::PRIVATE) }; + debug_assert_eq!(ret, abi::errno::SUCCESS); + } + } +} + +struct Condvar { + condvar: AtomicU32, +} + +impl Condvar { + #[inline] + pub fn new() -> Self { + Condvar { + condvar: AtomicU32::new(abi::CONDVAR_HAS_NO_WAITERS.0), + } + } + + #[inline] + pub fn wait(&self, lock: &LockGuard<'_>) { + unsafe { + let subscription = abi::subscription { + type_: abi::eventtype::CONDVAR, + union: abi::subscription_union { + condvar: abi::subscription_condvar { + condvar: self.ptr(), + condvar_scope: abi::scope::PRIVATE, + lock: lock.ptr(), + lock_scope: abi::scope::PRIVATE, + }, + }, + ..mem::zeroed() + }; + let mut event: abi::event = mem::uninitialized(); + let mut nevents: usize = mem::uninitialized(); + + let ret = abi::poll(&subscription, &mut event, 1, &mut nevents); + debug_assert_eq!(ret, abi::errno::SUCCESS); + debug_assert_eq!(event.error, abi::errno::SUCCESS); + } + } + + /// Waits for a signal on the condvar. + /// Returns false if it times out before anyone notified us. + #[inline] + pub fn wait_timeout(&self, lock: &LockGuard<'_>, timeout: abi::timestamp) -> bool { + unsafe { + let subscriptions = [ + abi::subscription { + type_: abi::eventtype::CONDVAR, + union: abi::subscription_union { + condvar: abi::subscription_condvar { + condvar: self.ptr(), + condvar_scope: abi::scope::PRIVATE, + lock: lock.ptr(), + lock_scope: abi::scope::PRIVATE, + }, + }, + ..mem::zeroed() + }, + abi::subscription { + type_: abi::eventtype::CLOCK, + union: abi::subscription_union { + clock: abi::subscription_clock { + clock_id: abi::clockid::MONOTONIC, + timeout, + ..mem::zeroed() + }, + }, + ..mem::zeroed() + }, + ]; + let mut events: [abi::event; 2] = mem::uninitialized(); + let mut nevents: usize = mem::uninitialized(); + + let ret = abi::poll(subscriptions.as_ptr(), events.as_mut_ptr(), 2, &mut nevents); + debug_assert_eq!(ret, abi::errno::SUCCESS); + for i in 0..nevents { + debug_assert_eq!(events[i].error, abi::errno::SUCCESS); + if events[i].type_ == abi::eventtype::CONDVAR { + return true; + } + } + } + false + } + + #[inline] + pub fn notify(&self) { + let ret = unsafe { abi::condvar_signal(self.ptr(), abi::scope::PRIVATE, 1) }; + debug_assert_eq!(ret, abi::errno::SUCCESS); + } + + #[inline] + fn ptr(&self) -> *mut abi::condvar { + &self.condvar as *const AtomicU32 as *mut abi::condvar + } +} + +// Helper type for putting a thread to sleep until some other thread wakes it up +pub struct ThreadParker { + should_park: Cell, + lock: Lock, + condvar: Condvar, +} + +impl ThreadParker { + pub const IS_CHEAP_TO_CONSTRUCT: bool = true; + + #[inline] + pub fn new() -> ThreadParker { + ThreadParker { + should_park: Cell::new(false), + lock: Lock::new(), + condvar: Condvar::new(), + } + } + + // Prepares the parker. This should be called before adding it to the queue. + #[inline] + pub fn prepare_park(&self) { + self.should_park.set(true); + } + + // Checks if the park timed out. This should be called while holding the + // queue lock after park_until has returned false. + #[inline] + pub fn timed_out(&self) -> bool { + // We need to grab the lock here because another thread may be + // concurrently executing UnparkHandle::unpark, which is done without + // holding the queue lock. + let _guard = self.lock.lock(); + self.should_park.get() + } + + // Parks the thread until it is unparked. This should be called after it has + // been added to the queue, after unlocking the queue. + #[inline] + pub fn park(&self) { + let guard = self.lock.lock(); + while self.should_park.get() { + self.condvar.wait(&guard); + } + } + + // Parks the thread until it is unparked or the timeout is reached. This + // should be called after it has been added to the queue, after unlocking + // the queue. Returns true if we were unparked and false if we timed out. + #[inline] + pub fn park_until(&self, timeout: Instant) -> bool { + let guard = self.lock.lock(); + while self.should_park.get() { + if let Some(duration_left) = timeout.checked_duration_since(Instant::now()) { + if let Ok(nanos_left) = abi::timestamp::try_from(duration_left.as_nanos()) { + self.condvar.wait_timeout(&guard, nanos_left); + } else { + // remaining timeout overflows an abi::timestamp. Sleep indefinitely + self.condvar.wait(&guard); + } + } else { + // We timed out + return false; + } + } + true + } + + // Locks the parker to prevent the target thread from exiting. This is + // necessary to ensure that thread-local ThreadData objects remain valid. + // This should be called while holding the queue lock. + #[inline] + pub fn unpark_lock(&self) -> UnparkHandle<'_> { + let _lock_guard = self.lock.lock(); + + UnparkHandle { + thread_parker: self, + _lock_guard, + } + } +} + +// Handle for a thread that is about to be unparked. We need to mark the thread +// as unparked while holding the queue lock, but we delay the actual unparking +// until after the queue lock is released. +pub struct UnparkHandle<'a> { + thread_parker: *const ThreadParker, + _lock_guard: LockGuard<'a>, +} + +impl UnparkHandle<'_> { + // Wakes up the parked thread. This should be called after the queue lock is + // released to avoid blocking the queue for too long. + #[inline] + pub fn unpark(self) { + unsafe { + (*self.thread_parker).should_park.set(false); + + // We notify while holding the lock here to avoid races with the target + // thread. In particular, the thread could exit after we unlock the + // mutex, which would make the condvar access invalid memory. + (*self.thread_parker).condvar.notify(); + } + } +} + +#[inline] +pub fn thread_yield() { + thread::yield_now(); +} diff --git a/third_party/rust/parking_lot_core/src/thread_parker/generic.rs b/third_party/rust/parking_lot_core/src/thread_parker/generic.rs index f26d4561ddd5..bcfc86dfcb6b 100644 --- a/third_party/rust/parking_lot_core/src/thread_parker/generic.rs +++ b/third_party/rust/parking_lot_core/src/thread_parker/generic.rs @@ -5,62 +5,59 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. -use std::sync::{Condvar, Mutex, MutexGuard}; -use std::cell::Cell; -use std::time::Instant; +//! A simple spin lock based thread parker. Used on platforms without better +//! parking facilities available. + +use core::sync::atomic::{spin_loop_hint, AtomicBool, Ordering}; +use std::{thread, time::Instant}; // Helper type for putting a thread to sleep until some other thread wakes it up pub struct ThreadParker { - should_park: Cell, - mutex: Mutex<()>, - condvar: Condvar, + parked: AtomicBool, } impl ThreadParker { + pub const IS_CHEAP_TO_CONSTRUCT: bool = true; + + #[inline] pub fn new() -> ThreadParker { ThreadParker { - should_park: Cell::new(false), - mutex: Mutex::new(()), - condvar: Condvar::new(), + parked: AtomicBool::new(false), } } // Prepares the parker. This should be called before adding it to the queue. - pub unsafe fn prepare_park(&self) { - self.should_park.set(true); + #[inline] + pub fn prepare_park(&self) { + self.parked.store(true, Ordering::Relaxed); } // Checks if the park timed out. This should be called while holding the // queue lock after park_until has returned false. - pub unsafe fn timed_out(&self) -> bool { - // We need to grab the mutex here because another thread may be - // concurrently executing UnparkHandle::unpark, which is done without - // holding the queue lock. - let _lock = self.mutex.lock().unwrap(); - self.should_park.get() + #[inline] + pub fn timed_out(&self) -> bool { + self.parked.load(Ordering::Relaxed) != false } // Parks the thread until it is unparked. This should be called after it has // been added to the queue, after unlocking the queue. - pub unsafe fn park(&self) { - let mut lock = self.mutex.lock().unwrap(); - while self.should_park.get() { - lock = self.condvar.wait(lock).unwrap(); + #[inline] + pub fn park(&self) { + while self.parked.load(Ordering::Acquire) != false { + spin_loop_hint(); } } // Parks the thread until it is unparked or the timeout is reached. This // should be called after it has been added to the queue, after unlocking // the queue. Returns true if we were unparked and false if we timed out. - pub unsafe fn park_until(&self, timeout: Instant) -> bool { - let mut lock = self.mutex.lock().unwrap(); - while self.should_park.get() { - let now = Instant::now(); - if timeout <= now { + #[inline] + pub fn park_until(&self, timeout: Instant) -> bool { + while self.parked.load(Ordering::Acquire) != false { + if Instant::now() >= timeout { return false; } - let (new_lock, _) = self.condvar.wait_timeout(lock, timeout - now).unwrap(); - lock = new_lock; + spin_loop_hint(); } true } @@ -68,31 +65,27 @@ impl ThreadParker { // Locks the parker to prevent the target thread from exiting. This is // necessary to ensure that thread-local ThreadData objects remain valid. // This should be called while holding the queue lock. - pub unsafe fn unpark_lock(&self) -> UnparkHandle { - UnparkHandle { - thread_parker: self, - _guard: self.mutex.lock().unwrap(), - } + #[inline] + pub fn unpark_lock(&self) -> UnparkHandle { + // We don't need to lock anything, just clear the state + self.parked.store(false, Ordering::Release); + UnparkHandle(()) } } // Handle for a thread that is about to be unparked. We need to mark the thread // as unparked while holding the queue lock, but we delay the actual unparking // until after the queue lock is released. -pub struct UnparkHandle<'a> { - thread_parker: *const ThreadParker, - _guard: MutexGuard<'a, ()>, -} +pub struct UnparkHandle(()); -impl<'a> UnparkHandle<'a> { +impl UnparkHandle { // Wakes up the parked thread. This should be called after the queue lock is // released to avoid blocking the queue for too long. - pub unsafe fn unpark(self) { - (*self.thread_parker).should_park.set(false); - - // We notify while holding the lock here to avoid races with the target - // thread. In particular, the thread could exit after we unlock the - // mutex, which would make the condvar access invalid memory. - (*self.thread_parker).condvar.notify_one(); - } + #[inline] + pub fn unpark(self) {} +} + +#[inline] +pub fn thread_yield() { + thread::yield_now(); } diff --git a/third_party/rust/parking_lot_core/src/thread_parker/linux.rs b/third_party/rust/parking_lot_core/src/thread_parker/linux.rs index aee380dd3ee2..319d04ea4e51 100644 --- a/third_party/rust/parking_lot_core/src/thread_parker/linux.rs +++ b/third_party/rust/parking_lot_core/src/thread_parker/linux.rs @@ -5,9 +5,12 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. -use std::sync::atomic::{AtomicI32, Ordering}; -use std::time::Instant; +use core::{ + ptr, + sync::atomic::{AtomicI32, Ordering}, +}; use libc; +use std::{thread, time::Instant}; const FUTEX_WAIT: i32 = 0; const FUTEX_WAKE: i32 = 1; @@ -28,6 +31,9 @@ pub struct ThreadParker { } impl ThreadParker { + pub const IS_CHEAP_TO_CONSTRUCT: bool = true; + + #[inline] pub fn new() -> ThreadParker { ThreadParker { futex: AtomicI32::new(0), @@ -35,41 +41,32 @@ impl ThreadParker { } // Prepares the parker. This should be called before adding it to the queue. - pub unsafe fn prepare_park(&self) { + #[inline] + pub fn prepare_park(&self) { self.futex.store(1, Ordering::Relaxed); } // Checks if the park timed out. This should be called while holding the // queue lock after park_until has returned false. - pub unsafe fn timed_out(&self) -> bool { + #[inline] + pub fn timed_out(&self) -> bool { self.futex.load(Ordering::Relaxed) != 0 } // Parks the thread until it is unparked. This should be called after it has // been added to the queue, after unlocking the queue. - pub unsafe fn park(&self) { + #[inline] + pub fn park(&self) { while self.futex.load(Ordering::Acquire) != 0 { - let r = libc::syscall( - libc::SYS_futex, - &self.futex, - FUTEX_WAIT | FUTEX_PRIVATE, - 1, - 0, - ); - debug_assert!(r == 0 || r == -1); - if r == -1 { - debug_assert!( - *libc::__errno_location() == libc::EINTR - || *libc::__errno_location() == libc::EAGAIN - ); - } + self.futex_wait(None); } } // Parks the thread until it is unparked or the timeout is reached. This // should be called after it has been added to the queue, after unlocking // the queue. Returns true if we were unparked and false if we timed out. - pub unsafe fn park_until(&self, timeout: Instant) -> bool { + #[inline] + pub fn park_until(&self, timeout: Instant) -> bool { while self.futex.load(Ordering::Acquire) != 0 { let now = Instant::now(); if timeout <= now { @@ -85,29 +82,43 @@ impl ThreadParker { tv_sec: diff.as_secs() as libc::time_t, tv_nsec: diff.subsec_nanos() as tv_nsec_t, }; - let r = libc::syscall( + self.futex_wait(Some(ts)); + } + true + } + + #[inline] + fn futex_wait(&self, ts: Option) { + let ts_ptr = ts + .as_ref() + .map(|ts_ref| ts_ref as *const _) + .unwrap_or(ptr::null()); + let r = unsafe { + libc::syscall( libc::SYS_futex, &self.futex, FUTEX_WAIT | FUTEX_PRIVATE, 1, - &ts, - ); - debug_assert!(r == 0 || r == -1); - if r == -1 { + ts_ptr, + ) + }; + debug_assert!(r == 0 || r == -1); + if r == -1 { + unsafe { debug_assert!( *libc::__errno_location() == libc::EINTR || *libc::__errno_location() == libc::EAGAIN - || *libc::__errno_location() == libc::ETIMEDOUT + || (ts.is_some() && *libc::__errno_location() == libc::ETIMEDOUT) ); } } - true } // Locks the parker to prevent the target thread from exiting. This is // necessary to ensure that thread-local ThreadData objects remain valid. // This should be called while holding the queue lock. - pub unsafe fn unpark_lock(&self) -> UnparkHandle { + #[inline] + pub fn unpark_lock(&self) -> UnparkHandle { // We don't need to lock anything, just clear the state self.futex.store(0, Ordering::Release); @@ -125,13 +136,20 @@ pub struct UnparkHandle { impl UnparkHandle { // Wakes up the parked thread. This should be called after the queue lock is // released to avoid blocking the queue for too long. - pub unsafe fn unpark(self) { + #[inline] + pub fn unpark(self) { // The thread data may have been freed at this point, but it doesn't // matter since the syscall will just return EFAULT in that case. - let r = libc::syscall(libc::SYS_futex, self.futex, FUTEX_WAKE | FUTEX_PRIVATE, 1); + let r = + unsafe { libc::syscall(libc::SYS_futex, self.futex, FUTEX_WAKE | FUTEX_PRIVATE, 1) }; debug_assert!(r == 0 || r == 1 || r == -1); if r == -1 { - debug_assert_eq!(*libc::__errno_location(), libc::EFAULT); + debug_assert_eq!(unsafe { *libc::__errno_location() }, libc::EFAULT); } } } + +#[inline] +pub fn thread_yield() { + thread::yield_now(); +} diff --git a/third_party/rust/parking_lot_core/src/thread_parker/redox.rs b/third_party/rust/parking_lot_core/src/thread_parker/redox.rs new file mode 100644 index 000000000000..1c631a1fda88 --- /dev/null +++ b/third_party/rust/parking_lot_core/src/thread_parker/redox.rs @@ -0,0 +1,150 @@ +// Copyright 2016 Amanieu d'Antras +// +// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use core::{ + ptr, + sync::atomic::{AtomicI32, Ordering}, +}; +use std::{thread, time::Instant}; +use syscall::{ + call::futex, + data::TimeSpec, + error::{Error, EAGAIN, EFAULT, EINTR, ETIMEDOUT}, + flag::{FUTEX_WAIT, FUTEX_WAKE}, +}; + +const UNPARKED: i32 = 0; +const PARKED: i32 = 1; + +// Helper type for putting a thread to sleep until some other thread wakes it up +pub struct ThreadParker { + futex: AtomicI32, +} + +impl ThreadParker { + pub const IS_CHEAP_TO_CONSTRUCT: bool = true; + + #[inline] + pub fn new() -> ThreadParker { + ThreadParker { + futex: AtomicI32::new(UNPARKED), + } + } + + // Prepares the parker. This should be called before adding it to the queue. + #[inline] + pub fn prepare_park(&self) { + self.futex.store(PARKED, Ordering::Relaxed); + } + + // Checks if the park timed out. This should be called while holding the + // queue lock after park_until has returned false. + #[inline] + pub fn timed_out(&self) -> bool { + self.futex.load(Ordering::Relaxed) != UNPARKED + } + + // Parks the thread until it is unparked. This should be called after it has + // been added to the queue, after unlocking the queue. + #[inline] + pub fn park(&self) { + while self.futex.load(Ordering::Acquire) != UNPARKED { + self.futex_wait(None); + } + } + + // Parks the thread until it is unparked or the timeout is reached. This + // should be called after it has been added to the queue, after unlocking + // the queue. Returns true if we were unparked and false if we timed out. + #[inline] + pub fn park_until(&self, timeout: Instant) -> bool { + while self.futex.load(Ordering::Acquire) != UNPARKED { + let now = Instant::now(); + if timeout <= now { + return false; + } + let diff = timeout - now; + if diff.as_secs() > i64::max_value() as u64 { + // Timeout overflowed, just sleep indefinitely + self.park(); + return true; + } + let ts = TimeSpec { + tv_sec: diff.as_secs() as i64, + tv_nsec: diff.subsec_nanos() as i32, + }; + self.futex_wait(Some(ts)); + } + true + } + + #[inline] + fn futex_wait(&self, ts: Option) { + let ts_ptr = ts + .as_ref() + .map(|ts_ref| ts_ref as *const _) + .unwrap_or(ptr::null()); + let r = unsafe { + futex( + self.ptr(), + FUTEX_WAIT, + PARKED, + ts_ptr as usize, + ptr::null_mut(), + ) + }; + match r { + Ok(r) => debug_assert_eq!(r, 0), + Err(Error { errno }) => { + debug_assert!(errno == EINTR || errno == EAGAIN || errno == ETIMEDOUT); + } + } + } + + // Locks the parker to prevent the target thread from exiting. This is + // necessary to ensure that thread-local ThreadData objects remain valid. + // This should be called while holding the queue lock. + #[inline] + pub fn unpark_lock(&self) -> UnparkHandle { + // We don't need to lock anything, just clear the state + self.futex.store(UNPARKED, Ordering::Release); + + UnparkHandle { futex: self.ptr() } + } + + #[inline] + fn ptr(&self) -> *mut i32 { + &self.futex as *const AtomicI32 as *mut i32 + } +} + +// Handle for a thread that is about to be unparked. We need to mark the thread +// as unparked while holding the queue lock, but we delay the actual unparking +// until after the queue lock is released. +pub struct UnparkHandle { + futex: *mut i32, +} + +impl UnparkHandle { + // Wakes up the parked thread. This should be called after the queue lock is + // released to avoid blocking the queue for too long. + #[inline] + pub fn unpark(self) { + // The thread data may have been freed at this point, but it doesn't + // matter since the syscall will just return EFAULT in that case. + let r = unsafe { futex(self.futex, FUTEX_WAKE, PARKED, 0, ptr::null_mut()) }; + match r { + Ok(num_woken) => debug_assert!(num_woken == 0 || num_woken == 1), + Err(Error { errno }) => debug_assert_eq!(errno, EFAULT), + } + } +} + +#[inline] +pub fn thread_yield() { + thread::yield_now(); +} diff --git a/third_party/rust/parking_lot_core/src/thread_parker/sgx.rs b/third_party/rust/parking_lot_core/src/thread_parker/sgx.rs new file mode 100644 index 000000000000..c5ef6cbeabeb --- /dev/null +++ b/third_party/rust/parking_lot_core/src/thread_parker/sgx.rs @@ -0,0 +1,108 @@ +// Copyright 2016 Amanieu d'Antras +// +// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use core::sync::atomic::{AtomicBool, Ordering}; +use std::{ + io, + os::fortanix_sgx::{ + thread::current as current_tcs, + usercalls::{ + self, + raw::{Tcs, EV_UNPARK, WAIT_INDEFINITE}, + }, + }, + thread, + time::Instant, +}; + +// Helper type for putting a thread to sleep until some other thread wakes it up +pub struct ThreadParker { + parked: AtomicBool, + tcs: Tcs, +} + +impl ThreadParker { + pub const IS_CHEAP_TO_CONSTRUCT: bool = true; + + #[inline] + pub fn new() -> ThreadParker { + ThreadParker { + parked: AtomicBool::new(false), + tcs: current_tcs(), + } + } + + // Prepares the parker. This should be called before adding it to the queue. + #[inline] + pub fn prepare_park(&self) { + self.parked.store(true, Ordering::Relaxed); + } + + // Checks if the park timed out. This should be called while holding the + // queue lock after park_until has returned false. + #[inline] + pub fn timed_out(&self) -> bool { + self.parked.load(Ordering::Relaxed) + } + + // Parks the thread until it is unparked. This should be called after it has + // been added to the queue, after unlocking the queue. + #[inline] + pub fn park(&self) { + while self.parked.load(Ordering::Acquire) { + let result = usercalls::wait(EV_UNPARK, WAIT_INDEFINITE); + debug_assert_eq!(result.expect("wait returned error") & EV_UNPARK, EV_UNPARK); + } + } + + // Parks the thread until it is unparked or the timeout is reached. This + // should be called after it has been added to the queue, after unlocking + // the queue. Returns true if we were unparked and false if we timed out. + #[inline] + pub fn park_until(&self, _timeout: Instant) -> bool { + // FIXME: https://github.com/fortanix/rust-sgx/issues/31 + panic!("timeout not supported in SGX"); + } + + // Locks the parker to prevent the target thread from exiting. This is + // necessary to ensure that thread-local ThreadData objects remain valid. + // This should be called while holding the queue lock. + #[inline] + pub fn unpark_lock(&self) -> UnparkHandle { + // We don't need to lock anything, just clear the state + self.parked.store(false, Ordering::Release); + UnparkHandle(self.tcs) + } +} + +// Handle for a thread that is about to be unparked. We need to mark the thread +// as unparked while holding the queue lock, but we delay the actual unparking +// until after the queue lock is released. +pub struct UnparkHandle(Tcs); + +impl UnparkHandle { + // Wakes up the parked thread. This should be called after the queue lock is + // released to avoid blocking the queue for too long. + #[inline] + pub fn unpark(self) { + let result = usercalls::send(EV_UNPARK, Some(self.0)); + if cfg!(debug_assertions) { + if let Err(error) = result { + // `InvalidInput` may be returned if the thread we send to has + // already been unparked and exited. + if error.kind() != io::ErrorKind::InvalidInput { + panic!("send returned an unexpected error: {:?}", error); + } + } + } + } +} + +#[inline] +pub fn thread_yield() { + thread::yield_now(); +} diff --git a/third_party/rust/parking_lot_core/src/thread_parker/unix.rs b/third_party/rust/parking_lot_core/src/thread_parker/unix.rs index a9e7600c2bd9..8712ebdf1bb0 100644 --- a/third_party/rust/parking_lot_core/src/thread_parker/unix.rs +++ b/third_party/rust/parking_lot_core/src/thread_parker/unix.rs @@ -5,12 +5,26 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. -use std::cell::{Cell, UnsafeCell}; -use std::time::{Duration, Instant}; -use libc; -use std::mem; #[cfg(any(target_os = "macos", target_os = "ios"))] -use std::ptr; +use core::ptr; +use core::{ + cell::{Cell, UnsafeCell}, + mem, +}; +use libc; +use std::{ + thread, + time::{Duration, Instant}, +}; + +// x32 Linux uses a non-standard type for tv_nsec in timespec. +// See https://sourceware.org/bugzilla/show_bug.cgi?id=16437 +#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))] +#[allow(non_camel_case_types)] +type tv_nsec_t = i64; +#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))] +#[allow(non_camel_case_types)] +type tv_nsec_t = libc::c_long; // Helper type for putting a thread to sleep until some other thread wakes it up pub struct ThreadParker { @@ -21,6 +35,9 @@ pub struct ThreadParker { } impl ThreadParker { + pub const IS_CHEAP_TO_CONSTRUCT: bool = false; + + #[inline] pub fn new() -> ThreadParker { ThreadParker { should_park: Cell::new(false), @@ -32,8 +49,10 @@ impl ThreadParker { // Initializes the condvar to use CLOCK_MONOTONIC instead of CLOCK_REALTIME. #[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))] + #[inline] unsafe fn init(&self) {} #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))] + #[inline] unsafe fn init(&self) { let mut attr: libc::pthread_condattr_t = mem::uninitialized(); let r = libc::pthread_condattr_init(&mut attr); @@ -47,6 +66,7 @@ impl ThreadParker { } // Prepares the parker. This should be called before adding it to the queue. + #[inline] pub unsafe fn prepare_park(&self) { self.should_park.set(true); if !self.initialized.get() { @@ -57,6 +77,7 @@ impl ThreadParker { // Checks if the park timed out. This should be called while holding the // queue lock after park_until has returned false. + #[inline] pub unsafe fn timed_out(&self) -> bool { // We need to grab the mutex here because another thread may be // concurrently executing UnparkHandle::unpark, which is done without @@ -71,6 +92,7 @@ impl ThreadParker { // Parks the thread until it is unparked. This should be called after it has // been added to the queue, after unlocking the queue. + #[inline] pub unsafe fn park(&self) { let r = libc::pthread_mutex_lock(self.mutex.get()); debug_assert_eq!(r, 0); @@ -85,6 +107,7 @@ impl ThreadParker { // Parks the thread until it is unparked or the timeout is reached. This // should be called after it has been added to the queue, after unlocking // the queue. Returns true if we were unparked and false if we timed out. + #[inline] pub unsafe fn park_until(&self, timeout: Instant) -> bool { let r = libc::pthread_mutex_lock(self.mutex.get()); debug_assert_eq!(r, 0); @@ -120,6 +143,7 @@ impl ThreadParker { // Locks the parker to prevent the target thread from exiting. This is // necessary to ensure that thread-local ThreadData objects remain valid. // This should be called while holding the queue lock. + #[inline] pub unsafe fn unpark_lock(&self) -> UnparkHandle { let r = libc::pthread_mutex_lock(self.mutex.get()); debug_assert_eq!(r, 0); @@ -131,6 +155,7 @@ impl ThreadParker { } impl Drop for ThreadParker { + #[inline] fn drop(&mut self) { // On DragonFly pthread_mutex_destroy() returns EINVAL if called on a // mutex that was just initialized with libc::PTHREAD_MUTEX_INITIALIZER. @@ -163,6 +188,7 @@ pub struct UnparkHandle { impl UnparkHandle { // Wakes up the parked thread. This should be called after the queue lock is // released to avoid blocking the queue for too long. + #[inline] pub unsafe fn unpark(self) { (*self.thread_parker).should_park.set(false); @@ -178,18 +204,20 @@ impl UnparkHandle { // Returns the current time on the clock used by pthread_cond_t as a timespec. #[cfg(any(target_os = "macos", target_os = "ios"))] -unsafe fn timespec_now() -> libc::timespec { - let mut now: libc::timeval = mem::uninitialized(); - let r = libc::gettimeofday(&mut now, ptr::null_mut()); +#[inline] +fn timespec_now() -> libc::timespec { + let mut now: libc::timeval = unsafe { mem::uninitialized() }; + let r = unsafe { libc::gettimeofday(&mut now, ptr::null_mut()) }; debug_assert_eq!(r, 0); libc::timespec { tv_sec: now.tv_sec, - tv_nsec: now.tv_usec as libc::c_long * 1000, + tv_nsec: now.tv_usec as tv_nsec_t * 1000, } } #[cfg(not(any(target_os = "macos", target_os = "ios")))] -unsafe fn timespec_now() -> libc::timespec { - let mut now: libc::timespec = mem::uninitialized(); +#[inline] +fn timespec_now() -> libc::timespec { + let mut now: libc::timespec = unsafe { mem::uninitialized() }; let clock = if cfg!(target_os = "android") { // Android doesn't support pthread_condattr_setclock, so we need to // specify the timeout in CLOCK_REALTIME. @@ -197,21 +225,22 @@ unsafe fn timespec_now() -> libc::timespec { } else { libc::CLOCK_MONOTONIC }; - let r = libc::clock_gettime(clock, &mut now); + let r = unsafe { libc::clock_gettime(clock, &mut now) }; debug_assert_eq!(r, 0); now } // Converts a relative timeout into an absolute timeout in the clock used by // pthread_cond_t. -unsafe fn timeout_to_timespec(timeout: Duration) -> Option { +#[inline] +fn timeout_to_timespec(timeout: Duration) -> Option { // Handle overflows early on if timeout.as_secs() > libc::time_t::max_value() as u64 { return None; } let now = timespec_now(); - let mut nsec = now.tv_nsec + timeout.subsec_nanos() as libc::c_long; + let mut nsec = now.tv_nsec + timeout.subsec_nanos() as tv_nsec_t; let mut sec = now.tv_sec.checked_add(timeout.as_secs() as libc::time_t); if nsec >= 1_000_000_000 { nsec -= 1_000_000_000; @@ -223,3 +252,8 @@ unsafe fn timeout_to_timespec(timeout: Duration) -> Option { tv_sec: sec, }) } + +#[inline] +pub fn thread_yield() { + thread::yield_now(); +} diff --git a/third_party/rust/parking_lot_core/src/thread_parker/wasm.rs b/third_party/rust/parking_lot_core/src/thread_parker/wasm.rs new file mode 100644 index 000000000000..7a9f7abfb5c1 --- /dev/null +++ b/third_party/rust/parking_lot_core/src/thread_parker/wasm.rs @@ -0,0 +1,108 @@ +// Copyright 2016 Amanieu d'Antras +// +// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use core::{ + arch::wasm32, + sync::atomic::{AtomicI32, Ordering}, +}; +use std::{convert::TryFrom, thread, time::Instant}; + +// Helper type for putting a thread to sleep until some other thread wakes it up +pub struct ThreadParker { + parked: AtomicI32, +} + +const UNPARKED: i32 = 0; +const PARKED: i32 = 1; + +impl ThreadParker { + pub const IS_CHEAP_TO_CONSTRUCT: bool = true; + + #[inline] + pub fn new() -> ThreadParker { + ThreadParker { + parked: AtomicI32::new(UNPARKED), + } + } + + // Prepares the parker. This should be called before adding it to the queue. + #[inline] + pub fn prepare_park(&self) { + self.parked.store(PARKED, Ordering::Relaxed); + } + + // Checks if the park timed out. This should be called while holding the + // queue lock after park_until has returned false. + #[inline] + pub fn timed_out(&self) -> bool { + self.parked.load(Ordering::Relaxed) == PARKED + } + + // Parks the thread until it is unparked. This should be called after it has + // been added to the queue, after unlocking the queue. + #[inline] + pub fn park(&self) { + while self.parked.load(Ordering::Acquire) == PARKED { + let r = unsafe { wasm32::i32_atomic_wait(self.ptr(), PARKED, -1) }; + // we should have either woken up (0) or got a not-equal due to a + // race (1). We should never time out (2) + debug_assert!(r == 0 || r == 1); + } + } + + // Parks the thread until it is unparked or the timeout is reached. This + // should be called after it has been added to the queue, after unlocking + // the queue. Returns true if we were unparked and false if we timed out. + #[inline] + pub fn park_until(&self, timeout: Instant) -> bool { + while self.parked.load(Ordering::Acquire) == PARKED { + if let Some(left) = timeout.checked_duration_since(Instant::now()) { + let nanos_left = i64::try_from(left.as_nanos()).unwrap_or(i64::max_value()); + let r = unsafe { wasm32::i32_atomic_wait(self.ptr(), PARKED, nanos_left) }; + debug_assert!(r == 0 || r == 1 || r == 2); + } else { + return false; + } + } + true + } + + // Locks the parker to prevent the target thread from exiting. This is + // necessary to ensure that thread-local ThreadData objects remain valid. + // This should be called while holding the queue lock. + #[inline] + pub fn unpark_lock(&self) -> UnparkHandle { + // We don't need to lock anything, just clear the state + self.parked.store(UNPARKED, Ordering::Release); + UnparkHandle(self.ptr()) + } + + #[inline] + fn ptr(&self) -> *mut i32 { + &self.parked as *const AtomicI32 as *mut i32 + } +} + +// Handle for a thread that is about to be unparked. We need to mark the thread +// as unparked while holding the queue lock, but we delay the actual unparking +// until after the queue lock is released. +pub struct UnparkHandle(*mut i32); + +impl UnparkHandle { + // Wakes up the parked thread. This should be called after the queue lock is + // released to avoid blocking the queue for too long. + #[inline] + pub fn unpark(self) { + let num_notified = unsafe { wasm32::atomic_notify(self.0 as *mut i32, 1) }; + debug_assert!(num_notified == 0 || num_notified == 1); + } +} + +#[inline] +pub fn thread_yield() { + thread::yield_now(); +} diff --git a/third_party/rust/parking_lot_core/src/thread_parker/windows/keyed_event.rs b/third_party/rust/parking_lot_core/src/thread_parker/windows/keyed_event.rs index 599d66f19cc8..71b55f0f3eab 100644 --- a/third_party/rust/parking_lot_core/src/thread_parker/windows/keyed_event.rs +++ b/third_party/rust/parking_lot_core/src/thread_parker/windows/keyed_event.rs @@ -5,18 +5,26 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::time::Instant; -use std::ptr; -use std::mem; - -use winapi::shared::minwindef::{TRUE, ULONG}; -use winapi::shared::ntdef::NTSTATUS; -use winapi::shared::ntstatus::{STATUS_SUCCESS, STATUS_TIMEOUT}; -use winapi::um::handleapi::CloseHandle; -use winapi::um::libloaderapi::{GetModuleHandleA, GetProcAddress}; -use winapi::um::winnt::{ACCESS_MASK, GENERIC_READ, GENERIC_WRITE, LPCSTR}; -use winapi::um::winnt::{BOOLEAN, HANDLE, LARGE_INTEGER, PHANDLE, PLARGE_INTEGER, PVOID}; +use core::{mem, ptr}; +use std::{ + sync::atomic::{AtomicUsize, Ordering}, + time::Instant, +}; +use winapi::{ + shared::{ + minwindef::{TRUE, ULONG}, + ntdef::NTSTATUS, + ntstatus::{STATUS_SUCCESS, STATUS_TIMEOUT}, + }, + um::{ + handleapi::CloseHandle, + libloaderapi::{GetModuleHandleA, GetProcAddress}, + winnt::{ + ACCESS_MASK, BOOLEAN, GENERIC_READ, GENERIC_WRITE, HANDLE, LARGE_INTEGER, LPCSTR, + PHANDLE, PLARGE_INTEGER, PVOID, + }, + }, +}; const STATE_UNPARKED: usize = 0; const STATE_PARKED: usize = 1; @@ -40,73 +48,82 @@ pub struct KeyedEvent { } impl KeyedEvent { + #[inline] unsafe fn wait_for(&self, key: PVOID, timeout: PLARGE_INTEGER) -> NTSTATUS { (self.NtWaitForKeyedEvent)(self.handle, key, 0, timeout) } + #[inline] unsafe fn release(&self, key: PVOID) -> NTSTATUS { (self.NtReleaseKeyedEvent)(self.handle, key, 0, ptr::null_mut()) } #[allow(non_snake_case)] - pub unsafe fn create() -> Option { - let ntdll = GetModuleHandleA(b"ntdll.dll\0".as_ptr() as LPCSTR); - if ntdll.is_null() { - return None; - } + pub fn create() -> Option { + unsafe { + let ntdll = GetModuleHandleA(b"ntdll.dll\0".as_ptr() as LPCSTR); + if ntdll.is_null() { + return None; + } - let NtCreateKeyedEvent = GetProcAddress(ntdll, b"NtCreateKeyedEvent\0".as_ptr() as LPCSTR); - if NtCreateKeyedEvent.is_null() { - return None; - } - let NtReleaseKeyedEvent = - GetProcAddress(ntdll, b"NtReleaseKeyedEvent\0".as_ptr() as LPCSTR); - if NtReleaseKeyedEvent.is_null() { - return None; - } - let NtWaitForKeyedEvent = - GetProcAddress(ntdll, b"NtWaitForKeyedEvent\0".as_ptr() as LPCSTR); - if NtWaitForKeyedEvent.is_null() { - return None; - } + let NtCreateKeyedEvent = + GetProcAddress(ntdll, b"NtCreateKeyedEvent\0".as_ptr() as LPCSTR); + if NtCreateKeyedEvent.is_null() { + return None; + } + let NtReleaseKeyedEvent = + GetProcAddress(ntdll, b"NtReleaseKeyedEvent\0".as_ptr() as LPCSTR); + if NtReleaseKeyedEvent.is_null() { + return None; + } + let NtWaitForKeyedEvent = + GetProcAddress(ntdll, b"NtWaitForKeyedEvent\0".as_ptr() as LPCSTR); + if NtWaitForKeyedEvent.is_null() { + return None; + } - let NtCreateKeyedEvent: extern "system" fn( - KeyedEventHandle: PHANDLE, - DesiredAccess: ACCESS_MASK, - ObjectAttributes: PVOID, - Flags: ULONG, - ) -> NTSTATUS = mem::transmute(NtCreateKeyedEvent); - let mut handle = mem::uninitialized(); - let status = NtCreateKeyedEvent( - &mut handle, - GENERIC_READ | GENERIC_WRITE, - ptr::null_mut(), - 0, - ); - if status != STATUS_SUCCESS { - return None; - } + let NtCreateKeyedEvent: extern "system" fn( + KeyedEventHandle: PHANDLE, + DesiredAccess: ACCESS_MASK, + ObjectAttributes: PVOID, + Flags: ULONG, + ) -> NTSTATUS = mem::transmute(NtCreateKeyedEvent); + let mut handle = mem::uninitialized(); + let status = NtCreateKeyedEvent( + &mut handle, + GENERIC_READ | GENERIC_WRITE, + ptr::null_mut(), + 0, + ); + if status != STATUS_SUCCESS { + return None; + } - Some(KeyedEvent { - handle, - NtReleaseKeyedEvent: mem::transmute(NtReleaseKeyedEvent), - NtWaitForKeyedEvent: mem::transmute(NtWaitForKeyedEvent), - }) + Some(KeyedEvent { + handle, + NtReleaseKeyedEvent: mem::transmute(NtReleaseKeyedEvent), + NtWaitForKeyedEvent: mem::transmute(NtWaitForKeyedEvent), + }) + } } - pub unsafe fn prepare_park(&'static self, key: &AtomicUsize) { + #[inline] + pub fn prepare_park(&'static self, key: &AtomicUsize) { key.store(STATE_PARKED, Ordering::Relaxed); } - pub unsafe fn timed_out(&'static self, key: &AtomicUsize) -> bool { + #[inline] + pub fn timed_out(&'static self, key: &AtomicUsize) -> bool { key.load(Ordering::Relaxed) == STATE_TIMED_OUT } + #[inline] pub unsafe fn park(&'static self, key: &AtomicUsize) { let status = self.wait_for(key as *const _ as PVOID, ptr::null_mut()); debug_assert_eq!(status, STATUS_SUCCESS); } + #[inline] pub unsafe fn park_until(&'static self, key: &AtomicUsize, timeout: Instant) -> bool { let now = Instant::now(); if timeout <= now { @@ -152,6 +169,7 @@ impl KeyedEvent { false } + #[inline] pub unsafe fn unpark_lock(&'static self, key: &AtomicUsize) -> UnparkHandle { // If the state was STATE_PARKED then we need to wake up the thread if key.swap(STATE_UNPARKED, Ordering::Relaxed) == STATE_PARKED { @@ -169,6 +187,7 @@ impl KeyedEvent { } impl Drop for KeyedEvent { + #[inline] fn drop(&mut self) { unsafe { let ok = CloseHandle(self.handle); @@ -188,6 +207,7 @@ pub struct UnparkHandle { impl UnparkHandle { // Wakes up the parked thread. This should be called after the queue lock is // released to avoid blocking the queue for too long. + #[inline] pub unsafe fn unpark(self) { if !self.key.is_null() { let status = self.keyed_event.release(self.key as PVOID); diff --git a/third_party/rust/parking_lot_core/src/thread_parker/windows/mod.rs b/third_party/rust/parking_lot_core/src/thread_parker/windows/mod.rs index a91ab61b5cb1..32f51c2de53a 100644 --- a/third_party/rust/parking_lot_core/src/thread_parker/windows/mod.rs +++ b/third_party/rust/parking_lot_core/src/thread_parker/windows/mod.rs @@ -5,7 +5,10 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. -use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; +use core::{ + ptr, + sync::atomic::{AtomicPtr, AtomicUsize, Ordering}, +}; use std::time::Instant; mod keyed_event; @@ -16,16 +19,23 @@ enum Backend { WaitAddress(waitaddress::WaitAddress), } -impl Backend { - unsafe fn get() -> &'static Backend { - static BACKEND: AtomicUsize = ATOMIC_USIZE_INIT; +static BACKEND: AtomicPtr = AtomicPtr::new(ptr::null_mut()); +impl Backend { + #[inline] + fn get() -> &'static Backend { // Fast path: use the existing object - let backend = BACKEND.load(Ordering::Acquire); - if backend != 0 { - return &*(backend as *const Backend); + let backend_ptr = BACKEND.load(Ordering::Acquire); + if !backend_ptr.is_null() { + return unsafe { &*backend_ptr }; }; + Backend::create() + } + + #[cold] + #[inline(never)] + fn create() -> &'static Backend { // Try to create a new Backend let backend; if let Some(waitaddress) = waitaddress::WaitAddress::create() { @@ -39,14 +49,21 @@ impl Backend { ); } - // Try to create a new object - let backend = Box::into_raw(Box::new(backend)); - match BACKEND.compare_exchange(0, backend as usize, Ordering::Release, Ordering::Relaxed) { - Ok(_) => &*(backend as *const Backend), - Err(x) => { - // We lost the race, free our object and return the global one - Box::from_raw(backend); - &*(x as *const Backend) + // Try to set our new Backend as the global one + let backend_ptr = Box::into_raw(Box::new(backend)); + match BACKEND.compare_exchange( + ptr::null_mut(), + backend_ptr, + Ordering::Release, + Ordering::Relaxed, + ) { + Ok(_) => unsafe { &*backend_ptr }, + Err(global_backend_ptr) => { + unsafe { + // We lost the race, free our object and return the global one + Box::from_raw(backend_ptr); + &*global_backend_ptr + } } } } @@ -59,18 +76,22 @@ pub struct ThreadParker { } impl ThreadParker { + pub const IS_CHEAP_TO_CONSTRUCT: bool = true; + + #[inline] pub fn new() -> ThreadParker { // Initialize the backend here to ensure we don't get any panics // later on, which could leave synchronization primitives in a broken // state. ThreadParker { key: AtomicUsize::new(0), - backend: unsafe { Backend::get() }, + backend: Backend::get(), } } // Prepares the parker. This should be called before adding it to the queue. - pub unsafe fn prepare_park(&self) { + #[inline] + pub fn prepare_park(&self) { match *self.backend { Backend::KeyedEvent(ref x) => x.prepare_park(&self.key), Backend::WaitAddress(ref x) => x.prepare_park(&self.key), @@ -79,7 +100,8 @@ impl ThreadParker { // Checks if the park timed out. This should be called while holding the // queue lock after park_until has returned false. - pub unsafe fn timed_out(&self) -> bool { + #[inline] + pub fn timed_out(&self) -> bool { match *self.backend { Backend::KeyedEvent(ref x) => x.timed_out(&self.key), Backend::WaitAddress(ref x) => x.timed_out(&self.key), @@ -88,6 +110,7 @@ impl ThreadParker { // Parks the thread until it is unparked. This should be called after it has // been added to the queue, after unlocking the queue. + #[inline] pub unsafe fn park(&self) { match *self.backend { Backend::KeyedEvent(ref x) => x.park(&self.key), @@ -98,6 +121,7 @@ impl ThreadParker { // Parks the thread until it is unparked or the timeout is reached. This // should be called after it has been added to the queue, after unlocking // the queue. Returns true if we were unparked and false if we timed out. + #[inline] pub unsafe fn park_until(&self, timeout: Instant) -> bool { match *self.backend { Backend::KeyedEvent(ref x) => x.park_until(&self.key, timeout), @@ -108,6 +132,7 @@ impl ThreadParker { // Locks the parker to prevent the target thread from exiting. This is // necessary to ensure that thread-local ThreadData objects remain valid. // This should be called while holding the queue lock. + #[inline] pub unsafe fn unpark_lock(&self) -> UnparkHandle { match *self.backend { Backend::KeyedEvent(ref x) => UnparkHandle::KeyedEvent(x.unpark_lock(&self.key)), @@ -127,6 +152,7 @@ pub enum UnparkHandle { impl UnparkHandle { // Wakes up the parked thread. This should be called after the queue lock is // released to avoid blocking the queue for too long. + #[inline] pub unsafe fn unpark(self) { match self { UnparkHandle::KeyedEvent(x) => x.unpark(), @@ -134,3 +160,30 @@ impl UnparkHandle { } } } + +// Yields the rest of the current timeslice to the OS +#[inline] +pub fn thread_yield() { + // Note that this is manually defined here rather than using the definition + // through `winapi`. The `winapi` definition comes from the `synchapi` + // header which enables the "synchronization.lib" library. It turns out, + // however that `Sleep` comes from `kernel32.dll` so this activation isn't + // necessary. + // + // This was originally identified in rust-lang/rust where on MinGW the + // libsynchronization.a library pulls in a dependency on a newer DLL not + // present in older versions of Windows. (see rust-lang/rust#49438) + // + // This is a bit of a hack for now and ideally we'd fix MinGW's own import + // libraries, but that'll probably take a lot longer than patching this here + // and avoiding the `synchapi` feature entirely. + extern "system" { + fn Sleep(a: winapi::shared::minwindef::DWORD); + } + unsafe { + // We don't use SwitchToThread here because it doesn't consider all + // threads in the system and the thread we are waiting for may not get + // selected. + Sleep(0); + } +} diff --git a/third_party/rust/parking_lot_core/src/thread_parker/windows/waitaddress.rs b/third_party/rust/parking_lot_core/src/thread_parker/windows/waitaddress.rs index 7918d7b66e42..0ec780404d88 100644 --- a/third_party/rust/parking_lot_core/src/thread_parker/windows/waitaddress.rs +++ b/third_party/rust/parking_lot_core/src/thread_parker/windows/waitaddress.rs @@ -5,17 +5,24 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. -use std::sync::atomic::{AtomicUsize, Ordering}; +use core::{ + mem, + sync::atomic::{AtomicUsize, Ordering}, +}; use std::time::Instant; -use std::mem; - -use winapi::shared::basetsd::SIZE_T; -use winapi::shared::minwindef::{BOOL, DWORD, FALSE, TRUE}; -use winapi::shared::winerror::ERROR_TIMEOUT; -use winapi::um::errhandlingapi::GetLastError; -use winapi::um::libloaderapi::{GetModuleHandleA, GetProcAddress}; -use winapi::um::winbase::INFINITE; -use winapi::um::winnt::{LPCSTR, PVOID}; +use winapi::{ + shared::{ + basetsd::SIZE_T, + minwindef::{BOOL, DWORD, FALSE, TRUE}, + winerror::ERROR_TIMEOUT, + }, + um::{ + errhandlingapi::GetLastError, + libloaderapi::{GetModuleHandleA, GetProcAddress}, + winbase::INFINITE, + winnt::{LPCSTR, PVOID}, + }, +}; #[allow(non_snake_case)] pub struct WaitAddress { @@ -30,58 +37,60 @@ pub struct WaitAddress { impl WaitAddress { #[allow(non_snake_case)] - pub unsafe fn create() -> Option { - // MSDN claims that that WaitOnAddress and WakeByAddressSingle are - // located in kernel32.dll, but they are lying... - let synch_dll = GetModuleHandleA(b"api-ms-win-core-synch-l1-2-0.dll\0".as_ptr() as LPCSTR); - if synch_dll.is_null() { - return None; - } + pub fn create() -> Option { + unsafe { + // MSDN claims that that WaitOnAddress and WakeByAddressSingle are + // located in kernel32.dll, but they are lying... + let synch_dll = + GetModuleHandleA(b"api-ms-win-core-synch-l1-2-0.dll\0".as_ptr() as LPCSTR); + if synch_dll.is_null() { + return None; + } - let WaitOnAddress = GetProcAddress(synch_dll, b"WaitOnAddress\0".as_ptr() as LPCSTR); - if WaitOnAddress.is_null() { - return None; + let WaitOnAddress = GetProcAddress(synch_dll, b"WaitOnAddress\0".as_ptr() as LPCSTR); + if WaitOnAddress.is_null() { + return None; + } + let WakeByAddressSingle = + GetProcAddress(synch_dll, b"WakeByAddressSingle\0".as_ptr() as LPCSTR); + if WakeByAddressSingle.is_null() { + return None; + } + Some(WaitAddress { + WaitOnAddress: mem::transmute(WaitOnAddress), + WakeByAddressSingle: mem::transmute(WakeByAddressSingle), + }) } - let WakeByAddressSingle = - GetProcAddress(synch_dll, b"WakeByAddressSingle\0".as_ptr() as LPCSTR); - if WakeByAddressSingle.is_null() { - return None; - } - Some(WaitAddress { - WaitOnAddress: mem::transmute(WaitOnAddress), - WakeByAddressSingle: mem::transmute(WakeByAddressSingle), - }) } - pub unsafe fn prepare_park(&'static self, key: &AtomicUsize) { + #[inline] + pub fn prepare_park(&'static self, key: &AtomicUsize) { key.store(1, Ordering::Relaxed); } - pub unsafe fn timed_out(&'static self, key: &AtomicUsize) -> bool { + #[inline] + pub fn timed_out(&'static self, key: &AtomicUsize) -> bool { key.load(Ordering::Relaxed) != 0 } - pub unsafe fn park(&'static self, key: &AtomicUsize) { + #[inline] + pub fn park(&'static self, key: &AtomicUsize) { while key.load(Ordering::Acquire) != 0 { - let cmp = 1usize; - let r = (self.WaitOnAddress)( - key as *const _ as PVOID, - &cmp as *const _ as PVOID, - mem::size_of::() as SIZE_T, - INFINITE, - ); + let r = self.wait_on_address(key, INFINITE); debug_assert!(r == TRUE); } } - pub unsafe fn park_until(&'static self, key: &AtomicUsize, timeout: Instant) -> bool { + #[inline] + pub fn park_until(&'static self, key: &AtomicUsize, timeout: Instant) -> bool { while key.load(Ordering::Acquire) != 0 { let now = Instant::now(); if timeout <= now { return false; } let diff = timeout - now; - let timeout = diff.as_secs() + let timeout = diff + .as_secs() .checked_mul(1000) .and_then(|x| x.checked_add((diff.subsec_nanos() as u64 + 999999) / 1000000)) .map(|ms| { @@ -92,21 +101,15 @@ impl WaitAddress { } }) .unwrap_or(INFINITE); - let cmp = 1usize; - let r = (self.WaitOnAddress)( - key as *const _ as PVOID, - &cmp as *const _ as PVOID, - mem::size_of::() as SIZE_T, - timeout, - ); - if r == FALSE { - debug_assert_eq!(GetLastError(), ERROR_TIMEOUT); + if self.wait_on_address(key, timeout) == FALSE { + debug_assert_eq!(unsafe { GetLastError() }, ERROR_TIMEOUT); } } true } - pub unsafe fn unpark_lock(&'static self, key: &AtomicUsize) -> UnparkHandle { + #[inline] + pub fn unpark_lock(&'static self, key: &AtomicUsize) -> UnparkHandle { // We don't need to lock anything, just clear the state key.store(0, Ordering::Release); @@ -115,6 +118,17 @@ impl WaitAddress { waitaddress: self, } } + + #[inline] + fn wait_on_address(&'static self, key: &AtomicUsize, timeout: DWORD) -> BOOL { + let cmp = 1usize; + (self.WaitOnAddress)( + key as *const _ as PVOID, + &cmp as *const _ as PVOID, + mem::size_of::() as SIZE_T, + timeout, + ) + } } // Handle for a thread that is about to be unparked. We need to mark the thread @@ -128,7 +142,8 @@ pub struct UnparkHandle { impl UnparkHandle { // Wakes up the parked thread. This should be called after the queue lock is // released to avoid blocking the queue for too long. - pub unsafe fn unpark(self) { + #[inline] + pub fn unpark(self) { (self.waitaddress.WakeByAddressSingle)(self.key as PVOID); } } diff --git a/third_party/rust/parking_lot_core/src/word_lock.rs b/third_party/rust/parking_lot_core/src/word_lock.rs index 466d38f35462..a6a10e830be2 100644 --- a/third_party/rust/parking_lot_core/src/word_lock.rs +++ b/third_party/rust/parking_lot_core/src/word_lock.rs @@ -5,15 +5,13 @@ // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. -use std::sync::atomic::{fence, AtomicUsize, Ordering}; -use std::ptr; -use std::mem; -use std::cell::Cell; -use std::thread::LocalKey; -#[cfg(not(feature = "nightly"))] -use std::panic; -use spinwait::SpinWait; -use thread_parker::ThreadParker; +use crate::spinwait::SpinWait; +use crate::thread_parker::ThreadParker; +use core::{ + cell::Cell, + mem, ptr, + sync::atomic::{fence, AtomicUsize, Ordering}, +}; struct ThreadData { parker: ThreadParker, @@ -36,7 +34,9 @@ struct ThreadData { } impl ThreadData { + #[inline] fn new() -> ThreadData { + assert!(mem::align_of::() > !QUEUE_MASK); ThreadData { parker: ThreadParker::new(), queue_tail: Cell::new(ptr::null()), @@ -46,31 +46,28 @@ impl ThreadData { } } -// Returns a ThreadData structure for the current thread -unsafe fn get_thread_data(local: &mut Option) -> &ThreadData { - // Try to read from thread-local storage, but return None if the TLS has - // already been destroyed. - #[cfg(feature = "nightly")] - fn try_get_tls(key: &'static LocalKey) -> Option<*const ThreadData> { - key.try_with(|x| x as *const ThreadData).ok() - } - #[cfg(not(feature = "nightly"))] - fn try_get_tls(key: &'static LocalKey) -> Option<*const ThreadData> { - panic::catch_unwind(|| key.with(|x| x as *const ThreadData)).ok() - } - +// Invokes the given closure with a reference to the current thread `ThreadData`. +#[inline] +fn with_thread_data(f: F) -> T +where + F: FnOnce(&ThreadData) -> T, +{ + let mut thread_data_ptr = ptr::null(); // If ThreadData is expensive to construct, then we want to use a cached // version in thread-local storage if possible. - if !cfg!(windows) && !cfg!(all(feature = "nightly", target_os = "linux")) { + if !ThreadParker::IS_CHEAP_TO_CONSTRUCT { thread_local!(static THREAD_DATA: ThreadData = ThreadData::new()); - if let Some(tls) = try_get_tls(&THREAD_DATA) { - return &*tls; + if let Ok(tls_thread_data) = THREAD_DATA.try_with(|x| x as *const ThreadData) { + thread_data_ptr = tls_thread_data; } } - // Otherwise just create a ThreadData on the stack - *local = Some(ThreadData::new()); - local.as_ref().unwrap() + let mut thread_data_storage = None; + if thread_data_ptr.is_null() { + thread_data_ptr = thread_data_storage.get_or_insert_with(ThreadData::new); + } + + f(unsafe { &*thread_data_ptr }) } const LOCKED_BIT: usize = 1; @@ -84,16 +81,14 @@ pub struct WordLock { } impl WordLock { - #[inline] - pub fn new() -> WordLock { - WordLock { - state: AtomicUsize::new(0), - } - } + pub const INIT: WordLock = WordLock { + state: AtomicUsize::new(0), + }; #[inline] - pub unsafe fn lock(&self) { - if self.state + pub fn lock(&self) { + if self + .state .compare_exchange_weak(0, LOCKED_BIT, Ordering::Acquire, Ordering::Relaxed) .is_ok() { @@ -102,10 +97,11 @@ impl WordLock { self.lock_slow(); } + /// Must not be called on an already unlocked `WordLock`! #[inline] pub unsafe fn unlock(&self) { let state = self.state.fetch_sub(LOCKED_BIT, Ordering::Release); - if state & QUEUE_LOCKED_BIT != 0 || state & QUEUE_MASK == 0 { + if state.is_queue_locked() || state.queue_head().is_null() { return; } self.unlock_slow(); @@ -113,12 +109,12 @@ impl WordLock { #[cold] #[inline(never)] - unsafe fn lock_slow(&self) { + fn lock_slow(&self) { let mut spinwait = SpinWait::new(); let mut state = self.state.load(Ordering::Relaxed); loop { // Grab the lock if it isn't locked, even if there is a queue on it - if state & LOCKED_BIT == 0 { + if !state.is_locked() { match self.state.compare_exchange_weak( state, state | LOCKED_BIT, @@ -132,55 +128,62 @@ impl WordLock { } // If there is no queue, try spinning a few times - if state & QUEUE_MASK == 0 && spinwait.spin() { + if state.queue_head().is_null() && spinwait.spin() { state = self.state.load(Ordering::Relaxed); continue; } // Get our thread data and prepare it for parking - let mut thread_data = None; - let thread_data = get_thread_data(&mut thread_data); - assert!(mem::align_of_val(thread_data) > !QUEUE_MASK); - thread_data.parker.prepare_park(); + state = with_thread_data(|thread_data| { + // The pthread implementation is still unsafe, so we need to surround `prepare_park` + // with `unsafe {}`. + #[allow(unused_unsafe)] + unsafe { + thread_data.parker.prepare_park(); + } - // Add our thread to the front of the queue - let queue_head = (state & QUEUE_MASK) as *const ThreadData; - if queue_head.is_null() { - thread_data.queue_tail.set(thread_data); - thread_data.prev.set(ptr::null()); - } else { - thread_data.queue_tail.set(ptr::null()); - thread_data.prev.set(ptr::null()); - thread_data.next.set(queue_head); - } - if let Err(x) = self.state.compare_exchange_weak( - state, - (state & !QUEUE_MASK) | thread_data as *const _ as usize, - Ordering::Release, - Ordering::Relaxed, - ) { - state = x; - continue; - } + // Add our thread to the front of the queue + let queue_head = state.queue_head(); + if queue_head.is_null() { + thread_data.queue_tail.set(thread_data); + thread_data.prev.set(ptr::null()); + } else { + thread_data.queue_tail.set(ptr::null()); + thread_data.prev.set(ptr::null()); + thread_data.next.set(queue_head); + } + if let Err(x) = self.state.compare_exchange_weak( + state, + state.with_queue_head(thread_data), + Ordering::Release, + Ordering::Relaxed, + ) { + return x; + } - // Sleep until we are woken up by an unlock - thread_data.parker.park(); + // Sleep until we are woken up by an unlock + // Ignoring unused unsafe, since it's only a few platforms where this is unsafe. + #[allow(unused_unsafe)] + unsafe { + thread_data.parker.park(); + } - // Loop back and try locking again - spinwait.reset(); - self.state.load(Ordering::Relaxed); + // Loop back and try locking again + spinwait.reset(); + self.state.load(Ordering::Relaxed) + }); } } #[cold] #[inline(never)] - unsafe fn unlock_slow(&self) { + fn unlock_slow(&self) { let mut state = self.state.load(Ordering::Relaxed); loop { // We just unlocked the WordLock. Just check if there is a thread // to wake up. If the queue is locked then another thread is already // taking care of waking up a thread. - if state & QUEUE_LOCKED_BIT != 0 || state & QUEUE_MASK == 0 { + if state.is_queue_locked() || state.queue_head().is_null() { return; } @@ -201,27 +204,31 @@ impl WordLock { // First, we need to fill in the prev pointers for any newly added // threads. We do this until we reach a node that we previously // processed, which has a non-null queue_tail pointer. - let queue_head = (state & QUEUE_MASK) as *const ThreadData; + let queue_head = state.queue_head(); let mut queue_tail; let mut current = queue_head; loop { - queue_tail = (*current).queue_tail.get(); + queue_tail = unsafe { (*current).queue_tail.get() }; if !queue_tail.is_null() { break; } - let next = (*current).next.get(); - (*next).prev.set(current); - current = next; + unsafe { + let next = (*current).next.get(); + (*next).prev.set(current); + current = next; + } } // Set queue_tail on the queue head to indicate that the whole list // has prev pointers set correctly. - (*queue_head).queue_tail.set(queue_tail); + unsafe { + (*queue_head).queue_tail.set(queue_tail); + } // If the WordLock is locked, then there is no point waking up a // thread now. Instead we let the next unlocker take care of waking // up a thread. - if state & LOCKED_BIT != 0 { + if state.is_locked() { match self.state.compare_exchange_weak( state, state & !QUEUE_LOCKED_BIT, @@ -238,7 +245,7 @@ impl WordLock { } // Remove the last thread from the queue and unlock the queue - let new_tail = (*queue_tail).prev.get(); + let new_tail = unsafe { (*queue_tail).prev.get() }; if new_tail.is_null() { loop { match self.state.compare_exchange_weak( @@ -254,7 +261,7 @@ impl WordLock { // If the compare_exchange failed because a new thread was // added to the queue then we need to re-scan the queue to // find the previous element. - if state & QUEUE_MASK == 0 { + if state.queue_head().is_null() { continue; } else { // Need an acquire fence before reading the new queue @@ -263,7 +270,9 @@ impl WordLock { } } } else { - (*queue_head).queue_tail.set(new_tail); + unsafe { + (*queue_head).queue_tail.set(new_tail); + } self.state.fetch_and(!QUEUE_LOCKED_BIT, Ordering::Release); } @@ -271,8 +280,39 @@ impl WordLock { // we don't need to worry about any races here since the thread is // guaranteed to be sleeping right now and we are the only one who // can wake it up. - (*queue_tail).parker.unpark_lock().unpark(); + unsafe { + (*queue_tail).parker.unpark_lock().unpark(); + } break; } } } + +trait LockState { + fn is_locked(self) -> bool; + fn is_queue_locked(self) -> bool; + fn queue_head(self) -> *const ThreadData; + fn with_queue_head(self, thread_data: *const ThreadData) -> Self; +} + +impl LockState for usize { + #[inline] + fn is_locked(self) -> bool { + self & LOCKED_BIT != 0 + } + + #[inline] + fn is_queue_locked(self) -> bool { + self & QUEUE_LOCKED_BIT != 0 + } + + #[inline] + fn queue_head(self) -> *const ThreadData { + (self & QUEUE_MASK) as *const ThreadData + } + + #[inline] + fn with_queue_head(self, thread_data: *const ThreadData) -> Self { + (self & !QUEUE_MASK) | thread_data as *const _ as usize + } +} diff --git a/third_party/rust/scopeguard-0.3.2/.cargo-checksum.json b/third_party/rust/scopeguard-0.3.2/.cargo-checksum.json new file mode 100644 index 000000000000..6635efad2c20 --- /dev/null +++ b/third_party/rust/scopeguard-0.3.2/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"9be8c3913111b0a14c16ff1c5dc5613033b3ba6fd9af93262de9e119d0909a90","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"7b63ecd5f1902af1b63729947373683c32745c16a10e8e6292e2e2dcd7e90ae0","README.rst":"ae67e170de747e739273914a468cb93e6e4079b8e277c224c62a18353f660a11","examples/readme.rs":"5a01391acf2acc52a7a2e0ba58dc8ded3e8cc57d54b45778af5e8ba577158f86","src/lib.rs":"2ec3a38a7ca647c94a4c054a2938afd4a07e9cf636b432824623c2fa27a192a3"},"package":"c79eb2c3ac4bc2507cda80e7f3ac5b88bd8eae4c0914d5663e6a8933994be918"} \ No newline at end of file diff --git a/third_party/rust/scopeguard-0.3.2/Cargo.toml b/third_party/rust/scopeguard-0.3.2/Cargo.toml new file mode 100644 index 000000000000..9e0f6c9d6275 --- /dev/null +++ b/third_party/rust/scopeguard-0.3.2/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "scopeguard" +version = "0.3.2" + +license = "MIT/Apache-2.0" +repository = "https://github.com/bluss/scopeguard" +documentation = "https://docs.rs/scopeguard/" +authors = ["bluss"] + +description = """ +A RAII scope guard that will run a given closure when it goes out of scope, +even if the code between panics (assuming unwinding panic). + +Defines the macros `defer!` and `defer_on_unwind!`; the latter only runs +if the scope is extited through unwinding on panic. +""" + +keywords = ["scope-guard", "defer", "panic"] +categories = ["rust-patterns"] + +[features] +default = ["use_std"] +use_std = [] + +[package.metadata.release] +#no-dev-version = true diff --git a/third_party/rust/scopeguard-0.3.2/LICENSE-APACHE b/third_party/rust/scopeguard-0.3.2/LICENSE-APACHE new file mode 100644 index 000000000000..16fe87b06e80 --- /dev/null +++ b/third_party/rust/scopeguard-0.3.2/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/third_party/rust/scopeguard-0.3.2/LICENSE-MIT b/third_party/rust/scopeguard-0.3.2/LICENSE-MIT new file mode 100644 index 000000000000..e69282e381bc --- /dev/null +++ b/third_party/rust/scopeguard-0.3.2/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2015 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/third_party/rust/scopeguard-0.3.2/README.rst b/third_party/rust/scopeguard-0.3.2/README.rst new file mode 100644 index 000000000000..1c1daed51892 --- /dev/null +++ b/third_party/rust/scopeguard-0.3.2/README.rst @@ -0,0 +1,76 @@ + +scopeguard +========== + +Rust crate for a convenient RAII scope guard that will run a given closure when +it goes out of scope, even if the code between panics (assuming unwinding panic). + +The `defer!` macro and `guard` are `no_std` compatible (require only core), +but the on unwinding strategy requires linking to `std`. + +Requires Rust 1.11. + + +Please read the `API documentation here`__ + +__ https://docs.rs/scopeguard/ + +|build_status|_ |crates|_ + +.. |build_status| image:: https://travis-ci.org/bluss/scopeguard.svg +.. _build_status: https://travis-ci.org/bluss/scopeguard + +.. |crates| image:: http://meritbadge.herokuapp.com/scopeguard +.. _crates: https://crates.io/crates/scopeguard + +How to use +---------- + +.. code:: rust + + #[macro_use(defer)] extern crate scopeguard; + + use scopeguard::guard; + + fn f() { + defer!(println!("Called at return or panic")); + panic!(); + } + + use std::fs::File; + use std::io::Write; + + fn g() { + let f = File::create("newfile.txt").unwrap(); + let mut file = guard(f, |f| { + // write file at return or panic + let _ = f.sync_all(); + }); + // Access the file through the scope guard itself + file.write(b"test me\n").unwrap(); + } + +Recent Changes +-------------- + +- 0.3.2 + + - Add crate categories + +- 0.3.1 + + - Add ``defer_on_unwind!``, ``Strategy`` trait + - Rename ``Guard`` → ``ScopeGuard`` + - Add ``ScopeGuard::with_strategy``. + - ``ScopeGuard`` now implements ``Debug``. + - Require Rust 1.11 + +- 0.2.0 + + - Require Rust 1.6 + - Use `no_std` unconditionally + - No other changes + +- 0.1.2 + + - Add macro ``defer!()`` diff --git a/third_party/rust/scopeguard-0.3.2/examples/readme.rs b/third_party/rust/scopeguard-0.3.2/examples/readme.rs new file mode 100644 index 000000000000..904bc544eb30 --- /dev/null +++ b/third_party/rust/scopeguard-0.3.2/examples/readme.rs @@ -0,0 +1,27 @@ + +#[macro_use(defer)] extern crate scopeguard; + +use scopeguard::guard; + +fn f() { + defer!(println!("Called at return or panic")); + panic!(); +} + +use std::fs::File; +use std::io::Write; + +fn g() { + let f = File::create("newfile.txt").unwrap(); + let mut file = guard(f, |f| { + // write file at return or panic + let _ = f.sync_all(); + }); + // Access the file through the scope guard itself + file.write(b"test me\n").unwrap(); +} + +fn main() { + f(); + g(); +} diff --git a/third_party/rust/scopeguard-0.3.2/src/lib.rs b/third_party/rust/scopeguard-0.3.2/src/lib.rs new file mode 100644 index 000000000000..8f8f2b5fc8e4 --- /dev/null +++ b/third_party/rust/scopeguard-0.3.2/src/lib.rs @@ -0,0 +1,268 @@ +//! A scope guard will run a given closure when it goes out of scope, +//! even if the code between panics. +//! (as long as panic doesn't abort) + +#![cfg_attr(not(any(test, feature = "use_std")), no_std)] + +//! +//! +//! Crate features: +//! +//! - `use_std` +//! + Enabled by default. Enables the `OnUnwind` strategy. +//! + Disable to use `no_std`. + +#[cfg(not(any(test, feature = "use_std")))] +extern crate core as std; + +use std::fmt; +use std::marker::PhantomData; +use std::ops::{Deref, DerefMut}; + +pub trait Strategy { + /// Return `true` if the guard’s associated code should run + /// (in the context where this method is called). + fn should_run() -> bool; +} + +/// Always run on scope exit. +/// +/// “Always” run: on regular exit from a scope or on unwinding from a panic. +/// Can not run on abort, process exit, and other catastrophic events where +/// destructors don’t run. +#[derive(Debug)] +pub enum Always {} + +/// Run on scope exit through unwinding. +/// +/// Requires crate feature `use_std`. +#[cfg(feature = "use_std")] +#[derive(Debug)] +pub enum OnUnwind {} + +/// Run on regular scope exit, when not unwinding. +/// +/// Requires crate feature `use_std`. +#[cfg(feature = "use_std")] +#[derive(Debug)] +#[cfg(test)] +enum OnSuccess {} + +impl Strategy for Always { + #[inline(always)] + fn should_run() -> bool { true } +} + +#[cfg(feature = "use_std")] +impl Strategy for OnUnwind { + #[inline(always)] + fn should_run() -> bool { std::thread::panicking() } +} + +#[cfg(feature = "use_std")] +#[cfg(test)] +impl Strategy for OnSuccess { + #[inline(always)] + fn should_run() -> bool { !std::thread::panicking() } +} + +/// Macro to create a `ScopeGuard` (always run). +/// +/// The macro takes one expression `$e`, which is the body of a closure +/// that will run when the scope is exited. The expression can +/// be a whole block. +#[macro_export] +macro_rules! defer { + ($e:expr) => { + let _guard = $crate::guard((), |_| $e); + } +} + +/// Macro to create a `ScopeGuard` (run on successful scope exit). +/// +/// The macro takes one expression `$e`, which is the body of a closure +/// that will run when the scope is exited. The expression can +/// be a whole block. +/// +/// Requires crate feature `use_std`. +#[cfg(test)] +macro_rules! defer_on_success { + ($e:expr) => { + let _guard = $crate::guard_on_success((), |_| $e); + } +} + +/// Macro to create a `ScopeGuard` (run on unwinding from panic). +/// +/// The macro takes one expression `$e`, which is the body of a closure +/// that will run when the scope is exited. The expression can +/// be a whole block. +/// +/// Requires crate feature `use_std`. +#[macro_export] +macro_rules! defer_on_unwind { + ($e:expr) => { + let _guard = $crate::guard_on_unwind((), |_| $e); + } +} + +/// `ScopeGuard` is a scope guard that may own a protected value. +/// +/// If you place a guard in a local variable, the closure can +/// run regardless how you leave the scope — through regular return or panic +/// (except if panic or other code aborts; so as long as destructors run). +/// It is run only once. +/// +/// The `S` parameter for [`Strategy`](Strategy.t.html) determines if +/// the closure actually runs. +/// +/// The guard's closure will be called with a mut ref to the held value +/// in the destructor. It's called only once. +/// +/// The `ScopeGuard` implements `Deref` so that you can access the inner value. +pub struct ScopeGuard + where F: FnMut(&mut T) +{ + __dropfn: F, + __value: T, + strategy: PhantomData, +} +impl ScopeGuard + where F: FnMut(&mut T), + S: Strategy, +{ + /// Create a `ScopeGuard` that owns `v` (accessible through deref) and calls + /// `dropfn` when its destructor runs. + /// + /// The `Strategy` decides whether the scope guard's closure should run. + pub fn with_strategy(v: T, dropfn: F) -> ScopeGuard { + ScopeGuard { + __value: v, + __dropfn: dropfn, + strategy: PhantomData, + } + } +} + + +/// Create a new `ScopeGuard` owning `v` and with deferred closure `dropfn`. +pub fn guard(v: T, dropfn: F) -> ScopeGuard + where F: FnMut(&mut T) +{ + ScopeGuard::with_strategy(v, dropfn) +} + +#[cfg(feature = "use_std")] +/// Create a new `ScopeGuard` owning `v` and with deferred closure `dropfn`. +/// +/// Requires crate feature `use_std`. +#[cfg(test)] +fn guard_on_success(v: T, dropfn: F) -> ScopeGuard + where F: FnMut(&mut T) +{ + ScopeGuard::with_strategy(v, dropfn) +} + +#[cfg(feature = "use_std")] +/// Create a new `ScopeGuard` owning `v` and with deferred closure `dropfn`. +/// +/// Requires crate feature `use_std`. +pub fn guard_on_unwind(v: T, dropfn: F) -> ScopeGuard + where F: FnMut(&mut T) +{ + ScopeGuard::with_strategy(v, dropfn) +} + +impl Deref for ScopeGuard + where F: FnMut(&mut T) +{ + type Target = T; + fn deref(&self) -> &T { + &self.__value + } + +} + +impl DerefMut for ScopeGuard + where F: FnMut(&mut T) +{ + fn deref_mut(&mut self) -> &mut T { + &mut self.__value + } +} + +impl Drop for ScopeGuard + where F: FnMut(&mut T) +{ + fn drop(&mut self) { + if S::should_run() { + (self.__dropfn)(&mut self.__value) + } + } +} + +impl fmt::Debug for ScopeGuard + where T: fmt::Debug, + F: FnMut(&mut T), + S: Strategy + fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("ScopeGuard") + .field("value", &self.__value) + .finish() + } +} + +#[cfg(test)] +mod tests { + use std::cell::Cell; + use std::panic::catch_unwind; + use std::panic::AssertUnwindSafe; + + #[test] + fn test_defer() { + let drops = Cell::new(0); + defer!(drops.set(1000)); + assert_eq!(drops.get(), 0); + } + + #[test] + fn test_defer_success_1() { + let drops = Cell::new(0); + { + defer_on_success!(drops.set(1)); + assert_eq!(drops.get(), 0); + } + assert_eq!(drops.get(), 1); + } + + #[test] + fn test_defer_success_2() { + let drops = Cell::new(0); + let _ = catch_unwind(AssertUnwindSafe(|| { + defer_on_success!(drops.set(1)); + panic!("failure") + })); + assert_eq!(drops.get(), 0); + } + + #[test] + fn test_defer_unwind_1() { + let drops = Cell::new(0); + let _ = catch_unwind(AssertUnwindSafe(|| { + defer_on_unwind!(drops.set(1)); + assert_eq!(drops.get(), 0); + panic!("failure") + })); + assert_eq!(drops.get(), 1); + } + + #[test] + fn test_defer_unwind_2() { + let drops = Cell::new(0); + { + defer_on_unwind!(drops.set(1)); + } + assert_eq!(drops.get(), 0); + } +} diff --git a/third_party/rust/scopeguard/.cargo-checksum.json b/third_party/rust/scopeguard/.cargo-checksum.json index 6635efad2c20..b80325b72956 100644 --- a/third_party/rust/scopeguard/.cargo-checksum.json +++ b/third_party/rust/scopeguard/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"9be8c3913111b0a14c16ff1c5dc5613033b3ba6fd9af93262de9e119d0909a90","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"7b63ecd5f1902af1b63729947373683c32745c16a10e8e6292e2e2dcd7e90ae0","README.rst":"ae67e170de747e739273914a468cb93e6e4079b8e277c224c62a18353f660a11","examples/readme.rs":"5a01391acf2acc52a7a2e0ba58dc8ded3e8cc57d54b45778af5e8ba577158f86","src/lib.rs":"2ec3a38a7ca647c94a4c054a2938afd4a07e9cf636b432824623c2fa27a192a3"},"package":"c79eb2c3ac4bc2507cda80e7f3ac5b88bd8eae4c0914d5663e6a8933994be918"} \ No newline at end of file +{"files":{"Cargo.toml":"3338b13bca1bd8bf830f563dd201bc5deed2a4848c7f6485c40ea2f3469c8279","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"7b63ecd5f1902af1b63729947373683c32745c16a10e8e6292e2e2dcd7e90ae0","README.rst":"dc20b385e388c7989454e3a6a96e8112e48680258895562097bf8c809b4d8106","examples/readme.rs":"d00fe19aecd7ca1632bcf176306f7a13ed8fdefa890761aa2c532f8c97532a33","src/lib.rs":"957f1f548d91129c6c9b248dd28c3857a1c988123a70da34dacef416d4204bec"},"package":"b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d"} \ No newline at end of file diff --git a/third_party/rust/scopeguard/Cargo.toml b/third_party/rust/scopeguard/Cargo.toml index 9e0f6c9d6275..c8b6f85ebad8 100644 --- a/third_party/rust/scopeguard/Cargo.toml +++ b/third_party/rust/scopeguard/Cargo.toml @@ -1,26 +1,28 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g. crates.io) dependencies +# +# If you believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + [package] name = "scopeguard" -version = "0.3.2" - +version = "1.0.0" +authors = ["bluss"] +description = "A RAII scope guard that will run a given closure when it goes out of scope,\neven if the code between panics (assuming unwinding panic).\n\nDefines the macros `defer!`, `defer_on_unwind!`, `defer_on_success!` as\nshorthands for guards with one of the implemented strategies.\n" +documentation = "https://docs.rs/scopeguard/" +keywords = ["scope-guard", "defer", "panic", "unwind"] +categories = ["rust-patterns", "no-std"] license = "MIT/Apache-2.0" repository = "https://github.com/bluss/scopeguard" -documentation = "https://docs.rs/scopeguard/" -authors = ["bluss"] - -description = """ -A RAII scope guard that will run a given closure when it goes out of scope, -even if the code between panics (assuming unwinding panic). - -Defines the macros `defer!` and `defer_on_unwind!`; the latter only runs -if the scope is extited through unwinding on panic. -""" - -keywords = ["scope-guard", "defer", "panic"] -categories = ["rust-patterns"] +[package.metadata.release] +no-dev-version = true [features] default = ["use_std"] use_std = [] - -[package.metadata.release] -#no-dev-version = true diff --git a/third_party/rust/scopeguard/README.rst b/third_party/rust/scopeguard/README.rst index 1c1daed51892..016f17539759 100644 --- a/third_party/rust/scopeguard/README.rst +++ b/third_party/rust/scopeguard/README.rst @@ -6,9 +6,9 @@ Rust crate for a convenient RAII scope guard that will run a given closure when it goes out of scope, even if the code between panics (assuming unwinding panic). The `defer!` macro and `guard` are `no_std` compatible (require only core), -but the on unwinding strategy requires linking to `std`. +but the on unwinding / not on uwinding strategies requires linking to `std`. -Requires Rust 1.11. +Requires Rust 1.20. Please read the `API documentation here`__ @@ -47,12 +47,34 @@ How to use let _ = f.sync_all(); }); // Access the file through the scope guard itself - file.write(b"test me\n").unwrap(); + file.write_all(b"test me\n").unwrap(); } Recent Changes -------------- +- 1.0.0 + + - Change the closure type from ``FnMut(&mut T)`` to ``FnOnce(T)``: + Passing the inner value by value instead of a mutable reference is a + breaking change, but allows the guard closure to consume it. (by @tormol) + + - Add ``defer_on_success!{}``, ``guard_on_success()`` and ``OnSuccess`` + strategy, which triggers when scope is exited *without* panic. It's the + opposite to ``OnUnwind`` / ``guard_on_unwind()`` / ``defer_on_unwind!{}``. + + - Add ``ScopeGuard::into_inner()``, which "defuses" the guard and returns the + guarded value. (by @tormol) + + - Implement ``Sync`` for guards with non-``Sync`` closures. + + - Require Rust 1.20 + +- 0.3.3 + + - Use ``#[inline]`` on a few more functions by @stjepang (#14) + - Add examples to crate documentation + - 0.3.2 - Add crate categories diff --git a/third_party/rust/scopeguard/examples/readme.rs b/third_party/rust/scopeguard/examples/readme.rs index 904bc544eb30..0710f9f5a140 100644 --- a/third_party/rust/scopeguard/examples/readme.rs +++ b/third_party/rust/scopeguard/examples/readme.rs @@ -18,7 +18,7 @@ fn g() { let _ = f.sync_all(); }); // Access the file through the scope guard itself - file.write(b"test me\n").unwrap(); + file.write_all(b"test me\n").unwrap(); } fn main() { diff --git a/third_party/rust/scopeguard/src/lib.rs b/third_party/rust/scopeguard/src/lib.rs index 8f8f2b5fc8e4..553420ed7883 100644 --- a/third_party/rust/scopeguard/src/lib.rs +++ b/third_party/rust/scopeguard/src/lib.rs @@ -1,24 +1,203 @@ +#![cfg_attr(not(any(test, feature = "use_std")), no_std)] +#![doc(html_root_url = "https://docs.rs/scopeguard/1/")] + //! A scope guard will run a given closure when it goes out of scope, //! even if the code between panics. //! (as long as panic doesn't abort) - -#![cfg_attr(not(any(test, feature = "use_std")), no_std)] - +//! +//! # Examples +//! +//! ## Hello World +//! +//! This example creates a scope guard with an example function: +//! +//! ``` +//! extern crate scopeguard; +//! +//! fn f() { +//! let _guard = scopeguard::guard((), |_| { +//! println!("Hello Scope Exit!"); +//! }); +//! +//! // rest of the code here. +//! +//! // Here, at the end of `_guard`'s scope, the guard's closure is called. +//! // It is also called if we exit this scope through unwinding instead. +//! } +//! # fn main() { +//! # f(); +//! # } +//! ``` +//! +//! ## `defer!` +//! +//! Use the `defer` macro to run an operation at scope exit, +//! either regular scope exit or during unwinding from a panic. +//! +//! ``` +//! #[macro_use(defer)] extern crate scopeguard; +//! +//! use std::cell::Cell; +//! +//! fn main() { +//! // use a cell to observe drops during and after the scope guard is active +//! let drop_counter = Cell::new(0); +//! { +//! // Create a scope guard using `defer!` for the current scope +//! defer! {{ +//! drop_counter.set(1 + drop_counter.get()); +//! }}; +//! +//! // Do regular operations here in the meantime. +//! +//! // Just before scope exit: it hasn't run yet. +//! assert_eq!(drop_counter.get(), 0); +//! +//! // The following scope end is where the defer closure is called +//! } +//! assert_eq!(drop_counter.get(), 1); +//! } +//! ``` +//! +//! ## Scope Guard with Value +//! +//! If the scope guard closure needs to access an outer value that is also +//! mutated outside of the scope guard, then you may want to use the scope guard +//! with a value. The guard works like a smart pointer, so the inner value can +//! be accessed by reference or by mutable reference. +//! +//! ### 1. The guard owns a file +//! +//! In this example, the scope guard owns a file and ensures pending writes are +//! synced at scope exit. +//! +//! ``` +//! extern crate scopeguard; +//! +//! use std::fs::*; +//! use std::io::{self, Write}; +//! # // Mock file so that we don't actually write a file +//! # struct MockFile; +//! # impl MockFile { +//! # fn create(_s: &str) -> io::Result { Ok(MockFile) } +//! # fn write_all(&self, _b: &[u8]) -> io::Result<()> { Ok(()) } +//! # fn sync_all(&self) -> io::Result<()> { Ok(()) } +//! # } +//! # use self::MockFile as File; +//! +//! fn try_main() -> io::Result<()> { +//! let f = File::create("newfile.txt")?; +//! let mut file = scopeguard::guard(f, |f| { +//! // ensure we flush file at return or panic +//! let _ = f.sync_all(); +//! }); +//! // Access the file through the scope guard itself +//! file.write_all(b"test me\n").map(|_| ()) +//! } +//! +//! fn main() { +//! try_main().unwrap(); +//! } +//! +//! ``` +//! +//! ### 2. The guard restores an invariant on scope exit +//! +//! ``` +//! extern crate scopeguard; +//! +//! use std::mem::ManuallyDrop; +//! use std::ptr; +//! +//! // This function, just for this example, takes the first element +//! // and inserts it into the assumed sorted tail of the vector. +//! // +//! // For optimization purposes we temporarily violate an invariant of the +//! // Vec, that it owns all of its elements. +//! // +//! // The safe approach is to use swap, which means two writes to memory, +//! // the optimization is to use a “hole” which uses only one write of memory +//! // for each position it moves. +//! // +//! // We *must* use a scope guard to run this code safely. We +//! // are running arbitrary user code (comparison operators) that may panic. +//! // The scope guard ensures we restore the invariant after successful +//! // exit or during unwinding from panic. +//! fn insertion_sort_first(v: &mut Vec) +//! where T: PartialOrd +//! { +//! struct Hole<'a, T: 'a> { +//! v: &'a mut Vec, +//! index: usize, +//! value: ManuallyDrop, +//! } +//! +//! unsafe { +//! // Create a moved-from location in the vector, a “hole”. +//! let value = ptr::read(&v[0]); +//! let mut hole = Hole { v: v, index: 0, value: ManuallyDrop::new(value) }; +//! +//! // Use a scope guard with a value. +//! // At scope exit, plug the hole so that the vector is fully +//! // initialized again. +//! // The scope guard owns the hole, but we can access it through the guard. +//! let mut hole_guard = scopeguard::guard(hole, |hole| { +//! // plug the hole in the vector with the value that was // taken out +//! let index = hole.index; +//! ptr::copy_nonoverlapping(&*hole.value, &mut hole.v[index], 1); +//! }); +//! +//! // run algorithm that moves the hole in the vector here +//! // move the hole until it's in a sorted position +//! for i in 1..hole_guard.v.len() { +//! if *hole_guard.value >= hole_guard.v[i] { +//! // move the element back and the hole forward +//! let index = hole_guard.index; +//! ptr::copy_nonoverlapping(&hole_guard.v[index + 1], &mut hole_guard.v[index], 1); +//! hole_guard.index += 1; +//! } else { +//! break; +//! } +//! } +//! +//! // When the scope exits here, the Vec becomes whole again! +//! } +//! } +//! +//! fn main() { +//! let string = String::from; +//! let mut data = vec![string("c"), string("a"), string("b"), string("d")]; +//! insertion_sort_first(&mut data); +//! assert_eq!(data, vec!["a", "b", "c", "d"]); +//! } +//! +//! ``` //! //! -//! Crate features: +//! # Crate Features //! //! - `use_std` -//! + Enabled by default. Enables the `OnUnwind` strategy. +//! + Enabled by default. Enables the `OnUnwind` and `OnSuccess` strategies. //! + Disable to use `no_std`. +//! +//! # Rust Version +//! +//! This version of the crate requires Rust 1.20 or later. +//! +//! The scopeguard 1.x release series will use a carefully considered version +//! upgrade policy, where in a later 1.x version, we will raise the minimum +//! required Rust version. #[cfg(not(any(test, feature = "use_std")))] extern crate core as std; use std::fmt; use std::marker::PhantomData; +use std::mem::{self, ManuallyDrop}; use std::ops::{Deref, DerefMut}; +use std::ptr; +/// Controls in which cases the associated code should be run pub trait Strategy { /// Return `true` if the guard’s associated code should run /// (in the context where this method is called). @@ -45,8 +224,7 @@ pub enum OnUnwind {} /// Requires crate feature `use_std`. #[cfg(feature = "use_std")] #[derive(Debug)] -#[cfg(test)] -enum OnSuccess {} +pub enum OnSuccess {} impl Strategy for Always { #[inline(always)] @@ -55,14 +233,13 @@ impl Strategy for Always { #[cfg(feature = "use_std")] impl Strategy for OnUnwind { - #[inline(always)] + #[inline] fn should_run() -> bool { std::thread::panicking() } } #[cfg(feature = "use_std")] -#[cfg(test)] impl Strategy for OnSuccess { - #[inline(always)] + #[inline] fn should_run() -> bool { !std::thread::panicking() } } @@ -74,7 +251,7 @@ impl Strategy for OnSuccess { #[macro_export] macro_rules! defer { ($e:expr) => { - let _guard = $crate::guard((), |_| $e); + let _guard = $crate::guard((), |()| $e); } } @@ -85,10 +262,11 @@ macro_rules! defer { /// be a whole block. /// /// Requires crate feature `use_std`. -#[cfg(test)] +#[cfg(feature = "use_std")] +#[macro_export] macro_rules! defer_on_success { ($e:expr) => { - let _guard = $crate::guard_on_success((), |_| $e); + let _guard = $crate::guard_on_success((), |()| $e); } } @@ -99,10 +277,11 @@ macro_rules! defer_on_success { /// be a whole block. /// /// Requires crate feature `use_std`. +#[cfg(feature = "use_std")] #[macro_export] macro_rules! defer_on_unwind { ($e:expr) => { - let _guard = $crate::guard_on_unwind((), |_| $e); + let _guard = $crate::guard_on_unwind((), |()| $e); } } @@ -116,105 +295,187 @@ macro_rules! defer_on_unwind { /// The `S` parameter for [`Strategy`](Strategy.t.html) determines if /// the closure actually runs. /// -/// The guard's closure will be called with a mut ref to the held value -/// in the destructor. It's called only once. +/// The guard's closure will be called with the held value in the destructor. /// /// The `ScopeGuard` implements `Deref` so that you can access the inner value. -pub struct ScopeGuard - where F: FnMut(&mut T) +pub struct ScopeGuard + where F: FnOnce(T), + S: Strategy, { - __dropfn: F, - __value: T, - strategy: PhantomData, + value: ManuallyDrop, + dropfn: ManuallyDrop, + strategy: PhantomData S>, } + impl ScopeGuard - where F: FnMut(&mut T), + where F: FnOnce(T), S: Strategy, { /// Create a `ScopeGuard` that owns `v` (accessible through deref) and calls /// `dropfn` when its destructor runs. /// /// The `Strategy` decides whether the scope guard's closure should run. + #[inline] pub fn with_strategy(v: T, dropfn: F) -> ScopeGuard { ScopeGuard { - __value: v, - __dropfn: dropfn, + value: ManuallyDrop::new(v), + dropfn: ManuallyDrop::new(dropfn), strategy: PhantomData, } } + + /// “Defuse” the guard and extract the value without calling the closure. + /// + /// ``` + /// extern crate scopeguard; + /// use scopeguard::{guard, ScopeGuard}; + /// + /// fn conditional() -> bool { true } + /// + /// fn main() { + /// let mut guard = guard(Vec::new(), |mut v| v.clear()); + /// guard.push(1); + /// + /// if conditional() { + /// // a condition maybe makes us decide to + /// // “defuse” the guard and get back its inner parts + /// let value = ScopeGuard::into_inner(guard); + /// } else { + /// // guard still exists in this branch + /// } + /// } + /// ``` + #[inline] + pub fn into_inner(guard: Self) -> T { + // Cannot pattern match out of Drop-implementing types, so + // ptr::read the value and forget the guard. + unsafe { + let value = ptr::read(&*guard.value); + // read the closure so that it is dropped, and assign it to a local + // variable to ensure that it is only dropped after the guard has + // been forgotten. (In case the Drop impl of the closure, or that + // of any consumed captured variable, panics). + let _dropfn = ptr::read(&*guard.dropfn); + mem::forget(guard); + value + } + } } /// Create a new `ScopeGuard` owning `v` and with deferred closure `dropfn`. +#[inline] pub fn guard(v: T, dropfn: F) -> ScopeGuard - where F: FnMut(&mut T) + where F: FnOnce(T) { ScopeGuard::with_strategy(v, dropfn) } -#[cfg(feature = "use_std")] /// Create a new `ScopeGuard` owning `v` and with deferred closure `dropfn`. /// /// Requires crate feature `use_std`. -#[cfg(test)] -fn guard_on_success(v: T, dropfn: F) -> ScopeGuard - where F: FnMut(&mut T) +#[cfg(feature = "use_std")] +#[inline] +pub fn guard_on_success(v: T, dropfn: F) -> ScopeGuard + where F: FnOnce(T) { ScopeGuard::with_strategy(v, dropfn) } -#[cfg(feature = "use_std")] /// Create a new `ScopeGuard` owning `v` and with deferred closure `dropfn`. /// /// Requires crate feature `use_std`. +/// +/// ## Examples +/// +/// For performance reasons, or to emulate “only run guard on unwind” in +/// no-std environments, we can also use the default guard and simply manually +/// defuse it at the end of scope like the following example. (The performance +/// reason would be if the [`OnUnwind`]'s call to [std::thread::panicking()] is +/// an issue.) +/// +/// ``` +/// extern crate scopeguard; +/// +/// use scopeguard::ScopeGuard; +/// # fn main() { +/// { +/// let guard = scopeguard::guard((), |_| { }); +/// +/// // rest of the code here +/// +/// // we reached the end of scope without unwinding - defuse it +/// ScopeGuard::into_inner(guard); +/// } +/// # } +/// ``` +#[cfg(feature = "use_std")] +#[inline] pub fn guard_on_unwind(v: T, dropfn: F) -> ScopeGuard - where F: FnMut(&mut T) + where F: FnOnce(T) { ScopeGuard::with_strategy(v, dropfn) } -impl Deref for ScopeGuard - where F: FnMut(&mut T) +// ScopeGuard can be Sync even if F isn't because the closure is +// not accessible from references. +// The guard does not store any instance of S, so it is also irellevant. +unsafe impl Sync for ScopeGuard + where T: Sync, + F: FnOnce(T), + S: Strategy +{ } + +impl Deref for ScopeGuard + where F: FnOnce(T), + S: Strategy { type Target = T; fn deref(&self) -> &T { - &self.__value + &*self.value } - } -impl DerefMut for ScopeGuard - where F: FnMut(&mut T) +impl DerefMut for ScopeGuard + where F: FnOnce(T), + S: Strategy { fn deref_mut(&mut self) -> &mut T { - &mut self.__value + &mut*self.value } } -impl Drop for ScopeGuard - where F: FnMut(&mut T) +impl Drop for ScopeGuard + where F: FnOnce(T), + S: Strategy { fn drop(&mut self) { + // This is OK because the fields are `ManuallyDrop`s + // which will not be dropped by the compiler. + let (value, dropfn) = unsafe { + (ptr::read(&*self.value), ptr::read(&*self.dropfn)) + }; if S::should_run() { - (self.__dropfn)(&mut self.__value) + dropfn(value); } } } impl fmt::Debug for ScopeGuard where T: fmt::Debug, - F: FnMut(&mut T), - S: Strategy + fmt::Debug, + F: FnOnce(T), + S: Strategy { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("ScopeGuard") - .field("value", &self.__value) + f.debug_struct(stringify!(ScopeGuard)) + .field("value", &*self.value) .finish() } } #[cfg(test)] mod tests { + use super::*; use std::cell::Cell; use std::panic::catch_unwind; use std::panic::AssertUnwindSafe; @@ -226,6 +487,7 @@ mod tests { assert_eq!(drops.get(), 0); } + #[cfg(feature = "use_std")] #[test] fn test_defer_success_1() { let drops = Cell::new(0); @@ -236,6 +498,7 @@ mod tests { assert_eq!(drops.get(), 1); } + #[cfg(feature = "use_std")] #[test] fn test_defer_success_2() { let drops = Cell::new(0); @@ -246,6 +509,7 @@ mod tests { assert_eq!(drops.get(), 0); } + #[cfg(feature = "use_std")] #[test] fn test_defer_unwind_1() { let drops = Cell::new(0); @@ -257,6 +521,7 @@ mod tests { assert_eq!(drops.get(), 1); } + #[cfg(feature = "use_std")] #[test] fn test_defer_unwind_2() { let drops = Cell::new(0); @@ -265,4 +530,49 @@ mod tests { } assert_eq!(drops.get(), 0); } + + #[test] + fn test_only_dropped_by_closure_when_run() { + let value_drops = Cell::new(0); + let value = guard((), |()| value_drops.set(1 + value_drops.get())); + let closure_drops = Cell::new(0); + let guard = guard(value, |_| closure_drops.set(1 + closure_drops.get())); + assert_eq!(value_drops.get(), 0); + assert_eq!(closure_drops.get(), 0); + drop(guard); + assert_eq!(value_drops.get(), 1); + assert_eq!(closure_drops.get(), 1); + } + + #[cfg(feature = "use_std")] + #[test] + fn test_dropped_once_when_not_run() { + let value_drops = Cell::new(0); + let value = guard((), |()| value_drops.set(1 + value_drops.get())); + let captured_drops = Cell::new(0); + let captured = guard((), |()| captured_drops.set(1 + captured_drops.get())); + let closure_drops = Cell::new(0); + let guard = guard_on_unwind(value, |value| { + drop(value); + drop(captured); + closure_drops.set(1 + closure_drops.get()) + }); + assert_eq!(value_drops.get(), 0); + assert_eq!(captured_drops.get(), 0); + assert_eq!(closure_drops.get(), 0); + drop(guard); + assert_eq!(value_drops.get(), 1); + assert_eq!(captured_drops.get(), 1); + assert_eq!(closure_drops.get(), 0); + } + + #[test] + fn test_into_inner() { + let dropped = Cell::new(false); + let value = guard(42, |_| dropped.set(true)); + let guard = guard(value, |_| dropped.set(true)); + let inner = ScopeGuard::into_inner(guard); + assert_eq!(dropped.get(), false); + assert_eq!(*inner, 42); + } } diff --git a/third_party/rust/uuid/.cargo-checksum.json b/third_party/rust/uuid/.cargo-checksum.json index 0561a28a9c5e..5fed3eeb6a93 100644 --- a/third_party/rust/uuid/.cargo-checksum.json +++ b/third_party/rust/uuid/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"CODEOWNERS":"65d3fcb4156a2d5bce80d382a34044753e384d7f1eb71cdc646de400a0b969c8","CODE_OF_CONDUCT.md":"7d9c9062ee835c2dfd348cfddb938c563f3a7b1140dd090444a03ea1d73626b1","CONTRIBUTING.md":"cb8fa34c1d15542a318a7cbe3cd41d364f6100d49043825413f3ebb4c7471c99","COPYRIGHT":"b4b2c0de2a05de3372d5c828128413ce82bb7dba2272487b7729f09cc3d3519d","Cargo.toml":"71a942d30769089e36cbe32fade63a078961f3cab23d41ab87f5c8e8efcd9348","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"436bc5a105d8e57dcd8778730f3754f7bf39c14d2f530e4cde4bd2d17a83ec3d","README.md":"01e0a86cc2b9f29fbef04623e3f8e8ca46252fd8d5360b83780fa816f6012011","benches/format_str.rs":"2bcff80d8ab03c83ccc61c756f75a0c7aead821f68b29eeaf38f4ba6344bfc6e","benches/invalid_parse_str.rs":"7f44d2ebec6ee1368d179f12dd09a288d589b252434082b2de134a658b460812","benches/mod.rs":"4733d7aa62dafe3e85ab90dca518b57f350a5538ea5643c5313e63939e884a45","benches/serde_support.rs":"afc719718c9a5d705b60bc9cd39720b921d9ee63ccf11c4b2900f02beea70c1b","benches/slog_support/mod.rs":"1be626f0a6921f4e6bd333ce7ab4a6c4da1fb6f3ae1c503672b4ba168a70c01d","benches/slog_support/parse_str.rs":"9c63ee7047ac8b9d08f02b7081020dd7300f84f302068927c859bbe26cea66a3","benches/valid_parse_str.rs":"7db47c7d25b20c8da03f25107fbea2b5c97fc814ff226e8489eda374f477eeac","src/adapter/core_support/mod.rs":"65bebe5034e450782ec9b0942bd4d758795ee315095fcc3f54412630a987f938","src/adapter/mod.rs":"8f3acffda66148a7095286309f32b823c66826bfe35742fbc63bf1a1e07365a5","src/core_support.rs":"f5c83e3e16a32ae93c76e0e1699f3a07872788e782ce5ee4c2f8229a06a871c8","src/lib.rs":"1d0492819a93dc386e90659a21f6231a01dd172552a1221a298c089db7c671b1","src/parser/core_support.rs":"a8621aa837da2f4cd82f86539c2f3f153c52fcea21c223aa098e5873cbf36d0f","src/parser/mod.rs":"51526e211c95730c830512007da23dfc9f88d1feccc9ddf881c541c1d5e01e2a","src/parser/std_support.rs":"4398d708bd42e8d3cb31eed8ada92615fb1cbfc70bfb3c7cbe952f47c7fe1183","src/prelude.rs":"89553bb9a75e3801698f57fcc099235e5213452738cace4ab190d9444a1adfa4","src/serde_support.rs":"fdc5be309b9fc062832f3a2b114d7a06e8c8357f6569796ed1fc7848a5ebc155","src/slog_support.rs":"370f891a73f99436baecd21f5f2b7d7c89842336aad99167b07ca3f03c48a70c","src/std_support.rs":"50d2bdaaae64e4d22d9180404ac8600944683dcdc51d7de6587a6fdb29193a70","src/test_util.rs":"1dfc1ab68bb403dd6d696fafeb7c00be59c37b51155703f3033ebf1062dd629f","src/u128_support.rs":"97ca20af9117e44bad72f987488efb0173699a22e0c646b268e0fe3dd90355a7","src/v1.rs":"82654b0cadfa56fd0140d78db5ab2d9869ea3d8eaaede0b975d42904317e9da4","src/v3.rs":"d25899b070bd791bc2b784d828399f5bce25f77300765dfd96e76583f31047f3","src/v4.rs":"0cc02041d1215826e9fa2493fb4d97b1d15bc4925450db22eba86123680586ef","src/v5.rs":"11aeea13d38c5e3c5d7cc8bf571ac1ce57a0d46f363b90a991ed43dc1cc9caaa"},"package":"dab5c5526c5caa3d106653401a267fed923e7046f35895ffcb5ca42db64942e6"} \ No newline at end of file +{"files":{"CODEOWNERS":"65d3fcb4156a2d5bce80d382a34044753e384d7f1eb71cdc646de400a0b969c8","CODE_OF_CONDUCT.md":"7d9c9062ee835c2dfd348cfddb938c563f3a7b1140dd090444a03ea1d73626b1","CONTRIBUTING.md":"c2b507733d5af2de972d63237a094a135935ad45cc74dedb79c199d841f35a3e","COPYRIGHT":"b4b2c0de2a05de3372d5c828128413ce82bb7dba2272487b7729f09cc3d3519d","Cargo.toml":"2647794e162e5e764854003d4e0ca2e2d0de5f7c11e3ec61ab53fae310328aab","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"436bc5a105d8e57dcd8778730f3754f7bf39c14d2f530e4cde4bd2d17a83ec3d","README.md":"f82b58d44ed24b07cc8e3a14e233ff2d0aa297732da1b789f16a84293de39e23","README.tpl":"1d5787815cea427e5cf0cce634bc629b03d51dda355fa52de500a9658c45625b","benches/format_str.rs":"0d080946d397a2578a978105a5a27309edded7115d2081e683170f0bf96edc3e","benches/invalid_parse_str.rs":"7f44d2ebec6ee1368d179f12dd09a288d589b252434082b2de134a658b460812","benches/mod.rs":"4733d7aa62dafe3e85ab90dca518b57f350a5538ea5643c5313e63939e884a45","benches/serde_support.rs":"afc719718c9a5d705b60bc9cd39720b921d9ee63ccf11c4b2900f02beea70c1b","benches/slog_support/mod.rs":"1be626f0a6921f4e6bd333ce7ab4a6c4da1fb6f3ae1c503672b4ba168a70c01d","benches/slog_support/parse_str.rs":"9c63ee7047ac8b9d08f02b7081020dd7300f84f302068927c859bbe26cea66a3","benches/valid_parse_str.rs":"7db47c7d25b20c8da03f25107fbea2b5c97fc814ff226e8489eda374f477eeac","src/adapter/compact.rs":"fa76330d5ff33fbb0f7da5898caee64d0e74fbe435c86621fefb864ee43560ec","src/adapter/core_support/mod.rs":"65bebe5034e450782ec9b0942bd4d758795ee315095fcc3f54412630a987f938","src/adapter/mod.rs":"6051f59190e438bbbd702e173fc85cc410b0d9b07b553991600064a34d53d2da","src/builder.rs":"86d8607e783a4a7429712edeae4ed34aff776064f1ce4b99d8b71461146b3e38","src/core_support.rs":"c5c94c3eab19a5833ec588db86d10aa94731f724d7fc5554f6e47d072ccdd88b","src/lib.rs":"2794fa11c36c09f4e356ce8ad0859f0df7eced8b03716e7df2aa971f30ff5059","src/parser/core_support.rs":"e37812662674fef191aca4574d705cbfaac6516a99a6871d88f7843494e5e653","src/parser/mod.rs":"51526e211c95730c830512007da23dfc9f88d1feccc9ddf881c541c1d5e01e2a","src/parser/std_support.rs":"4398d708bd42e8d3cb31eed8ada92615fb1cbfc70bfb3c7cbe952f47c7fe1183","src/prelude.rs":"c2c359c483993ffa3b2469ee5017d68d5c9d0c4226758112f585949e76971fff","src/serde_support.rs":"8821ba4b73f35d9a1ab19b3a32922cbdc991a7dce07062ca98230f45fdd57d98","src/slog_support.rs":"370f891a73f99436baecd21f5f2b7d7c89842336aad99167b07ca3f03c48a70c","src/std_support.rs":"eeb0d6560f96c8ce3cacafa6fe5342a8ad3b86387bf3455fb66459e28b39a6e1","src/test_util.rs":"1dfc1ab68bb403dd6d696fafeb7c00be59c37b51155703f3033ebf1062dd629f","src/u128_support.rs":"97ca20af9117e44bad72f987488efb0173699a22e0c646b268e0fe3dd90355a7","src/v1.rs":"82654b0cadfa56fd0140d78db5ab2d9869ea3d8eaaede0b975d42904317e9da4","src/v3.rs":"d25899b070bd791bc2b784d828399f5bce25f77300765dfd96e76583f31047f3","src/v4.rs":"c38784386b1f44d6333c4447140dd8ba0deec2d8c5bace5abd0e48f523716b0b","src/v5.rs":"11aeea13d38c5e3c5d7cc8bf571ac1ce57a0d46f363b90a991ed43dc1cc9caaa","src/winapi_support.rs":"13d2d83dd14ece29dfd88b4c5985ef62ff8017278bac0809dba334483881c457"},"package":"90dbc611eb48397705a6b0f6e917da23ae517e4d127123d2cf7674206627d32a"} \ No newline at end of file diff --git a/third_party/rust/uuid/CONTRIBUTING.md b/third_party/rust/uuid/CONTRIBUTING.md index 1d085ca2b5af..2bd9f87b3034 100644 --- a/third_party/rust/uuid/CONTRIBUTING.md +++ b/third_party/rust/uuid/CONTRIBUTING.md @@ -88,7 +88,7 @@ If the pull request is still a work in progress, prepend`[WIP] ` in your title. `WIP bot` will make sure that the PR doesn't accidentally get merged. > Uuid Project has a minimum rust version policy. Currently `uuid` should -compile with atleast `1.18.0`, and is enforced on our CI builds. +compile with atleast `1.22.0`, and is enforced on our CI builds. When you feel that the PR is ready, please ping one of the maintainers so they can review your changes. diff --git a/third_party/rust/uuid/Cargo.toml b/third_party/rust/uuid/Cargo.toml index aa95e11dcbfb..81d607d373e9 100644 --- a/third_party/rust/uuid/Cargo.toml +++ b/third_party/rust/uuid/Cargo.toml @@ -12,7 +12,7 @@ [package] name = "uuid" -version = "0.7.1" +version = "0.7.4" authors = ["Ashley Mannix", "Christopher Armstrong", "Dylan DPC", "Hunar Roop Kahlon"] exclude = [".github/**", ".travis.yml", "appveyor.yml", "bors.toml"] description = "A library to generate and parse UUIDs." @@ -22,7 +22,8 @@ readme = "README.md" license = "Apache-2.0 OR MIT" repository = "https://github.com/uuid-rs/uuid" [package.metadata.docs.rs] -all-features = true +default-target = "x86_64-pc-windows-msvc" +features = ["guid", "serde", "slog", "v1", "v3", "v4", "v5"] [package.metadata.playground] features = ["serde", "u128", "v1", "v3", "v4", "v5"] @@ -33,11 +34,11 @@ optional = true default-features = false [dependencies.md5] -version = "0.3" +version = "0.6" optional = true [dependencies.rand] -version = "0.5" +version = "0.6" optional = true [dependencies.serde] @@ -55,6 +56,9 @@ optional = true [dev-dependencies.bincode] version = "1.0" +[dev-dependencies.serde_derive] +version = "1.0.79" + [dev-dependencies.serde_json] version = "1.0" @@ -64,10 +68,30 @@ version = "1.0.56" [features] const_fn = ["nightly"] default = ["std"] +guid = ["winapi"] nightly = [] std = [] +stdweb = ["rand/stdweb"] u128 = ["byteorder"] v1 = [] -v3 = ["md5", "rand"] +v3 = ["md5"] v4 = ["rand"] -v5 = ["sha1", "rand"] +v5 = ["sha1"] +wasm-bindgen = ["rand/wasm-bindgen"] +[target."cfg(windows)".dependencies.winapi] +version = "0.3" +optional = true +[badges.appveyor] +repository = "uuid-rs/uuid" + +[badges.is-it-maintained-issue-resolution] +repository = "uuid-rs/uuid" + +[badges.is-it-maintained-open-issues] +repository = "uuid-rs/uuid" + +[badges.maintenance] +status = "actively-developed" + +[badges.travis-ci] +repository = "uuid-rs/uuid" diff --git a/third_party/rust/uuid/README.md b/third_party/rust/uuid/README.md index e5ff5c9801ef..32b5db021864 100644 --- a/third_party/rust/uuid/README.md +++ b/third_party/rust/uuid/README.md @@ -1,81 +1,98 @@ uuid -==== +--------- -[![Build Status](https://travis-ci.org/uuid-rs/uuid.svg?branch=master)](https://travis-ci.org/uuid-rs/uuid) -[![Appveyor Status](https://ci.appveyor.com/api/projects/status/github/uuid-rs/uuid?branch=master&svg=true)](https://ci.appveyor.com/project/KodrAus/uuid) -[![Latest Version](https://img.shields.io/crates/v/uuid.svg)](https://crates.io/crates/uuid) +[![Latest Version](https://img.shields.io/crates/v/uuid.svg)](https://crates.io/crates/uuid) [![Join the chat at https://gitter.im/uuid-rs/Lobby](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/uuid-rs/Lobby?utm_source=badge&utm_medium=badge&utm_content=badge) ![Minimum rustc version](https://img.shields.io/badge/rustc-1.22.0+-yellow.svg) +[![Build Status](https://ci.appveyor.com/api/projects/status/github/uuid-rs/uuid?branch=master&svg=true)](https://ci.appveyor.com/project/uuid-rs/uuid/branch/master) +[![Build Status](https://travis-ci.org/uuid-rs/uuid.svg?branch=master)](https://travis-ci.org/uuid-rs/uuid) +[![Average time to resolve an issue](https://isitmaintained.com/badge/resolution/uuid-rs/uuid.svg)](https://isitmaintained.com/project/uuid-rs/uuid "Average time to resolve an issue") +[![Percentage of issues still open](https://isitmaintained.com/badge/open/uuid-rs/uuid.svg)](https://isitmaintained.com/project/uuid-rs/uuid "Percentage of issues still open") -A Rust library to generate and parse UUIDs. +--- -Provides support for Universally Unique Identifiers (UUIDs). A UUID is a unique -128-bit number, stored as 16 octets. UUIDs are used to assign unique identifiers -to entities without requiring a central allocating authority. +Generate and parse UUIDs. + +Provides support for Universally Unique Identifiers (UUIDs). A UUID is a +unique 128-bit number, stored as 16 octets. UUIDs are used to assign +unique identifiers to entities without requiring a central allocating +authority. They are particularly useful in distributed systems, though can be used in -disparate areas, such as databases and network protocols. Typically a UUID is -displayed in a readable string form as a sequence of hexadecimal digits, +disparate areas, such as databases and network protocols. Typically a UUID +is displayed in a readable string form as a sequence of hexadecimal digits, separated into groups by hyphens. -The uniqueness property is not strictly guaranteed, however for all practical -purposes, it can be assumed that an unintentional collision would be extremely -unlikely. +The uniqueness property is not strictly guaranteed, however for all +practical purposes, it can be assumed that an unintentional collision would +be extremely unlikely. -[Documentation](https://docs.rs/uuid) +## Dependencies -## Usage +By default, this crate depends on nothing but `std` and cannot generate +[`Uuid`]s. You need to enable the following Cargo features to enable +various pieces of functionality: -Add this to your `Cargo.toml`: +* `v1` - adds the `Uuid::new_v1` function and the ability to create a V1 + using an implementation of `uuid::v1::ClockSequence` (usually +`uuid::v1::Context`) and a timestamp from `time::timespec`. +* `v3` - adds the `Uuid::new_v3` function and the ability to create a V3 + UUID based on the MD5 hash of some data. +* `v4` - adds the `Uuid::new_v4` function and the ability to randomly + generate a `Uuid`. +* `v5` - adds the `Uuid::new_v5` function and the ability to create a V5 + UUID based on the SHA1 hash of some data. +* `serde` - adds the ability to serialize and deserialize a `Uuid` using the + `serde` crate. + +You need to enable one of the following Cargo features together with +`v3`, `v4` or `v5` feature if you're targeting `wasm32` architecture: + +* `stdweb` - enables support for `OsRng` on `wasm32-unknown-unknown` via + `stdweb` combined with `cargo-web` +* `wasm-bindgen` - `wasm-bindgen` enables support for `OsRng` on + `wasm32-unknown-unknown` via [`wasm-bindgen`] + +By default, `uuid` can be depended on with: ```toml [dependencies] uuid = "0.7" ``` -and this to your crate root: +To activate various features, use syntax like: -```rust -extern crate uuid; +```toml +[dependencies] +uuid = { version = "0.7", features = ["serde", "v4"] } +``` + +You can disable default features with: + +```toml +[dependencies] +uuid = { version = "0.7", default-features = false } ``` ## Examples -To parse a simple UUID, then print the version and urn string format: +To parse a UUID given in the simple format and print it as a urn: ```rust -extern crate uuid; use uuid::Uuid; fn main() { - let my_uuid = Uuid::parse_str("936DA01F9ABD4d9d80C702AF85C822A8").unwrap(); - println!("Parsed a version {} UUID.", my_uuid.get_version_num()); - println!("{}", my_uuid); + let my_uuid = + Uuid::parse_str("936DA01F9ABD4d9d80C702AF85C822A8").unwrap(); + println!("{}", my_uuid.to_urn()); } ``` -The library supports 5 versions of UUID: +To create a new random (V4) UUID and print it out in hexadecimal form: -Name | Version ----------|---------- -Mac | Version 1: MAC address -Dce | Version 2: DCE Security -Md5 | Version 3: MD5 hash -Random | Version 4: Random -Sha1 | Version 5: SHA-1 hash +```ignore,rust +// Note that this requires the `v4` feature enabled in the uuid crate. -To create a new random (V4) UUID and print it out in hexadecimal form, first -you'll need to change how you depend on `uuid`: - -```toml -[dependencies] -uuid = { version = "0.7", features = ["v4"] } -``` - -Next, you'll write: - -```rust -extern crate uuid; use uuid::Uuid; fn main() { @@ -84,26 +101,35 @@ fn main() { } ``` -To create a new sha1-hash based (V5) UUID and print it out in hexadecimal form, -you'll also need to change how you depend on `uuid`: +## Strings -```toml -[dependencies] -uuid = { version = "0.7", features = ["v5"] } -``` +Examples of string representations: -Next, you'll write: - -```rust -extern crate uuid; -use uuid::Uuid; - -fn main() { - let my_uuid = Uuid::new_v5(&Uuid::NAMESPACE_DNS, "foo".as_bytes()); - println!("{}", my_uuid); -} -``` +* simple: `936DA01F9ABD4d9d80C702AF85C822A8` +* hyphenated: `550e8400-e29b-41d4-a716-446655440000` +* urn: `urn:uuid:F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4` ## References -[Wikipedia: Universally Unique Identifier](https://en.wikipedia.org/wiki/Universally_unique_identifier) +* [Wikipedia: Universally Unique Identifier]( http://en.wikipedia.org/wiki/Universally_unique_identifier) +* [RFC4122: A Universally Unique IDentifier (UUID) URN Namespace]( http://tools.ietf.org/html/rfc4122) + +[`wasm-bindgen`]: https://github.com/rustwasm/wasm-bindgen + +[`Uuid`]: https://docs.rs/uuid/0.7.4/uuid/struct.Uuid.html + +--- +# License + +Licensed under either of + +* Apache License, Version 2.0, (LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0) +* MIT license (LICENSE-MIT or https://opensource.org/licenses/MIT) + +at your option. + +## Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. diff --git a/third_party/rust/uuid/README.tpl b/third_party/rust/uuid/README.tpl new file mode 100644 index 000000000000..186ef0b7a4a7 --- /dev/null +++ b/third_party/rust/uuid/README.tpl @@ -0,0 +1,29 @@ +{{crate}} +--------- + +[![Latest Version](https://img.shields.io/crates/v/uuid.svg)](https://crates.io/crates/uuid) +[![Join the chat at https://gitter.im/uuid-rs/Lobby](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/uuid-rs/Lobby?utm_source=badge&utm_medium=badge&utm_content=badge) +![Minimum rustc version](https://img.shields.io/badge/rustc-1.22.0+-yellow.svg) +{{badges}} + +--- + +{{readme}} + +[`Uuid`]: https://docs.rs/uuid/{{version}}/uuid/struct.Uuid.html + +--- +# License + +Licensed under either of + +* Apache License, Version 2.0, (LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0) +* MIT license (LICENSE-MIT or https://opensource.org/licenses/MIT) + +at your option. + +## Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. diff --git a/third_party/rust/uuid/benches/format_str.rs b/third_party/rust/uuid/benches/format_str.rs old mode 100755 new mode 100644 index 0d06c9003dae..0bb67e3101c2 --- a/third_party/rust/uuid/benches/format_str.rs +++ b/third_party/rust/uuid/benches/format_str.rs @@ -41,7 +41,7 @@ fn bench_encode_hyphen(b: &mut Bencher) { let uuid = Uuid::parse_str("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4").unwrap(); b.iter(|| { let mut buffer = [0_u8; 36]; - // uuid.to_hyphenated().encode_lower(&mut buffer); + uuid.to_hyphenated().encode_lower(&mut buffer); test::black_box(buffer); }); } @@ -51,7 +51,7 @@ fn bench_encode_simple(b: &mut Bencher) { let uuid = Uuid::parse_str("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4").unwrap(); b.iter(|| { let mut buffer = [0_u8; 32]; - // uuid.to_simple().encode_lower(&mut buffer); + uuid.to_simple().encode_lower(&mut buffer); test::black_box(buffer); }) } @@ -61,7 +61,7 @@ fn bench_encode_urn(b: &mut Bencher) { let uuid = Uuid::parse_str("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4").unwrap(); b.iter(|| { let mut buffer = [0_u8; 36 + 9]; - // uuid.to_urn().encode_lower(&mut buffer); + uuid.to_urn().encode_lower(&mut buffer); test::black_box(buffer); }) } diff --git a/third_party/rust/uuid/benches/serde_support.rs b/third_party/rust/uuid/benches/serde_support.rs old mode 100755 new mode 100644 diff --git a/third_party/rust/uuid/src/adapter/compact.rs b/third_party/rust/uuid/src/adapter/compact.rs new file mode 100644 index 000000000000..57891f0f7a01 --- /dev/null +++ b/third_party/rust/uuid/src/adapter/compact.rs @@ -0,0 +1,84 @@ +//! Module for use with `#[serde(with = "...")]` to serialize a [`Uuid`] +//! as a `[u8; 16] +//! +//! [`Uuid`]: ../../struct.Uuid.html + +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +use prelude::*; + +/// Serializer for a [`Uuid`] into a `[u8; 16]` +/// +/// [`Uuid`]: ../../struct.Uuid.html +pub fn serialize( + u: &Uuid, + serializer: S, +) -> Result { + u.as_bytes().serialize(serializer) +} + +/// Deserializer from a `[u8; 16]` into a [`Uuid`] +/// +/// [`Uuid`]: ../../struct.Uuid.html +pub fn deserialize<'de, D: Deserializer<'de>>( + deserializer: D, +) -> Result { + let bytes = <[u8; 16]>::deserialize(deserializer)?; + + Ok(Uuid::from_bytes(bytes)) +} + +#[cfg(test)] +mod tests { + use serde_test; + + use prelude::*; + + #[derive(Serialize, Debug, Deserialize, PartialEq)] + struct UuidContainer { + #[serde(with = "super")] + u: Uuid, + } + + #[test] + fn test_serialize_compact() { + use serde_test::Configure; + + let uuid_bytes = b"F9168C5E-CEB2-4F"; + let container = UuidContainer { + u: Uuid::from_slice(uuid_bytes).unwrap(), + }; + + // more complex because of the struct wrapping the actual UUID + // serialization + serde_test::assert_tokens( + &container.compact(), + &[ + serde_test::Token::Struct { + name: "UuidContainer", + len: 1, + }, + serde_test::Token::Str("u"), + serde_test::Token::Tuple { len: 16 }, + serde_test::Token::U8(uuid_bytes[0]), + serde_test::Token::U8(uuid_bytes[1]), + serde_test::Token::U8(uuid_bytes[2]), + serde_test::Token::U8(uuid_bytes[3]), + serde_test::Token::U8(uuid_bytes[4]), + serde_test::Token::U8(uuid_bytes[5]), + serde_test::Token::U8(uuid_bytes[6]), + serde_test::Token::U8(uuid_bytes[7]), + serde_test::Token::U8(uuid_bytes[8]), + serde_test::Token::U8(uuid_bytes[9]), + serde_test::Token::U8(uuid_bytes[10]), + serde_test::Token::U8(uuid_bytes[11]), + serde_test::Token::U8(uuid_bytes[12]), + serde_test::Token::U8(uuid_bytes[13]), + serde_test::Token::U8(uuid_bytes[14]), + serde_test::Token::U8(uuid_bytes[15]), + serde_test::Token::TupleEnd, + serde_test::Token::StructEnd, + ], + ) + } +} diff --git a/third_party/rust/uuid/src/adapter/mod.rs b/third_party/rust/uuid/src/adapter/mod.rs index e35c37de31a4..88fb2719c90f 100644 --- a/third_party/rust/uuid/src/adapter/mod.rs +++ b/third_party/rust/uuid/src/adapter/mod.rs @@ -18,6 +18,9 @@ use prelude::*; mod core_support; +#[cfg(feature = "serde")] +pub mod compact; + /// An adaptor for formatting an [`Uuid`] as a hyphenated string. /// /// Takes an owned instance of the [`Uuid`]. @@ -230,12 +233,11 @@ fn encode<'a>( // before this point. That's exactly the (0-indexed) group // number. let hyphens_before = if hyphens { group } else { 0 }; - for idx in BYTE_POSITIONS[group]..BYTE_POSITIONS[group + 1] { let b = bytes[idx]; let out_idx = hyphens_before + 2 * idx; - buffer[out_idx + 0] = hex[(b >> 4) as usize]; + buffer[out_idx] = hex[(b >> 4) as usize]; buffer[out_idx + 1] = hex[(b & 0b1111) as usize]; } @@ -273,101 +275,95 @@ impl Hyphenated { Hyphenated(uuid) } - // Writes the [`Uuid`] as a lower-case hyphenated string to - // `buffer`, and returns the subslice of the buffer that contains the - // encoded UUID. - // - // This is slightly more efficient than using the formatting - // infrastructure as it avoids virtual calls, and may avoid - // double buffering. - // - // [`Uuid`]: ../struct.Uuid.html - // - // # Panics - // - // Panics if the buffer is not large enough: it must have length at least - // [`LENGTH`]. [`Uuid::encode_buffer`] can be used to get a - // sufficiently-large temporary buffer. - // - // [`LENGTH`]: #associatedconstant.LENGTH - // [`Uuid::encode_buffer`]: ../struct.Uuid.html#method.encode_buffer - // - // # Examples - // - // ```rust - // use uuid::Uuid; - // - // let uuid = Uuid::parse_str("936DA01f9abd4d9d80c702af85c822a8").unwrap(); - // - // // the encoded portion is returned - // assert_eq!( - // uuid.to_hyphenated() - // .encode_lower(&mut Uuid::encode_buffer()), - // "936da01f-9abd-4d9d-80c7-02af85c822a8" - // ); - // - // // the buffer is mutated directly, and trailing contents remains - // let mut buf = [b'!'; 40]; - // uuid.to_hyphenated().encode_lower(&mut buf); - // assert_eq!( - // &buf as &[_], - // b"936da01f-9abd-4d9d-80c7-02af85c822a8!!!!" as &[_] - // ); - // ``` - // */ - pub(crate) fn encode_lower<'buf>( - &self, - buffer: &'buf mut [u8], - ) -> &'buf mut str { + /// Writes the [`Uuid`] as a lower-case hyphenated string to + /// `buffer`, and returns the subslice of the buffer that contains the + /// encoded UUID. + /// + /// This is slightly more efficient than using the formatting + /// infrastructure as it avoids virtual calls, and may avoid + /// double buffering. + /// + /// [`Uuid`]: ../struct.Uuid.html + /// + /// # Panics + /// + /// Panics if the buffer is not large enough: it must have length at least + /// [`LENGTH`]. [`Uuid::encode_buffer`] can be used to get a + /// sufficiently-large temporary buffer. + /// + /// [`LENGTH`]: #associatedconstant.LENGTH + /// [`Uuid::encode_buffer`]: ../struct.Uuid.html#method.encode_buffer + /// + /// # Examples + /// + /// ```rust + /// use uuid::Uuid; + /// + /// let uuid = Uuid::parse_str("936DA01f9abd4d9d80c702af85c822a8").unwrap(); + /// + /// // the encoded portion is returned + /// assert_eq!( + /// uuid.to_hyphenated() + /// .encode_lower(&mut Uuid::encode_buffer()), + /// "936da01f-9abd-4d9d-80c7-02af85c822a8" + /// ); + /// + /// // the buffer is mutated directly, and trailing contents remains + /// let mut buf = [b'!'; 40]; + /// uuid.to_hyphenated().encode_lower(&mut buf); + /// assert_eq!( + /// &buf as &[_], + /// b"936da01f-9abd-4d9d-80c7-02af85c822a8!!!!" as &[_] + /// ); + /// ``` + /// */ + pub fn encode_lower<'buf>(&self, buffer: &'buf mut [u8]) -> &'buf mut str { encode(buffer, 0, &self.0, true, false) } - // Writes the [`Uuid`] as an upper-case hyphenated string to - // `buffer`, and returns the subslice of the buffer that contains the - // encoded UUID. - // - // This is slightly more efficient than using the formatting - // infrastructure as it avoids virtual calls, and may avoid - // double buffering. - // - // [`Uuid`]: ../struct.Uuid.html - // - // # Panics - // - // Panics if the buffer is not large enough: it must have length at least - // [`LENGTH`]. [`Uuid::encode_buffer`] can be used to get a - // sufficiently-large temporary buffer. - // - // [`LENGTH`]: #associatedconstant.LENGTH - // [`Uuid::encode_buffer`]: ../struct.Uuid.html#method.encode_buffer - // - // # Examples - // - // ```rust - // use uuid::Uuid; - // - // let uuid = Uuid::parse_str("936da01f9abd4d9d80c702af85c822a8").unwrap(); - // - // // the encoded portion is returned - // assert_eq!( - // uuid.to_hyphenated() - // .encode_upper(&mut Uuid::encode_buffer()), - // "936DA01F-9ABD-4D9D-80C7-02AF85C822A8" - // ); - // - // // the buffer is mutated directly, and trailing contents remains - // let mut buf = [b'!'; 40]; - // uuid.to_hyphenated().encode_upper(&mut buf); - // assert_eq!( - // &buf as &[_], - // b"936DA01F-9ABD-4D9D-80C7-02AF85C822A8!!!!" as &[_] - // ); - // ``` - // */ - pub(crate) fn encode_upper<'buf>( - &self, - buffer: &'buf mut [u8], - ) -> &'buf mut str { + /// Writes the [`Uuid`] as an upper-case hyphenated string to + /// `buffer`, and returns the subslice of the buffer that contains the + /// encoded UUID. + /// + /// This is slightly more efficient than using the formatting + /// infrastructure as it avoids virtual calls, and may avoid + /// double buffering. + /// + /// [`Uuid`]: ../struct.Uuid.html + /// + /// # Panics + /// + /// Panics if the buffer is not large enough: it must have length at least + /// [`LENGTH`]. [`Uuid::encode_buffer`] can be used to get a + /// sufficiently-large temporary buffer. + /// + /// [`LENGTH`]: #associatedconstant.LENGTH + /// [`Uuid::encode_buffer`]: ../struct.Uuid.html#method.encode_buffer + /// + /// # Examples + /// + /// ```rust + /// use uuid::Uuid; + /// + /// let uuid = Uuid::parse_str("936da01f9abd4d9d80c702af85c822a8").unwrap(); + /// + /// // the encoded portion is returned + /// assert_eq!( + /// uuid.to_hyphenated() + /// .encode_upper(&mut Uuid::encode_buffer()), + /// "936DA01F-9ABD-4D9D-80C7-02AF85C822A8" + /// ); + /// + /// // the buffer is mutated directly, and trailing contents remains + /// let mut buf = [b'!'; 40]; + /// uuid.to_hyphenated().encode_upper(&mut buf); + /// assert_eq!( + /// &buf as &[_], + /// b"936DA01F-9ABD-4D9D-80C7-02AF85C822A8!!!!" as &[_] + /// ); + /// ``` + /// */ + pub fn encode_upper<'buf>(&self, buffer: &'buf mut [u8]) -> &'buf mut str { encode(buffer, 0, &self.0, true, true) } } @@ -396,108 +392,102 @@ impl<'a> HyphenatedRef<'a> { HyphenatedRef(uuid) } - // Writes the [`Uuid`] as a lower-case hyphenated string to - // `buffer`, and returns the subslice of the buffer that contains the - // encoded UUID. - // - // This is slightly more efficient than using the formatting - // infrastructure as it avoids virtual calls, and may avoid - // double buffering. - // - // [`Uuid`]: ../struct.Uuid.html - // - // # Panics - // - // Panics if the buffer is not large enough: it must have length at least - // [`LENGTH`]. [`Uuid::encode_buffer`] can be used to get a - // sufficiently-large temporary buffer. - // - // [`LENGTH`]: #associatedconstant.LENGTH - // [`Uuid::encode_buffer`]: ../struct.Uuid.html#method.encode_buffer - // - // # Examples - // - // ```rust - // use uuid::Uuid; - // - // let uuid = Uuid::parse_str("936DA01f9abd4d9d80c702af85c822a8").unwrap(); - // - // // the encoded portion is returned - // assert_eq!( - // uuid.to_hyphenated() - // .encode_lower(&mut Uuid::encode_buffer()), - // "936da01f-9abd-4d9d-80c7-02af85c822a8" - // ); - // - // // the buffer is mutated directly, and trailing contents remains - // let mut buf = [b'!'; 40]; - // uuid.to_hyphenated().encode_lower(&mut buf); - // assert_eq!( - // uuid.to_hyphenated().encode_lower(&mut buf), - // "936da01f-9abd-4d9d-80c7-02af85c822a8" - // ); - // assert_eq!( - // &buf as &[_], - // b"936da01f-9abd-4d9d-80c7-02af85c822a8!!!!" as &[_] - // ); - // ``` - // */ - pub(crate) fn encode_lower<'buf>( - &self, - buffer: &'buf mut [u8], - ) -> &'buf mut str { + /// Writes the [`Uuid`] as a lower-case hyphenated string to + /// `buffer`, and returns the subslice of the buffer that contains the + /// encoded UUID. + /// + /// This is slightly more efficient than using the formatting + /// infrastructure as it avoids virtual calls, and may avoid + /// double buffering. + /// + /// [`Uuid`]: ../struct.Uuid.html + /// + /// # Panics + /// + /// Panics if the buffer is not large enough: it must have length at least + /// [`LENGTH`]. [`Uuid::encode_buffer`] can be used to get a + /// sufficiently-large temporary buffer. + /// + /// [`LENGTH`]: #associatedconstant.LENGTH + /// [`Uuid::encode_buffer`]: ../struct.Uuid.html#method.encode_buffer + /// + /// # Examples + /// + /// ```rust + /// use uuid::Uuid; + /// + /// let uuid = Uuid::parse_str("936DA01f9abd4d9d80c702af85c822a8").unwrap(); + /// + /// // the encoded portion is returned + /// assert_eq!( + /// uuid.to_hyphenated() + /// .encode_lower(&mut Uuid::encode_buffer()), + /// "936da01f-9abd-4d9d-80c7-02af85c822a8" + /// ); + /// + /// // the buffer is mutated directly, and trailing contents remains + /// let mut buf = [b'!'; 40]; + /// uuid.to_hyphenated().encode_lower(&mut buf); + /// assert_eq!( + /// uuid.to_hyphenated().encode_lower(&mut buf), + /// "936da01f-9abd-4d9d-80c7-02af85c822a8" + /// ); + /// assert_eq!( + /// &buf as &[_], + /// b"936da01f-9abd-4d9d-80c7-02af85c822a8!!!!" as &[_] + /// ); + /// ``` + /// */ + pub fn encode_lower<'buf>(&self, buffer: &'buf mut [u8]) -> &'buf mut str { encode(buffer, 0, self.0, true, false) } - // Writes the [`Uuid`] as an upper-case hyphenated string to - // `buffer`, and returns the subslice of the buffer that contains the - // encoded UUID. - // - // This is slightly more efficient than using the formatting - // infrastructure as it avoids virtual calls, and may avoid - // double buffering. - // - // [`Uuid`]: ../struct.Uuid.html - // - // # Panics - // - // Panics if the buffer is not large enough: it must have length at least - // [`LENGTH`]. [`Uuid::encode_buffer`] can be used to get a - // sufficiently-large temporary buffer. - // - // [`LENGTH`]: #associatedconstant.LENGTH - // [`Uuid::encode_buffer`]: ../struct.Uuid.html#method.encode_buffer - // - // # Examples - // - // ```rust - // use uuid::Uuid; - // - // let uuid = Uuid::parse_str("936da01f9abd4d9d80c702af85c822a8").unwrap(); - // - // // the encoded portion is returned - // assert_eq!( - // uuid.to_hyphenated() - // .encode_upper(&mut Uuid::encode_buffer()), - // "936DA01F-9ABD-4D9D-80C7-02AF85C822A8" - // ); - // - // // the buffer is mutated directly, and trailing contents remains - // let mut buf = [b'!'; 40]; - // assert_eq!( - // uuid.to_hyphenated().encode_upper(&mut buf), - // "936DA01F-9ABD-4D9D-80C7-02AF85C822A8" - // ); - // assert_eq!( - // &buf as &[_], - // b"936DA01F-9ABD-4D9D-80C7-02AF85C822A8!!!!" as &[_] - // ); - // ``` - // */ - pub(crate) fn encode_upper<'buf>( - &self, - buffer: &'buf mut [u8], - ) -> &'buf mut str { + /// Writes the [`Uuid`] as an upper-case hyphenated string to + /// `buffer`, and returns the subslice of the buffer that contains the + /// encoded UUID. + /// + /// This is slightly more efficient than using the formatting + /// infrastructure as it avoids virtual calls, and may avoid + /// double buffering. + /// + /// [`Uuid`]: ../struct.Uuid.html + /// + /// # Panics + /// + /// Panics if the buffer is not large enough: it must have length at least + /// [`LENGTH`]. [`Uuid::encode_buffer`] can be used to get a + /// sufficiently-large temporary buffer. + /// + /// [`LENGTH`]: #associatedconstant.LENGTH + /// [`Uuid::encode_buffer`]: ../struct.Uuid.html#method.encode_buffer + /// + /// # Examples + /// + /// ```rust + /// use uuid::Uuid; + /// + /// let uuid = Uuid::parse_str("936da01f9abd4d9d80c702af85c822a8").unwrap(); + /// + /// // the encoded portion is returned + /// assert_eq!( + /// uuid.to_hyphenated() + /// .encode_upper(&mut Uuid::encode_buffer()), + /// "936DA01F-9ABD-4D9D-80C7-02AF85C822A8" + /// ); + /// + /// // the buffer is mutated directly, and trailing contents remains + /// let mut buf = [b'!'; 40]; + /// assert_eq!( + /// uuid.to_hyphenated().encode_upper(&mut buf), + /// "936DA01F-9ABD-4D9D-80C7-02AF85C822A8" + /// ); + /// assert_eq!( + /// &buf as &[_], + /// b"936DA01F-9ABD-4D9D-80C7-02AF85C822A8!!!!" as &[_] + /// ); + /// ``` + /// */ + pub fn encode_upper<'buf>(&self, buffer: &'buf mut [u8]) -> &'buf mut str { encode(buffer, 0, self.0, true, true) } } @@ -526,99 +516,93 @@ impl Simple { Simple(uuid) } - // Writes the [`Uuid`] as a lower-case simple string to `buffer`, - // and returns the subslice of the buffer that contains the encoded UUID. - // - // This is slightly more efficient than using the formatting - // infrastructure as it avoids virtual calls, and may avoid - // double buffering. - // - // [`Uuid`]: ../struct.Uuid.html - // - // # Panics - // - // Panics if the buffer is not large enough: it must have length at least - // [`LENGTH`]. [`Uuid::encode_buffer`] can be used to get a - // sufficiently-large temporary buffer. - // - // [`LENGTH`]: #associatedconstant.LENGTH - // [`Uuid::encode_buffer`]: ../struct.Uuid.html#method.encode_buffer - // - // # Examples - // - // ```rust - // use uuid::Uuid; - // - // let uuid = Uuid::parse_str("936DA01f9abd4d9d80c702af85c822a8").unwrap(); - // - // // the encoded portion is returned - // assert_eq!( - // uuid.to_simple().encode_lower(&mut Uuid::encode_buffer()), - // "936da01f9abd4d9d80c702af85c822a8" - // ); - // - // // the buffer is mutated directly, and trailing contents remains - // let mut buf = [b'!'; 36]; - // assert_eq!( - // uuid.to_simple().encode_lower(&mut buf), - // "936da01f9abd4d9d80c702af85c822a8" - // ); - // assert_eq!( - // &buf as &[_], - // b"936da01f9abd4d9d80c702af85c822a8!!!!" as &[_] - // ); - // ``` - // */ - pub(crate) fn encode_lower<'buf>( - &self, - buffer: &'buf mut [u8], - ) -> &'buf mut str { + /// Writes the [`Uuid`] as a lower-case simple string to `buffer`, + /// and returns the subslice of the buffer that contains the encoded UUID. + /// + /// This is slightly more efficient than using the formatting + /// infrastructure as it avoids virtual calls, and may avoid + /// double buffering. + /// + /// [`Uuid`]: ../struct.Uuid.html + /// + /// # Panics + /// + /// Panics if the buffer is not large enough: it must have length at least + /// [`LENGTH`]. [`Uuid::encode_buffer`] can be used to get a + /// sufficiently-large temporary buffer. + /// + /// [`LENGTH`]: #associatedconstant.LENGTH + /// [`Uuid::encode_buffer`]: ../struct.Uuid.html#method.encode_buffer + /// + /// # Examples + /// + /// ```rust + /// use uuid::Uuid; + /// + /// let uuid = Uuid::parse_str("936DA01f9abd4d9d80c702af85c822a8").unwrap(); + /// + /// // the encoded portion is returned + /// assert_eq!( + /// uuid.to_simple().encode_lower(&mut Uuid::encode_buffer()), + /// "936da01f9abd4d9d80c702af85c822a8" + /// ); + /// + /// // the buffer is mutated directly, and trailing contents remains + /// let mut buf = [b'!'; 36]; + /// assert_eq!( + /// uuid.to_simple().encode_lower(&mut buf), + /// "936da01f9abd4d9d80c702af85c822a8" + /// ); + /// assert_eq!( + /// &buf as &[_], + /// b"936da01f9abd4d9d80c702af85c822a8!!!!" as &[_] + /// ); + /// ``` + /// */ + pub fn encode_lower<'buf>(&self, buffer: &'buf mut [u8]) -> &'buf mut str { encode(buffer, 0, &self.0, false, false) } - // Writes the [`Uuid`] as an upper-case simple string to `buffer`, - // and returns the subslice of the buffer that contains the encoded UUID. - // - // [`Uuid`]: ../struct.Uuid.html - // - // # Panics - // - // Panics if the buffer is not large enough: it must have length at least - // [`LENGTH`]. [`Uuid::encode_buffer`] can be used to get a - // sufficiently-large temporary buffer. - // - // [`LENGTH`]: #associatedconstant.LENGTH - // [`Uuid::encode_buffer`]: ../struct.Uuid.html#method.encode_buffer - // - // # Examples - // - // ```rust - // use uuid::Uuid; - // - // let uuid = Uuid::parse_str("936da01f9abd4d9d80c702af85c822a8").unwrap(); - // - // // the encoded portion is returned - // assert_eq!( - // uuid.to_simple().encode_upper(&mut Uuid::encode_buffer()), - // "936DA01F9ABD4D9D80C702AF85C822A8" - // ); - // - // // the buffer is mutated directly, and trailing contents remains - // let mut buf = [b'!'; 36]; - // assert_eq!( - // uuid.to_simple().encode_upper(&mut buf), - // "936DA01F9ABD4D9D80C702AF85C822A8" - // ); - // assert_eq!( - // &buf as &[_], - // b"936DA01F9ABD4D9D80C702AF85C822A8!!!!" as &[_] - // ); - // ``` - // */ - pub(crate) fn encode_upper<'buf>( - &self, - buffer: &'buf mut [u8], - ) -> &'buf mut str { + /// Writes the [`Uuid`] as an upper-case simple string to `buffer`, + /// and returns the subslice of the buffer that contains the encoded UUID. + /// + /// [`Uuid`]: ../struct.Uuid.html + /// + /// # Panics + /// + /// Panics if the buffer is not large enough: it must have length at least + /// [`LENGTH`]. [`Uuid::encode_buffer`] can be used to get a + /// sufficiently-large temporary buffer. + /// + /// [`LENGTH`]: #associatedconstant.LENGTH + /// [`Uuid::encode_buffer`]: ../struct.Uuid.html#method.encode_buffer + /// + /// # Examples + /// + /// ```rust + /// use uuid::Uuid; + /// + /// let uuid = Uuid::parse_str("936da01f9abd4d9d80c702af85c822a8").unwrap(); + /// + /// // the encoded portion is returned + /// assert_eq!( + /// uuid.to_simple().encode_upper(&mut Uuid::encode_buffer()), + /// "936DA01F9ABD4D9D80C702AF85C822A8" + /// ); + /// + /// // the buffer is mutated directly, and trailing contents remains + /// let mut buf = [b'!'; 36]; + /// assert_eq!( + /// uuid.to_simple().encode_upper(&mut buf), + /// "936DA01F9ABD4D9D80C702AF85C822A8" + /// ); + /// assert_eq!( + /// &buf as &[_], + /// b"936DA01F9ABD4D9D80C702AF85C822A8!!!!" as &[_] + /// ); + /// ``` + /// */ + pub fn encode_upper<'buf>(&self, buffer: &'buf mut [u8]) -> &'buf mut str { encode(buffer, 0, &self.0, false, true) } } @@ -647,99 +631,93 @@ impl<'a> SimpleRef<'a> { SimpleRef(uuid) } - // Writes the [`Uuid`] as a lower-case simple string to `buffer`, - // and returns the subslice of the buffer that contains the encoded UUID. - // - // This is slightly more efficient than using the formatting - // infrastructure as it avoids virtual calls, and may avoid - // double buffering. - // - // [`Uuid`]: ../struct.Uuid.html - // - // # Panics - // - // Panics if the buffer is not large enough: it must have length at least - // [`LENGTH`]. [`Uuid::encode_buffer`] can be used to get a - // sufficiently-large temporary buffer. - // - // [`LENGTH`]: #associatedconstant.LENGTH - // [`Uuid::encode_buffer`]: ../struct.Uuid.html#method.encode_buffer - // - // # Examples - // - // ```rust - // use uuid::Uuid; - // - // let uuid = Uuid::parse_str("936DA01f9abd4d9d80c702af85c822a8").unwrap(); - // - // // the encoded portion is returned - // assert_eq!( - // uuid.to_simple().encode_lower(&mut Uuid::encode_buffer()), - // "936da01f9abd4d9d80c702af85c822a8" - // ); - // - // // the buffer is mutated directly, and trailing contents remains - // let mut buf = [b'!'; 36]; - // assert_eq!( - // uuid.to_simple().encode_lower(&mut buf), - // "936da01f9abd4d9d80c702af85c822a8" - // ); - // assert_eq!( - // &buf as &[_], - // b"936da01f9abd4d9d80c702af85c822a8!!!!" as &[_] - // ); - // ``` - // */ - pub(crate) fn encode_lower<'buf>( - &self, - buffer: &'buf mut [u8], - ) -> &'buf mut str { + /// Writes the [`Uuid`] as a lower-case simple string to `buffer`, + /// and returns the subslice of the buffer that contains the encoded UUID. + /// + /// This is slightly more efficient than using the formatting + /// infrastructure as it avoids virtual calls, and may avoid + /// double buffering. + /// + /// [`Uuid`]: ../struct.Uuid.html + /// + /// # Panics + /// + /// Panics if the buffer is not large enough: it must have length at least + /// [`LENGTH`]. [`Uuid::encode_buffer`] can be used to get a + /// sufficiently-large temporary buffer. + /// + /// [`LENGTH`]: #associatedconstant.LENGTH + /// [`Uuid::encode_buffer`]: ../struct.Uuid.html#method.encode_buffer + /// + /// # Examples + /// + /// ```rust + /// use uuid::Uuid; + /// + /// let uuid = Uuid::parse_str("936DA01f9abd4d9d80c702af85c822a8").unwrap(); + /// + /// // the encoded portion is returned + /// assert_eq!( + /// uuid.to_simple().encode_lower(&mut Uuid::encode_buffer()), + /// "936da01f9abd4d9d80c702af85c822a8" + /// ); + /// + /// // the buffer is mutated directly, and trailing contents remains + /// let mut buf = [b'!'; 36]; + /// assert_eq!( + /// uuid.to_simple().encode_lower(&mut buf), + /// "936da01f9abd4d9d80c702af85c822a8" + /// ); + /// assert_eq!( + /// &buf as &[_], + /// b"936da01f9abd4d9d80c702af85c822a8!!!!" as &[_] + /// ); + /// ``` + /// */ + pub fn encode_lower<'buf>(&self, buffer: &'buf mut [u8]) -> &'buf mut str { encode(buffer, 0, self.0, false, false) } - // Writes the [`Uuid`] as an upper-case simple string to `buffer`, - // and returns the subslice of the buffer that contains the encoded UUID. - // - // [`Uuid`]: ../struct.Uuid.html - // - // # Panics - // - // Panics if the buffer is not large enough: it must have length at least - // [`LENGTH`]. [`Uuid::encode_buffer`] can be used to get a - // sufficiently-large temporary buffer. - // - // [`LENGTH`]: #associatedconstant.LENGTH - // [`Uuid::encode_buffer`]: ../struct.Uuid.html#method.encode_buffer - // - // # Examples - // - // ```rust - // use uuid::Uuid; - // - // let uuid = Uuid::parse_str("936da01f9abd4d9d80c702af85c822a8").unwrap(); - // - // // the encoded portion is returned - // assert_eq!( - // uuid.to_simple().encode_upper(&mut Uuid::encode_buffer()), - // "936DA01F9ABD4D9D80C702AF85C822A8" - // ); - // - // // the buffer is mutated directly, and trailing contents remains - // let mut buf = [b'!'; 36]; - // assert_eq!( - // uuid.to_simple().encode_upper(&mut buf), - // "936DA01F9ABD4D9D80C702AF85C822A8" - // ); - // assert_eq!( - // &buf as &[_], - // b"936DA01F9ABD4D9D80C702AF85C822A8!!!!" as &[_] - // ); - // ``` - // */ - pub(crate) fn encode_upper<'buf>( - &self, - buffer: &'buf mut [u8], - ) -> &'buf mut str { + /// Writes the [`Uuid`] as an upper-case simple string to `buffer`, + /// and returns the subslice of the buffer that contains the encoded UUID. + /// + /// [`Uuid`]: ../struct.Uuid.html + /// + /// # Panics + /// + /// Panics if the buffer is not large enough: it must have length at least + /// [`LENGTH`]. [`Uuid::encode_buffer`] can be used to get a + /// sufficiently-large temporary buffer. + /// + /// [`LENGTH`]: #associatedconstant.LENGTH + /// [`Uuid::encode_buffer`]: ../struct.Uuid.html#method.encode_buffer + /// + /// # Examples + /// + /// ```rust + /// use uuid::Uuid; + /// + /// let uuid = Uuid::parse_str("936da01f9abd4d9d80c702af85c822a8").unwrap(); + /// + /// // the encoded portion is returned + /// assert_eq!( + /// uuid.to_simple().encode_upper(&mut Uuid::encode_buffer()), + /// "936DA01F9ABD4D9D80C702AF85C822A8" + /// ); + /// + /// // the buffer is mutated directly, and trailing contents remains + /// let mut buf = [b'!'; 36]; + /// assert_eq!( + /// uuid.to_simple().encode_upper(&mut buf), + /// "936DA01F9ABD4D9D80C702AF85C822A8" + /// ); + /// assert_eq!( + /// &buf as &[_], + /// b"936DA01F9ABD4D9D80C702AF85C822A8!!!!" as &[_] + /// ); + /// ``` + /// */ + pub fn encode_upper<'buf>(&self, buffer: &'buf mut [u8]) -> &'buf mut str { encode(buffer, 0, self.0, false, true) } } @@ -768,107 +746,101 @@ impl Urn { Urn(uuid) } - // Writes the [`Uuid`] as a lower-case URN string to - // `buffer`, and returns the subslice of the buffer that contains the - // encoded UUID. - // - // This is slightly more efficient than using the formatting - // infrastructure as it avoids virtual calls, and may avoid - // double buffering. - // - // [`Uuid`]: ../struct.Uuid.html - // - // # Panics - // - // Panics if the buffer is not large enough: it must have length at least - // [`LENGTH`]. [`Uuid::encode_buffer`] can be used to get a - // sufficiently-large temporary buffer. - // - // [`LENGTH`]: #associatedconstant.LENGTH - // [`Uuid::encode_buffer`]: ../struct.Uuid.html#method.encode_buffer - // - // # Examples - // - // ```rust - // use uuid::Uuid; - // - // let uuid = Uuid::parse_str("936DA01f9abd4d9d80c702af85c822a8").unwrap(); - // - // // the encoded portion is returned - // assert_eq!( - // uuid.to_urn().encode_lower(&mut Uuid::encode_buffer()), - // "urn:uuid:936da01f-9abd-4d9d-80c7-02af85c822a8" - // ); - // - // // the buffer is mutated directly, and trailing contents remains - // let mut buf = [b'!'; 49]; - // uuid.to_urn().encode_lower(&mut buf); - // assert_eq!( - // uuid.to_urn().encode_lower(&mut buf), - // "urn:uuid:936da01f-9abd-4d9d-80c7-02af85c822a8" - // ); - // assert_eq!( - // &buf as &[_], - // b"urn:uuid:936da01f-9abd-4d9d-80c7-02af85c822a8!!!!" as &[_] - // ); - // ``` - // */ - pub(crate) fn encode_lower<'buf>( - &self, - buffer: &'buf mut [u8], - ) -> &'buf mut str { + /// Writes the [`Uuid`] as a lower-case URN string to + /// `buffer`, and returns the subslice of the buffer that contains the + /// encoded UUID. + /// + /// This is slightly more efficient than using the formatting + /// infrastructure as it avoids virtual calls, and may avoid + /// double buffering. + /// + /// [`Uuid`]: ../struct.Uuid.html + /// + /// # Panics + /// + /// Panics if the buffer is not large enough: it must have length at least + /// [`LENGTH`]. [`Uuid::encode_buffer`] can be used to get a + /// sufficiently-large temporary buffer. + /// + /// [`LENGTH`]: #associatedconstant.LENGTH + /// [`Uuid::encode_buffer`]: ../struct.Uuid.html#method.encode_buffer + /// + /// # Examples + /// + /// ```rust + /// use uuid::Uuid; + /// + /// let uuid = Uuid::parse_str("936DA01f9abd4d9d80c702af85c822a8").unwrap(); + /// + /// // the encoded portion is returned + /// assert_eq!( + /// uuid.to_urn().encode_lower(&mut Uuid::encode_buffer()), + /// "urn:uuid:936da01f-9abd-4d9d-80c7-02af85c822a8" + /// ); + /// + /// // the buffer is mutated directly, and trailing contents remains + /// let mut buf = [b'!'; 49]; + /// uuid.to_urn().encode_lower(&mut buf); + /// assert_eq!( + /// uuid.to_urn().encode_lower(&mut buf), + /// "urn:uuid:936da01f-9abd-4d9d-80c7-02af85c822a8" + /// ); + /// assert_eq!( + /// &buf as &[_], + /// b"urn:uuid:936da01f-9abd-4d9d-80c7-02af85c822a8!!!!" as &[_] + /// ); + /// ``` + /// */ + pub fn encode_lower<'buf>(&self, buffer: &'buf mut [u8]) -> &'buf mut str { buffer[..9].copy_from_slice(b"urn:uuid:"); encode(buffer, 9, &self.0, true, false) } - // Writes the [`Uuid`] as an upper-case URN string to - // `buffer`, and returns the subslice of the buffer that contains the - // encoded UUID. - // - // This is slightly more efficient than using the formatting - // infrastructure as it avoids virtual calls, and may avoid - // double buffering. - // - // [`Uuid`]: ../struct.Uuid.html - // - // # Panics - // - // Panics if the buffer is not large enough: it must have length at least - // [`LENGTH`]. [`Uuid::encode_buffer`] can be used to get a - // sufficiently-large temporary buffer. - // - // [`LENGTH`]: #associatedconstant.LENGTH - // [`Uuid::encode_buffer`]: ../struct.Uuid.html#method.encode_buffer - // - // # Examples - // - // ```rust - // use uuid::Uuid; - // - // let uuid = Uuid::parse_str("936da01f9abd4d9d80c702af85c822a8").unwrap(); - // - // // the encoded portion is returned - // assert_eq!( - // uuid.to_urn().encode_upper(&mut Uuid::encode_buffer()), - // "urn:uuid:936DA01F-9ABD-4D9D-80C7-02AF85C822A8" - // ); - // - // // the buffer is mutated directly, and trailing contents remains - // let mut buf = [b'!'; 49]; - // assert_eq!( - // uuid.to_urn().encode_upper(&mut buf), - // "urn:uuid:936DA01F-9ABD-4D9D-80C7-02AF85C822A8" - // ); - // assert_eq!( - // &buf as &[_], - // b"urn:uuid:936DA01F-9ABD-4D9D-80C7-02AF85C822A8!!!!" as &[_] - // ); - // ``` - // */ - pub(crate) fn encode_upper<'buf>( - &self, - buffer: &'buf mut [u8], - ) -> &'buf mut str { + /// Writes the [`Uuid`] as an upper-case URN string to + /// `buffer`, and returns the subslice of the buffer that contains the + /// encoded UUID. + /// + /// This is slightly more efficient than using the formatting + /// infrastructure as it avoids virtual calls, and may avoid + /// double buffering. + /// + /// [`Uuid`]: ../struct.Uuid.html + /// + /// # Panics + /// + /// Panics if the buffer is not large enough: it must have length at least + /// [`LENGTH`]. [`Uuid::encode_buffer`] can be used to get a + /// sufficiently-large temporary buffer. + /// + /// [`LENGTH`]: #associatedconstant.LENGTH + /// [`Uuid::encode_buffer`]: ../struct.Uuid.html#method.encode_buffer + /// + /// # Examples + /// + /// ```rust + /// use uuid::Uuid; + /// + /// let uuid = Uuid::parse_str("936da01f9abd4d9d80c702af85c822a8").unwrap(); + /// + /// // the encoded portion is returned + /// assert_eq!( + /// uuid.to_urn().encode_upper(&mut Uuid::encode_buffer()), + /// "urn:uuid:936DA01F-9ABD-4D9D-80C7-02AF85C822A8" + /// ); + /// + /// // the buffer is mutated directly, and trailing contents remains + /// let mut buf = [b'!'; 49]; + /// assert_eq!( + /// uuid.to_urn().encode_upper(&mut buf), + /// "urn:uuid:936DA01F-9ABD-4D9D-80C7-02AF85C822A8" + /// ); + /// assert_eq!( + /// &buf as &[_], + /// b"urn:uuid:936DA01F-9ABD-4D9D-80C7-02AF85C822A8!!!!" as &[_] + /// ); + /// ``` + /// */ + pub fn encode_upper<'buf>(&self, buffer: &'buf mut [u8]) -> &'buf mut str { buffer[..9].copy_from_slice(b"urn:uuid:"); encode(buffer, 9, &self.0, true, true) } @@ -898,107 +870,101 @@ impl<'a> UrnRef<'a> { UrnRef(&uuid) } - // Writes the [`Uuid`] as a lower-case URN string to - // `buffer`, and returns the subslice of the buffer that contains the - // encoded UUID. - // - // This is slightly more efficient than using the formatting - // infrastructure as it avoids virtual calls, and may avoid - // double buffering. - // - // [`Uuid`]: ../struct.Uuid.html - // - // # Panics - // - // Panics if the buffer is not large enough: it must have length at least - // [`LENGTH`]. [`Uuid::encode_buffer`] can be used to get a - // sufficiently-large temporary buffer. - // - // [`LENGTH`]: #associatedconstant.LENGTH - // [`Uuid::encode_buffer`]: ../struct.Uuid.html#method.encode_buffer - // - // # Examples - // - // ```rust - // use uuid::Uuid; - // - // let uuid = Uuid::parse_str("936DA01f9abd4d9d80c702af85c822a8").unwrap(); - // - // // the encoded portion is returned - // assert_eq!( - // uuid.to_urn().encode_lower(&mut Uuid::encode_buffer()), - // "urn:uuid:936da01f-9abd-4d9d-80c7-02af85c822a8" - // ); - // - // // the buffer is mutated directly, and trailing contents remains - // let mut buf = [b'!'; 49]; - // uuid.to_urn().encode_lower(&mut buf); - // assert_eq!( - // uuid.to_urn().encode_lower(&mut buf), - // "urn:uuid:936da01f-9abd-4d9d-80c7-02af85c822a8" - // ); - // assert_eq!( - // &buf as &[_], - // b"urn:uuid:936da01f-9abd-4d9d-80c7-02af85c822a8!!!!" as &[_] - // ); - // ``` - // */ - pub(crate) fn encode_lower<'buf>( - &self, - buffer: &'buf mut [u8], - ) -> &'buf mut str { + /// Writes the [`Uuid`] as a lower-case URN string to + /// `buffer`, and returns the subslice of the buffer that contains the + /// encoded UUID. + /// + /// This is slightly more efficient than using the formatting + /// infrastructure as it avoids virtual calls, and may avoid + /// double buffering. + /// + /// [`Uuid`]: ../struct.Uuid.html + /// + /// # Panics + /// + /// Panics if the buffer is not large enough: it must have length at least + /// [`LENGTH`]. [`Uuid::encode_buffer`] can be used to get a + /// sufficiently-large temporary buffer. + /// + /// [`LENGTH`]: #associatedconstant.LENGTH + /// [`Uuid::encode_buffer`]: ../struct.Uuid.html#method.encode_buffer + /// + /// # Examples + /// + /// ```rust + /// use uuid::Uuid; + /// + /// let uuid = Uuid::parse_str("936DA01f9abd4d9d80c702af85c822a8").unwrap(); + /// + /// // the encoded portion is returned + /// assert_eq!( + /// uuid.to_urn().encode_lower(&mut Uuid::encode_buffer()), + /// "urn:uuid:936da01f-9abd-4d9d-80c7-02af85c822a8" + /// ); + /// + /// // the buffer is mutated directly, and trailing contents remains + /// let mut buf = [b'!'; 49]; + /// uuid.to_urn().encode_lower(&mut buf); + /// assert_eq!( + /// uuid.to_urn().encode_lower(&mut buf), + /// "urn:uuid:936da01f-9abd-4d9d-80c7-02af85c822a8" + /// ); + /// assert_eq!( + /// &buf as &[_], + /// b"urn:uuid:936da01f-9abd-4d9d-80c7-02af85c822a8!!!!" as &[_] + /// ); + /// ``` + /// */ + pub fn encode_lower<'buf>(&self, buffer: &'buf mut [u8]) -> &'buf mut str { buffer[..9].copy_from_slice(b"urn:uuid:"); encode(buffer, 9, self.0, true, false) } - // Writes the [`Uuid`] as an upper-case URN string to - // `buffer`, and returns the subslice of the buffer that contains the - // encoded UUID. - // - // This is slightly more efficient than using the formatting - // infrastructure as it avoids virtual calls, and may avoid - // double buffering. - // - // [`Uuid`]: ../struct.Uuid.html - // - // # Panics - // - // Panics if the buffer is not large enough: it must have length at least - // [`LENGTH`]. [`Uuid::encode_buffer`] can be used to get a - // sufficiently-large temporary buffer. - // - // [`LENGTH`]: #associatedconstant.LENGTH - // [`Uuid::encode_buffer`]: ../struct.Uuid.html#method.encode_buffer - // - // # Examples - // - // ```rust - // use uuid::Uuid; - // - // let uuid = Uuid::parse_str("936da01f9abd4d9d80c702af85c822a8").unwrap(); - // - // // the encoded portion is returned - // assert_eq!( - // uuid.to_urn().encode_upper(&mut Uuid::encode_buffer()), - // "urn:uuid:936DA01F-9ABD-4D9D-80C7-02AF85C822A8" - // ); - // - // // the buffer is mutated directly, and trailing contents remains - // let mut buf = [b'!'; 49]; - // assert_eq!( - // uuid.to_urn().encode_upper(&mut buf), - // "urn:uuid:936DA01F-9ABD-4D9D-80C7-02AF85C822A8" - // ); - // assert_eq!( - // &buf as &[_], - // b"urn:uuid:936DA01F-9ABD-4D9D-80C7-02AF85C822A8!!!!" as &[_] - // ); - // ``` - // */ - pub(crate) fn encode_upper<'buf>( - &self, - buffer: &'buf mut [u8], - ) -> &'buf mut str { + /// Writes the [`Uuid`] as an upper-case URN string to + /// `buffer`, and returns the subslice of the buffer that contains the + /// encoded UUID. + /// + /// This is slightly more efficient than using the formatting + /// infrastructure as it avoids virtual calls, and may avoid + /// double buffering. + /// + /// [`Uuid`]: ../struct.Uuid.html + /// + /// # Panics + /// + /// Panics if the buffer is not large enough: it must have length at least + /// [`LENGTH`]. [`Uuid::encode_buffer`] can be used to get a + /// sufficiently-large temporary buffer. + /// + /// [`LENGTH`]: #associatedconstant.LENGTH + /// [`Uuid::encode_buffer`]: ../struct.Uuid.html#method.encode_buffer + /// + /// # Examples + /// + /// ```rust + /// use uuid::Uuid; + /// + /// let uuid = Uuid::parse_str("936da01f9abd4d9d80c702af85c822a8").unwrap(); + /// + /// // the encoded portion is returned + /// assert_eq!( + /// uuid.to_urn().encode_upper(&mut Uuid::encode_buffer()), + /// "urn:uuid:936DA01F-9ABD-4D9D-80C7-02AF85C822A8" + /// ); + /// + /// // the buffer is mutated directly, and trailing contents remains + /// let mut buf = [b'!'; 49]; + /// assert_eq!( + /// uuid.to_urn().encode_upper(&mut buf), + /// "urn:uuid:936DA01F-9ABD-4D9D-80C7-02AF85C822A8" + /// ); + /// assert_eq!( + /// &buf as &[_], + /// b"urn:uuid:936DA01F-9ABD-4D9D-80C7-02AF85C822A8!!!!" as &[_] + /// ); + /// ``` + /// */ + pub fn encode_upper<'buf>(&self, buffer: &'buf mut [u8]) -> &'buf mut str { buffer[..9].copy_from_slice(b"urn:uuid:"); encode(buffer, 9, self.0, true, true) } diff --git a/third_party/rust/uuid/src/builder.rs b/third_party/rust/uuid/src/builder.rs new file mode 100644 index 000000000000..a807bc67cdd5 --- /dev/null +++ b/third_party/rust/uuid/src/builder.rs @@ -0,0 +1,224 @@ +// Copyright 2013-2014 The Rust Project Developers. +// Copyright 2018 The Uuid Project Developers. +// +// See the COPYRIGHT file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A Builder type for [`Uuid`]s. +//! +//! [`Uuid`]: ../struct.Uuid.html + +use prelude::*; +use BytesError; + +/// A builder struct for creating a [`Uuid`] +/// +/// # Examples +/// +/// Creating a v4 `Uuid` from externally generated bytes: +/// +/// ``` +/// use uuid::{Builder, Variant, Version}; +/// +/// # let rng = || [ +/// # 70, 235, 208, 238, 14, 109, 67, 201, 185, 13, 204, 195, 90, +/// # 145, 63, 62, +/// # ]; +/// let random_bytes = rng(); +/// let uuid = Builder::from_bytes(random_bytes) +/// .set_variant(Variant::RFC4122) +/// .set_version(Version::Random) +/// .build(); +/// ``` +#[allow(missing_copy_implementations)] +#[derive(Debug)] +pub struct Builder(Uuid); + +impl Builder { + /// Creates a `Builder` using the supplied big-endian bytes. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use uuid::Builder; + /// use uuid::Bytes; + /// + /// let bytes: Bytes = [ + /// 70, 235, 208, 238, 14, 109, 67, 201, 185, 13, 204, 195, 90, 145, 63, 62, + /// ]; + /// + /// let mut builder = Builder::from_bytes(bytes); + /// let uuid = builder.build().to_hyphenated().to_string(); + /// + /// let expected_uuid = String::from("46ebd0ee-0e6d-43c9-b90d-ccc35a913f3e"); + /// + /// assert_eq!(expected_uuid, uuid); + /// ``` + /// + /// An incorrect number of bytes: + /// + /// ```compile_fail + /// use uuid::Builder; + /// use uuid::Bytes; + /// + /// let bytes: Bytes = [4, 54, 67, 12, 43, 2, 98, 76]; // doesn't compile + /// + /// let uuid = Builder::from_bytes(bytes); + /// ``` + pub fn from_bytes(b: Bytes) -> Self { + Builder(Uuid::from_bytes(b)) + } + + /// Creates a `Builder` using the supplied big-endian bytes. + /// + /// # Errors + /// + /// This function will return an error if `b` has any length other than 16. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use uuid::Builder; + /// + /// let bytes = [4, 54, 67, 12, 43, 2, 98, 76, 32, 50, 87, 5, 1, 33, 43, 87]; + /// + /// let builder = Builder::from_slice(&bytes); + /// let uuid = + /// builder.map(|mut builder| builder.build().to_hyphenated().to_string()); + /// + /// let expected_uuid = + /// Ok(String::from("0436430c-2b02-624c-2032-570501212b57")); + /// + /// assert_eq!(expected_uuid, uuid); + /// ``` + /// + /// An incorrect number of bytes: + /// + /// ``` + /// use uuid::prelude::*; + /// use uuid::Builder; + /// + /// let bytes = [4, 54, 67, 12, 43, 2, 98, 76]; + /// + /// let builder = Builder::from_slice(&bytes); + /// + /// assert!(builder.is_err()); + /// ``` + pub fn from_slice(b: &[u8]) -> Result { + const BYTES_LEN: usize = 16; + + let len = b.len(); + + if len != BYTES_LEN { + return Err(BytesError::new(BYTES_LEN, len)); + } + + let mut bytes: Bytes = [0; 16]; + bytes.copy_from_slice(b); + Ok(Self::from_bytes(bytes)) + } + + /// Creates a `Builder` from four field values. + /// + /// # Errors + /// + /// This function will return an error if `d4`'s length is not 8 bytes. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use uuid::Builder; + /// + /// let d4 = [12, 3, 9, 56, 54, 43, 8, 9]; + /// + /// let builder = Builder::from_fields(42, 12, 5, &d4); + /// let uuid = + /// builder.map(|mut builder| builder.build().to_hyphenated().to_string()); + /// + /// let expected_uuid = + /// Ok(String::from("0000002a-000c-0005-0c03-0938362b0809")); + /// + /// assert_eq!(expected_uuid, uuid); + /// ``` + /// + /// An invalid length: + /// + /// ``` + /// use uuid::prelude::*; + /// + /// let d4 = [12]; + /// + /// let builder = uuid::Builder::from_fields(42, 12, 5, &d4); + /// + /// assert!(builder.is_err()); + /// ``` + pub fn from_fields( + d1: u32, + d2: u16, + d3: u16, + d4: &[u8], + ) -> Result { + Uuid::from_fields(d1, d2, d3, d4).map(Builder) + } + + /// Creates a `Builder` with an initial [`Uuid::nil`] + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use uuid::Builder; + /// + /// let mut builder = Builder::nil(); + /// + /// assert_eq!( + /// builder.build().to_hyphenated().to_string(), + /// "00000000-0000-0000-0000-000000000000" + /// ); + /// ``` + pub fn nil() -> Self { + Builder(Uuid::nil()) + } + + /// Specifies the variant of the internal [`Uuid`]. + pub fn set_variant(&mut self, v: Variant) -> &mut Self { + self.0.set_variant(v); + self + } + + /// Specifies the version number of the internal [`Uuid`]. + pub fn set_version(&mut self, v: Version) -> &mut Self { + self.0.set_version(v); + self + } + + /// Hands over the internal constructed [`Uuid`] + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use uuid::Builder; + /// + /// let uuid = Builder::nil().build(); + /// + /// assert_eq!( + /// uuid.to_hyphenated().to_string(), + /// "00000000-0000-0000-0000-000000000000" + /// ); + /// ``` + pub fn build(&mut self) -> Uuid { + self.0 + } +} diff --git a/third_party/rust/uuid/src/core_support.rs b/third_party/rust/uuid/src/core_support.rs index 82864d9ce848..d01f21074b21 100644 --- a/third_party/rust/uuid/src/core_support.rs +++ b/third_party/rust/uuid/src/core_support.rs @@ -13,6 +13,28 @@ use core::{fmt, str}; use parser; use prelude::*; +impl From for super::Error { + fn from(err: super::BytesError) -> Self { + super::Error::Bytes(err) + } +} + +impl fmt::Debug for Uuid { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::LowerHex::fmt(self, f) + } +} + +impl fmt::Display for super::Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + super::Error::Bytes(ref err) => fmt::Display::fmt(&err, f), + super::Error::Parse(ref err) => fmt::Display::fmt(&err, f), + } + } +} + impl fmt::Display for Uuid { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self, f) diff --git a/third_party/rust/uuid/src/lib.rs b/third_party/rust/uuid/src/lib.rs index c27e4046f0b7..9547171f41a4 100644 --- a/third_party/rust/uuid/src/lib.rs +++ b/third_party/rust/uuid/src/lib.rs @@ -12,7 +12,7 @@ //! Generate and parse UUIDs. //! //! Provides support for Universally Unique Identifiers (UUIDs). A UUID is a -//! unique 128-bit number, stored as 16 octets. UUIDs are used to assign +//! unique 128-bit number, stored as 16 octets. UUIDs are used to assign //! unique identifiers to entities without requiring a central allocating //! authority. //! @@ -43,6 +43,14 @@ //! * `serde` - adds the ability to serialize and deserialize a `Uuid` using the //! `serde` crate. //! +//! You need to enable one of the following Cargo features together with +//! `v3`, `v4` or `v5` feature if you're targeting `wasm32` architecture: +//! +//! * `stdweb` - enables support for `OsRng` on `wasm32-unknown-unknown` via +//! `stdweb` combined with `cargo-web` +//! * `wasm-bindgen` - `wasm-bindgen` enables support for `OsRng` on +//! `wasm32-unknown-unknown` via [`wasm-bindgen`] +//! //! By default, `uuid` can be depended on with: //! //! ```toml @@ -101,10 +109,10 @@ //! //! # References //! -//! * [Wikipedia: Universally Unique Identifier]( -//! http://en.wikipedia.org/wiki/Universally_unique_identifier) -//! * [RFC4122: A Universally Unique IDentifier (UUID) URN Namespace]( -//! http://tools.ietf.org/html/rfc4122) +//! * [Wikipedia: Universally Unique Identifier]( http://en.wikipedia.org/wiki/Universally_unique_identifier) +//! * [RFC4122: A Universally Unique IDentifier (UUID) URN Namespace]( http://tools.ietf.org/html/rfc4122) +//! +//! [`wasm-bindgen`]: https://github.com/rustwasm/wasm-bindgen #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(feature = "const_fn", feature(const_fn))] @@ -116,7 +124,7 @@ #![doc( html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://www.rust-lang.org/favicon.ico", - html_root_url = "https://docs.rs/uuid" + html_root_url = "https://docs.rs/uuid/0.7.4" )] #[cfg(feature = "byteorder")] @@ -131,18 +139,26 @@ extern crate rand; extern crate serde; #[cfg(all(feature = "serde", test))] extern crate serde_test; +#[cfg(all(feature = "serde", test))] +#[macro_use] +extern crate serde_derive; #[cfg(feature = "sha1")] extern crate sha1; #[cfg(feature = "slog")] #[cfg_attr(test, macro_use)] extern crate slog; +#[cfg(feature = "winapi")] +extern crate winapi; pub mod adapter; +pub mod builder; pub mod parser; pub mod prelude; #[cfg(feature = "v1")] pub mod v1; +pub use builder::Builder; + mod core_support; #[cfg(feature = "serde")] mod serde_support; @@ -154,12 +170,41 @@ mod std_support; mod test_util; #[cfg(feature = "u128")] mod u128_support; -#[cfg(feature = "v3")] +#[cfg(all( + feature = "v3", + any( + not(target_arch = "wasm32"), + all( + target_arch = "wasm32", + any(feature = "stdweb", feature = "wasm-bindgen") + ) + ) +))] mod v3; -#[cfg(feature = "v4")] +#[cfg(all( + feature = "v4", + any( + not(target_arch = "wasm32"), + all( + target_arch = "wasm32", + any(feature = "stdweb", feature = "wasm-bindgen") + ) + ) +))] mod v4; -#[cfg(feature = "v5")] +#[cfg(all( + feature = "v5", + any( + not(target_arch = "wasm32"), + all( + target_arch = "wasm32", + any(feature = "stdweb", feature = "wasm-bindgen") + ) + ) +))] mod v5; +#[cfg(all(windows, feature = "winapi"))] +mod winapi_support; /// A 128-bit (16 byte) buffer containing the ID. pub type Bytes = [u8; 16]; @@ -173,6 +218,38 @@ pub struct BytesError { found: usize, } +/// A general error that can occur when handling [`Uuid`]s. +/// +/// Although specialized error types exist in the crate, +/// sometimes where particular error type occurred is hidden +/// until errors need to be handled. This allows to enumerate +/// the errors. +/// +/// [`Uuid`]: struct.Uuid.html +// TODO: improve the doc +// BODY: This detail should be fine for initial merge + +// TODO: write tests for Error +// BODY: not immediately blocking, but should be covered for 1.0 +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub enum Error { + /// An error occurred while handling [`Uuid`] bytes. + /// + /// See [`BytesError`] + /// + /// [`BytesError`]: struct.BytesError.html + /// [`Uuid`]: struct.Uuid.html + Bytes(BytesError), + + /// An error occurred while parsing a [`Uuid`] string. + /// + /// See [`parser::ParseError`] + /// + /// [`parser::ParseError`]: parser/enum.ParseError.html + /// [`Uuid`]: struct.Uuid.html + Parse(parser::ParseError), +} + /// The version of the UUID, denoting the generating algorithm. #[derive(Debug, PartialEq, Copy, Clone)] #[repr(C)] @@ -208,7 +285,7 @@ pub enum Variant { } /// A Universally Unique Identifier (UUID). -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Uuid(Bytes); impl BytesError { @@ -246,10 +323,7 @@ impl BytesError { #[cfg(feature = "const_fn")] #[inline] pub const fn new(expected: usize, found: usize) -> Self { - BytesError { - expected: expected, - found: found, - } + BytesError { expected, found } } /// Create a new [`UuidError`]. @@ -347,7 +421,7 @@ impl Uuid { Uuid::from_bytes([0; 16]) } - /// Creates a `Uuid` from four field values. + /// Creates a `Uuid` from four field values in big-endian order. /// /// # Errors /// @@ -418,7 +492,64 @@ impl Uuid { ])) } - /// Creates a `Uuid` using the supplied bytes. + /// Creates a `Uuid` from four field values in little-endian order. + /// + /// The bytes in the `d1`, `d2` and `d3` fields will + /// be converted into big-endian order. + /// + /// # Examples + /// + /// ``` + /// use uuid::Uuid; + /// + /// let d1 = 0xAB3F1097u32; + /// let d2 = 0x501Eu16; + /// let d3 = 0xB736u16; + /// let d4 = [12, 3, 9, 56, 54, 43, 8, 9]; + /// + /// let uuid = Uuid::from_fields_le(d1, d2, d3, &d4); + /// let uuid = uuid.map(|uuid| uuid.to_hyphenated().to_string()); + /// + /// let expected_uuid = + /// Ok(String::from("97103fab-1e50-36b7-0c03-0938362b0809")); + /// + /// assert_eq!(expected_uuid, uuid); + /// ``` + pub fn from_fields_le( + d1: u32, + d2: u16, + d3: u16, + d4: &[u8], + ) -> Result { + const D4_LEN: usize = 8; + + let len = d4.len(); + + if len != D4_LEN { + return Err(BytesError::new(D4_LEN, len)); + } + + Ok(Uuid::from_bytes([ + d1 as u8, + (d1 >> 8) as u8, + (d1 >> 16) as u8, + (d1 >> 24) as u8, + (d2) as u8, + (d2 >> 8) as u8, + d3 as u8, + (d3 >> 8) as u8, + d4[0], + d4[1], + d4[2], + d4[3], + d4[4], + d4[5], + d4[6], + d4[7], + ])) + } + + /// Creates a `Uuid` using the supplied big-endian bytes. /// /// # Errors /// @@ -469,78 +600,13 @@ impl Uuid { Ok(Uuid::from_bytes(bytes)) } - /// Creates a `Uuid` using the supplied bytes. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use uuid::Bytes; - /// use uuid::Uuid; - /// - /// let bytes: Bytes = [ - /// 70, 235, 208, 238, 14, 109, 67, 201, 185, 13, 204, 195, 90, 145, 63, - /// 62, - /// ]; - /// - /// let uuid = Uuid::from_bytes(bytes); - /// let uuid = uuid.to_hyphenated().to_string(); - /// - /// let expected_uuid = String::from("46ebd0ee-0e6d-43c9-b90d-ccc35a913f3e"); - /// - /// assert_eq!(expected_uuid, uuid); - /// ``` - /// - /// An incorrect number of bytes: - /// - /// ```compile_fail - /// use uuid::Uuid; - /// use uuid::UuidBytes; - /// - /// let bytes: UuidBytes = [4, 54, 67, 12, 43, 2, 98, 76]; // doesn't - /// compile - /// - /// let uuid = Uuid::from_bytes(bytes); - /// ``` + /// Creates a `Uuid` using the supplied big-endian bytes. #[cfg(not(feature = "const_fn"))] pub fn from_bytes(bytes: Bytes) -> Uuid { Uuid(bytes) } - /// Creates a `Uuid` using the supplied bytes. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use uuid::Bytes; - /// use uuid::Uuid; - /// - /// let bytes: Bytes = [ - /// 70, 235, 208, 238, 14, 109, 67, 201, 185, 13, 204, 195, 90, 145, 63, - /// 62, - /// ]; - /// - /// let uuid = Uuid::from_bytes(bytes); - /// let uuid = uuid.to_hyphenated().to_string(); - /// - /// let expected_uuid = String::from("46ebd0ee-0e6d-43c9-b90d-ccc35a913f3e"); - /// - /// assert_eq!(expected_uuid, uuid); - /// ``` - /// - /// An incorrect number of bytes: - /// - /// ```compile_fail - /// use uuid::Bytes; - /// use uuid::Uuid; - /// - /// let bytes: Bytes = [4, 54, 67, 12, 43, 2, 98, 76]; // doesn't compile - /// - /// let uuid = Uuid::from_bytes(bytes); - /// ``` + /// Creates a `Uuid` using the supplied big-endian bytes. #[cfg(feature = "const_fn")] pub const fn from_bytes(bytes: Bytes) -> Uuid { Uuid(bytes) @@ -558,8 +624,7 @@ impl Uuid { /// use uuid::Uuid; /// /// let bytes: Bytes = [ - /// 70, 235, 208, 238, 14, 109, 67, 201, 185, 13, 204, 195, 90, 145, 63, - /// 62, + /// 70, 235, 208, 238, 14, 109, 67, 201, 185, 13, 204, 195, 90, 145, 63, 62, /// ]; /// let uuid = Uuid::from_random_bytes(bytes); /// let uuid = uuid.to_hyphenated().to_string(); @@ -568,6 +633,10 @@ impl Uuid { /// /// assert_eq!(expected_uuid, uuid); /// ``` + #[deprecated( + since = "0.7.2", + note = "please use the `uuid::Builder` instead to set the variant and version" + )] pub fn from_random_bytes(bytes: Bytes) -> Uuid { let mut uuid = Uuid::from_bytes(bytes); uuid.set_variant(Variant::RFC4122); @@ -638,7 +707,7 @@ impl Uuid { } } - /// Returns the four field values of the UUID. + /// Returns the four field values of the UUID in big-endian order. /// /// These values can be passed to the `from_fields()` method to get the /// original `Uuid` back. @@ -649,15 +718,14 @@ impl Uuid { /// * The second field value represents the second group of (four) hex /// digits, taken as a big-endian `u16` value. For V1 UUIDs, this field /// represents the middle 16 bits of the timestamp. - /// * The third field value represents the third group of (four) hex - /// digits, taken as a big-endian `u16` value. The 4 most significant - /// bits give the UUID version, and for V1 UUIDs, the last 12 bits - /// represent the high 12 bits of the timestamp. - /// * The last field value represents the last two groups of four and - /// twelve hex digits, taken in order. The first 1-3 bits of this - /// indicate the UUID variant, and for V1 UUIDs, the next 13-15 bits - /// indicate the clock sequence and the last 48 bits indicate the node - /// ID. + /// * The third field value represents the third group of (four) hex digits, + /// taken as a big-endian `u16` value. The 4 most significant bits give + /// the UUID version, and for V1 UUIDs, the last 12 bits represent the + /// high 12 bits of the timestamp. + /// * The last field value represents the last two groups of four and twelve + /// hex digits, taken in order. The first 1-3 bits of this indicate the + /// UUID variant, and for V1 UUIDs, the next 13-15 bits indicate the clock + /// sequence and the last 48 bits indicate the node ID. /// /// # Examples /// @@ -695,49 +763,53 @@ impl Uuid { (d1, d2, d3, d4) } - /// Returns an array of 16 octets containing the UUID data. + /// Returns the four field values of the UUID in little-endian order. + /// + /// The bytes in the returned integer fields will + /// be converted from big-endian order. /// /// # Examples /// /// ``` /// use uuid::Uuid; /// - /// let uuid = Uuid::nil(); - /// assert_eq!(uuid.as_bytes(), &[0; 16]); - /// - /// let uuid = Uuid::parse_str("936DA01F9ABD4d9d80C702AF85C822A8").unwrap(); + /// let uuid = Uuid::parse_str("936DA01F-9ABD-4D9D-80C7-02AF85C822A8").unwrap(); /// assert_eq!( - /// uuid.as_bytes(), - /// &[ - /// 147, 109, 160, 31, 154, 189, 77, 157, 128, 199, 2, 175, 133, 200, - /// 34, 168, - /// ] + /// uuid.to_fields_le(), + /// ( + /// 0x1FA06D93, + /// 0xBD9A, + /// 0x9D4D, + /// b"\x80\xC7\x02\xAF\x85\xC8\x22\xA8" + /// ) /// ); /// ``` + pub fn to_fields_le(&self) -> (u32, u16, u16, &[u8; 8]) { + let d1 = u32::from(self.as_bytes()[0]) + | u32::from(self.as_bytes()[1]) << 8 + | u32::from(self.as_bytes()[2]) << 16 + | u32::from(self.as_bytes()[3]) << 24; + + let d2 = + u16::from(self.as_bytes()[4]) | u16::from(self.as_bytes()[5]) << 8; + + let d3 = + u16::from(self.as_bytes()[6]) | u16::from(self.as_bytes()[7]) << 8; + + let d4: &[u8; 8] = + unsafe { &*(self.as_bytes()[8..16].as_ptr() as *const [u8; 8]) }; + (d1, d2, d3, d4) + } + + /// Returns an array of 16 octets containing the UUID data. + /// This method wraps [`Uuid::as_bytes`] #[cfg(feature = "const_fn")] pub const fn as_bytes(&self) -> &Bytes { &self.0 } /// Returns an array of 16 octets containing the UUID data. - /// - /// # Examples - /// - /// ``` - /// use uuid::Uuid; - /// - /// let uuid = Uuid::nil(); - /// assert_eq!(uuid.as_bytes(), &[0; 16]); - /// - /// let uuid = Uuid::parse_str("936DA01F9ABD4d9d80C702AF85C822A8").unwrap(); - /// assert_eq!( - /// uuid.as_bytes(), - /// &[ - /// 147, 109, 160, 31, 154, 189, 77, 157, 128, 199, 2, 175, 133, 200, - /// 34, 168 - /// ] - /// ); - /// ``` + /// This method wraps [`Uuid::as_bytes`] #[cfg(not(feature = "const_fn"))] pub fn as_bytes(&self) -> &Bytes { &self.0 @@ -923,33 +995,34 @@ impl Uuid { pub fn is_nil(&self) -> bool { self.as_bytes().iter().all(|&b| b == 0) } - // A buffer that can be used for `encode_...` calls, that is - // guaranteed to be long enough for any of the adapters. - // - // # Examples - // - // ```rust - // use uuid::Uuid; - // - // let uuid = Uuid::nil(); - // - // assert_eq!( - // uuid.to_simple().encode_lower(&mut Uuid::encode_buffer()), - // "00000000000000000000000000000000" - // ); - // - // assert_eq!( - // uuid.to_hyphenated() - // .encode_lower(&mut Uuid::encode_buffer()), - // "00000000-0000-0000-0000-000000000000" - // ); - // - // assert_eq!( - // uuid.to_urn().encode_lower(&mut Uuid::encode_buffer()), - // "urn:uuid:00000000-0000-0000-0000-000000000000" - // ); - // ``` - pub(crate) fn encode_buffer() -> [u8; adapter::Urn::LENGTH] { + + /// A buffer that can be used for `encode_...` calls, that is + /// guaranteed to be long enough for any of the adapters. + /// + /// # Examples + /// + /// ```rust + /// use uuid::Uuid; + /// + /// let uuid = Uuid::nil(); + /// + /// assert_eq!( + /// uuid.to_simple().encode_lower(&mut Uuid::encode_buffer()), + /// "00000000000000000000000000000000" + /// ); + /// + /// assert_eq!( + /// uuid.to_hyphenated() + /// .encode_lower(&mut Uuid::encode_buffer()), + /// "00000000-0000-0000-0000-000000000000" + /// ); + /// + /// assert_eq!( + /// uuid.to_urn().encode_lower(&mut Uuid::encode_buffer()), + /// "urn:uuid:00000000-0000-0000-0000-000000000000" + /// ); + /// ``` + pub fn encode_buffer() -> [u8; adapter::Urn::LENGTH] { [0; adapter::Urn::LENGTH] } } @@ -1192,20 +1265,14 @@ mod tests { // Valid assert!(Uuid::parse_str("00000000000000000000000000000000").is_ok()); - assert!( - Uuid::parse_str("67e55044-10b1-426f-9247-bb680e5fe0c8").is_ok() - ); - assert!( - Uuid::parse_str("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4").is_ok() - ); + assert!(Uuid::parse_str("67e55044-10b1-426f-9247-bb680e5fe0c8").is_ok()); + assert!(Uuid::parse_str("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4").is_ok()); assert!(Uuid::parse_str("67e5504410b1426f9247bb680e5fe0c8").is_ok()); - assert!( - Uuid::parse_str("01020304-1112-2122-3132-414243444546").is_ok() - ); - assert!( - Uuid::parse_str("urn:uuid:67e55044-10b1-426f-9247-bb680e5fe0c8") - .is_ok() - ); + assert!(Uuid::parse_str("01020304-1112-2122-3132-414243444546").is_ok()); + assert!(Uuid::parse_str( + "urn:uuid:67e55044-10b1-426f-9247-bb680e5fe0c8" + ) + .is_ok()); // Nil let nil = Uuid::nil(); @@ -1358,6 +1425,20 @@ mod tests { assert_eq!(result, expected); } + #[test] + fn test_from_fields_le() { + let d1: u32 = 0xa4a3a2a1; + let d2: u16 = 0xb2b1; + let d3: u16 = 0xc2c1; + let d4 = [0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8]; + + let u = Uuid::from_fields_le(d1, d2, d3, &d4).unwrap(); + + let expected = "a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8"; + let result = u.to_simple().to_string(); + assert_eq!(result, expected); + } + #[test] fn test_as_fields() { let u = test_util::new(); @@ -1386,6 +1467,38 @@ mod tests { assert_eq!(d4_in, d4_out); } + #[test] + fn test_fields_le_roundtrip() { + let d1_in: u32 = 0xa4a3a2a1; + let d2_in: u16 = 0xb2b1; + let d3_in: u16 = 0xc2c1; + let d4_in = &[0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8]; + + let u = Uuid::from_fields_le(d1_in, d2_in, d3_in, d4_in).unwrap(); + let (d1_out, d2_out, d3_out, d4_out) = u.to_fields_le(); + + assert_eq!(d1_in, d1_out); + assert_eq!(d2_in, d2_out); + assert_eq!(d3_in, d3_out); + assert_eq!(d4_in, d4_out); + } + + #[test] + fn test_fields_le_are_actually_le() { + let d1_in: u32 = 0xa1a2a3a4; + let d2_in: u16 = 0xb1b2; + let d3_in: u16 = 0xc1c2; + let d4_in = &[0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8]; + + let u = Uuid::from_fields(d1_in, d2_in, d3_in, d4_in).unwrap(); + let (d1_out, d2_out, d3_out, d4_out) = u.to_fields_le(); + + assert_eq!(d1_in, d1_out.swap_bytes()); + assert_eq!(d2_in, d2_out.swap_bytes()); + assert_eq!(d3_in, d3_out.swap_bytes()); + assert_eq!(d4_in, d4_out); + } + #[test] fn test_from_slice() { let b = [ @@ -1436,6 +1549,7 @@ mod tests { } #[test] + #[allow(deprecated)] fn test_from_random_bytes() { let b = [ 0xa1, 0xa2, 0xa3, 0xa4, 0xb1, 0xb2, 0xc1, 0xc2, 0xd1, 0xd2, 0xd3, diff --git a/third_party/rust/uuid/src/parser/core_support.rs b/third_party/rust/uuid/src/parser/core_support.rs index d358beddbe18..d050d743ffab 100644 --- a/third_party/rust/uuid/src/parser/core_support.rs +++ b/third_party/rust/uuid/src/parser/core_support.rs @@ -12,6 +12,12 @@ use core::fmt; use parser; +impl From for ::Error { + fn from(err: parser::ParseError) -> Self { + ::Error::Parse(err) + } +} + impl<'a> fmt::Display for parser::Expected { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { @@ -33,13 +39,9 @@ impl fmt::Display for parser::ParseError { expected, found, index, - } => write!( - f, - "expected {:?}, found {} at {}", - expected.chars(), - found, - index - ), + } => { + write!(f, "expected {}, found {} at {}", expected, found, index) + } parser::ParseError::InvalidGroupCount { ref expected, found, diff --git a/third_party/rust/uuid/src/prelude.rs b/third_party/rust/uuid/src/prelude.rs index 5351fd01fbb5..e8f76d44e265 100644 --- a/third_party/rust/uuid/src/prelude.rs +++ b/third_party/rust/uuid/src/prelude.rs @@ -29,13 +29,15 @@ //! //! Currently the prelude reexports the following: //! -//! [`uuid`]`::{`[`Uuid`], [`Variant`], [`Version`]`}`: The fundamental -//! types used in [`uuid`] crate. +//! [`uuid`]`::{`[`Error`], [`Uuid`], [`Variant`], [`Version`], +//! builder::[`Builder`]`}`: The fundamental types used in [`uuid`] crate. //! //! [`uuid`]: ../index.html +//! [`Error`]: ../enum.Error.html //! [`Uuid`]: ../struct.Uuid.html //! [`Variant`]: ../enum.Variant.html //! [`Version`]: ../enum.Version.html +//! [`Builder`]: ../builder/struct.Builder.html //! #![cfg_attr(feature = "v1", doc = " @@ -46,6 +48,6 @@ handling uuid version 1. Requires feature `v1`. [`Context`]: ../v1/struct.Context.html [`ClockSequence`]: ../v1/trait.ClockSequence.html")] -pub use super::{Bytes, Uuid, Variant, Version}; +pub use super::{Builder, Bytes, Error, Uuid, Variant, Version}; #[cfg(feature = "v1")] pub use v1::{ClockSequence, Context}; diff --git a/third_party/rust/uuid/src/serde_support.rs b/third_party/rust/uuid/src/serde_support.rs index f71eb4400d00..826ff530077d 100644 --- a/third_party/rust/uuid/src/serde_support.rs +++ b/third_party/rust/uuid/src/serde_support.rs @@ -13,6 +13,7 @@ use core::fmt; use prelude::*; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; +#[cfg(feature = "serde")] impl Serialize for Uuid { fn serialize( &self, @@ -27,6 +28,7 @@ impl Serialize for Uuid { } } +#[cfg(feature = "serde")] impl<'de> Deserialize<'de> for Uuid { fn deserialize>( deserializer: D, @@ -86,8 +88,8 @@ impl<'de> Deserialize<'de> for Uuid { } } -#[cfg(test)] -mod tests { +#[cfg(all(test, feature = "serde"))] +mod serde_tests { use serde_test; use prelude::*; diff --git a/third_party/rust/uuid/src/std_support.rs b/third_party/rust/uuid/src/std_support.rs index f6f9aea48554..c561d60acc76 100644 --- a/third_party/rust/uuid/src/std_support.rs +++ b/third_party/rust/uuid/src/std_support.rs @@ -11,8 +11,17 @@ use std::error; -impl error::Error for ::BytesError { +impl error::Error for super::BytesError { fn description(&self) -> &str { "invalid number of uuid bytes" } } + +impl error::Error for super::Error { + fn description(&self) -> &str { + match *self { + super::Error::Bytes(ref err) => error::Error::description(err), + super::Error::Parse(ref err) => error::Error::description(err), + } + } +} diff --git a/third_party/rust/uuid/src/v4.rs b/third_party/rust/uuid/src/v4.rs index 2a84753941aa..fe2ee818b4a7 100644 --- a/third_party/rust/uuid/src/v4.rs +++ b/third_party/rust/uuid/src/v4.rs @@ -31,7 +31,10 @@ impl Uuid { rng.fill_bytes(&mut bytes); - Self::from_random_bytes(bytes) + Builder::from_bytes(bytes) + .set_variant(Variant::RFC4122) + .set_version(Version::Random) + .build() } } diff --git a/third_party/rust/uuid/src/winapi_support.rs b/third_party/rust/uuid/src/winapi_support.rs new file mode 100644 index 000000000000..82d62093ee6e --- /dev/null +++ b/third_party/rust/uuid/src/winapi_support.rs @@ -0,0 +1,81 @@ +use prelude::*; + +use BytesError; + +use winapi::shared::guiddef; + +#[cfg(feature = "guid")] +impl Uuid { + /// Attempts to create a [`Uuid`] from a little endian winapi `GUID` + /// + /// [`Uuid`]: ../struct.Uuid.html + pub fn from_guid(guid: guiddef::GUID) -> Result { + Uuid::from_fields_le( + guid.Data1 as u32, + guid.Data2 as u16, + guid.Data3 as u16, + &(guid.Data4 as [u8; 8]), + ) + } + + /// Converts a [`Uuid`] into a little endian winapi `GUID` + /// + /// [`Uuid`]: ../struct.Uuid.html + pub fn to_guid(&self) -> guiddef::GUID { + let (data1, data2, data3, data4) = self.to_fields_le(); + + guiddef::GUID { + Data1: data1, + Data2: data2, + Data3: data3, + Data4: *data4, + } + } +} + +#[cfg(feature = "guid")] +#[cfg(test)] +mod tests { + use prelude::*; + use std::str::FromStr; + use winapi::shared::guiddef; + + #[test] + fn test_from_guid() { + let guid = guiddef::GUID { + Data1: 0x4a35229d, + Data2: 0x5527, + Data3: 0x4f30, + Data4: [0x86, 0x47, 0x9d, 0xc5, 0x4e, 0x1e, 0xe1, 0xe8], + }; + + let uuid = Uuid::from_guid(guid).unwrap(); + assert_eq!( + "9d22354a-2755-304f-8647-9dc54e1ee1e8", + uuid.to_hyphenated().to_string() + ); + } + + #[test] + fn test_guid_roundtrip() { + let guid_in = guiddef::GUID { + Data1: 0x4a35229d, + Data2: 0x5527, + Data3: 0x4f30, + Data4: [0x86, 0x47, 0x9d, 0xc5, 0x4e, 0x1e, 0xe1, 0xe8], + }; + + let uuid = Uuid::from_guid(guid_in).unwrap(); + let guid_out = uuid.to_guid(); + + assert_eq!( + (guid_in.Data1, guid_in.Data2, guid_in.Data3, guid_in.Data4), + ( + guid_out.Data1, + guid_out.Data2, + guid_out.Data3, + guid_out.Data4 + ) + ); + } +} diff --git a/toolkit/components/normandy/lib/RecipeRunner.jsm b/toolkit/components/normandy/lib/RecipeRunner.jsm index e0a4bbeb4a13..9a7d9053e553 100644 --- a/toolkit/components/normandy/lib/RecipeRunner.jsm +++ b/toolkit/components/normandy/lib/RecipeRunner.jsm @@ -41,6 +41,10 @@ const DEV_MODE_PREF = `${PREF_PREFIX}.dev_mode`; const API_URL_PREF = `${PREF_PREFIX}.api_url`; const LAZY_CLASSIFY_PREF = `${PREF_PREFIX}.experiments.lazy_classify`; +// Timer last update preference. +// see https://searchfox.org/mozilla-central/rev/11cfa0462/toolkit/components/timermanager/UpdateTimerManager.jsm#8 +const TIMER_LAST_UPDATE_PREF = `app.update.lastUpdateTime.${TIMER_NAME}`; + const PREFS_TO_WATCH = [ RUN_INTERVAL_PREF, TELEMETRY_ENABLED_PREF, @@ -71,9 +75,13 @@ function cacheProxy(target) { var RecipeRunner = { async init() { + this.running = false; this.enabled = null; + this.loadFromRemoteSettings = false; + this.checkPrefs(); // sets this.enabled this.watchPrefs(); + await this.setUpRemoteSettings(); // Run if enabled immediately on first run, or if dev mode is enabled. const firstRun = Services.prefs.getBoolPref(FIRST_RUN_PREF, true); @@ -190,6 +198,43 @@ var RecipeRunner = { timerManager.unregisterTimer(TIMER_NAME); }, + async setUpRemoteSettings() { + const feature = "normandy-remote-settings"; + + if (await FeatureGate.isEnabled(feature)) { + this.attachRemoteSettings(); + } + const observer = { + onEnable: this.attachRemoteSettings.bind(this), + onDisable: this.detachRemoteSettings.bind(this), + }; + await FeatureGate.addObserver(feature, observer); + CleanupManager.addCleanupHandler(() => FeatureGate.removeObserver(feature, observer)); + }, + + attachRemoteSettings() { + this.loadFromRemoteSettings = true; + if (!this._onSync) { + this._onSync = async () => { + if (!this.enabled) { + return; + } + this.run({ trigger: "sync" }); + }; + + gRemoteSettingsClient.on("sync", this._onSync); + } + }, + + detachRemoteSettings() { + this.loadFromRemoteSettings = false; + if (this._onSync) { + // Ignore if no event listener was setup or was already removed (ie. pref changed while enabled). + gRemoteSettingsClient.off("sync", this._onSync); + } + this._onSync = null; + }, + updateRunInterval() { // Run once every `runInterval` wall-clock seconds. This is managed by setting a "last ran" // timestamp, and running if it is more than `runInterval` seconds ago. Even with very short @@ -198,51 +243,69 @@ var RecipeRunner = { timerManager.registerTimer(TIMER_NAME, () => this.run(), runInterval); }, - async run() { - Services.obs.notifyObservers(null, "recipe-runner:start"); - this.clearCaches(); - // Unless lazy classification is enabled, prep the classify cache. - if (!Services.prefs.getBoolPref(LAZY_CLASSIFY_PREF, false)) { - try { - await ClientEnvironment.getClientClassification(); - } catch (err) { - // Try to go on without this data; the filter expressions will - // gracefully fail without this info if they need it. - } - } + async run(options = {}) { + const { trigger = "timer" } = options; - // Fetch recipes before execution in case we fail and exit early. - let recipesToRun; - try { - recipesToRun = await this.loadRecipes(); - } catch (e) { - // Either we failed at fetching the recipes from server (legacy), - // or the recipes signature verification failed. - let status = Uptake.RUNNER_SERVER_ERROR; - if (/NetworkError/.test(e)) { - status = Uptake.RUNNER_NETWORK_ERROR; - } else if (e instanceof NormandyApi.InvalidSignatureError) { - status = Uptake.RUNNER_INVALID_SIGNATURE; - } - await Uptake.reportRunner(status); + if (this.running) { + // Do nothing if already running. return; } + try { + this.running = true; - const actions = new ActionsManager(); + Services.obs.notifyObservers(null, "recipe-runner:start"); + this.clearCaches(); + // Unless lazy classification is enabled, prep the classify cache. + if (!Services.prefs.getBoolPref(LAZY_CLASSIFY_PREF, false)) { + try { + await ClientEnvironment.getClientClassification(); + } catch (err) { + // Try to go on without this data; the filter expressions will + // gracefully fail without this info if they need it. + } + } - // Execute recipes, if we have any. - if (recipesToRun.length === 0) { - log.debug("No recipes to execute"); - } else { - for (const recipe of recipesToRun) { - await actions.runRecipe(recipe); + // Fetch recipes before execution in case we fail and exit early. + let recipesToRun; + try { + recipesToRun = await this.loadRecipes(); + } catch (e) { + // Either we failed at fetching the recipes from server (legacy), + // or the recipes signature verification failed. + let status = Uptake.RUNNER_SERVER_ERROR; + if (/NetworkError/.test(e)) { + status = Uptake.RUNNER_NETWORK_ERROR; + } else if (e instanceof NormandyApi.InvalidSignatureError) { + status = Uptake.RUNNER_INVALID_SIGNATURE; + } + await Uptake.reportRunner(status); + return; + } + + const actions = new ActionsManager(); + + // Execute recipes, if we have any. + if (recipesToRun.length === 0) { + log.debug("No recipes to execute"); + } else { + for (const recipe of recipesToRun) { + await actions.runRecipe(recipe); + } + } + + await actions.finalize(); + + await Uptake.reportRunner(Uptake.RUNNER_SUCCESS); + Services.obs.notifyObservers(null, "recipe-runner:end"); + } finally { + this.running = false; + if (trigger != "timer") { + // `run()` was executed outside the scheduled timer. + // Update the last time it ran to make sure it is rescheduled later. + const lastUpdateTime = Math.round(Date.now() / 1000); + Services.prefs.setIntPref(TIMER_LAST_UPDATE_PREF, lastUpdateTime); } } - - await actions.finalize(); - - await Uptake.reportRunner(Uptake.RUNNER_SUCCESS); - Services.obs.notifyObservers(null, "recipe-runner:end"); }, /** @@ -251,7 +314,7 @@ var RecipeRunner = { async loadRecipes() { // If RemoteSettings is enabled, we read the list of recipes from there. // The JEXL filtering is done via the provided callback (see `gRemoteSettingsClient`). - if (await FeatureGate.isEnabled("normandy-remote-settings")) { + if (this.loadFromRemoteSettings) { // First, fetch recipes whose JEXL filters match. const entries = await gRemoteSettingsClient.get(); // Then, verify the signature of each recipe. It will throw if invalid. diff --git a/toolkit/components/normandy/test/browser/browser_RecipeRunner.js b/toolkit/components/normandy/test/browser/browser_RecipeRunner.js index 1ef527da3296..ff5e2816e226 100644 --- a/toolkit/components/normandy/test/browser/browser_RecipeRunner.js +++ b/toolkit/components/normandy/test/browser/browser_RecipeRunner.js @@ -455,3 +455,129 @@ decorate_task( } } ); + +decorate_task( + withPrefEnv({ + set: [ + ["features.normandy-remote-settings.enabled", false], + ], + }), + withStub(RecipeRunner, "run"), + async function testRunOnSyncRemoteSettings( + runStub, + ) { + const rsClient = RecipeRunner._remoteSettingsClientForTesting; + + // Runner disabled + pref off. + RecipeRunner.disable(); + await rsClient.emit("sync", {}); + ok(!runStub.called, "run() should not be called if disabled"); + runStub.reset(); + + // Runner enabled + pref off. + RecipeRunner.enable(); + await rsClient.emit("sync", {}); + ok(!runStub.called, "run() should not be called if pref not set"); + runStub.reset(); + + await SpecialPowers.pushPrefEnv({ set: [["features.normandy-remote-settings.enabled", true]] }); + + // Runner enabled + pref on. + await rsClient.emit("sync", {}); + ok(runStub.called, "run() should be called if pref is set"); + runStub.reset(); + + // Runner disabled + pref on. + RecipeRunner.disable(); + await rsClient.emit("sync", {}); + ok(!runStub.called, "run() should not be called if disabled with pref set"); + runStub.reset(); + + // Runner re-enabled + pref on. + RecipeRunner.enable(); + await rsClient.emit("sync", {}); + ok(runStub.called, "run() should be called at most once if runner is re-enabled"); + runStub.reset(); + + await SpecialPowers.pushPrefEnv({ set: [["features.normandy-remote-settings.enabled", false]] }); + + // Runner enabled + pref off. + await rsClient.emit("sync", {}); + ok(!runStub.called, "run() should not be called if pref is unset"); + runStub.reset(); + + // Runner disabled + pref off. + RecipeRunner.disable(); + await rsClient.emit("sync", {}); + ok(!runStub.called, "run() should still not be called if disabled"); + RecipeRunner.enable(); + } +); + +decorate_task( + withStub(RecipeRunner, "loadRecipes"), + async function testRunCanRunOnlyOnce( + loadRecipesStub, + ) { + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + loadRecipesStub.returns(new Promise((resolve) => setTimeout(() => resolve([]), 10))); + + // // Run 2 in parallel. + await Promise.all([ + RecipeRunner.run(), + RecipeRunner.run(), + ]); + + is(loadRecipesStub.callCount, 1, "run() is no-op if already running"); + } +); + +decorate_task( + withPrefEnv({ + set: [ + ["features.normandy-remote-settings.enabled", true], + ], + }), + withStub(RecipeRunner, "loadRecipes"), + withStub(ActionsManager.prototype, "finalize"), + withStub(Uptake, "reportRunner"), + async function testSyncDelaysTimer( + loadRecipesStub, + finalizeStub, + reportRecipeStub, + ) { + loadRecipesStub.returns(Promise.resolve([])); + // Set a timer interval as small as possible so that the UpdateTimerManager + // will pick the recipe runner as the most imminent timer to run on `notify()`. + Services.prefs.setIntPref("app.normandy.run_interval_seconds", 1); + // This will refresh the timer interval. + RecipeRunner.unregisterTimer(); + RecipeRunner.registerTimer(); + + // Simulate timer notification. + const service = Cc["@mozilla.org/updates/timer-manager;1"].getService(Ci.nsITimerCallback); + const newTimer = () => { + const t = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + t.initWithCallback(() => { }, 10, Ci.nsITimer.TYPE_ONE_SHOT); + return t; + }; + // Run timer once, to make sure this test works as expected. + const startTime = Date.now(); + const endPromise = TestUtils.topicObserved("recipe-runner:end"); + service.notify(newTimer()); + await endPromise; // will timeout if run() not called. + const timerLatency = Date.now() - startTime; + + // Run once from sync event. + const rsClient = RecipeRunner._remoteSettingsClientForTesting; + await rsClient.emit("sync", {}); // waits for listeners to run. + + // Run timer again. + service.notify(newTimer()); + // Wait at least as long as the latency we had above. Ten times as a margin. + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + await new Promise((resolve) => setTimeout(resolve, timerLatency * 10)); + + is(loadRecipesStub.callCount, 2, "run() does not run again from timer after sync"); + } +); diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 8c1f6e90fc87..133797f7057c 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -1073,6 +1073,17 @@ "bug_numbers": [1226196], "description": "Resident memory size (KB)" }, + "MEMORY_RESIDENT_PEAK": { + "record_in_processes": ["main", "content"], + "alert_emails": ["memshrink-telemetry-alerts@mozilla.com", "kgupta@mozilla.com"], + "expires_in_version": "never", + "kind": "exponential", + "low": 32768, + "high": 16777216, + "n_buckets": 100, + "bug_numbers": [1551648], + "description": "Peak resident memory size (KB)" + }, "MEMORY_TOTAL": { "record_in_processes": ["main"], "alert_emails": ["memshrink-telemetry-alerts@mozilla.com", "erahm@mozilla.com"], diff --git a/toolkit/content/aboutTelemetry.js b/toolkit/content/aboutTelemetry.js index 39504f36fcb6..428254a29cc6 100644 --- a/toolkit/content/aboutTelemetry.js +++ b/toolkit/content/aboutTelemetry.js @@ -1628,7 +1628,7 @@ class Section { if (data && Object.keys(data).length > 0) { let s = GenericSubsection.renderSubsectionHeader(process, true, section); let heading = document.createElement("h2"); - heading.textContent = process; + document.l10n.setAttributes(heading, "about-telemetry-process", {process}); s.appendChild(heading); this.renderData(data, s); diff --git a/toolkit/locales/en-US/toolkit/about/aboutTelemetry.ftl b/toolkit/locales/en-US/toolkit/about/aboutTelemetry.ftl index b7c9311cc0cd..e27f582c0193 100644 --- a/toolkit/locales/en-US/toolkit/about/aboutTelemetry.ftl +++ b/toolkit/locales/en-US/toolkit/about/aboutTelemetry.ftl @@ -142,3 +142,6 @@ about-telemetry-origin-count = count # Variables: # $telemetryServerOwner (String): the value of the toolkit.telemetry.server_owner preference. Typically "Mozilla" about-telemetry-origins-explanation = Firefox Origin Telemetry encodes data before it is sent so that { $telemetryServerOwner } can count things, but not know whether or not any given { -brand-product-name } contributed to that count. (learn more) +# Variables: +# $process (String): type of process in subsection headers ( e.g. "content", "parent" ) +about-telemetry-process = { $process } process diff --git a/tools/lint/rust/__init__.py b/tools/lint/rust/__init__.py new file mode 100644 index 000000000000..6dbd7b73c6b5 --- /dev/null +++ b/tools/lint/rust/__init__.py @@ -0,0 +1,133 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from __future__ import absolute_import, print_function +from collections import namedtuple + +import os +import signal +import which +import re + +from mozlint import result +from mozlint.pathutils import expand_exclusions +from mozprocess import ProcessHandler + +RUSTFMT_NOT_FOUND = """ +Could not find rustfmt! Install rustfmt and try again. + + $ rustup component add rustfmt + +And make sure that it is in the PATH +""".strip() + + +RUSTFMT_INSTALL_ERROR = """ +Unable to install correct version of rustfmt +Try to install it manually with: + $ rustup component add rustfmt +""".strip() + + +def parse_issues(config, output, paths): + RustfmtDiff = namedtuple("RustfmtDiff", ["file", "line", "diff"]) + issues = [] + diff_line = re.compile("^Diff in (.*) at line ([0-9]*):") + file = "" + line_no = 0 + diff = "" + for line in output: + line = line.decode("utf-8") + match = diff_line.match(line) + if match: + if diff: + issues.append(RustfmtDiff(file, line_no, diff.rstrip("\n"))) + diff = "" + file, line_no = match.groups() + else: + diff += line + "\n" + # the algorithm above will always skip adding the last issue + issues.append(RustfmtDiff(file, line_no, diff)) + results = [] + for issue in issues: + # rustfmt can not be supplied the paths to the files we want to analyze + # therefore, for each issue detected, we check if any of the the paths + # supplied are part of the file name. + # This just filters out the issues that are not part of paths. + if any([path in file for path in paths]): + res = { + "path": issue.file, + "diff": issue.diff, + "level": "warning", + "lineno": issue.line, + } + results.append(result.from_config(config, **res)) + return results + + +class RustfmtProcess(ProcessHandler): + def __init__(self, config, *args, **kwargs): + self.config = config + kwargs["stream"] = False + ProcessHandler.__init__(self, *args, **kwargs) + + def run(self, *args, **kwargs): + orig = signal.signal(signal.SIGINT, signal.SIG_IGN) + ProcessHandler.run(self, *args, **kwargs) + signal.signal(signal.SIGINT, orig) + + +def run_process(config, cmd): + proc = RustfmtProcess(config, cmd) + proc.run() + try: + proc.wait() + except KeyboardInterrupt: + proc.kill() + + return proc.output + + +def get_rustfmt_binary(): + """ + Returns the path of the first rustfmt binary available + if not found returns None + """ + binary = os.environ.get("RUSTFMT") + if binary: + return binary + + try: + return which.which("rustfmt") + except which.WhichError: + return None + + +def run_rustfmt(config, paths, fix=None): + binary = get_rustfmt_binary() + + if not binary: + print(RUSTFMT_NOT_FOUND) + if "MOZ_AUTOMATION" in os.environ: + return 1 + return [] + + cmd_args = [binary] + if not fix: + cmd_args.append("--check") + base_command = cmd_args + paths + return parse_issues(config, run_process(config, base_command), paths) + + +def lint(paths, config, fix=None, **lintargs): + + files = list(expand_exclusions(paths, config, lintargs['root'])) + + # to retrieve the future changes + results = run_rustfmt(config, files, fix=False) + + if fix and results: + # To do the actual change + run_rustfmt(config, files, fix=True) + return results diff --git a/tools/lint/rustfmt.yml b/tools/lint/rustfmt.yml new file mode 100644 index 000000000000..59358eae12c0 --- /dev/null +++ b/tools/lint/rustfmt.yml @@ -0,0 +1,11 @@ +--- +rust: + description: Reformat rust + include: + - servo/ + extensions: + - rs + support-files: + - 'tools/lint/rust/**' + type: external + payload: rust:lint diff --git a/xpcom/base/MemoryTelemetry.cpp b/xpcom/base/MemoryTelemetry.cpp index a700ea8df3d6..e90d126bd580 100644 --- a/xpcom/base/MemoryTelemetry.cpp +++ b/xpcom/base/MemoryTelemetry.cpp @@ -306,6 +306,7 @@ nsresult MemoryTelemetry::GatherReports( RECORD(MEMORY_VSIZE_MAX_CONTIGUOUS, VsizeMaxContiguous, UNITS_BYTES); #endif RECORD(MEMORY_RESIDENT_FAST, ResidentFast, UNITS_BYTES); + RECORD(MEMORY_RESIDENT_PEAK, ResidentPeak, UNITS_BYTES); RECORD(MEMORY_UNIQUE, ResidentUnique, UNITS_BYTES); RECORD(MEMORY_HEAP_ALLOCATED, HeapAllocated, UNITS_BYTES); RECORD(MEMORY_HEAP_OVERHEAD_FRACTION, HeapOverheadFraction,