forked from ExodusOSS/bytes
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathutf16.node.js
More file actions
79 lines (64 loc) · 3.29 KB
/
utf16.node.js
File metadata and controls
79 lines (64 loc) · 3.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import { nativeDecoder, isDeno, isLE } from './fallback/_utils.js'
import { E_STRICT, E_STRICT_UNICODE } from './fallback/utf16.js'
if (Buffer.TYPED_ARRAY_SUPPORT) throw new Error('Unexpected Buffer polyfill')
const { isWellFormed } = String.prototype
const to8 = (a) => new Uint8Array(a.buffer, a.byteOffset, a.byteLength)
// Unlike utf8, operates on Uint16Arrays by default
function encode(str, loose = false, format = 'uint16') {
if (typeof str !== 'string') throw new TypeError('Input is not a string')
if (format !== 'uint16' && format !== 'uint8-le' && format !== 'uint8-be') {
throw new TypeError('Unknown format')
}
if (!isWellFormed.call(str)) {
if (!loose) throw new TypeError(E_STRICT_UNICODE)
str = nativeDecoder.decode(Buffer.from(str)) // well, let's fix up (Buffer doesn't do this with utf16 encoding)
}
const ble = Buffer.from(str, 'utf-16le')
if (format === 'uint8-le') return to8(ble)
if (format === 'uint8-be') return to8(ble.swap16())
if (format === 'uint16') {
const b = ble.byteOffset % 2 === 0 ? ble : Buffer.from(ble) // it should be already aligned, but just in case
if (!isLE) b.swap16()
return new Uint16Array(b.buffer, b.byteOffset, b.byteLength / 2)
}
throw new Error('Unreachable')
}
const swapped = (x, swap) =>
swap ? Buffer.from(x).swap16() : Buffer.from(x.buffer, x.byteOffset, x.byteLength)
// We skip TextDecoder on Node.js, as it's is somewhy significantly slower than Buffer for utf16
function decodeNode(input, loose = false, format = 'uint16') {
let ble
if (format === 'uint16') {
if (!(input instanceof Uint16Array)) throw new TypeError('Expected an Uint16Array')
ble = swapped(input, !isLE)
} else if (format === 'uint8-le' || format === 'uint8-be') {
if (!(input instanceof Uint8Array)) throw new TypeError('Expected an Uint8Array')
if (input.byteLength % 2 !== 0) throw new TypeError('Expected even number of bytes')
ble = swapped(input, format === 'uint8-be')
} else {
throw new TypeError('Unknown format')
}
const str = ble.ucs2Slice(0, ble.byteLength)
if (isWellFormed.call(str)) return str
if (!loose) throw new TypeError(E_STRICT)
return nativeDecoder.decode(Buffer.from(str)) // fixup (see above)
}
function decodeDecoder(input, loose = false, format = 'uint16') {
let encoding
if (format === 'uint16') {
if (!(input instanceof Uint16Array)) throw new TypeError('Expected an Uint16Array')
encoding = isLE ? 'utf-16le' : 'utf-16be'
} else if (format === 'uint8-le' || format === 'uint8-be') {
if (!(input instanceof Uint8Array)) throw new TypeError('Expected an Uint8Array')
if (input.byteLength % 2 !== 0) throw new TypeError('Expected even number of bytes')
encoding = format === 'uint8-le' ? 'utf-16le' : 'utf-16be'
} else {
throw new TypeError('Unknown format')
}
return new TextDecoder(encoding, { ignoreBOM: true, fatal: !loose }).decode(input) // TODO: cache decoder?
}
const decode = isDeno ? decodeDecoder : decodeNode
export const utf16fromString = (str, format = 'uint16') => encode(str, false, format)
export const utf16fromStringLoose = (str, format = 'uint16') => encode(str, true, format)
export const utf16toString = (arr, format = 'uint16') => decode(arr, false, format)
export const utf16toStringLoose = (arr, format = 'uint16') => decode(arr, true, format)