-
-
Notifications
You must be signed in to change notification settings - Fork 536
/
StringEncoding.ts
150 lines (115 loc) · 4.2 KB
/
StringEncoding.ts
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import UnsupportedOperationException from '../UnsupportedOperationException';
import CharacterSetECI from '../common/CharacterSetECI';
/**
* Responsible for en/decoding strings.
*/
export default class StringEncoding {
/**
* Allows the user to set a custom decoding function
* so more encoding formats the native ones can be supported.
*/
public static customDecoder: (bytes: Uint8Array, encodingName: string) => string;
/**
* Allows the user to set a custom encoding function
* so more encoding formats the native ones can be supported.
*/
public static customEncoder: (s: string, encodingName: string) => Uint8Array;
/**
* Decodes some Uint8Array to a string format.
*/
public static decode(bytes: Uint8Array, encoding: string | CharacterSetECI): string {
const encodingName = this.encodingName(encoding);
if (this.customDecoder) {
return this.customDecoder(bytes, encodingName);
}
// Increases browser support.
if (typeof TextDecoder === 'undefined' || this.shouldDecodeOnFallback(encodingName)) {
return this.decodeFallback(bytes, encodingName);
}
return new TextDecoder(encodingName).decode(bytes);
}
/**
* Checks if the decoding method should use the fallback for decoding
* once Node TextDecoder doesn't support all encoding formats.
*
* @param encodingName
*/
private static shouldDecodeOnFallback(encodingName: string): boolean {
return !StringEncoding.isBrowser() && encodingName === 'ISO-8859-1';
}
/**
* Encodes some string into a Uint8Array.
*/
public static encode(s: string, encoding: string | CharacterSetECI): Uint8Array {
const encodingName = this.encodingName(encoding);
if (this.customEncoder) {
return this.customEncoder(s, encodingName);
}
// Increases browser support.
if (typeof TextEncoder === 'undefined') {
return this.encodeFallback(s);
}
// TextEncoder only encodes to UTF8 by default as specified by encoding.spec.whatwg.org
return new TextEncoder().encode(s);
}
private static isBrowser(): boolean {
return (typeof window !== 'undefined' && {}.toString.call(window) === '[object Window]');
}
/**
* Returns the string value from some encoding character set.
*/
public static encodingName(encoding: string | CharacterSetECI): string {
return typeof encoding === 'string'
? encoding
: encoding.getName();
}
/**
* Returns character set from some encoding character set.
*/
public static encodingCharacterSet(encoding: string | CharacterSetECI): CharacterSetECI {
if (encoding instanceof CharacterSetECI) {
return encoding;
}
return CharacterSetECI.getCharacterSetECIByName(encoding);
}
/**
* Runs a fallback for the native decoding funcion.
*/
private static decodeFallback(bytes: Uint8Array, encoding: string | CharacterSetECI): string {
const characterSet = this.encodingCharacterSet(encoding);
if (StringEncoding.isDecodeFallbackSupported(characterSet)) {
let s = '';
for (let i = 0, length = bytes.length; i < length; i++) {
let h = bytes[i].toString(16);
if (h.length < 2) {
h = '0' + h;
}
s += '%' + h;
}
return decodeURIComponent(s);
}
if (characterSet.equals(CharacterSetECI.UnicodeBigUnmarked)) {
return String.fromCharCode.apply(null, new Uint16Array(bytes.buffer));
}
throw new UnsupportedOperationException(`Encoding ${this.encodingName(encoding)} not supported by fallback.`);
}
private static isDecodeFallbackSupported(characterSet: CharacterSetECI) {
return characterSet.equals(CharacterSetECI.UTF8) ||
characterSet.equals(CharacterSetECI.ISO8859_1) ||
characterSet.equals(CharacterSetECI.ASCII);
}
/**
* Runs a fallback for the native encoding funcion.
*
* @see https://stackoverflow.com/a/17192845/4367683
*/
private static encodeFallback(s: string): Uint8Array {
const encodedURIstring = btoa(unescape(encodeURIComponent(s)));
const charList = encodedURIstring.split('');
const uintArray = [];
for (let i = 0; i < charList.length; i++) {
uintArray.push(charList[i].charCodeAt(0));
}
return new Uint8Array(uintArray);
}
}