optimize.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. /* Copyright 2014 Mozilla Foundation
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. exports.optimizeCMap = function (data) {
  16. let i = 1;
  17. while (i < data.body.length) {
  18. if (data.body[i - 1].type === data.body[i].type) {
  19. data.body[i - 1].items = data.body[i - 1].items.concat(
  20. data.body[i].items
  21. );
  22. data.body.splice(i, 1);
  23. } else {
  24. i++;
  25. }
  26. }
  27. // split into groups with different lengths
  28. i = 0;
  29. while (i < data.body.length) {
  30. const item = data.body[i];
  31. const keys = Object.keys(item.items[0]).filter(function (val) {
  32. return typeof item.items[0][val] === "string";
  33. });
  34. let j = 1;
  35. while (j < item.items.length) {
  36. let different = false;
  37. for (let q = 0; q < keys.length && !different; q++) {
  38. different =
  39. item.items[j - 1][keys[q]].length !== item.items[j][keys[q]].length;
  40. }
  41. if (different) {
  42. break;
  43. }
  44. j++;
  45. }
  46. if (j < item.items.length) {
  47. data.body.splice(i + 1, 0, {
  48. type: item.type,
  49. items: item.items.splice(j, item.items.length - j),
  50. });
  51. }
  52. i++;
  53. }
  54. // find sequences of single char ranges
  55. i = 0;
  56. while (i < data.body.length) {
  57. const item = data.body[i];
  58. if (item.type === 3 || item.type === 5) {
  59. let j = 0;
  60. while (j < item.items.length) {
  61. const q = j;
  62. while (
  63. j < item.items.length &&
  64. item.items[j].start === item.items[j].end
  65. ) {
  66. j++;
  67. }
  68. if (j - q >= 9) {
  69. if (j < item.items.length) {
  70. data.body.splice(i + 1, 0, {
  71. type: item.type,
  72. items: item.items.splice(j, item.items.length - j),
  73. });
  74. }
  75. if (q > 0) {
  76. data.body.splice(i + 1, 0, {
  77. type: item.type - 1,
  78. items: item.items.splice(q, j - q).map(function (val) {
  79. return { char: val.start, code: val.code };
  80. }),
  81. });
  82. i++;
  83. } else {
  84. item.type -= 1;
  85. item.items = item.items.map(function (val) {
  86. return { char: val.start, code: val.code };
  87. });
  88. }
  89. continue;
  90. }
  91. j++;
  92. }
  93. }
  94. i++;
  95. }
  96. // find sequences of increasing code/ranges order
  97. i = 0;
  98. while (i < data.body.length) {
  99. const item = data.body[i];
  100. if (item.type >= 2 && item.type <= 5) {
  101. let j = 1;
  102. const startProp = item.type === 2 || item.type === 4 ? "char" : "start";
  103. const endProp = item.type === 2 || item.type === 4 ? "char" : "end";
  104. while (j < item.items.length) {
  105. const q = j - 1;
  106. while (
  107. j < item.items.length &&
  108. incHex(item.items[j - 1][endProp]) === item.items[j][startProp]
  109. ) {
  110. j++;
  111. }
  112. if (j - q >= 9) {
  113. if (j < item.items.length) {
  114. data.body.splice(i + 1, 0, {
  115. type: item.type,
  116. items: item.items.splice(j, item.items.length - j),
  117. });
  118. }
  119. if (q > 0) {
  120. data.body.splice(i + 1, 0, {
  121. type: item.type,
  122. items: item.items.splice(q, j - q),
  123. sequence: true,
  124. });
  125. i++;
  126. } else {
  127. item.sequence = true;
  128. }
  129. continue;
  130. }
  131. j++;
  132. }
  133. }
  134. i++;
  135. }
  136. // split non-sequences two groups where codes are close
  137. i = 0;
  138. while (i < data.body.length) {
  139. const item = data.body[i];
  140. if (!item.sequence && (item.type === 2 || item.type === 3)) {
  141. const subitems = item.items;
  142. const codes = subitems.map(function (val) {
  143. return val.code;
  144. });
  145. codes.sort(function (a, b) {
  146. return a - b;
  147. });
  148. const maxDistance = 100,
  149. minItems = 10,
  150. itemsPerBucket = 50;
  151. if (
  152. subitems.length > minItems &&
  153. codes[codes.length - 1] - codes[0] > maxDistance
  154. ) {
  155. const gapsCount = Math.max(2, (subitems.length / itemsPerBucket) | 0);
  156. const gaps = [];
  157. for (let q = 0; q < gapsCount; q++) {
  158. gaps.push({ length: 0 });
  159. }
  160. for (let j = 1; j < codes.length; j++) {
  161. const gapLength = codes[j] - codes[j - 1];
  162. let q = 0;
  163. while (q < gaps.length && gaps[q].length > gapLength) {
  164. q++;
  165. }
  166. if (q >= gaps.length) {
  167. continue;
  168. }
  169. let q0 = q;
  170. while (q < gaps.length) {
  171. if (gaps[q].length < gaps[q0].length) {
  172. q0 = q;
  173. }
  174. q++;
  175. }
  176. gaps[q0] = { length: gapLength, boundary: codes[j] };
  177. }
  178. const groups = gaps
  179. .filter(function (g) {
  180. return g.length >= maxDistance;
  181. })
  182. .map(function (g) {
  183. return g.boundary;
  184. });
  185. groups.sort(function (a, b) {
  186. return a - b;
  187. });
  188. if (groups.length > 1) {
  189. const buckets = [(item.items = [])];
  190. for (let j = 0; j < groups.length; j++) {
  191. const newItem = { type: item.type, items: [] };
  192. buckets.push(newItem.items);
  193. i++;
  194. data.body.splice(i, 0, newItem);
  195. }
  196. for (const subitem of subitems) {
  197. const { code } = subitem;
  198. let q = 0;
  199. while (q < groups.length && groups[q] <= code) {
  200. q++;
  201. }
  202. buckets[q].push(subitem);
  203. }
  204. }
  205. }
  206. }
  207. i++;
  208. }
  209. };
  210. function incHex(a) {
  211. let c = 1,
  212. s = "";
  213. for (let i = a.length - 1; i >= 0; i--) {
  214. c += parseInt(a[i], 16);
  215. if (c >= 16) {
  216. s = "0" + s;
  217. c = 1;
  218. } else {
  219. s = c.toString(16) + s;
  220. c = 0;
  221. }
  222. }
  223. return s;
  224. }