File size: 4,998 Bytes
78c921d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

import { FileHandle } from './interfaces.js';
import { ByteStream, AsyncByteStream } from './stream.js';
import { ArrayBufferViewInput, toUint8Array } from '../util/buffer.js';

/** @ignore */
export class RandomAccessFile extends ByteStream {
    public size: number;
    public position = 0;
    protected buffer: Uint8Array | null;
    constructor(buffer: ArrayBufferViewInput, byteLength?: number) {
        super();
        this.buffer = toUint8Array(buffer);
        this.size = typeof byteLength === 'undefined' ? this.buffer!.byteLength : byteLength;
    }
    public readInt32(position: number) {
        const { buffer, byteOffset } = this.readAt(position, 4);
        return new DataView(buffer, byteOffset).getInt32(0, true);
    }
    public seek(position: number) {
        this.position = Math.min(position, this.size);
        return position < this.size;
    }
    public read(nBytes?: number | null) {
        const { buffer, size, position } = this;
        if (buffer && position < size) {
            if (typeof nBytes !== 'number') { nBytes = Number.POSITIVE_INFINITY; }
            this.position = Math.min(size,
                position + Math.min(size - position, nBytes));
            return buffer.subarray(position, this.position);
        }
        return null;
    }
    public readAt(position: number, nBytes: number) {
        const buf = this.buffer;
        const end = Math.min(this.size, position + nBytes);
        return buf ? buf.subarray(position, end) : new Uint8Array(nBytes);
    }
    public close() { this.buffer && (this.buffer = null); }
    public throw(value?: any) { this.close(); return { done: true, value }; }
    public return(value?: any) { this.close(); return { done: true, value }; }
}

/** @ignore */
export class AsyncRandomAccessFile extends AsyncByteStream {
    declare public size: number;
    public position = 0;
    public _pending?: Promise<void>;
    protected _handle: FileHandle | null;
    constructor(file: FileHandle, byteLength?: number) {
        super();
        this._handle = file;
        if (typeof byteLength === 'number') {
            this.size = byteLength;
        } else {
            this._pending = (async () => {
                this.size = (await file.stat()).size;
                delete this._pending;
            })();
        }
    }
    public async readInt32(position: number) {
        const { buffer, byteOffset } = await this.readAt(position, 4);
        return new DataView(buffer, byteOffset).getInt32(0, true);
    }
    public async seek(position: number) {
        this._pending && await this._pending;
        this.position = Math.min(position, this.size);
        return position < this.size;
    }
    public async read(nBytes?: number | null) {
        this._pending && await this._pending;
        const { _handle: file, size, position } = this;
        if (file && position < size) {
            if (typeof nBytes !== 'number') { nBytes = Number.POSITIVE_INFINITY; }
            let pos = position, offset = 0, bytesRead = 0;
            const end = Math.min(size, pos + Math.min(size - pos, nBytes));
            const buffer = new Uint8Array(Math.max(0, (this.position = end) - pos));
            while ((pos += bytesRead) < end && (offset += bytesRead) < buffer.byteLength) {
                ({ bytesRead } = await file.read(buffer, offset, buffer.byteLength - offset, pos));
            }
            return buffer;
        }
        return null;
    }
    public async readAt(position: number, nBytes: number) {
        this._pending && await this._pending;
        const { _handle: file, size } = this;
        if (file && (position + nBytes) < size) {
            const end = Math.min(size, position + nBytes);
            const buffer = new Uint8Array(end - position);
            return (await file.read(buffer, 0, nBytes, position)).buffer;
        }
        return new Uint8Array(nBytes);
    }
    public async close() { const f = this._handle; this._handle = null; f && await f.close(); }
    public async throw(value?: any) { await this.close(); return { done: true, value }; }
    public async return(value?: any) { await this.close(); return { done: true, value }; }
}