File size: 2,583 Bytes
11fcc5a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import { AbstractStorage } from "./storage";
import { uuid } from "$lib/util";

export class MemoryStorage extends AbstractStorage {
    #chunkSize: number;
    #actualSize: number = 0;
    #chunks: Uint8Array[] = [];

    constructor(chunkSize: number) {
        super();
        this.#chunkSize = chunkSize;
    }

    static async init(expectedSize: number) {
        const MB = 1024 * 1024;
        const chunkSize = Math.min(512 * MB, expectedSize);

        const storage = new this(chunkSize);

        // since we expect the output file to be roughly the same size
        // as inputs, preallocate its size for the output
        for (
            let toAllocate = expectedSize;
            toAllocate > 0;
            toAllocate -= chunkSize
        ) {
            storage.#chunks.push(new Uint8Array(chunkSize));
        }

        return storage;
    }

    async res() {
        // if we didn't need as much space as we allocated for some reason,
        // shrink the buffers so that we don't inflate the file with zeroes
        const outputView: Uint8Array[] = [];

        for (let i = 0; i < this.#chunks.length; ++i) {
            outputView.push(
                this.#chunks[i].subarray(
                    0,
                    Math.min(this.#chunkSize, this.#actualSize),
                ),
            );

            this.#actualSize -= this.#chunkSize;
            if (this.#actualSize <= 0) {
                break;
            }
        }

        return new File(outputView, uuid());
    }

    #expand(size: number) {
        while (size > this.#chunkSize * this.#chunks.length) {
            this.#chunks.push(new Uint8Array(this.#chunkSize));
        }
    }

    async write(data: Uint8Array | Int8Array, pos: number) {
        const writeEnd = pos + data.length;
        this.#expand(writeEnd);

        const chunkIndex = pos / this.#chunkSize | 0;
        const offset = pos - (this.#chunkSize * chunkIndex);

        if (offset + data.length > this.#chunkSize) {
            this.#chunks[chunkIndex].set(
                data.subarray(0, this.#chunkSize - offset),
                offset,
            );
            this.#chunks[chunkIndex + 1].set(
                data.subarray(this.#chunkSize - offset),
                0,
            );
        } else {
            this.#chunks[chunkIndex].set(data, offset);
        }

        this.#actualSize = Math.max(writeEnd, this.#actualSize);
        return data.length;
    }

    async destroy() {
        this.#chunks = [];
    }

    static async isAvailable() {
        return true;
    }
}