智慧水务管理系统 - 精河县供水工程综合管理平台

estimateDataURLDecodedBytes.js 3.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. /**
  2. * Estimate decoded byte length of a data:// URL *without* allocating large buffers.
  3. * - For base64: compute exact decoded size using length and padding;
  4. * handle %XX at the character-count level (no string allocation).
  5. * - For non-base64: compute the exact percent-decoded UTF-8 byte length.
  6. *
  7. * @param {string} url
  8. * @returns {number}
  9. */
  10. const isHexDigit = (charCode) =>
  11. (charCode >= 48 && charCode <= 57) ||
  12. (charCode >= 65 && charCode <= 70) ||
  13. (charCode >= 97 && charCode <= 102);
  14. const isPercentEncodedByte = (str, i, len) =>
  15. i + 2 < len && isHexDigit(str.charCodeAt(i + 1)) && isHexDigit(str.charCodeAt(i + 2));
  16. export default function estimateDataURLDecodedBytes(url) {
  17. if (!url || typeof url !== 'string') return 0;
  18. if (!url.startsWith('data:')) return 0;
  19. const comma = url.indexOf(',');
  20. if (comma < 0) return 0;
  21. const meta = url.slice(5, comma);
  22. const body = url.slice(comma + 1);
  23. const isBase64 = /;base64/i.test(meta);
  24. if (isBase64) {
  25. let effectiveLen = body.length;
  26. const len = body.length; // cache length
  27. for (let i = 0; i < len; i++) {
  28. if (body.charCodeAt(i) === 37 /* '%' */ && i + 2 < len) {
  29. const a = body.charCodeAt(i + 1);
  30. const b = body.charCodeAt(i + 2);
  31. const isHex = isHexDigit(a) && isHexDigit(b);
  32. if (isHex) {
  33. effectiveLen -= 2;
  34. i += 2;
  35. }
  36. }
  37. }
  38. let pad = 0;
  39. let idx = len - 1;
  40. const tailIsPct3D = (j) =>
  41. j >= 2 &&
  42. body.charCodeAt(j - 2) === 37 && // '%'
  43. body.charCodeAt(j - 1) === 51 && // '3'
  44. (body.charCodeAt(j) === 68 || body.charCodeAt(j) === 100); // 'D' or 'd'
  45. if (idx >= 0) {
  46. if (body.charCodeAt(idx) === 61 /* '=' */) {
  47. pad++;
  48. idx--;
  49. } else if (tailIsPct3D(idx)) {
  50. pad++;
  51. idx -= 3;
  52. }
  53. }
  54. if (pad === 1 && idx >= 0) {
  55. if (body.charCodeAt(idx) === 61 /* '=' */) {
  56. pad++;
  57. } else if (tailIsPct3D(idx)) {
  58. pad++;
  59. }
  60. }
  61. const groups = Math.floor(effectiveLen / 4);
  62. const bytes = groups * 3 - (pad || 0);
  63. return bytes > 0 ? bytes : 0;
  64. }
  65. // Compute UTF-8 byte length directly from UTF-16 code units without allocating
  66. // a byte buffer (TextEncoder.encode would defeat the DoS guard on large bodies).
  67. // Valid %XX triplets count as one decoded byte; this matches the bytes that
  68. // decodeURIComponent(body) would produce before Buffer re-encodes the string.
  69. let bytes = 0;
  70. for (let i = 0, len = body.length; i < len; i++) {
  71. const c = body.charCodeAt(i);
  72. if (c === 37 /* '%' */ && isPercentEncodedByte(body, i, len)) {
  73. bytes += 1;
  74. i += 2;
  75. } else if (c < 0x80) {
  76. bytes += 1;
  77. } else if (c < 0x800) {
  78. bytes += 2;
  79. } else if (c >= 0xd800 && c <= 0xdbff && i + 1 < len) {
  80. const next = body.charCodeAt(i + 1);
  81. if (next >= 0xdc00 && next <= 0xdfff) {
  82. bytes += 4;
  83. i++;
  84. } else {
  85. bytes += 3;
  86. }
  87. } else {
  88. bytes += 3;
  89. }
  90. }
  91. return bytes;
  92. }