• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2024-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
16declare function requireNapi(napiModuleName: string): any;
17const stream = requireNapi('util.stream');
18const fileIo = requireNapi('file.fs');
19
20interface ReadStreamOptions {
21    start?: number;
22    end?: number;
23}
24
25interface WriteStreamOptions {
26    start?: number;
27    mode?: number;
28}
29
30class ReadStream extends stream.Readable {
31    private pathInner: string;
32    private bytesReadInner: number;
33    private offset: number;
34    private start?: number;
35    private end?: number;
36    // @ts-ignore
37    private stream?: fileIo.Stream;
38
39    constructor(path: string, options?: ReadStreamOptions) {
40        super();
41        this.pathInner = path;
42        this.bytesReadInner = 0;
43        this.start = options?.start;
44        this.end = options?.end;
45        this.stream = fileIo.createStreamSync(this.pathInner, 'r');
46        this.offset = this.start ?? 0;
47    }
48
49    get path(): string {
50        return this.pathInner;
51    }
52
53    get bytesRead(): number {
54        return this.bytesReadInner;
55    }
56
57    // @ts-ignore
58    seek(offset: number, whence?: fileIo.WhenceType): number {
59        if (whence === undefined) {
60            this.offset = this.stream?.seek(offset);
61        } else {
62            this.offset = this.stream?.seek(offset, whence);
63        }
64        return this.offset;
65    }
66
67    close(): void {
68        this.stream?.close();
69    }
70
71    doInitialize(callback: Function): void {
72        callback();
73    }
74
75    doRead(size: number): void {
76        let readSize = size;
77        if (this.end !== undefined) {
78            if (this.offset > this.end) {
79                this.push(null);
80                return;
81            }
82            if (this.offset + readSize > this.end) {
83                readSize = this.end - this.offset;
84            }
85        }
86        let buffer = new ArrayBuffer(readSize);
87        const off = this.offset;
88        this.offset += readSize;
89        this.stream?.read(buffer, { offset: off, length: readSize })
90            .then((readOut: number) => {
91                if (readOut > 0) {
92                    this.bytesReadInner += readOut;
93                    this.push(new Uint8Array(buffer.slice(0, readOut)));
94                }
95                if (readOut !== readSize || readOut < size) {
96                    this.offset = this.offset - readSize + readOut;
97                    this.push(null);
98                }
99            });
100    }
101}
102
103class WriteStream extends stream.Writable {
104    private pathInner: string;
105    private bytesWrittenInner: number;
106    private offset: number;
107    private mode: string;
108    private start?: number;
109    // @ts-ignore
110    private stream?: fileIo.Stream;
111
112    constructor(path: string, options?: WriteStreamOptions) {
113        super();
114        this.pathInner = path;
115        this.bytesWrittenInner = 0;
116        this.start = options?.start;
117        this.mode = this.convertOpenMode(options?.mode);
118        this.stream = fileIo.createStreamSync(this.pathInner, this.mode);
119        this.offset = this.start ?? 0;
120    }
121
122    get path(): string {
123        return this.pathInner;
124    }
125
126    get bytesWritten(): number {
127        return this.bytesWrittenInner;
128    }
129
130    // @ts-ignore
131    seek(offset: number, whence?: fileIo.WhenceType): number {
132        if (whence === undefined) {
133            this.offset = this.stream?.seek(offset);
134        } else {
135            this.offset = this.stream?.seek(offset, whence);
136        }
137        return this.offset;
138    }
139
140    close(): void {
141        this.stream?.close();
142    }
143
144    closeSync(): void {
145        this.stream?.closeSync();
146    }
147
148    doInitialize(callback: Function): void {
149        callback();
150    }
151
152    doWrite(chunk: string | Uint8Array, encoding: string, callback: Function): void {
153        this.stream?.write(chunk, { offset: this.offset })
154            .then((writeIn: number) => {
155                this.offset += writeIn;
156                this.bytesWrittenInner += writeIn;
157                callback();
158            })
159            .finally(() => {
160                this.stream?.flush();
161            });
162    }
163
164    convertOpenMode(mode?: number): string {
165        let modeStr = 'w';
166        if (mode === undefined) {
167            return modeStr;
168        }
169        if (mode & fileIo.OpenMode.WRITE_ONLY) {
170            modeStr = 'w';
171        }
172        if (mode & fileIo.OpenMode.READ_WRITE) {
173            modeStr = 'w+';
174        }
175        if ((mode & fileIo.OpenMode.WRITE_ONLY) && (mode & fileIo.OpenMode.APPEND)) {
176            modeStr = 'a';
177        }
178        if ((mode & fileIo.OpenMode.READ_WRITE) && (mode & fileIo.OpenMode.APPEND)) {
179            modeStr = 'a+';
180        }
181        return modeStr;
182    }
183}
184
185export default {
186    ReadStream: ReadStream,
187    WriteStream: WriteStream,
188};
189