simplify/modularize styleSectionsEqual
+ 1.5x speedup thanks to checkedInB memoization + more strict type comparison + two-way array comparison (more correct, even if there's no practical difference)
This commit is contained in:
		
							parent
							
								
									db1dc0df7b
								
							
						
					
					
						commit
						dcfb8ad356
					
				
							
								
								
									
										108
									
								
								storage.js
									
									
									
									
									
								
							
							
						
						
									
										108
									
								
								storage.js
									
									
									
									
									
								
							|  | @ -333,69 +333,52 @@ function styleCodeEmpty(code) { | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| function styleSectionsEqual(styleA, styleB) { | ||||
|   if (!styleA.sections || !styleB.sections) { | ||||
| function styleSectionsEqual({sections: a}, {sections: b}) { | ||||
|   if (!a || !b) { | ||||
|     return undefined; | ||||
|   } | ||||
|   if (styleA.sections.length != styleB.sections.length) { | ||||
|   if (a.length != b.length) { | ||||
|     return false; | ||||
|   } | ||||
|   const propNames = ['code', 'urlPrefixes', 'urls', 'domains', 'regexps']; | ||||
|   const typeBcaches = []; | ||||
|   checkingEveryInA: | ||||
|   for (const sectionA of styleA.sections) { | ||||
|     const typeAcache = new Map(); | ||||
|     for (const name of propNames) { | ||||
|       typeAcache.set(name, getType(sectionA[name])); | ||||
|   const checkedInB = []; | ||||
|   return a.every(sectionA => b.some(sectionB => { | ||||
|     if (!checkedInB.includes(sectionB) && propertiesEqual(sectionA, sectionB)) { | ||||
|       checkedInB.push(sectionB); | ||||
|       return true; | ||||
|     } | ||||
|     lookingForDupeInB: | ||||
|     for (let i = 0, sectionB; (sectionB = styleB.sections[i]); i++) { | ||||
|       const typeBcache = typeBcaches[i] = typeBcaches[i] || new Map(); | ||||
|       comparingProps: | ||||
|       for (const name of propNames) { | ||||
|         const propA = sectionA[name]; | ||||
|         const typeA = typeAcache.get(name); | ||||
|         const propB = sectionB[name]; | ||||
|         let typeB = typeBcache.get(name); | ||||
|         if (!typeB) { | ||||
|           typeB = getType(propB); | ||||
|           typeBcache.set(name, typeB); | ||||
|         } | ||||
|         if (typeA != typeB) { | ||||
|           const bothEmptyOrUndefined = | ||||
|             (typeA == 'undefined' || (typeA == 'array' && propA.length == 0)) && | ||||
|             (typeB == 'undefined' || (typeB == 'array' && propB.length == 0)); | ||||
|           if (bothEmptyOrUndefined) { | ||||
|             continue comparingProps; | ||||
|           } else { | ||||
|             continue lookingForDupeInB; | ||||
|           } | ||||
|         } | ||||
|         if (typeA == 'undefined') { | ||||
|           continue comparingProps; | ||||
|         } | ||||
|         if (typeA == 'array') { | ||||
|           if (propA.length != propB.length) { | ||||
|             continue lookingForDupeInB; | ||||
|           } | ||||
|           for (const item of propA) { | ||||
|             if (propB.indexOf(item) < 0) { | ||||
|               continue lookingForDupeInB; | ||||
|             } | ||||
|           } | ||||
|           continue comparingProps; | ||||
|         } | ||||
|         if (typeA == 'string' && propA != propB) { | ||||
|           continue lookingForDupeInB; | ||||
|         } | ||||
|   })); | ||||
| 
 | ||||
|   function propertiesEqual(secA, secB) { | ||||
|     for (const name of ['urlPrefixes', 'urls', 'domains', 'regexps']) { | ||||
|       if (!equalOrEmpty(secA[name], secB[name], 'every', arrayMirrors)) { | ||||
|         return false; | ||||
|       } | ||||
|       // dupe found
 | ||||
|       continue checkingEveryInA; | ||||
|     } | ||||
|     // dupe not found
 | ||||
|     return false; | ||||
|     return equalOrEmpty(secA.code, secB.code, 'substr', (a, b) => a == b); | ||||
|   } | ||||
| 
 | ||||
|   function equalOrEmpty(a, b, telltale, comparator) { | ||||
|     const typeA = a && typeof a[telltale] == 'function'; | ||||
|     const typeB = b && typeof b[telltale] == 'function'; | ||||
|     return ( | ||||
|       (a === null || a === undefined || (typeA && !a.length)) && | ||||
|       (b === null || b === undefined || (typeB && !b.length)) | ||||
|     ) || typeA && typeB && a.length == b.length && comparator(a, b); | ||||
|   } | ||||
| 
 | ||||
|   function arrayMirrors(array1, array2) { | ||||
|     for (const el of array1) { | ||||
|       if (array2.indexOf(el) < 0) { | ||||
|         return false; | ||||
|       } | ||||
|     } | ||||
|     for (const el of array2) { | ||||
|       if (array1.indexOf(el) < 0) { | ||||
|         return false; | ||||
|       } | ||||
|     } | ||||
|     return true; | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -515,18 +498,3 @@ function getDomains(url) { | |||
|   } | ||||
|   return domains; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| function getType(o) { | ||||
|   if (typeof o == 'undefined' || typeof o == 'string') { | ||||
|     return typeof o; | ||||
|   } | ||||
|   // with the persistent cachedStyles the Array reference is usually different
 | ||||
|   // so let's check for e.g. type of 'every' which is only present on arrays
 | ||||
|   // (in the context of our extension)
 | ||||
|   if (o instanceof Array || typeof o.every == 'function') { | ||||
|     return 'array'; | ||||
|   } | ||||
|   console.warn('Unsupported type:', o); | ||||
|   return 'undefined'; | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user