hypercore 10.38.1 → 11.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/oplog.js DELETED
@@ -1,228 +0,0 @@
1
- const cenc = require('compact-encoding')
2
- const b4a = require('b4a')
3
- const { crc32 } = require('crc-universal')
4
- const { OPLOG_CORRUPT, OPLOG_HEADER_OVERFLOW, WRITE_FAILED } = require('hypercore-errors')
5
-
6
- module.exports = class Oplog {
7
- constructor (storage, { pageSize = 4096, headerEncoding = cenc.raw, entryEncoding = cenc.raw, readonly = false } = {}) {
8
- this.storage = storage
9
- this.headerEncoding = headerEncoding
10
- this.entryEncoding = entryEncoding
11
- this.readonly = readonly
12
- this.flushed = false
13
- this.byteLength = 0
14
- this.length = 0
15
-
16
- this._headers = [1, 0]
17
- this._pageSize = pageSize
18
- this._entryOffset = pageSize * 2
19
- }
20
-
21
- _addHeader (state, len, headerBit, partialBit) {
22
- // add the uint header (frame length and flush info)
23
- state.start = state.start - len - 4
24
- cenc.uint32.encode(state, (len << 2) | headerBit | partialBit)
25
-
26
- // crc32 the length + header-bit + content and prefix it
27
- state.start -= 8
28
- cenc.uint32.encode(state, crc32(state.buffer.subarray(state.start + 4, state.start + 8 + len)))
29
- state.start += len + 4
30
- }
31
-
32
- _decodeEntry (state, enc) {
33
- if (state.end - state.start < 8) return null
34
- const cksum = cenc.uint32.decode(state)
35
- const l = cenc.uint32.decode(state)
36
- const length = l >>> 2
37
- const headerBit = l & 1
38
- const partialBit = l & 2
39
-
40
- if (state.end - state.start < length) return null
41
-
42
- const end = state.start + length
43
-
44
- if (crc32(state.buffer.subarray(state.start - 4, end)) !== cksum) {
45
- return null
46
- }
47
-
48
- const result = { header: headerBit, partial: partialBit !== 0, byteLength: length + 8, message: null }
49
-
50
- try {
51
- result.message = enc.decode({ start: state.start, end, buffer: state.buffer })
52
- } catch {
53
- return null
54
- }
55
-
56
- state.start = end
57
-
58
- return result
59
- }
60
-
61
- async open () {
62
- const buffer = await this._readAll() // TODO: stream the oplog in on load maybe?
63
- const state = { start: 0, end: buffer.byteLength, buffer }
64
- const result = { header: null, entries: [] }
65
-
66
- this.byteLength = 0
67
- this.length = 0
68
-
69
- const h1 = this._decodeEntry(state, this.headerEncoding)
70
- state.start = this._pageSize
71
-
72
- const h2 = this._decodeEntry(state, this.headerEncoding)
73
- state.start = this._entryOffset
74
-
75
- if (!h1 && !h2) {
76
- // reset state...
77
- this.flushed = false
78
- this._headers[0] = 1
79
- this._headers[1] = 0
80
-
81
- if (buffer.byteLength >= this._entryOffset) {
82
- throw OPLOG_CORRUPT()
83
- }
84
- return result
85
- }
86
-
87
- this.flushed = true
88
-
89
- if (h1 && !h2) {
90
- this._headers[0] = h1.header
91
- this._headers[1] = h1.header
92
- } else if (!h1 && h2) {
93
- this._headers[0] = (h2.header + 1) & 1
94
- this._headers[1] = h2.header
95
- } else {
96
- this._headers[0] = h1.header
97
- this._headers[1] = h2.header
98
- }
99
-
100
- const header = (this._headers[0] + this._headers[1]) & 1
101
- const decoded = []
102
-
103
- result.header = header ? h2.message : h1.message
104
-
105
- while (true) {
106
- const entry = this._decodeEntry(state, this.entryEncoding)
107
- if (!entry) break
108
- if (entry.header !== header) break
109
-
110
- decoded.push(entry)
111
- }
112
-
113
- while (decoded.length > 0 && decoded[decoded.length - 1].partial) decoded.pop()
114
-
115
- for (const e of decoded) {
116
- result.entries.push(e.message)
117
- this.byteLength += e.byteLength
118
- this.length++
119
- }
120
-
121
- const size = this.byteLength + this._entryOffset
122
-
123
- if (size === buffer.byteLength) return result
124
-
125
- await new Promise((resolve, reject) => {
126
- if (this.readonly) return resolve()
127
- this.storage.truncate(size, err => {
128
- if (err) return reject(err)
129
- resolve()
130
- })
131
- })
132
-
133
- return result
134
- }
135
-
136
- _readAll () {
137
- return new Promise((resolve, reject) => {
138
- this.storage.open(err => {
139
- if (err && err.code !== 'ENOENT') return reject(err)
140
- if (err) return resolve(b4a.alloc(0))
141
- this.storage.stat((err, stat) => {
142
- if (err && err.code !== 'ENOENT') return reject(err)
143
- this.storage.read(0, stat.size, (err, buf) => {
144
- if (err) return reject(err)
145
- resolve(buf)
146
- })
147
- })
148
- })
149
- })
150
- }
151
-
152
- flush (header) {
153
- const state = { start: 8, end: 8, buffer: null }
154
- const i = this._headers[0] === this._headers[1] ? 1 : 0
155
- const bit = (this._headers[i] + 1) & 1
156
-
157
- this.headerEncoding.preencode(state, header)
158
- if (state.end > this._pageSize) throw OPLOG_HEADER_OVERFLOW()
159
- state.buffer = b4a.allocUnsafe(state.end)
160
- this.headerEncoding.encode(state, header)
161
- this._addHeader(state, state.end - 8, bit, 0)
162
-
163
- return this._writeHeaderAndTruncate(i, bit, state.buffer)
164
- }
165
-
166
- _writeHeaderAndTruncate (i, bit, buf) {
167
- return new Promise((resolve, reject) => {
168
- this.storage.write(i === 0 ? 0 : this._pageSize, buf, err => {
169
- if (err) return reject(err)
170
-
171
- this.storage.truncate(this._entryOffset, err => {
172
- if (err) return reject(err)
173
-
174
- this._headers[i] = bit
175
- this.byteLength = 0
176
- this.length = 0
177
- this.flushed = true
178
-
179
- resolve()
180
- })
181
- })
182
- })
183
- }
184
-
185
- append (batch, atomic = true) {
186
- if (!Array.isArray(batch)) batch = [batch]
187
-
188
- const state = { start: 0, end: batch.length * 8, buffer: null }
189
- const bit = (this._headers[0] + this._headers[1]) & 1
190
-
191
- for (let i = 0; i < batch.length; i++) {
192
- this.entryEncoding.preencode(state, batch[i])
193
- }
194
-
195
- state.buffer = b4a.allocUnsafe(state.end)
196
-
197
- for (let i = 0; i < batch.length; i++) {
198
- const start = state.start += 8 // space for header
199
- const partial = (atomic && i < batch.length - 1) ? 2 : 0
200
- this.entryEncoding.encode(state, batch[i])
201
- this._addHeader(state, state.start - start, bit, partial)
202
- }
203
-
204
- return this._append(state.buffer, batch.length)
205
- }
206
-
207
- close () {
208
- return new Promise((resolve, reject) => {
209
- this.storage.close(err => {
210
- if (err) return reject(err)
211
- resolve()
212
- })
213
- })
214
- }
215
-
216
- _append (buf, count) {
217
- return new Promise((resolve, reject) => {
218
- this.storage.write(this._entryOffset + this.byteLength, buf, err => {
219
- if (err) return reject(WRITE_FAILED(err.message))
220
-
221
- this.byteLength += buf.byteLength
222
- this.length += count
223
-
224
- resolve()
225
- })
226
- })
227
- }
228
- }