Implement LazyArray.splice(), implement unshift/shift/push/pop in terms of splice...
[logjson.git] / CopyCollector.mjs
1 import assert from 'assert'
2 import fsPromises from 'fs/promises'
3
4 class CopyCollector {
5   constructor(block_size) {
6     this.block_size = block_size || 0x1000
7     this.log = null
8     this.eof = 0
9     this.translate = {}
10     this.write_list = []
11     this.write_list_len = 0
12   }
13
14   async create(path) {
15     assert(this.log === null)
16
17     this.log = await fsPromises.open(path, 'w+')
18     this.eof = 0
19     this.translate = {}
20     this.write_list = []
21     this.write_list_len = 0
22   }
23
24   async close() {
25     assert(this.log !== null)
26
27     //await this.flush()
28     await this.log.close()
29     this.log = null
30   }
31
32   async write(value) {
33     let ptr = this.eof + this.write_list_len
34
35     let buffer = Buffer.from(
36       JSON.stringify(value, null, 2) + '\n',
37       'utf-8'
38     )
39     this.write_list.push(buffer)
40     this.write_list_len += buffer.length
41
42     if (this.write_list_len >= this.block_size)
43       await this.flush()
44
45     return [ptr, buffer.length - 1]
46   }
47
48   async write_root(value) {
49     let buffer = Buffer.from(
50       `<${JSON.stringify(value, null, 2)}>\n`,
51       'utf-8'
52     )
53     this.write_list.push(buffer)
54     this.write_list_len += buffer.length
55
56     if (this.write_list_len >= this.block_size)
57       await this.flush()
58   }
59
60   async flush() {
61     if (this.write_list_len === 0)
62       return
63
64     let buffer = Buffer.concat(this.write_list)
65     let len = buffer.length
66     assert(len === this.write_list_len)
67     assert(
68       (await this.log.write(buffer, 0, len, this.eof)).bytesWritten === len
69     )
70     this.eof += len
71
72     this.write_list = []
73     this.write_list_len = 0
74   }
75
76   async copy_pass(log, value) {
77     let copy = async ptr_len => {
78       let [ptr, len] = ptr_len
79
80       if (Object.prototype.hasOwnProperty.call(this.translate, ptr))
81         return this.translate[ptr]
82
83       let buffer = Buffer.alloc(len)
84       assert(
85         (await log.read(buffer, 0, len, ptr)).bytesRead === len
86       )
87       let value = JSON.parse(buffer.toString('utf-8'))
88
89       assert(typeof value === 'object' && value !== null)
90       if (value instanceof Array)
91         for (let i = 0; i < value.length; ++i) {
92           let child_value = value[i]
93           if (child_value instanceof Array)
94             value[i] = await copy(child_value)
95         }
96       else
97         for (let i in value) {
98           let child_value = value[i]
99           if (child_value instanceof Array)
100             value[i] = await copy(child_value)
101         }
102
103       let result = await this.write(value)
104       this.translate[ptr] = result
105       return result
106     }
107
108     if (value instanceof Array)
109       value = await copy(value)
110     await this.write_root(value)
111     await this.flush()
112     return value
113   }
114 }
115
116 export default CopyCollector