decode.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. 'use strict';
  2. // Adapted from:
  3. // Copyright (c) 2017-2019 Justin Ridgewell, MIT Licensed, https://github.com/jridgewell/safe-decode-string-component
  4. // Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>, MIT Licensed, http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
  5. const internals = {};
  6. exports.decode = function (string) {
  7. let percentPos = string.indexOf('%');
  8. if (percentPos === -1) {
  9. return string;
  10. }
  11. let decoded = '';
  12. let last = 0;
  13. let codepoint = 0;
  14. let startOfOctets = percentPos;
  15. let state = internals.utf8.accept;
  16. while (percentPos > -1 &&
  17. percentPos < string.length) {
  18. const high = internals.resolveHex(string[percentPos + 1], 4);
  19. const low = internals.resolveHex(string[percentPos + 2], 0);
  20. const byte = high | low;
  21. const type = internals.utf8.data[byte];
  22. state = internals.utf8.data[256 + state + type];
  23. codepoint = (codepoint << 6) | (byte & internals.utf8.data[364 + type]);
  24. if (state === internals.utf8.accept) {
  25. decoded += string.slice(last, startOfOctets);
  26. decoded += codepoint <= 0xFFFF
  27. ? String.fromCharCode(codepoint)
  28. : String.fromCharCode(0xD7C0 + (codepoint >> 10), 0xDC00 + (codepoint & 0x3FF));
  29. codepoint = 0;
  30. last = percentPos + 3;
  31. percentPos = string.indexOf('%', last);
  32. startOfOctets = percentPos;
  33. continue;
  34. }
  35. if (state === internals.utf8.reject) {
  36. return null;
  37. }
  38. percentPos += 3;
  39. if (percentPos >= string.length ||
  40. string[percentPos] !== '%') {
  41. return null;
  42. }
  43. }
  44. return decoded + string.slice(last);
  45. };
  46. internals.resolveHex = function (char, shift) {
  47. const i = internals.hex[char];
  48. return i === undefined ? 255 : i << shift;
  49. };
  50. internals.hex = {
  51. '0': 0, '1': 1, '2': 2, '3': 3, '4': 4,
  52. '5': 5, '6': 6, '7': 7, '8': 8, '9': 9,
  53. 'a': 10, 'A': 10, 'b': 11, 'B': 11, 'c': 12,
  54. 'C': 12, 'd': 13, 'D': 13, 'e': 14, 'E': 14,
  55. 'f': 15, 'F': 15
  56. };
  57. internals.utf8 = {
  58. accept: 12,
  59. reject: 0,
  60. data: [
  61. // Maps bytes to character to a transition
  62. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  63. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  64. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  65. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  66. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  67. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  68. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  69. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  70. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  71. 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
  72. 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
  73. 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
  74. 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
  75. 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
  76. 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 7, 7,
  77. 10, 9, 9, 9, 11, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
  78. // Maps a state to a new state when adding a transition
  79. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  80. 12, 0, 0, 0, 0, 24, 36, 48, 60, 72, 84, 96,
  81. 0, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0,
  82. 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0,
  83. 0, 24, 24, 24, 0, 0, 0, 0, 0, 0, 0, 0,
  84. 0, 24, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  85. 0, 48, 48, 48, 0, 0, 0, 0, 0, 0, 0, 0,
  86. 0, 0, 48, 48, 0, 0, 0, 0, 0, 0, 0, 0,
  87. 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  88. // Maps the current transition to a mask that needs to apply to the byte
  89. 0x7F, 0x3F, 0x3F, 0x3F, 0x00, 0x1F, 0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x07
  90. ]
  91. };