• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2022-2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import { int32, uint8 } from "./types"
17import { Array_from_int32 } from "./array"
18
19
20interface SystemTextEncoder {
21    encode(input?: string): Uint8Array;
22    encodeInto(src: string, dest: Uint8Array): void;
23}
24
25interface WithStreamOption {
26    stream: Boolean | undefined;
27}
28
29interface SystemTextDecoder {
30    decode(
31        input: ArrayBuffer | null | undefined | Uint8Array,
32        options: WithStreamOption | undefined
33    ): string;
34}
35
36export class CustomTextEncoder {
37    static readonly HeaderLen: int32 = Int32Array.BYTES_PER_ELEMENT
38
39    constructor(encoder:SystemTextEncoder|undefined = undefined) {
40        this.encoder = encoder
41    }
42
43    private readonly encoder: SystemTextEncoder|undefined
44
45    public static stringLength(input: string): int32 {
46        let length = 0
47        for (let i = 0; i < input.length; i++) {
48            length++
49            let cp = input.codePointAt(i)!
50            if (cp >= 0x10000) {
51                i++
52            }
53        }
54        return length
55    }
56
57    encodedLength(input: string): int32 {
58        let length = 0
59        for (let i = 0; i < input.length; i++) {
60            let cp = input.codePointAt(i)!
61            if (cp < 0x80) {
62                length += 1
63            } else if (cp < 0x800) {
64                length += 2
65            } else if (cp < 0x10000) {
66                length += 3
67            } else {
68                length += 4
69                i++
70            }
71        }
72        return length
73    }
74
75    private addLength(array: Uint8Array, offset: int32, length: int32 | number): void {
76        const len = length as int32
77        array.set(offset, len & 0xff)
78        array.set(offset + 1, (len >> 8) & 0xff)
79        array.set(offset + 2, (len >> 16) & 0xff)
80        array.set(offset + 3, (len >> 24) & 0xff)
81    }
82
83    static getHeaderLength(array: Uint8Array, offset: int32 = 0): int32 {
84        return (
85            (array.at(offset) as int32) |
86            (array.at(((offset + 1) << 8)) as int32) |
87            (array.at((offset + 2) << 16) as int32) |
88            (array.at((offset + 3) << 24)) as int32)
89    }
90
91    // Produces array of bytes with encoded string headed by 4 bytes (little endian) size information:
92    // [s0][s1][s2][s3] [c_0] ... [c_size-1]
93    encode(input: string | undefined, addLength: boolean = true): Uint8Array {
94        let headerLen = addLength ? CustomTextEncoder.HeaderLen : 0
95        let result: Uint8Array
96        if (!input) {
97            result = new Uint8Array(headerLen)
98        } else if (this.encoder !== undefined) {
99            result = this.encoder!.encode('s'.repeat(headerLen) + input)
100        } else {
101            let length = this.encodedLength(input)
102            result = new Uint8Array(length + headerLen)
103            this.encodeInto(input, result, headerLen)
104        }
105        if (addLength) {
106            this.addLength(result, 0, (result.length - headerLen) as int32)
107        }
108        return result
109    }
110
111    // Produces encoded array of strings with size information.
112    encodeArray(strings: Array<string>): Uint8Array {
113        let totalBytes = CustomTextEncoder.HeaderLen
114        let lengths = new Int32Array(strings.length)
115        for (let i = 0; i < lengths.length; i++) {
116            let len = this.encodedLength(strings[i])
117            lengths[i] = len
118            totalBytes += len + CustomTextEncoder.HeaderLen
119        }
120        let array = new Uint8Array(totalBytes)
121        let position = 0
122        this.addLength(array, position, lengths.length as int32)
123        position += CustomTextEncoder.HeaderLen
124        for (let i = 0; i < lengths.length; i++) {
125            this.addLength(array, position, lengths[i] as int32)
126            position += CustomTextEncoder.HeaderLen
127            this.encodeInto(strings[i], array, position)
128            position += lengths[i]
129        }
130        return array
131    }
132
133    encodeInto(input: string, result: Uint8Array, position: int32): Uint8Array {
134        if (this.encoder !== undefined) {
135            this.encoder!.encodeInto(input, result.subarray(position, result.length))
136            return result
137        }
138        let index = position
139        for (let stringPosition = 0; stringPosition < input.length; stringPosition++) {
140            let cp = input.codePointAt(stringPosition)!
141            if (cp < 0x80) {
142                result[index++] = (cp | 0)
143            } else if (cp < 0x800) {
144                result[index++] = ((cp >> 6) | 0xc0)
145                result[index++] = ((cp & 0x3f) | 0x80)
146            } else if (cp < 0x10000) {
147                result[index++] = ((cp >> 12) | 0xe0)
148                result[index++] = (((cp >> 6) & 0x3f) | 0x80)
149                result[index++] = ((cp & 0x3f) | 0x80)
150            } else {
151                result[index++] = ((cp >> 18) | 0xf0)
152                result[index++] = (((cp >> 12) & 0x3f) | 0x80)
153                result[index++] = (((cp >> 6) & 0x3f) | 0x80)
154                result[index++] = ((cp & 0x3f) | 0x80)
155                stringPosition++
156            }
157        }
158        result[index] = 0
159        return result
160    }
161}
162
163export class CustomTextDecoder {
164    static cpArrayMaxSize = 128
165    constructor(decoder: SystemTextDecoder|undefined = undefined) {
166        this.decoder = decoder
167    }
168
169    private readonly decoder: SystemTextDecoder|undefined
170
171    decode(input: Uint8Array): string {
172        if (this.decoder !== undefined) {
173            return this.decoder!.decode(input, undefined)
174        }
175
176        const cpSize = Math.min(CustomTextDecoder.cpArrayMaxSize, input.length)
177        let codePoints = new Int32Array(cpSize)
178        let cpIndex = 0;
179        let index = 0
180        let result = ""
181        while (index < input.length) {
182            let elem = input[index] as uint8
183            let lead = elem & 0xff
184            let count = 0
185            let value = 0
186            if (lead < 0x80) {
187                count = 1
188                value = elem
189            } else if ((lead >> 5) == 0x6) {
190                value = (((elem << 6) & 0x7ff) + (input[index + 1] & 0x3f)) as int32
191                count = 2
192            } else if ((lead >> 4) == 0xe) {
193                value = (((elem << 12) & 0xffff) + ((input[index + 1] << 6) & 0xfff) +
194                    (input[index + 2] & 0x3f)) as int32
195                count = 3
196            } else if ((lead >> 3) == 0x1e) {
197                value = (((elem << 18) & 0x1fffff) + ((input[index + 1] << 12) & 0x3ffff) +
198                    ((input[index + 2] << 6) & 0xfff) + (input[index + 3] & 0x3f)) as int32
199                count = 4
200            }
201            codePoints[cpIndex++] = value
202            if (cpIndex == cpSize) {
203                cpIndex = 0
204                //result += String.fromCodePoint(...codePoints)
205                result += String.fromCodePoint(...Array_from_int32(codePoints))
206            }
207            index += count
208        }
209        if (cpIndex > 0) {
210            result += String.fromCodePoint(...Array_from_int32(codePoints.slice(0, cpIndex)))
211        }
212        return result
213    }
214}
215