You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

202 lines
5.8 KiB

10 years ago
10 years ago
10 years ago
10 years ago
  1. /*******************************************************************************
  2. µMatrix - a Chromium browser extension to black/white list requests.
  3. Copyright (C) 2014 Raymond Hill
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see {http://www.gnu.org/licenses/}.
  14. Home: https://github.com/gorhill/uMatrix
  15. */
  16. /******************************************************************************/
  17. µMatrix.LiquidDict = (function() {
  18. /******************************************************************************/
  19. var LiquidDict = function() {
  20. this.dict = {};
  21. this.count = 0;
  22. this.duplicateCount = 0;
  23. this.bucketCount = 0;
  24. this.frozenBucketCount = 0;
  25. // Somewhat arbitrary: I need to come up with hard data to know at which
  26. // point binary search is better than indexOf.
  27. this.cutoff = 500;
  28. };
  29. /******************************************************************************/
  30. var meltBucket = function(ldict, len, bucket) {
  31. ldict.frozenBucketCount -= 1;
  32. var map = {};
  33. if ( bucket.charAt(0) === ' ' ) {
  34. bucket.trim().split(' ').map(function(k) {
  35. map[k] = true;
  36. });
  37. } else {
  38. var offset = 0;
  39. while ( offset < bucket.length ) {
  40. map[bucket.substring(offset, len)] = true;
  41. offset += len;
  42. }
  43. }
  44. return map;
  45. };
  46. /******************************************************************************/
  47. var melt = function(ldict) {
  48. var buckets = ldict.dict;
  49. var bucket;
  50. for ( var key in buckets ) {
  51. bucket = buckets[key];
  52. if ( typeof bucket === 'string' ) {
  53. buckets[key] = meltBucket(ldict, key.charCodeAt(0) & 0xFF, bucket);
  54. }
  55. }
  56. };
  57. /******************************************************************************/
  58. var freezeBucket = function(ldict, bucket) {
  59. ldict.frozenBucketCount += 1;
  60. var words = Object.keys(bucket);
  61. var wordLen = words[0].length;
  62. if ( wordLen * words.length < ldict.cutoff ) {
  63. return ' ' + words.join(' ') + ' ';
  64. }
  65. return words.sort().join('');
  66. };
  67. /******************************************************************************/
  68. // How the key is derived dictates the number and size of buckets.
  69. //
  70. // http://jsperf.com/makekey-concat-vs-join/3
  71. //
  72. // Question: Why is using a prototyped function better than a standalone
  73. // helper function?
  74. LiquidDict.prototype.makeKey = function(word) {
  75. var len = word.length;
  76. if ( len > 255 ) {
  77. len = 255;
  78. }
  79. var i = len >> 2;
  80. return String.fromCharCode(
  81. (word.charCodeAt( 0) & 0x03) << 14 |
  82. (word.charCodeAt( i) & 0x03) << 12 |
  83. (word.charCodeAt( i+i) & 0x03) << 10 |
  84. (word.charCodeAt(i+i+i) & 0x03) << 8 |
  85. len
  86. );
  87. };
  88. /******************************************************************************/
  89. LiquidDict.prototype.test = function(word) {
  90. var key = this.makeKey(word);
  91. var bucket = this.dict[key];
  92. if ( bucket === undefined ) {
  93. return false;
  94. }
  95. if ( typeof bucket === 'object' ) {
  96. return bucket[word] !== undefined;
  97. }
  98. if ( bucket.charAt(0) === ' ' ) {
  99. return bucket.indexOf(' ' + word + ' ') >= 0;
  100. }
  101. // binary search
  102. var len = word.length;
  103. var left = 0;
  104. // http://jsperf.com/or-vs-floor/3
  105. var right = ~~(bucket.length / len + 0.5);
  106. var i, needle;
  107. while ( left < right ) {
  108. i = left + right >> 1;
  109. needle = bucket.substr( len * i, len );
  110. if ( word < needle ) {
  111. right = i;
  112. } else if ( word > needle ) {
  113. left = i + 1;
  114. } else {
  115. return true;
  116. }
  117. }
  118. return false;
  119. };
  120. /******************************************************************************/
  121. LiquidDict.prototype.add = function(word) {
  122. var key = this.makeKey(word);
  123. if ( key === undefined ) {
  124. return false;
  125. }
  126. var bucket = this.dict[key];
  127. if ( bucket === undefined ) {
  128. this.dict[key] = bucket = {};
  129. this.bucketCount += 1;
  130. bucket[word] = true;
  131. this.count += 1;
  132. return true;
  133. } else if ( typeof bucket === 'string' ) {
  134. this.dict[key] = bucket = meltBucket(this, word.len, bucket);
  135. }
  136. if ( bucket[word] === undefined ) {
  137. bucket[word] = true;
  138. this.count += 1;
  139. return true;
  140. }
  141. this.duplicateCount += 1;
  142. return false;
  143. };
  144. /******************************************************************************/
  145. LiquidDict.prototype.freeze = function() {
  146. var buckets = this.dict;
  147. var bucket;
  148. for ( var key in buckets ) {
  149. bucket = buckets[key];
  150. if ( typeof bucket === 'object' ) {
  151. buckets[key] = freezeBucket(this, bucket);
  152. }
  153. }
  154. };
  155. /******************************************************************************/
  156. LiquidDict.prototype.reset = function() {
  157. this.dict = {};
  158. this.count = 0;
  159. this.duplicateCount = 0;
  160. this.bucketCount = 0;
  161. this.frozenBucketCount = 0;
  162. };
  163. /******************************************************************************/
  164. return LiquidDict;
  165. /******************************************************************************/
  166. })();
  167. /******************************************************************************/
  168. µMatrix.ubiquitousBlacklist = new µMatrix.LiquidDict();
  169. µMatrix.ubiquitousWhitelist = new µMatrix.LiquidDict();