@protobufjs/utf8
Advanced tools
| # Changelog | ||
| ## [1.1.2](https://github.com/protobufjs/protobuf.js/compare/utf8-v1.1.1...utf8-v1.1.2) (2026-07-05) | ||
| ### Performance Improvements | ||
| * Batch String.fromCharCode in @protobufjs/utf8 read ([#2355](https://github.com/protobufjs/protobuf.js/issues/2355)) ([d1ae3ce](https://github.com/protobufjs/protobuf.js/commit/d1ae3ce46370254ada0f3db689506a9139c8d1ee)) |
+29
-18
@@ -9,3 +9,3 @@ "use strict"; | ||
| var utf8 = exports, | ||
| replacementChar = "\ufffd"; | ||
| replacementCharCode = 0xFFFD; // U+FFFD REPLACEMENT CHARACTER | ||
@@ -43,30 +43,41 @@ /** | ||
| utf8.read = function utf8_read(buffer, start, end) { | ||
| if (end - start < 1) { | ||
| if (end - start < 1) | ||
| return ""; | ||
| } | ||
| var str = ""; | ||
| for (var i = start; i < end;) { | ||
| var t = buffer[i++]; | ||
| // Batch code units and flush via String.fromCharCode.apply in 8192-unit | ||
| // chunks to avoid the per-character ConsString buildup of `str += ...`. | ||
| var parts = null, | ||
| chunk = [], | ||
| i = 0, // chunk write index | ||
| t, t2, c2, c3; | ||
| while (start < end) { | ||
| t = buffer[start++]; | ||
| if (t <= 0x7F) { | ||
| str += String.fromCharCode(t); | ||
| chunk[i++] = t; | ||
| } else if (t >= 0xC0 && t < 0xE0) { | ||
| var c2 = (t & 0x1F) << 6 | buffer[i++] & 0x3F; | ||
| str += c2 >= 0x80 ? String.fromCharCode(c2) : replacementChar; | ||
| c2 = (t & 0x1F) << 6 | buffer[start++] & 0x3F; | ||
| chunk[i++] = c2 >= 0x80 ? c2 : replacementCharCode; | ||
| } else if (t >= 0xE0 && t < 0xF0) { | ||
| var c3 = (t & 0xF) << 12 | (buffer[i++] & 0x3F) << 6 | buffer[i++] & 0x3F; | ||
| str += c3 >= 0x800 ? String.fromCharCode(c3) : replacementChar; | ||
| c3 = (t & 0xF) << 12 | (buffer[start++] & 0x3F) << 6 | buffer[start++] & 0x3F; | ||
| chunk[i++] = c3 >= 0x800 ? c3 : replacementCharCode; | ||
| } else if (t >= 0xF0) { | ||
| var t2 = (t & 7) << 18 | (buffer[i++] & 0x3F) << 12 | (buffer[i++] & 0x3F) << 6 | buffer[i++] & 0x3F; | ||
| t2 = (t & 7) << 18 | (buffer[start++] & 0x3F) << 12 | (buffer[start++] & 0x3F) << 6 | buffer[start++] & 0x3F; | ||
| if (t2 < 0x10000 || t2 > 0x10FFFF) | ||
| str += replacementChar; | ||
| chunk[i++] = replacementCharCode; | ||
| else { | ||
| t2 -= 0x10000; | ||
| str += String.fromCharCode(0xD800 + (t2 >> 10)); | ||
| str += String.fromCharCode(0xDC00 + (t2 & 0x3FF)); | ||
| chunk[i++] = 0xD800 + (t2 >> 10); | ||
| chunk[i++] = 0xDC00 + (t2 & 0x3FF); | ||
| } | ||
| } | ||
| if (i > 8191) { | ||
| (parts || (parts = [])).push(String.fromCharCode.apply(String, chunk.slice(0, i))); | ||
| i = 0; | ||
| } | ||
| } | ||
| return str; | ||
| if (parts) { | ||
| if (i) | ||
| parts.push(String.fromCharCode.apply(String, chunk.slice(0, i))); | ||
| return parts.join(""); | ||
| } | ||
| return String.fromCharCode.apply(String, chunk.slice(0, i)); | ||
| }; | ||
@@ -73,0 +84,0 @@ |
+2
-2
| { | ||
| "name": "@protobufjs/utf8", | ||
| "description": "A minimal UTF8 implementation for number arrays.", | ||
| "version": "1.1.1", | ||
| "version": "1.1.2", | ||
| "author": "Daniel Wirtz <dcode+protobufjs@dcode.io>", | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "https://github.com/dcodeIO/protobuf.js.git" | ||
| "url": "https://github.com/protobufjs/protobuf.js.git" | ||
| }, | ||
@@ -10,0 +10,0 @@ "license": "BSD-3-Clause", |
+5
-0
@@ -50,2 +50,7 @@ var tape = require("tape"); | ||
| var longMultibyteStr = "\u20ac\u00df\u7a7a\u03bb\ud835\udd4f".repeat(20000); // 3-byte, 2-byte and surrogate-pair code points | ||
| var longMultibyte = Buffer.from(longMultibyteStr, "utf8"); | ||
| comp = utf8.read(longMultibyte, 0, longMultibyte.length); | ||
| test.equal(comp, longMultibyteStr, "should decode a large multibyte string spanning many flush boundaries"); | ||
| test.end(); | ||
@@ -52,0 +57,0 @@ }); |
42558
3.23%9
12.5%191
9.77%