1/* 2 * Copyright (c) 2024 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 doInitialize(callback: Function): void { 145 callback(); 146 } 147 148 doWrite(chunk: string | Uint8Array, encoding: string, callback: Function): void { 149 this.stream?.write(chunk, { offset: this.offset }) 150 .then((writeIn: number) => { 151 this.offset += writeIn; 152 this.bytesWrittenInner += writeIn; 153 callback(); 154 }) 155 .finally(() => { 156 this.stream?.flush(); 157 }); 158 } 159 160 convertOpenMode(mode?: number): string { 161 let modeStr = 'w'; 162 if (mode === undefined) { 163 return modeStr; 164 } 165 if (mode & fileIo.OpenMode.WRITE_ONLY) { 166 modeStr = 'w'; 167 } 168 if (mode & fileIo.OpenMode.READ_WRITE) { 169 modeStr = 'w+'; 170 } 171 if ((mode & fileIo.OpenMode.WRITE_ONLY) && (mode & fileIo.OpenMode.APPEND)) { 172 modeStr = 'a'; 173 } 174 if ((mode & fileIo.OpenMode.READ_WRITE) && (mode & fileIo.OpenMode.APPEND)) { 175 modeStr = 'a+'; 176 } 177 return modeStr; 178 } 179} 180 181export default { 182 ReadStream: ReadStream, 183 WriteStream: WriteStream, 184}; 185