Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: lib/matcher.js

Issue 29551648: Issue 5735 - Use JS Map instead of Object for matcher properties filterByKeyword and keywordByFilter (Closed) Base URL: https://github.com/adblockplus/adblockpluscore.git
Patch Set: use objects as keys Created Sept. 25, 2017, 1:44 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | test/filterListener.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>, 2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-present eyeo GmbH 3 * Copyright (C) 2006-present eyeo GmbH
4 * 4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify 5 * Adblock Plus is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as 6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation. 7 * published by the Free Software Foundation.
8 * 8 *
9 * Adblock Plus is distributed in the hope that it will be useful, 9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
(...skipping 19 matching lines...) Expand all
30 */ 30 */
31 function Matcher() 31 function Matcher()
32 { 32 {
33 this.clear(); 33 this.clear();
34 } 34 }
35 exports.Matcher = Matcher; 35 exports.Matcher = Matcher;
36 36
37 Matcher.prototype = { 37 Matcher.prototype = {
38 /** 38 /**
39 * Lookup table for filters by their associated keyword 39 * Lookup table for filters by their associated keyword
40 * @type {Object} 40 * @type {Map.<string,Filter>}
41 */ 41 */
42 filterByKeyword: null, 42 filterByKeyword: null,
43 43
44 /** 44 /**
45 * Lookup table for keywords by the filter text 45 * Lookup table for keywords by the filter
46 * @type {Object} 46 * @type {Map.<Filter,string>}
47 */ 47 */
48 keywordByFilter: null, 48 keywordByFilter: null,
49 49
50 /** 50 /**
51 * Removes all known filters 51 * Removes all known filters
52 */ 52 */
53 clear() 53 clear()
54 { 54 {
55 this.filterByKeyword = Object.create(null); 55 this.filterByKeyword = new Map();
56 this.keywordByFilter = Object.create(null); 56 this.keywordByFilter = new Map();
57 }, 57 },
58 58
59 /** 59 /**
60 * Adds a filter to the matcher 60 * Adds a filter to the matcher
61 * @param {RegExpFilter} filter 61 * @param {RegExpFilter} filter
62 */ 62 */
63 add(filter) 63 add(filter)
64 { 64 {
65 if (filter.text in this.keywordByFilter) 65 if (this.keywordByFilter.has(filter))
66 return; 66 return;
67 67
68 // Look for a suitable keyword 68 // Look for a suitable keyword
69 let keyword = this.findKeyword(filter); 69 let keyword = this.findKeyword(filter);
70 let oldEntry = this.filterByKeyword[keyword]; 70 let oldEntry = this.filterByKeyword.get(keyword);
71 if (typeof oldEntry == "undefined") 71 if (typeof oldEntry == "undefined")
72 this.filterByKeyword[keyword] = filter; 72 this.filterByKeyword.set(keyword, filter);
73 else if (oldEntry.length == 1) 73 else if (oldEntry.length == 1)
74 this.filterByKeyword[keyword] = [oldEntry, filter]; 74 this.filterByKeyword.set(keyword, [oldEntry, filter]);
75 else 75 else
76 oldEntry.push(filter); 76 oldEntry.push(filter);
77 this.keywordByFilter[filter.text] = keyword; 77 this.keywordByFilter.set(filter, keyword);
78 }, 78 },
79 79
80 /** 80 /**
81 * Removes a filter from the matcher 81 * Removes a filter from the matcher
82 * @param {RegExpFilter} filter 82 * @param {RegExpFilter} filter
83 */ 83 */
84 remove(filter) 84 remove(filter)
85 { 85 {
86 if (!(filter.text in this.keywordByFilter)) 86 let keyword = this.keywordByFilter.get(filter);
87 if (typeof keyword == "undefined")
87 return; 88 return;
88 89
89 let keyword = this.keywordByFilter[filter.text]; 90 let list = this.filterByKeyword.get(keyword);
90 let list = this.filterByKeyword[keyword];
91 if (list.length <= 1) 91 if (list.length <= 1)
92 delete this.filterByKeyword[keyword]; 92 this.filterByKeyword.delete(keyword);
93 else 93 else
94 { 94 {
95 let index = list.indexOf(filter); 95 let index = list.indexOf(filter);
96 if (index >= 0) 96 if (index >= 0)
97 { 97 {
98 list.splice(index, 1); 98 list.splice(index, 1);
99 if (list.length == 1) 99 if (list.length == 1)
100 this.filterByKeyword[keyword] = list[0]; 100 this.filterByKeyword.set(keyword, list[0]);
101 } 101 }
102 } 102 }
103 103
104 delete this.keywordByFilter[filter.text]; 104 this.keywordByFilter.delete(filter);
105 }, 105 },
106 106
107 /** 107 /**
108 * Chooses a keyword to be associated with the filter 108 * Chooses a keyword to be associated with the filter
109 * @param {Filter} filter 109 * @param {Filter} filter
110 * @return {string} keyword or an empty string if no keyword could be found 110 * @return {string} keyword or an empty string if no keyword could be found
111 */ 111 */
112 findKeyword(filter) 112 findKeyword(filter)
113 { 113 {
114 let result = ""; 114 let result = "";
(...skipping 15 matching lines...) Expand all
130 ); 130 );
131 if (!candidates) 131 if (!candidates)
132 return result; 132 return result;
133 133
134 let hash = this.filterByKeyword; 134 let hash = this.filterByKeyword;
135 let resultCount = 0xFFFFFF; 135 let resultCount = 0xFFFFFF;
136 let resultLength = 0; 136 let resultLength = 0;
137 for (let i = 0, l = candidates.length; i < l; i++) 137 for (let i = 0, l = candidates.length; i < l; i++)
138 { 138 {
139 let candidate = candidates[i].substr(1); 139 let candidate = candidates[i].substr(1);
140 let count = (candidate in hash ? hash[candidate].length : 0); 140 let filters = hash.get(candidate);
141 let count = typeof filters != "undefined" ? filters.length : 0;
141 if (count < resultCount || 142 if (count < resultCount ||
142 (count == resultCount && candidate.length > resultLength)) 143 (count == resultCount && candidate.length > resultLength))
143 { 144 {
144 result = candidate; 145 result = candidate;
145 resultCount = count; 146 resultCount = count;
146 resultLength = candidate.length; 147 resultLength = candidate.length;
147 } 148 }
148 } 149 }
149 return result; 150 return result;
150 }, 151 },
151 152
152 /** 153 /**
153 * Checks whether a particular filter is being matched against. 154 * Checks whether a particular filter is being matched against.
154 * @param {RegExpFilter} filter 155 * @param {RegExpFilter} filter
155 * @return {boolean} 156 * @return {boolean}
156 */ 157 */
157 hasFilter(filter) 158 hasFilter(filter)
158 { 159 {
159 return (filter.text in this.keywordByFilter); 160 return this.keywordByFilter.has(filter);
160 }, 161 },
161 162
162 /** 163 /**
163 * Returns the keyword used for a filter, null for unknown filters. 164 * Returns the keyword used for a filter, null for unknown filters.
164 * @param {RegExpFilter} filter 165 * @param {RegExpFilter} filter
165 * @return {string} 166 * @return {?string}
166 */ 167 */
167 getKeywordForFilter(filter) 168 getKeywordForFilter(filter)
168 { 169 {
169 if (filter.text in this.keywordByFilter) 170 let keyword = this.keywordByFilter.get(filter);
170 return this.keywordByFilter[filter.text]; 171 return typeof keyword != "undefined" ? keyword : null;
171 return null;
172 }, 172 },
173 173
174 /** 174 /**
175 * Checks whether the entries for a particular keyword match a URL 175 * Checks whether the entries for a particular keyword match a URL
176 * @param {string} keyword 176 * @param {string} keyword
177 * @param {string} location 177 * @param {string} location
178 * @param {number} typeMask 178 * @param {number} typeMask
179 * @param {string} docDomain 179 * @param {string} docDomain
180 * @param {boolean} thirdParty 180 * @param {boolean} thirdParty
181 * @param {string} sitekey 181 * @param {string} sitekey
182 * @param {boolean} specificOnly 182 * @param {boolean} specificOnly
183 * @return {?Filter} 183 * @return {?Filter}
184 */ 184 */
185 _checkEntryMatch(keyword, location, typeMask, docDomain, thirdParty, sitekey, 185 _checkEntryMatch(keyword, location, typeMask, docDomain, thirdParty, sitekey,
186 specificOnly) 186 specificOnly)
187 { 187 {
188 let list = this.filterByKeyword[keyword]; 188 let list = this.filterByKeyword.get(keyword);
189 if (typeof list == "undefined")
190 return null;
189 for (let i = 0; i < list.length; i++) 191 for (let i = 0; i < list.length; i++)
190 { 192 {
191 let filter = list[i]; 193 let filter = list[i];
192 194
193 if (specificOnly && filter.isGeneric() && 195 if (specificOnly && filter.isGeneric() &&
194 !(filter instanceof WhitelistFilter)) 196 !(filter instanceof WhitelistFilter))
195 continue; 197 continue;
196 198
197 if (filter.matches(location, typeMask, docDomain, thirdParty, sitekey)) 199 if (filter.matches(location, typeMask, docDomain, thirdParty, sitekey))
198 return filter; 200 return filter;
(...skipping 19 matching lines...) Expand all
218 * matching filter or null 220 * matching filter or null
219 */ 221 */
220 matchesAny(location, typeMask, docDomain, thirdParty, sitekey, specificOnly) 222 matchesAny(location, typeMask, docDomain, thirdParty, sitekey, specificOnly)
221 { 223 {
222 let candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); 224 let candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g);
223 if (candidates === null) 225 if (candidates === null)
224 candidates = []; 226 candidates = [];
225 candidates.push(""); 227 candidates.push("");
226 for (let i = 0, l = candidates.length; i < l; i++) 228 for (let i = 0, l = candidates.length; i < l; i++)
227 { 229 {
228 let substr = candidates[i]; 230 let result = this._checkEntryMatch(candidates[i], location, typeMask,
229 if (substr in this.filterByKeyword) 231 docDomain, thirdParty, sitekey,
230 { 232 specificOnly);
231 let result = this._checkEntryMatch(substr, location, typeMask, 233 if (result)
232 docDomain, thirdParty, sitekey, 234 return result;
233 specificOnly);
234 if (result)
235 return result;
236 }
237 } 235 }
238 236
239 return null; 237 return null;
240 } 238 }
241 }; 239 };
242 240
243 /** 241 /**
244 * Combines a matcher for blocking and exception rules, automatically sorts 242 * Combines a matcher for blocking and exception rules, automatically sorts
245 * rules into two Matcher instances. 243 * rules into two Matcher instances.
246 * @constructor 244 * @constructor
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after
395 { 393 {
396 let candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); 394 let candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g);
397 if (candidates === null) 395 if (candidates === null)
398 candidates = []; 396 candidates = [];
399 candidates.push(""); 397 candidates.push("");
400 398
401 let blacklistHit = null; 399 let blacklistHit = null;
402 for (let i = 0, l = candidates.length; i < l; i++) 400 for (let i = 0, l = candidates.length; i < l; i++)
403 { 401 {
404 let substr = candidates[i]; 402 let substr = candidates[i];
405 if (substr in this.whitelist.filterByKeyword) 403 let result = this.whitelist._checkEntryMatch(
406 { 404 substr, location, typeMask, docDomain, thirdParty, sitekey
407 let result = this.whitelist._checkEntryMatch( 405 );
408 substr, location, typeMask, docDomain, thirdParty, sitekey 406 if (result)
409 ); 407 return result;
410 if (result) 408 if (blacklistHit === null)
411 return result;
412 }
413 if (substr in this.blacklist.filterByKeyword && blacklistHit === null)
414 { 409 {
415 blacklistHit = this.blacklist._checkEntryMatch( 410 blacklistHit = this.blacklist._checkEntryMatch(
416 substr, location, typeMask, docDomain, thirdParty, sitekey, 411 substr, location, typeMask, docDomain, thirdParty, sitekey,
417 specificOnly 412 specificOnly
418 ); 413 );
419 } 414 }
420 } 415 }
421 return blacklistHit; 416 return blacklistHit;
422 }, 417 },
423 418
(...skipping 22 matching lines...) Expand all
446 441
447 return result; 442 return result;
448 } 443 }
449 }; 444 };
450 445
451 /** 446 /**
452 * Shared CombinedMatcher instance that should usually be used. 447 * Shared CombinedMatcher instance that should usually be used.
453 * @type {CombinedMatcher} 448 * @type {CombinedMatcher}
454 */ 449 */
455 exports.defaultMatcher = new CombinedMatcher(); 450 exports.defaultMatcher = new CombinedMatcher();
OLDNEW
« no previous file with comments | « no previous file | test/filterListener.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld