Skip to content

Commit 2ef8f54

Browse files
committed
feat: utf8fromString never returns pooled Uint8Arrays
1 parent fa1a81f commit 2ef8f54

File tree

3 files changed

+32
-15
lines changed

3 files changed

+32
-15
lines changed

fallback/_utils.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export const toBuf = (x) =>
1919
export const E_STRING = 'Input is not a string'
2020
export const E_STRICT_UNICODE = 'Input is not well-formed Unicode'
2121

22+
// Input is never pooled
2223
export function fromUint8(arr, format) {
2324
switch (format) {
2425
case 'uint8':
@@ -31,3 +32,21 @@ export function fromUint8(arr, format) {
3132

3233
throw new TypeError('Unexpected format')
3334
}
35+
36+
// Input can be pooled
37+
export function fromBuffer(arr, format) {
38+
switch (format) {
39+
case 'uint8':
40+
// byteOffset check is slightly faster and covers most pooling, so it comes first
41+
if (arr.length <= 64 || arr.byteOffset !== 0 || arr.byteLength !== arr.buffer.byteLength) {
42+
return new Uint8Array(arr)
43+
}
44+
45+
return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength)
46+
case 'buffer':
47+
if (arr.constructor !== Buffer) throw new Error('Unexpected')
48+
return arr
49+
}
50+
51+
throw new TypeError('Unexpected format')
52+
}

fallback/encoding.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -214,9 +214,7 @@ export class TextEncoder {
214214

215215
encode(str = '') {
216216
if (typeof str !== 'string') str = `${str}`
217-
const res = utf8fromStringLoose(str)
218-
// match new Uint8Array (per spec), which is non-pooled
219-
return res.byteOffset === 0 && res.length === res.buffer.byteLength ? res : res.slice(0)
217+
return utf8fromStringLoose(str) // non-pooled
220218
}
221219

222220
encodeInto(str, target) {

utf8.node.js

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { typedView } from './array.js'
2-
import { assertU8, E_STRING, E_STRICT_UNICODE } from './fallback/_utils.js'
1+
import { assertU8, fromBuffer, E_STRING, E_STRICT_UNICODE } from './fallback/_utils.js'
32
import { E_STRICT } from './fallback/utf8.js'
43
import { isAscii } from 'node:buffer'
54

@@ -16,26 +15,27 @@ try {
1615
// Without ICU, Node.js doesn't support fatal option for utf-8
1716
}
1817

19-
function encode(str, loose = false) {
18+
function encode(str, loose, format) {
2019
if (typeof str !== 'string') throw new TypeError(E_STRING)
2120
const strLength = str.length
2221
if (strLength === 0) return new Uint8Array() // faster than Uint8Array.of
2322
let res
2423
if (strLength > 0x4_00 && !isDeno) {
2524
// Faster for large strings
2625
const byteLength = Buffer.byteLength(str)
27-
res = Buffer.allocUnsafe(byteLength)
28-
const ascii = byteLength === strLength
29-
const written = ascii ? res.latin1Write(str) : res.utf8Write(str)
26+
res = format === 'buffer' ? Buffer.allocUnsafe(byteLength) : Buffer.allocUnsafeSlow(byteLength)
27+
const written = byteLength === strLength ? res.latin1Write(str) : res.utf8Write(str)
3028
if (written !== byteLength) throw new Error('Failed to write all bytes') // safeguard just in case
31-
if (ascii || loose) return res // no further checks needed
3229
} else {
3330
res = Buffer.from(str)
34-
if (res.length === strLength || loose) return res
3531
}
3632

37-
if (!isWellFormed.call(str)) throw new TypeError(E_STRICT_UNICODE)
38-
return res
33+
// Loose and ascii do not need the check
34+
if (!loose && res.length !== strLength && !isWellFormed.call(str)) {
35+
throw new TypeError(E_STRICT_UNICODE)
36+
}
37+
38+
return fromBuffer(res, format)
3939
}
4040

4141
function decode(arr, loose = false) {
@@ -61,7 +61,7 @@ function decode(arr, loose = false) {
6161
return str
6262
}
6363

64-
export const utf8fromString = (str, format = 'uint8') => typedView(encode(str, false), format)
65-
export const utf8fromStringLoose = (str, format = 'uint8') => typedView(encode(str, true), format)
64+
export const utf8fromString = (str, format = 'uint8') => encode(str, false, format)
65+
export const utf8fromStringLoose = (str, format = 'uint8') => encode(str, true, format)
6666
export const utf8toString = (arr) => decode(arr, false)
6767
export const utf8toStringLoose = (arr) => decode(arr, true)

0 commit comments

Comments
 (0)