LiquidDict.jsm 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. /*******************************************************************************
  2. ηMatrix - a browser extension to black/white list requests.
  3. Copyright (C) 2014-2019 Raymond Hill
  4. Copyright (C) 2019-2020-2021 Alessio Vanni
  5. This program is free software: you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation, either version 3 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program. If not, see {http://www.gnu.org/licenses/}.
  15. Home: https://gitlab.com/vannilla/ematrix
  16. uMatrix Home: https://github.com/gorhill/uMatrix
  17. */
  18. 'use strict';
  19. var EXPORTED_SYMBOLS = ['LiquidDict'];
  20. var LiquidDict = function () {
  21. this.dict = {};
  22. this.count = 0;
  23. this.duplicateCount = 0;
  24. this.bucketCount = 0;
  25. this.frozenCount = 0;
  26. this.cutoff = 500;
  27. };
  28. function meltBucket(dict, len, bucket) {
  29. let map = {};
  30. --dict.frozenCount;
  31. if (bucket.charAt(0) === ' ') {
  32. bucket.trim().split(' ').map(function (e) {
  33. map[e] = true;
  34. });
  35. } else {
  36. for (let i=0; i<bucket.length; i+=len) {
  37. map[bucket.substring(i, len)] = true;
  38. }
  39. }
  40. return map;
  41. }
  42. function melt(dict) {
  43. let buckets = dict.dict;
  44. for (let key in buckets) {
  45. let bucket = buckets[key];
  46. if (typeof bucket === 'string') {
  47. buckets[key] = meltBucket(dict, key.charCodeAt(0) & 0xFF, bucket);
  48. }
  49. }
  50. }
  51. function freezeBucket(dict, bucket) {
  52. let words = Object.keys(bucket);
  53. let wlen = words[0].length;
  54. ++dict.frozenCount;
  55. if (wlen * words.length < dict.cutoff) {
  56. return ' ' + words.join(' ') + ' ';
  57. }
  58. return words.sort().join('');
  59. }
  60. LiquidDict.prototype.makeKey = function (word) {
  61. let len = word.length;
  62. if (len > 255) {
  63. len = 255;
  64. }
  65. let i = len >> 2;
  66. return String.fromCharCode((word.charCodeAt(0) & 0x03) << 14
  67. | (word.charCodeAt(i) & 0x03) << 12
  68. | (word.charCodeAt(i+i) & 0x03) << 10
  69. | (word.charCodeAt(i+i+i) & 0x03) << 8
  70. | len);
  71. };
  72. LiquidDict.prototype.test = function (word) {
  73. let key = this.makeKey(word);
  74. let bucket = this.dict[key];
  75. if (bucket === undefined) {
  76. return false;
  77. }
  78. if (typeof bucket === 'object') {
  79. return bucket[word] !== undefined;
  80. }
  81. if (bucket.charAt(0) === ' ') {
  82. return bucket.indexOf(' ' + word + ' ') >= 0;
  83. }
  84. let len = word.length;
  85. let left = 0;
  86. let right = ~~(bucket.length / len + 0.5);
  87. while (left < right) {
  88. let i = left + right >> 1;
  89. let needle = bucket.substr(len * i, len);
  90. if (word < needle) {
  91. right = i;
  92. } else if (word > needle) {
  93. left = i + 1;
  94. } else {
  95. return true;
  96. }
  97. }
  98. return false;
  99. };
  100. LiquidDict.prototype.add = function (word) {
  101. let key = this.makeKey(word);
  102. if (key === undefined) {
  103. return false;
  104. }
  105. let bucket = this.dict[key];
  106. if (bucket === undefined) {
  107. this.dict[key] = bucket = {};
  108. ++this.bucketCount;
  109. bucket[word] = true;
  110. ++this.count;
  111. return true;
  112. } else if (typeof bucket === 'string') {
  113. this.dict[key] = bucket = meltBucket(this, word.len, bucket);
  114. }
  115. if (bucket[word] === undefined) {
  116. bucket[word] = true;
  117. ++this.count;
  118. return true;
  119. }
  120. ++this.duplicateCount;
  121. return false;
  122. };
  123. LiquidDict.prototype.freeze = function () {
  124. let buckets = this.dict;
  125. for (let key in buckets) {
  126. let bucket = buckets[key];
  127. if (typeof bucket === 'object') {
  128. buckets[key] = freezeBucket(this, bucket);
  129. }
  130. }
  131. };
  132. LiquidDict.prototype.reset = function () {
  133. this.dict = {};
  134. this.count = 0;
  135. this.duplicateCount = 0;
  136. this.bucketCount = 0;
  137. this.frozenBucketCount = 0;
  138. }