Index: lib/filterClasses.js |
=================================================================== |
--- a/lib/filterClasses.js |
+++ b/lib/filterClasses.js |
@@ -19,16 +19,17 @@ |
/** |
* @fileOverview Definition of Filter class and its subclasses. |
*/ |
const {extend} = require("./coreUtils"); |
const {filterToRegExp} = require("./common"); |
const {normalizeHostname, domainSuffixes} = require("./url"); |
+const {Cache} = require("./caching"); |
const {filterNotifier} = require("./filterNotifier"); |
const resources = require("../data/resources.json"); |
/** |
* Map of internal resources for URL rewriting. |
* @type {Map.<string,string>} |
*/ |
@@ -47,20 +48,24 @@ |
* Regular expression used to match the <code>^</code> suffix in an otherwise |
* literal pattern. |
* @type {RegExp} |
*/ |
// Note: This should match the pattern in lib/common.js |
let separatorRegExp = /[\x00-\x24\x26-\x2C\x2F\x3A-\x40\x5B-\x5E\x60\x7B-\x7F]/; |
/** |
- * All known unique domain sources mapped to their parsed values. |
- * @type {Map.<string,Map.<string,boolean>>} |
+ * Cache of domain maps. The domains part of filter text |
+ * (e.g. <code>example.com,~mail.example.com</code>) is often repeated across |
+ * filters. This cache enables deduplication of the <code>Map</code> objects |
+ * that specify on which domains the filter does and does not apply, which |
+ * reduces memory usage and improves performance. |
+ * @type {Map.<string, Map.<string, boolean>>} |
*/ |
-let knownDomainMaps = new Map(); |
+let domainMapCache = new Cache(1000); |
/** |
* Checks whether the given pattern is a string of literal characters with no |
* wildcards or any other special characters. If the pattern is prefixed with a |
* <code>||</code> or suffixed with a <code>^</code> but otherwise contains no |
* special characters, it is still considered to be a literal pattern. |
* @param {string} pattern |
* @returns {boolean} |
@@ -402,17 +407,17 @@ |
* @constructor |
* @augments Filter |
*/ |
function ActiveFilter(text, domains) |
{ |
Filter.call(this, text); |
if (domains) |
- this.domainSource = domains; |
+ this.domainSource = domains.toLowerCase(); |
Manish Jethani
2019/03/04 12:02:54
Lower-case in advance during initialization.
hub
2019/03/06 13:10:40
Do we know if there is a performance difference be
Manish Jethani
2019/03/06 18:11:10
You mean that we could require that the domains ar
hub
2019/03/06 18:14:25
I was just wondering if toLowerCase() is faster wh
Manish Jethani
2019/03/06 18:28:10
Ah, I see what you mean. I don't expect it to be f
|
} |
exports.ActiveFilter = ActiveFilter; |
ActiveFilter.prototype = extend(Filter, { |
_disabled: false, |
_hitCount: 0, |
_lastHit: 0, |
@@ -489,95 +494,67 @@ |
/** |
* Map containing domains that this filter should match on/not match |
* on or null if the filter should match on all domains |
* @type {?Map.<string,boolean>} |
*/ |
get domains() |
{ |
- let domains = null; |
+ let {domainSource} = this; |
Manish Jethani
2019/03/04 12:02:54
Tip: The diff of this function is smaller with the
|
+ if (!domainSource) |
+ return null; |
- if (this.domainSource) |
- { |
- // For most filter types this property is accessed only rarely, |
- // especially when the subscriptions are initially loaded. We defer any |
- // caching by default. |
- let cacheDomains = this._cacheDomains; |
- |
- let source = this.domainSource.toLowerCase(); |
+ let domains = domainMapCache.get(domainSource); |
+ if (domains) |
+ return domains; |
- let knownMap = knownDomainMaps.get(source); |
- if (knownMap) |
+ let list = domainSource.split(this.domainSeparator); |
+ if (list.length == 1 && list[0][0] != "~") |
+ { |
+ // Fast track for the common one-domain scenario |
+ domains = new Map([[list[0], true], ["", false]]); |
+ } |
+ else |
+ { |
+ let hasIncludes = false; |
+ for (let i = 0; i < list.length; i++) |
{ |
- domains = knownMap; |
- } |
- else |
- { |
- let list = source.split(this.domainSeparator); |
- if (list.length == 1 && list[0][0] != "~") |
+ let domain = list[i]; |
+ if (domain == "") |
+ continue; |
+ |
+ let include; |
+ if (domain[0] == "~") |
{ |
- // Fast track for the common one-domain scenario |
- domains = new Map([[list[0], true], ["", false]]); |
+ include = false; |
+ domain = domain.substring(1); |
} |
else |
{ |
- let hasIncludes = false; |
- for (let i = 0; i < list.length; i++) |
- { |
- let domain = list[i]; |
- if (domain == "") |
- continue; |
- |
- let include; |
- if (domain[0] == "~") |
- { |
- include = false; |
- domain = domain.substring(1); |
- } |
- else |
- { |
- include = true; |
- hasIncludes = true; |
- } |
- |
- if (!domains) |
- domains = new Map(); |
- |
- domains.set(domain, include); |
- } |
- |
- if (domains) |
- domains.set("", !hasIncludes); |
+ include = true; |
+ hasIncludes = true; |
} |
- if (!domains || cacheDomains) |
- knownDomainMaps.set(source, domains); |
+ if (!domains) |
+ domains = new Map(); |
+ |
+ domains.set(domain, include); |
} |
- if (!domains || cacheDomains) |
- { |
- this.domainSource = null; |
- Object.defineProperty(this, "domains", {value: domains}); |
- } |
+ if (domains) |
+ domains.set("", !hasIncludes); |
} |
- this._cacheDomains = true; |
+ domainMapCache.set(domainSource, domains); |
return domains; |
}, |
/** |
- * Whether the value of {@link ActiveFilter#domains} should be cached. |
- * @type {boolean} |
- * @private |
- */ |
- _cacheDomains: false, |
- |
- /** |
* Array containing public keys of websites that this filter should apply to |
* @type {?string[]} |
*/ |
sitekeys: null, |
/** |
* Checks whether this filter is active on a domain. |
* @param {string} [docDomain] domain name of the document that loads the URL |