core-3nweb-client-lib 0.21.0 → 0.22.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/build/api-defs/files.d.ts +8 -7
- package/build/core/asmail/inbox/attachments/fs.d.ts +2 -2
- package/build/core/asmail/inbox/attachments/fs.js +3 -2
- package/build/core/asmail/msg/common.d.ts +2 -2
- package/build/core/asmail/msg/common.js +3 -2
- package/build/core/asmail/msg/opener.d.ts +2 -2
- package/build/core/asmail/msg/opener.js +3 -2
- package/build/core/asmail/msg/packer.d.ts +2 -2
- package/build/core/asmail/msg/packer.js +7 -6
- package/build/core/storage/index.js +2 -2
- package/build/core/storage/local/obj-files.js +8 -5
- package/build/core/storage/local/obj-status.js +4 -3
- package/build/core/storage/synced/obj-status.js +4 -3
- package/build/ipc-via-protobuf/bytes.js +1 -1
- package/build/lib-client/3nstorage/xsp-fs/attrs.d.ts +28 -0
- package/build/lib-client/3nstorage/xsp-fs/attrs.js +337 -0
- package/build/lib-client/3nstorage/xsp-fs/common.d.ts +1 -1
- package/build/lib-client/3nstorage/xsp-fs/common.js +3 -2
- package/build/lib-client/3nstorage/xsp-fs/file-node.d.ts +31 -18
- package/build/lib-client/3nstorage/xsp-fs/file-node.js +130 -118
- package/build/lib-client/3nstorage/xsp-fs/file.d.ts +0 -1
- package/build/lib-client/3nstorage/xsp-fs/file.js +14 -49
- package/build/lib-client/3nstorage/xsp-fs/folder-node-serialization.d.ts +1 -1
- package/build/lib-client/3nstorage/xsp-fs/folder-node-serialization.js +95 -91
- package/build/lib-client/3nstorage/xsp-fs/folder-node.d.ts +19 -24
- package/build/lib-client/3nstorage/xsp-fs/folder-node.js +133 -190
- package/build/lib-client/3nstorage/xsp-fs/fs.d.ts +3 -4
- package/build/lib-client/3nstorage/xsp-fs/fs.js +18 -21
- package/build/lib-client/3nstorage/xsp-fs/link-node.d.ts +13 -8
- package/build/lib-client/3nstorage/xsp-fs/link-node.js +46 -38
- package/build/lib-client/3nstorage/xsp-fs/node-in-fs.d.ts +17 -35
- package/build/lib-client/3nstorage/xsp-fs/node-in-fs.js +34 -127
- package/build/lib-client/3nstorage/xsp-fs/node-persistence.d.ts +57 -0
- package/build/lib-client/3nstorage/xsp-fs/node-persistence.js +182 -0
- package/build/lib-client/3nstorage/xsp-fs/xsp-payload-v1.d.ts +3 -0
- package/build/lib-client/3nstorage/xsp-fs/xsp-payload-v1.js +87 -0
- package/build/lib-client/3nstorage/xsp-fs/xsp-payload-v2.d.ts +6 -0
- package/build/lib-client/3nstorage/xsp-fs/xsp-payload-v2.js +1022 -0
- package/build/lib-client/cryptor/cryptor-wasm.js +1 -1
- package/build/lib-client/cryptor/cryptor.wasm +0 -0
- package/build/lib-client/local-files/device-fs.js +7 -3
- package/build/lib-client/objs-on-disk/file-writing-proc.js +3 -2
- package/build/lib-client/objs-on-disk/obj-on-disk.js +8 -7
- package/build/lib-common/big-endian.d.ts +0 -24
- package/build/lib-common/big-endian.js +16 -78
- package/build/lib-common/exceptions/file.js +5 -1
- package/build/lib-common/obj-streaming/sink-utils.d.ts +0 -4
- package/build/lib-common/obj-streaming/sink-utils.js +4 -70
- package/build/lib-common/objs-on-disk/file-layout.js +2 -1
- package/build/lib-common/objs-on-disk/obj-file.js +2 -2
- package/build/lib-common/objs-on-disk/utils.js +2 -1
- package/build/lib-common/objs-on-disk/v1-obj-file-format.js +2 -1
- package/package.json +2 -2
- package/build/lib-client/files/file-attrs.d.ts +0 -76
- package/build/lib-client/files/file-attrs.js +0 -549
- package/build/lib-client/files/file-layout.d.ts +0 -56
- package/build/lib-client/files/file-layout.js +0 -456
- package/build/lib-client/files/file-sink.d.ts +0 -33
- package/build/lib-client/files/file-sink.js +0 -173
- package/build/lib-client/files/file-source.d.ts +0 -19
- package/build/lib-client/files/file-source.js +0 -115
|
@@ -0,0 +1,1022 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (C) 2020, 2022 3NSoft Inc.
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify it under
|
|
6
|
+
the terms of the GNU General Public License as published by the Free Software
|
|
7
|
+
Foundation, either version 3 of the License, or (at your option) any later
|
|
8
|
+
version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful, but
|
|
11
|
+
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
13
|
+
See the GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU General Public License along with
|
|
16
|
+
this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.makeWritablePayloadFromBase = exports.makeWritablePayload = exports.makeReadonlyPayload = void 0;
|
|
20
|
+
const assert_1 = require("../../../lib-common/assert");
|
|
21
|
+
const big_endian_1 = require("../../../lib-common/big-endian");
|
|
22
|
+
const attrs_1 = require("./attrs");
|
|
23
|
+
const processes_1 = require("../../../lib-common/processes");
|
|
24
|
+
const wrapping_1 = require("../../../lib-common/byte-streaming/wrapping");
|
|
25
|
+
const buffer_utils_1 = require("../../../lib-common/buffer-utils");
|
|
26
|
+
async function makeReadonlyPayload(src) {
|
|
27
|
+
const payload = await ReadonlyPayloadV2.makeFor(src);
|
|
28
|
+
return payload;
|
|
29
|
+
}
|
|
30
|
+
exports.makeReadonlyPayload = makeReadonlyPayload;
|
|
31
|
+
async function makeWritablePayload(sink, attrs) {
|
|
32
|
+
return WritablePayloadV2.makeInitiallyEmpty(sink, attrs);
|
|
33
|
+
}
|
|
34
|
+
exports.makeWritablePayload = makeWritablePayload;
|
|
35
|
+
async function makeWritablePayloadFromBase(sink, base, baseSrc) {
|
|
36
|
+
const payload = await WritablePayloadV2.makeOnBase(sink, base, baseSrc);
|
|
37
|
+
return payload;
|
|
38
|
+
}
|
|
39
|
+
exports.makeWritablePayloadFromBase = makeWritablePayloadFromBase;
|
|
40
|
+
const EMPTY_SECTION = 'empty';
|
|
41
|
+
const CONTENT_SECTION = 'content';
|
|
42
|
+
const PAD_SECTION = 'pad';
|
|
43
|
+
const XATTRS_SECTION = 'xattrs';
|
|
44
|
+
class ReadonlyPayloadV2 {
|
|
45
|
+
constructor(src, attrs, contentSections, xattrsSections) {
|
|
46
|
+
this.src = src;
|
|
47
|
+
this.attrs = attrs;
|
|
48
|
+
this.contentSections = contentSections;
|
|
49
|
+
this.xattrsSections = xattrsSections;
|
|
50
|
+
this.syncProc = new processes_1.SingleProc();
|
|
51
|
+
this.xattrs = undefined;
|
|
52
|
+
this.size = sizeOfContent(this.contentSections);
|
|
53
|
+
Object.seal(this);
|
|
54
|
+
}
|
|
55
|
+
static async makeFor(src) {
|
|
56
|
+
const { attrs, sectionsEnd, contentSections, xattrsSections } = await payloadV2.readFrom(src);
|
|
57
|
+
// sanity check during reconstruction of sections' layout in source
|
|
58
|
+
combineIntoSectionsInSrc(contentSections, xattrsSections, sectionsEnd);
|
|
59
|
+
return new ReadonlyPayloadV2(src, attrs, contentSections, xattrsSections);
|
|
60
|
+
}
|
|
61
|
+
getAttrs() {
|
|
62
|
+
const { ctime, mtime } = this.attrs;
|
|
63
|
+
return { ctime, mtime, size: this.size };
|
|
64
|
+
}
|
|
65
|
+
async getXAttrs() {
|
|
66
|
+
if (!this.xattrs) {
|
|
67
|
+
if (this.xattrsSections.length === 0) {
|
|
68
|
+
this.xattrs = attrs_1.XAttrs.makeEmpty();
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
const xattrsBytes = await this.syncProc.startOrChain(async () => {
|
|
72
|
+
const chunks = [];
|
|
73
|
+
for (const { ofsInSrc, len } of this.xattrsSections) {
|
|
74
|
+
const bytes = await sureReadOfBytesFrom(this.src, ofsInSrc, len);
|
|
75
|
+
chunks.push(bytes);
|
|
76
|
+
}
|
|
77
|
+
return chunks;
|
|
78
|
+
});
|
|
79
|
+
this.xattrs = attrs_1.XAttrs.parseFrom(xattrsBytes);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return this.xattrs;
|
|
83
|
+
}
|
|
84
|
+
readAllContent() {
|
|
85
|
+
return this.readSomeContentBytes(0, this.size);
|
|
86
|
+
}
|
|
87
|
+
async readSomeContentBytes(start, end) {
|
|
88
|
+
assert_1.assert(Number.isInteger(start) && (start >= 0));
|
|
89
|
+
if (start > this.size) {
|
|
90
|
+
start = this.size;
|
|
91
|
+
}
|
|
92
|
+
assert_1.assert(Number.isInteger(end) && (end >= start));
|
|
93
|
+
if (end > this.size) {
|
|
94
|
+
end = this.size;
|
|
95
|
+
}
|
|
96
|
+
if (start === end) {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
const startSecInd = this.contentSections
|
|
100
|
+
.findIndex(s => (s.ofs <= start));
|
|
101
|
+
const endSecInd = this.contentSections
|
|
102
|
+
.findIndex(s => ((s.ofs + s.len) >= end));
|
|
103
|
+
assert_1.assert((startSecInd >= 0) && (endSecInd >= 0) &&
|
|
104
|
+
(startSecInd <= endSecInd));
|
|
105
|
+
return await this.syncProc.startOrChain((startSecInd === endSecInd) ?
|
|
106
|
+
async () => {
|
|
107
|
+
const s = this.contentSections[startSecInd];
|
|
108
|
+
if (s.type === CONTENT_SECTION) {
|
|
109
|
+
const fstDelta = start - s.ofs;
|
|
110
|
+
return await sureReadOfBytesFrom(this.src, s.ofsInSrc + fstDelta, end - start);
|
|
111
|
+
}
|
|
112
|
+
else if (s.type === EMPTY_SECTION) {
|
|
113
|
+
return new Uint8Array(end - start);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
throw new Error(`This shouldn't reachable`);
|
|
117
|
+
}
|
|
118
|
+
} :
|
|
119
|
+
async () => {
|
|
120
|
+
const allBytes = Buffer.allocUnsafe(end - start);
|
|
121
|
+
let ofsInAllBytes = 0;
|
|
122
|
+
for (let i = startSecInd; i <= endSecInd; i += 1) {
|
|
123
|
+
const s = this.contentSections[i];
|
|
124
|
+
const fstDelta = ((i === startSecInd) ? start - s.ofs : 0);
|
|
125
|
+
const len = ((i === endSecInd) ? end - s.ofs : s.len);
|
|
126
|
+
if (s.type === CONTENT_SECTION) {
|
|
127
|
+
const bytes = await sureReadOfBytesFrom(this.src, s.ofsInSrc + fstDelta, len);
|
|
128
|
+
allBytes.set(bytes, ofsInAllBytes);
|
|
129
|
+
}
|
|
130
|
+
else if (s.type === EMPTY_SECTION) {
|
|
131
|
+
allBytes.fill(0, ofsInAllBytes, len);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
throw new Error(`This shouldn't reachable`);
|
|
135
|
+
}
|
|
136
|
+
ofsInAllBytes += len;
|
|
137
|
+
}
|
|
138
|
+
return allBytes;
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
makeFileByteSource() {
|
|
142
|
+
let pos = 0;
|
|
143
|
+
return wrapping_1.wrapAndSyncFileSource({
|
|
144
|
+
seek: async (ofs) => {
|
|
145
|
+
assert_1.assert(Number.isInteger(ofs) && (ofs >= 0) && (ofs <= this.size), `Offset must be an integer from 0 to size value, inclusive`);
|
|
146
|
+
pos = ofs;
|
|
147
|
+
},
|
|
148
|
+
getSize: async () => this.size,
|
|
149
|
+
getPosition: async () => pos,
|
|
150
|
+
read: async (len) => {
|
|
151
|
+
if (len === undefined) {
|
|
152
|
+
len = this.size - pos;
|
|
153
|
+
}
|
|
154
|
+
const bytes = await this.readSomeContentBytes(pos, pos + len);
|
|
155
|
+
if (bytes) {
|
|
156
|
+
pos += bytes.length;
|
|
157
|
+
}
|
|
158
|
+
return bytes;
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
Object.freeze(ReadonlyPayloadV2.prototype);
|
|
164
|
+
Object.freeze(ReadonlyPayloadV2);
|
|
165
|
+
function combineIntoSectionsInSrc(contentSections, xattrsSections, sectionsEnd) {
|
|
166
|
+
// combine and sort sections with bytes in payload
|
|
167
|
+
const withContent = contentSections
|
|
168
|
+
.filter(s => (s.type === CONTENT_SECTION));
|
|
169
|
+
const sectionsInSrc = xattrsSections.concat(withContent);
|
|
170
|
+
sectionsInSrc.sort(compareByOfsInSrc);
|
|
171
|
+
// add pad before sections, if boundaries check tells so
|
|
172
|
+
if (sectionsInSrc.length > 0) {
|
|
173
|
+
const fst = sectionsInSrc[0];
|
|
174
|
+
if (fst.ofsInSrc !== 0) {
|
|
175
|
+
sectionsInSrc.unshift({
|
|
176
|
+
type: PAD_SECTION,
|
|
177
|
+
ofsInSrc: 0,
|
|
178
|
+
len: fst.ofsInSrc
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
// add pads between sections, if boundaries check tells so
|
|
183
|
+
for (let i = 1; i < sectionsInSrc.length; i += 1) {
|
|
184
|
+
const prev = sectionsInSrc[i - 1];
|
|
185
|
+
const prevEnd = prev.ofsInSrc + prev.len;
|
|
186
|
+
const s = sectionsInSrc[i];
|
|
187
|
+
if (prevEnd !== s.ofsInSrc) {
|
|
188
|
+
if (prevEnd < s.ofsInSrc) {
|
|
189
|
+
sectionsInSrc.splice(i, 0, {
|
|
190
|
+
type: PAD_SECTION,
|
|
191
|
+
ofsInSrc: prevEnd,
|
|
192
|
+
len: s.ofsInSrc - prevEnd
|
|
193
|
+
});
|
|
194
|
+
i += 1;
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
throw payloadLayoutException(`One section overflows another`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// add pad after sections, if boundaries check tells so
|
|
202
|
+
const last = lastIn(sectionsInSrc);
|
|
203
|
+
const lastEnd = (last ? (last.ofsInSrc + last.len) : 0);
|
|
204
|
+
if (lastEnd !== sectionsEnd) {
|
|
205
|
+
if (lastEnd < sectionsEnd) {
|
|
206
|
+
sectionsInSrc.push({
|
|
207
|
+
type: PAD_SECTION,
|
|
208
|
+
ofsInSrc: lastEnd,
|
|
209
|
+
len: sectionsEnd - lastEnd
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
else if (lastEnd > sectionsEnd) {
|
|
213
|
+
throw payloadLayoutException(`Last section length overflows sections' end`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return sectionsInSrc;
|
|
217
|
+
}
|
|
218
|
+
function compareByOfsInSrc(a, b) {
|
|
219
|
+
if (a.ofsInSrc < b.ofsInSrc) {
|
|
220
|
+
return -1;
|
|
221
|
+
}
|
|
222
|
+
else if (a.ofsInSrc > b.ofsInSrc) {
|
|
223
|
+
return 1;
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
throw payloadLayoutException(`Have invalid pair in comparing ofsInSrc`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
function sizeOfContent(sections) {
|
|
230
|
+
const last = lastIn(sections);
|
|
231
|
+
return (last ? (last.ofs + last.len) : 0);
|
|
232
|
+
}
|
|
233
|
+
function lastIn(arr) {
|
|
234
|
+
return ((arr.length === 0) ? undefined : arr[arr.length - 1]);
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* This implementation has the following behaviour of packing payload bytes.
|
|
238
|
+
* Any bytes from base can only be cut. New content sections are appended.
|
|
239
|
+
* If content section is added and than deleted, section is marked as a pad.
|
|
240
|
+
* Extended attributes are written in one section, and any change in them
|
|
241
|
+
* removes this section competely, and writes a new one (if there are xattrs).
|
|
242
|
+
*/
|
|
243
|
+
class WritablePayloadV2 {
|
|
244
|
+
constructor(sink, base, attrs, contentSections, xattrsSections, sectionsEnd) {
|
|
245
|
+
this.sink = sink;
|
|
246
|
+
this.base = base;
|
|
247
|
+
this.attrs = attrs;
|
|
248
|
+
this.contentSections = contentSections;
|
|
249
|
+
this.xattrsSections = xattrsSections;
|
|
250
|
+
this.syncProc = new processes_1.SingleProc();
|
|
251
|
+
this.completionErr = undefined;
|
|
252
|
+
assert_1.assert(!!this.sink);
|
|
253
|
+
if ((this.contentSections.length + this.xattrsSections.length) === 0) {
|
|
254
|
+
this.sectionsInSink = [];
|
|
255
|
+
this.cutonlyAreaEnd = 0;
|
|
256
|
+
this.writePos = 0;
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
// sanity check during reconstruction of sections' layout in source
|
|
260
|
+
this.sectionsInSink = combineIntoSectionsInSrc(this.contentSections, this.xattrsSections, sectionsEnd);
|
|
261
|
+
this.cutonlyAreaEnd = sectionsEnd;
|
|
262
|
+
this.writePos = sectionsEnd;
|
|
263
|
+
}
|
|
264
|
+
Object.seal(this);
|
|
265
|
+
}
|
|
266
|
+
static makeInitiallyEmpty(sink, attrs) {
|
|
267
|
+
if (!attrs) {
|
|
268
|
+
attrs = attrs_1.CommonAttrs.makeForTimeNow();
|
|
269
|
+
}
|
|
270
|
+
return new WritablePayloadV2(sink, undefined, attrs, [], [], 0);
|
|
271
|
+
}
|
|
272
|
+
static async makeOnBase(sink, base, baseSrc) {
|
|
273
|
+
const { attrs, contentSections, xattrsSections, sectionsEnd } = await payloadV2.readFrom(baseSrc);
|
|
274
|
+
const payload = new WritablePayloadV2(sink, base, attrs, contentSections, xattrsSections, sectionsEnd);
|
|
275
|
+
await payload.removeLayoutBytes();
|
|
276
|
+
await payload.deletePadsInCutArea();
|
|
277
|
+
return payload;
|
|
278
|
+
}
|
|
279
|
+
async removeLayoutBytes() {
|
|
280
|
+
if (!this.sink) {
|
|
281
|
+
this.throwOnNoSink();
|
|
282
|
+
}
|
|
283
|
+
await this.sink.setSize(this.writePos);
|
|
284
|
+
}
|
|
285
|
+
async completeWriting(err) {
|
|
286
|
+
if (!this.sink) {
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
if (err !== undefined) {
|
|
290
|
+
this.completionErr = err;
|
|
291
|
+
await this.sink.done(err).catch(noop);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
if (!Object.isFrozen(this.attrs)) {
|
|
295
|
+
this.attrs.updateMTime();
|
|
296
|
+
}
|
|
297
|
+
try {
|
|
298
|
+
const { bytes, packLen } = payloadV2.pack(this.attrs, this.xattrsSections, this.contentSections);
|
|
299
|
+
await this.sink.spliceLayout(this.writePos, 0, packLen);
|
|
300
|
+
for (const buf of bytes) {
|
|
301
|
+
await this.sink.write(this.writePos, buf);
|
|
302
|
+
this.writePos += buf.length;
|
|
303
|
+
}
|
|
304
|
+
await this.sink.done();
|
|
305
|
+
}
|
|
306
|
+
catch (err) {
|
|
307
|
+
await this.sink.done(err);
|
|
308
|
+
throw err;
|
|
309
|
+
}
|
|
310
|
+
finally {
|
|
311
|
+
this.sink = undefined;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
get contentLen() {
|
|
315
|
+
return sizeOfContent(this.contentSections);
|
|
316
|
+
}
|
|
317
|
+
throwOnNoSink() {
|
|
318
|
+
throw (this.completionErr ?
|
|
319
|
+
this.completionErr : Error(`Payload has already completed`));
|
|
320
|
+
}
|
|
321
|
+
async spliceContent(contentOfs, del, ins) {
|
|
322
|
+
assert_1.assert(Number.isInteger(contentOfs) && (contentOfs >= 0), `Content offset should be a non-negative integer`);
|
|
323
|
+
assert_1.assert(Number.isInteger(del) && (del >= 0), `Number of bytes to delete should be a non-negative integer`);
|
|
324
|
+
if (!this.sink) {
|
|
325
|
+
this.throwOnNoSink();
|
|
326
|
+
}
|
|
327
|
+
try {
|
|
328
|
+
if (contentOfs >= this.contentLen) {
|
|
329
|
+
if (!ins) {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
// ignore del, as there is no content at this high offset
|
|
333
|
+
this.appendContentSectionInfo({
|
|
334
|
+
type: 'content',
|
|
335
|
+
len: ins.length,
|
|
336
|
+
ofs: contentOfs,
|
|
337
|
+
ofsInSrc: this.writePos,
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
if (del > 0) {
|
|
342
|
+
await this.deleteContentSection(contentOfs, del, !ins);
|
|
343
|
+
}
|
|
344
|
+
if (!ins) {
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
if (contentOfs === this.contentLen) {
|
|
348
|
+
this.appendContentSectionInfo({
|
|
349
|
+
type: 'content',
|
|
350
|
+
len: ins.length,
|
|
351
|
+
ofs: contentOfs,
|
|
352
|
+
ofsInSrc: this.writePos,
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
this.insertContentSection({
|
|
357
|
+
type: 'content',
|
|
358
|
+
len: ins.length,
|
|
359
|
+
ofs: contentOfs,
|
|
360
|
+
ofsInSrc: this.writePos,
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
await this.sink.spliceLayout(this.writePos, 0, ins.length);
|
|
365
|
+
await this.sink.write(this.writePos, ins);
|
|
366
|
+
this.writePos += ins.length;
|
|
367
|
+
}
|
|
368
|
+
catch (err) {
|
|
369
|
+
await this.completeWriting(err).catch(noop);
|
|
370
|
+
throw err;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* This appends given section to contentSections and sectionsInSink, ensuring
|
|
375
|
+
* there is no gaps. In case section can be merged with the last one, merge
|
|
376
|
+
* is performed instead of appending.
|
|
377
|
+
* @param s
|
|
378
|
+
*/
|
|
379
|
+
appendContentSectionInfo(s) {
|
|
380
|
+
const last = lastIn(this.contentSections);
|
|
381
|
+
if (last) {
|
|
382
|
+
const origContentEnd = last.ofs + last.len;
|
|
383
|
+
if (origContentEnd < s.ofs) {
|
|
384
|
+
if (last.type === 'empty') {
|
|
385
|
+
last.len += s.ofs - origContentEnd;
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
this.contentSections.push({
|
|
389
|
+
type: 'empty',
|
|
390
|
+
ofs: origContentEnd,
|
|
391
|
+
len: s.ofs - origContentEnd
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
else if ((last.type === 'content')
|
|
396
|
+
&& ((last.ofsInSrc + last.len) === s.ofsInSrc)) {
|
|
397
|
+
last.len += s.len;
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
else if (s.ofs > 0) {
|
|
402
|
+
this.contentSections.push({
|
|
403
|
+
type: 'empty',
|
|
404
|
+
ofs: 0,
|
|
405
|
+
len: s.ofs
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
this.contentSections.push(s);
|
|
409
|
+
const lastInSink = lastIn(this.sectionsInSink);
|
|
410
|
+
if (lastInSink) {
|
|
411
|
+
assert_1.assert((lastInSink.ofsInSrc + lastInSink.len) === s.ofsInSrc);
|
|
412
|
+
}
|
|
413
|
+
else {
|
|
414
|
+
assert_1.assert(s.ofsInSrc === 0);
|
|
415
|
+
}
|
|
416
|
+
this.sectionsInSink.push(s);
|
|
417
|
+
}
|
|
418
|
+
appendEmptySection(len) {
|
|
419
|
+
const last = lastIn(this.contentSections);
|
|
420
|
+
if (!last) {
|
|
421
|
+
this.contentSections.push({ type: 'empty', ofs: 0, len });
|
|
422
|
+
}
|
|
423
|
+
else if (last.type === 'content') {
|
|
424
|
+
const ofs = last.ofs + last.len;
|
|
425
|
+
this.contentSections.push({ type: 'empty', ofs, len });
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
assert_1.assert(last.type === 'empty');
|
|
429
|
+
last.len += len;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
insertContentSection(s) {
|
|
433
|
+
// make cut point with sections before and after
|
|
434
|
+
let before = undefined;
|
|
435
|
+
let after = undefined;
|
|
436
|
+
let indToSplice = undefined;
|
|
437
|
+
for (let i = 0; i < this.contentSections.length; i += 1) {
|
|
438
|
+
const c = this.contentSections[i];
|
|
439
|
+
if ((c.ofs + c.len) <= s.ofs) {
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
else if (c.ofs === s.ofs) {
|
|
443
|
+
indToSplice = i;
|
|
444
|
+
if (i > 0) {
|
|
445
|
+
before = this.contentSections[indToSplice - 1];
|
|
446
|
+
}
|
|
447
|
+
break;
|
|
448
|
+
}
|
|
449
|
+
else {
|
|
450
|
+
const lenBefore = s.ofs - c.ofs;
|
|
451
|
+
if (c.type === 'empty') {
|
|
452
|
+
after = {
|
|
453
|
+
type: 'empty',
|
|
454
|
+
len: c.len - lenBefore,
|
|
455
|
+
ofs: s.ofs
|
|
456
|
+
};
|
|
457
|
+
c.len = lenBefore;
|
|
458
|
+
}
|
|
459
|
+
else {
|
|
460
|
+
assert_1.assert(c.type === 'content');
|
|
461
|
+
after = {
|
|
462
|
+
type: 'content',
|
|
463
|
+
len: c.len - lenBefore,
|
|
464
|
+
ofs: s.ofs,
|
|
465
|
+
ofsInSrc: c.ofsInSrc + lenBefore
|
|
466
|
+
};
|
|
467
|
+
c.len = lenBefore;
|
|
468
|
+
const beforeIndInSink = this.sectionsInSink.indexOf(c);
|
|
469
|
+
assert_1.assert(beforeIndInSink >= 0);
|
|
470
|
+
this.sectionsInSink.splice(beforeIndInSink, 0, after);
|
|
471
|
+
}
|
|
472
|
+
before = c;
|
|
473
|
+
indToSplice = i + 1;
|
|
474
|
+
this.contentSections.splice(indToSplice, 0, after);
|
|
475
|
+
break;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
assert_1.assert(indToSplice !== undefined, `When there is no section after given one, it should've been appended with other method`);
|
|
479
|
+
// either merge s with before, or splice it in
|
|
480
|
+
if (before && (before.type === s.type)
|
|
481
|
+
&& ((before.ofs + before.len) === s.ofs)
|
|
482
|
+
&& ((before.ofsInSrc + before.len) === s.ofsInSrc)) {
|
|
483
|
+
before.len += s.len;
|
|
484
|
+
}
|
|
485
|
+
else {
|
|
486
|
+
this.contentSections.splice(indToSplice, 0, s);
|
|
487
|
+
const lastInSink = lastIn(this.sectionsInSink);
|
|
488
|
+
if (lastInSink) {
|
|
489
|
+
assert_1.assert((lastInSink.ofsInSrc + lastInSink.len) === s.ofsInSrc);
|
|
490
|
+
}
|
|
491
|
+
else {
|
|
492
|
+
assert_1.assert(s.ofsInSrc === 0);
|
|
493
|
+
}
|
|
494
|
+
this.sectionsInSink.push(s);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
async deleteContentSection(contentOfs, del, attemptJoinAtCut) {
|
|
498
|
+
if (!this.sink) {
|
|
499
|
+
this.throwOnNoSink();
|
|
500
|
+
}
|
|
501
|
+
// make splits around section(s) to delete
|
|
502
|
+
this.splitContentSectionsAt(contentOfs);
|
|
503
|
+
const fstInd = this.contentSections
|
|
504
|
+
.findIndex(s => (s.ofs === contentOfs));
|
|
505
|
+
assert_1.assert(fstInd >= 0);
|
|
506
|
+
this.splitContentSectionsAt(contentOfs + del);
|
|
507
|
+
let lastInd = this.contentSections
|
|
508
|
+
.findIndex(s => (s.ofs === (contentOfs + del)));
|
|
509
|
+
if (lastInd < 0) {
|
|
510
|
+
lastInd = this.contentSections.length - 1;
|
|
511
|
+
}
|
|
512
|
+
else {
|
|
513
|
+
lastInd -= 1;
|
|
514
|
+
}
|
|
515
|
+
// cut bytes removing info
|
|
516
|
+
for (let i = lastInd; i >= fstInd; i -= 1) {
|
|
517
|
+
const s = this.contentSections[i];
|
|
518
|
+
// remove s from contents info array
|
|
519
|
+
this.contentSections.splice(i, 1);
|
|
520
|
+
for (let k = i; k < this.contentSections.length; k += 1) {
|
|
521
|
+
const c = this.contentSections[k];
|
|
522
|
+
c.ofs -= s.len;
|
|
523
|
+
}
|
|
524
|
+
// remove bytes or turn s to pad
|
|
525
|
+
if (s.type === 'content') {
|
|
526
|
+
if (s.ofsInSrc >= this.cutonlyAreaEnd) {
|
|
527
|
+
s.type = 'pad';
|
|
528
|
+
delete s.ofs;
|
|
529
|
+
}
|
|
530
|
+
else {
|
|
531
|
+
await this.sink.spliceLayout(s.ofsInSrc, s.len, 0);
|
|
532
|
+
this.removeSectionInSink(s);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
else {
|
|
536
|
+
assert_1.assert(s.type === 'empty');
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
if (attemptJoinAtCut && (fstInd > 0)
|
|
540
|
+
&& (fstInd < this.contentSections.length)) {
|
|
541
|
+
const before = this.contentSections[fstInd - 1];
|
|
542
|
+
const after = this.contentSections[fstInd];
|
|
543
|
+
if ((before.type === 'content') && (after.type === 'content')) {
|
|
544
|
+
if ((before.ofsInSrc + before.len) === after.ofsInSrc) {
|
|
545
|
+
const indInSink = this.sectionsInSink.indexOf(after);
|
|
546
|
+
assert_1.assert(this.sectionsInSink[indInSink - 1] === before);
|
|
547
|
+
before.len += after.len;
|
|
548
|
+
this.contentSections.splice(fstInd, 1);
|
|
549
|
+
this.sectionsInSink.splice(indInSink, 1);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
else if ((before.type === 'empty') && (after.type === 'empty')) {
|
|
553
|
+
before.len += after.len;
|
|
554
|
+
this.contentSections.splice(fstInd, 1);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* This ensures that there is a split between section at a given offset.
|
|
560
|
+
* @param cutOfs
|
|
561
|
+
*/
|
|
562
|
+
splitContentSectionsAt(cutOfs) {
|
|
563
|
+
const ind = this.contentSections.findIndex(s => (((s.ofs + s.len) > cutOfs) && (s.ofs <= cutOfs)));
|
|
564
|
+
if (ind < 0) {
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
const s = this.contentSections[ind];
|
|
568
|
+
if (s.ofs === cutOfs) {
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
const tailLen = s.len - (cutOfs - s.ofs);
|
|
572
|
+
s.len -= tailLen;
|
|
573
|
+
if (s.type === 'content') {
|
|
574
|
+
const tail = {
|
|
575
|
+
type: 'content',
|
|
576
|
+
ofs: cutOfs,
|
|
577
|
+
len: tailLen,
|
|
578
|
+
ofsInSrc: s.ofsInSrc + s.len
|
|
579
|
+
};
|
|
580
|
+
this.contentSections.splice(ind + 1, 0, tail);
|
|
581
|
+
const indInSink = this.sectionsInSink.indexOf(s);
|
|
582
|
+
assert_1.assert(indInSink >= 0);
|
|
583
|
+
this.sectionsInSink.splice(indInSink + 1, 0, tail);
|
|
584
|
+
}
|
|
585
|
+
else {
|
|
586
|
+
assert_1.assert(s.type === 'empty');
|
|
587
|
+
const tail = {
|
|
588
|
+
type: 'empty',
|
|
589
|
+
ofs: cutOfs,
|
|
590
|
+
len: tailLen
|
|
591
|
+
};
|
|
592
|
+
this.contentSections.splice(ind + 1, 0, tail);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* Removes pad bytes in cuttable area. This call is synced.
|
|
597
|
+
*/
|
|
598
|
+
async deletePadsInCutArea() {
|
|
599
|
+
if (!this.sink) {
|
|
600
|
+
this.throwOnNoSink();
|
|
601
|
+
}
|
|
602
|
+
// locate sections in cut area
|
|
603
|
+
if (this.sectionsInSink.length === 0) {
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
let fstNoCutSectionInd = this.sectionsInSink
|
|
607
|
+
.findIndex(s => (s.ofsInSrc >= this.cutonlyAreaEnd));
|
|
608
|
+
if (fstNoCutSectionInd < 0) {
|
|
609
|
+
fstNoCutSectionInd = this.sectionsInSink.length;
|
|
610
|
+
}
|
|
611
|
+
// delete pads in cut areas
|
|
612
|
+
for (let i = (fstNoCutSectionInd - 1); i >= 0; i -= 1) {
|
|
613
|
+
const p = this.sectionsInSink[i];
|
|
614
|
+
if (p.type !== 'pad') {
|
|
615
|
+
continue;
|
|
616
|
+
}
|
|
617
|
+
await this.sink.spliceLayout(p.ofsInSrc, p.len, 0);
|
|
618
|
+
this.removeSectionInSink(p);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Removes given section from sectionsInSink array, adjusting related offset
|
|
623
|
+
* values.
|
|
624
|
+
* @param p this section must be from sectionsInSink, and completely in the
|
|
625
|
+
* area, where cut is allowed.
|
|
626
|
+
*/
|
|
627
|
+
removeSectionInSink(p) {
|
|
628
|
+
assert_1.assert(p.ofsInSrc < this.cutonlyAreaEnd);
|
|
629
|
+
const i = this.sectionsInSink.indexOf(p);
|
|
630
|
+
assert_1.assert(i >= 0);
|
|
631
|
+
this.sectionsInSink.splice(i, 1);
|
|
632
|
+
for (let k = i; k < this.sectionsInSink.length; k += 1) {
|
|
633
|
+
const s = this.sectionsInSink[k];
|
|
634
|
+
s.ofsInSrc -= p.len;
|
|
635
|
+
}
|
|
636
|
+
this.cutonlyAreaEnd -= p.len;
|
|
637
|
+
this.writePos -= p.len;
|
|
638
|
+
}
|
|
639
|
+
async replaceXAttrs(xattrs) {
|
|
640
|
+
if (!this.sink) {
|
|
641
|
+
this.throwOnNoSink();
|
|
642
|
+
}
|
|
643
|
+
if (this.xattrsSections.length > 0) {
|
|
644
|
+
for (const s of this.xattrsSections) {
|
|
645
|
+
if (s.ofsInSrc < this.cutonlyAreaEnd) {
|
|
646
|
+
await this.sink.spliceLayout(s.ofsInSrc, s.len, 0);
|
|
647
|
+
this.removeSectionInSink(s);
|
|
648
|
+
}
|
|
649
|
+
else {
|
|
650
|
+
s.type = 'pad';
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
this.xattrsSections.splice(0, this.xattrsSections.length);
|
|
654
|
+
}
|
|
655
|
+
if (xattrs.isEmpty) {
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
const packedXAttrs = xattrs.pack();
|
|
659
|
+
const len = buffer_utils_1.byteLengthIn(packedXAttrs);
|
|
660
|
+
if (len > 0) {
|
|
661
|
+
await this.sink.spliceLayout(this.writePos, 0, len);
|
|
662
|
+
const s = {
|
|
663
|
+
type: 'xattrs',
|
|
664
|
+
len,
|
|
665
|
+
ofsInSrc: this.writePos,
|
|
666
|
+
};
|
|
667
|
+
for (const bytes of packedXAttrs) {
|
|
668
|
+
await this.sink.write(this.writePos, bytes);
|
|
669
|
+
this.writePos += bytes.length;
|
|
670
|
+
}
|
|
671
|
+
this.xattrsSections.push(s);
|
|
672
|
+
this.sectionsInSink.push(s);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
async setXAttrs(xattrs) {
|
|
676
|
+
await this.syncProc.startOrChain(async () => {
|
|
677
|
+
try {
|
|
678
|
+
await this.replaceXAttrs(xattrs);
|
|
679
|
+
await this.completeWriting();
|
|
680
|
+
}
|
|
681
|
+
catch (err) {
|
|
682
|
+
await this.completeWriting(err);
|
|
683
|
+
throw err;
|
|
684
|
+
}
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
async writeAllContent(bytes, xattrs) {
|
|
688
|
+
await this.syncProc.startOrChain(async () => {
|
|
689
|
+
if (!this.sink) {
|
|
690
|
+
this.throwOnNoSink();
|
|
691
|
+
}
|
|
692
|
+
try {
|
|
693
|
+
if (this.contentSections.length > 0) {
|
|
694
|
+
for (const s of this.contentSections) {
|
|
695
|
+
if (s.type === 'empty') {
|
|
696
|
+
continue;
|
|
697
|
+
}
|
|
698
|
+
if (s.ofsInSrc < this.cutonlyAreaEnd) {
|
|
699
|
+
await this.sink.spliceLayout(s.ofsInSrc, s.len, 0);
|
|
700
|
+
this.removeSectionInSink(s);
|
|
701
|
+
}
|
|
702
|
+
else {
|
|
703
|
+
s.type = 'pad';
|
|
704
|
+
delete s.ofs;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
this.contentSections.splice(0, this.contentSections.length);
|
|
708
|
+
}
|
|
709
|
+
if (xattrs) {
|
|
710
|
+
await this.replaceXAttrs(xattrs);
|
|
711
|
+
}
|
|
712
|
+
const len = buffer_utils_1.byteLengthIn(bytes);
|
|
713
|
+
if (len > 0) {
|
|
714
|
+
await this.sink.spliceLayout(this.writePos, 0, len);
|
|
715
|
+
const s = {
|
|
716
|
+
type: 'content',
|
|
717
|
+
len,
|
|
718
|
+
ofs: 0,
|
|
719
|
+
ofsInSrc: this.writePos,
|
|
720
|
+
};
|
|
721
|
+
for (const chunk of bytes) {
|
|
722
|
+
await this.sink.write(this.writePos, chunk);
|
|
723
|
+
this.writePos += chunk.length;
|
|
724
|
+
}
|
|
725
|
+
this.contentSections.push(s);
|
|
726
|
+
this.sectionsInSink.push(s);
|
|
727
|
+
}
|
|
728
|
+
await this.completeWriting();
|
|
729
|
+
}
|
|
730
|
+
catch (err) {
|
|
731
|
+
await this.completeWriting(err).catch(noop);
|
|
732
|
+
throw err;
|
|
733
|
+
}
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
async makeFileByteSink(xattrs) {
|
|
737
|
+
if (xattrs) {
|
|
738
|
+
await this.syncProc.startOrChain(() => this.replaceXAttrs(xattrs));
|
|
739
|
+
}
|
|
740
|
+
return {
|
|
741
|
+
done: err => this.syncProc.startOrChain(() => this.completeWriting(err)),
|
|
742
|
+
getSize: () => this.syncProc.startOrChain(async () => {
|
|
743
|
+
return this.contentLen;
|
|
744
|
+
}),
|
|
745
|
+
showLayout: () => this.syncProc.startOrChain(async () => {
|
|
746
|
+
return {
|
|
747
|
+
base: this.base,
|
|
748
|
+
sections: this.contentSections.map(s => {
|
|
749
|
+
const { ofs, len } = s;
|
|
750
|
+
let src;
|
|
751
|
+
if (s.type === 'content') {
|
|
752
|
+
src = ((s.ofsInSrc < this.cutonlyAreaEnd) ?
|
|
753
|
+
'base' : 'new');
|
|
754
|
+
}
|
|
755
|
+
else {
|
|
756
|
+
src = s.type;
|
|
757
|
+
}
|
|
758
|
+
return { src, ofs, len };
|
|
759
|
+
})
|
|
760
|
+
};
|
|
761
|
+
}),
|
|
762
|
+
splice: (pos, del, bytes) => this.syncProc.startOrChain(() => this.spliceContent(pos, del, bytes)),
|
|
763
|
+
truncate: size => this.syncProc.startOrChain(async () => {
|
|
764
|
+
assert_1.assert(Number.isInteger(size) && (size >= 0), `Size should be a non-negative integer`);
|
|
765
|
+
const currentSize = this.contentLen;
|
|
766
|
+
if (size > currentSize) {
|
|
767
|
+
this.appendEmptySection(size - currentSize);
|
|
768
|
+
}
|
|
769
|
+
else if (size < currentSize) {
|
|
770
|
+
await this.spliceContent(size, currentSize - size);
|
|
771
|
+
}
|
|
772
|
+
})
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
Object.freeze(WritablePayloadV2.prototype);
|
|
777
|
+
Object.freeze(WritablePayloadV2);
|
|
778
|
+
function payloadLayoutException(msg, cause) {
|
|
779
|
+
return {
|
|
780
|
+
runtimeException: true,
|
|
781
|
+
type: 'payload-layout',
|
|
782
|
+
message: msg,
|
|
783
|
+
cause,
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
async function sureReadOfBytesFrom(src, ofs, len) {
|
|
787
|
+
await src.seek(ofs);
|
|
788
|
+
const bytes = await src.read(len);
|
|
789
|
+
assert_1.assert(!!bytes && (bytes.length === len));
|
|
790
|
+
return bytes;
|
|
791
|
+
}
|
|
792
|
+
function noop() { }
|
|
793
|
+
// XXX we may add smaller bytes for packing section info: there is enough values
|
|
794
|
+
/**
|
|
795
|
+
* Payload version 2 contains in this order:
|
|
796
|
+
* - content/xattr/pad bytes,
|
|
797
|
+
* - layout bytes,
|
|
798
|
+
* - 4 bytes with layout bytes length.
|
|
799
|
+
* Layout bytes contain in this order:
|
|
800
|
+
* - common attrs,
|
|
801
|
+
* - m xattrs sections,
|
|
802
|
+
* - n content or empty sections.
|
|
803
|
+
* Section is described with 9, 13, or 17 bytes.
|
|
804
|
+
* - 1 type byte,
|
|
805
|
+
* - 4 or 8 bytes with offset byte source,
|
|
806
|
+
* - 4 or 8 bytes with section's length.
|
|
807
|
+
* Values are:
|
|
808
|
+
* 0x01 - empty content section - 4 bytes for length
|
|
809
|
+
* 0x02 - empty content section - 8 bytes for length
|
|
810
|
+
* 0x11 - content section - 4 bytes for offset in source - 4 bytes for length
|
|
811
|
+
* 0x12 - content section - 8 bytes for offset in source - 4 bytes for length
|
|
812
|
+
* 0x13 - content section - 4 bytes for offset in source - 8 bytes for length
|
|
813
|
+
* 0x14 - content section - 8 bytes for offset in source - 8 bytes for length
|
|
814
|
+
* 0x21 - xattrs section - 4 bytes for offset in source - 4 bytes for length
|
|
815
|
+
* 0x22 - xattrs section - 8 bytes for offset in source - 4 bytes for length
|
|
816
|
+
*/
|
|
817
|
+
var payloadV2;
|
|
818
|
+
(function (payloadV2) {
|
|
819
|
+
const MIN_PAYLOAD_V2_LEN = attrs_1.CommonAttrs.PACK_LEN + 4;
|
|
820
|
+
async function readFrom(src) {
|
|
821
|
+
const { size: srcLen, isEndless } = await src.getSize();
|
|
822
|
+
if (isEndless) {
|
|
823
|
+
throw payloadLayoutException(`Payload v2 can't be present in endless byte source.`);
|
|
824
|
+
}
|
|
825
|
+
if (srcLen < MIN_PAYLOAD_V2_LEN) {
|
|
826
|
+
throw payloadLayoutException(`Byte source is too short for smallest payload v2`);
|
|
827
|
+
}
|
|
828
|
+
// - read layout length from last 4 bytes
|
|
829
|
+
const layoutLen = big_endian_1.uintFrom4Bytes(await sureReadOfBytesFrom(src, srcLen - 4, 4));
|
|
830
|
+
const sectionsEnd = srcLen - (layoutLen + 4);
|
|
831
|
+
if (sectionsEnd < 0) {
|
|
832
|
+
throw payloadLayoutException(`Layout length value in payload v2 is out of bound`);
|
|
833
|
+
}
|
|
834
|
+
// - read layout bytes
|
|
835
|
+
const layoutBytes = await sureReadOfBytesFrom(src, sectionsEnd, layoutLen);
|
|
836
|
+
let ofs = 0;
|
|
837
|
+
// - parse common attrs
|
|
838
|
+
const attrs = attrs_1.CommonAttrs.parse(layoutBytes);
|
|
839
|
+
ofs += attrs_1.CommonAttrs.PACK_LEN;
|
|
840
|
+
// - parse sections
|
|
841
|
+
const { contentSections, xattrsSections } = parseSections(layoutBytes, ofs);
|
|
842
|
+
return { attrs, sectionsEnd, contentSections, xattrsSections };
|
|
843
|
+
}
|
|
844
|
+
payloadV2.readFrom = readFrom;
|
|
845
|
+
function parseSections(bytes, i) {
|
|
846
|
+
const contentSections = [];
|
|
847
|
+
const xattrsSections = [];
|
|
848
|
+
let ofs = 0; // calculating offset in content
|
|
849
|
+
while (i < bytes.length) {
|
|
850
|
+
const sectionType = bytes[i];
|
|
851
|
+
let ofsInSrc;
|
|
852
|
+
let len;
|
|
853
|
+
switch (sectionType) {
|
|
854
|
+
case 0x01:
|
|
855
|
+
checkLen(bytes, i, 5);
|
|
856
|
+
len = nonZeroLengthFrom4Bytes(bytes, i + 1);
|
|
857
|
+
contentSections.push({ type: EMPTY_SECTION, len, ofs });
|
|
858
|
+
i += 5;
|
|
859
|
+
break;
|
|
860
|
+
case 0x02:
|
|
861
|
+
checkLen(bytes, i, 9);
|
|
862
|
+
len = nonZeroLengthFrom8Bytes(bytes, i + 1);
|
|
863
|
+
contentSections.push({ type: EMPTY_SECTION, len, ofs });
|
|
864
|
+
i += 9;
|
|
865
|
+
break;
|
|
866
|
+
case 0x11:
|
|
867
|
+
checkLen(bytes, i, 9);
|
|
868
|
+
ofsInSrc = big_endian_1.uintFrom4Bytes(bytes, i + 1);
|
|
869
|
+
len = nonZeroLengthFrom4Bytes(bytes, i + 5);
|
|
870
|
+
contentSections.push({ type: CONTENT_SECTION, len, ofs, ofsInSrc });
|
|
871
|
+
i += 9;
|
|
872
|
+
break;
|
|
873
|
+
case 0x12:
|
|
874
|
+
checkLen(bytes, i, 13);
|
|
875
|
+
ofsInSrc = big_endian_1.uintFrom8Bytes(bytes, i + 1);
|
|
876
|
+
len = nonZeroLengthFrom4Bytes(bytes, i + 9);
|
|
877
|
+
contentSections.push({ type: CONTENT_SECTION, len, ofs, ofsInSrc });
|
|
878
|
+
i += 13;
|
|
879
|
+
break;
|
|
880
|
+
case 0x13:
|
|
881
|
+
checkLen(bytes, i, 13);
|
|
882
|
+
ofsInSrc = big_endian_1.uintFrom4Bytes(bytes, i + 1);
|
|
883
|
+
len = nonZeroLengthFrom8Bytes(bytes, i + 5);
|
|
884
|
+
contentSections.push({ type: CONTENT_SECTION, len, ofs, ofsInSrc });
|
|
885
|
+
i += 13;
|
|
886
|
+
break;
|
|
887
|
+
case 0x14:
|
|
888
|
+
checkLen(bytes, i, 17);
|
|
889
|
+
ofsInSrc = big_endian_1.uintFrom8Bytes(bytes, i + 1);
|
|
890
|
+
len = nonZeroLengthFrom8Bytes(bytes, i + 9);
|
|
891
|
+
contentSections.push({ type: CONTENT_SECTION, len, ofs, ofsInSrc });
|
|
892
|
+
i += 17;
|
|
893
|
+
break;
|
|
894
|
+
case 0x21:
|
|
895
|
+
checkLen(bytes, i, 9);
|
|
896
|
+
ofsInSrc = big_endian_1.uintFrom4Bytes(bytes, i + 1);
|
|
897
|
+
len = nonZeroLengthFrom4Bytes(bytes, i + 5);
|
|
898
|
+
xattrsSections.push({ type: XATTRS_SECTION, ofsInSrc, len });
|
|
899
|
+
i += 9;
|
|
900
|
+
continue; // xattrs section doesn't advance ofs below
|
|
901
|
+
case 0x22:
|
|
902
|
+
checkLen(bytes, i, 13);
|
|
903
|
+
ofsInSrc = big_endian_1.uintFrom8Bytes(bytes, i + 1);
|
|
904
|
+
len = nonZeroLengthFrom4Bytes(bytes, i + 9);
|
|
905
|
+
xattrsSections.push({ type: XATTRS_SECTION, ofsInSrc, len });
|
|
906
|
+
i += 13;
|
|
907
|
+
continue; // xattrs section doesn't advance ofs below
|
|
908
|
+
default:
|
|
909
|
+
throw payloadLayoutException(`Unknown section type ${sectionType} in payload version 2`);
|
|
910
|
+
}
|
|
911
|
+
ofs += len;
|
|
912
|
+
}
|
|
913
|
+
return { contentSections, xattrsSections };
|
|
914
|
+
}
|
|
915
|
+
function checkLen(bytes, ofs, minLen) {
|
|
916
|
+
if ((ofs + minLen) > bytes.length) {
|
|
917
|
+
throw payloadLayoutException(`Unexpected short byte section in payload version 2`);
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
function nonZeroLengthFrom4Bytes(bytes, i) {
|
|
921
|
+
const len = big_endian_1.uintFrom4Bytes(bytes, i);
|
|
922
|
+
if (len === 0) {
|
|
923
|
+
throw payloadLayoutException(`Section with zero length in payload version 2`);
|
|
924
|
+
}
|
|
925
|
+
return len;
|
|
926
|
+
}
|
|
927
|
+
function nonZeroLengthFrom8Bytes(bytes, i) {
|
|
928
|
+
const len = big_endian_1.uintFrom8Bytes(bytes, i);
|
|
929
|
+
if (len === 0) {
|
|
930
|
+
throw payloadLayoutException(`Section with zero length in payload version 2`);
|
|
931
|
+
}
|
|
932
|
+
return len;
|
|
933
|
+
}
|
|
934
|
+
function pack(attrs, xattrs, content) {
|
|
935
|
+
const bytes = [];
|
|
936
|
+
bytes.push(attrs.pack());
|
|
937
|
+
for (const section of xattrs) {
|
|
938
|
+
bytes.push(packXAttrsSection(section));
|
|
939
|
+
}
|
|
940
|
+
for (const section of content) {
|
|
941
|
+
if (section.type === 'empty') {
|
|
942
|
+
bytes.push(packEmptySection(section));
|
|
943
|
+
}
|
|
944
|
+
else {
|
|
945
|
+
bytes.push(packContentSection(section));
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
const layoutLen = buffer_utils_1.byteLengthIn(bytes);
|
|
949
|
+
const lastBytes = Buffer.allocUnsafe(4);
|
|
950
|
+
big_endian_1.packUintTo4Bytes(layoutLen, lastBytes, 0);
|
|
951
|
+
bytes.push(lastBytes);
|
|
952
|
+
const packLen = layoutLen + 4;
|
|
953
|
+
return { bytes, packLen };
|
|
954
|
+
}
|
|
955
|
+
payloadV2.pack = pack;
|
|
956
|
+
function packEmptySection(section) {
|
|
957
|
+
if (section.len < 0x100000000) {
|
|
958
|
+
const bytes = Buffer.allocUnsafe(5);
|
|
959
|
+
bytes[0] = 0x01;
|
|
960
|
+
big_endian_1.packUintTo4Bytes(section.len, bytes, 1);
|
|
961
|
+
return bytes;
|
|
962
|
+
}
|
|
963
|
+
else {
|
|
964
|
+
const bytes = Buffer.allocUnsafe(9);
|
|
965
|
+
bytes[0] = 0x02;
|
|
966
|
+
big_endian_1.packUintTo8Bytes(section.len, bytes, 1);
|
|
967
|
+
return bytes;
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
function packContentSection(section) {
|
|
971
|
+
if (section.ofsInSrc < 0x100000000) {
|
|
972
|
+
if (section.len < 0x100000000) {
|
|
973
|
+
const bytes = Buffer.allocUnsafe(9);
|
|
974
|
+
bytes[0] = 0x11;
|
|
975
|
+
big_endian_1.packUintTo4Bytes(section.ofsInSrc, bytes, 1);
|
|
976
|
+
big_endian_1.packUintTo4Bytes(section.len, bytes, 5);
|
|
977
|
+
return bytes;
|
|
978
|
+
}
|
|
979
|
+
else {
|
|
980
|
+
const bytes = Buffer.allocUnsafe(13);
|
|
981
|
+
bytes[0] = 0x13;
|
|
982
|
+
big_endian_1.packUintTo4Bytes(section.ofsInSrc, bytes, 1);
|
|
983
|
+
big_endian_1.packUintTo8Bytes(section.len, bytes, 5);
|
|
984
|
+
return bytes;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
else {
|
|
988
|
+
if (section.len < 0x100000000) {
|
|
989
|
+
const bytes = Buffer.allocUnsafe(13);
|
|
990
|
+
bytes[0] = 0x12;
|
|
991
|
+
big_endian_1.packUintTo8Bytes(section.ofsInSrc, bytes, 1);
|
|
992
|
+
big_endian_1.packUintTo4Bytes(section.len, bytes, 9);
|
|
993
|
+
return bytes;
|
|
994
|
+
}
|
|
995
|
+
else {
|
|
996
|
+
const bytes = Buffer.allocUnsafe(17);
|
|
997
|
+
bytes[0] = 0x14;
|
|
998
|
+
big_endian_1.packUintTo8Bytes(section.ofsInSrc, bytes, 1);
|
|
999
|
+
big_endian_1.packUintTo8Bytes(section.len, bytes, 8);
|
|
1000
|
+
return bytes;
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
function packXAttrsSection(section) {
|
|
1005
|
+
if (section.ofsInSrc < 0x100000000) {
|
|
1006
|
+
const bytes = Buffer.allocUnsafe(9);
|
|
1007
|
+
bytes[0] = 0x21;
|
|
1008
|
+
big_endian_1.packUintTo4Bytes(section.ofsInSrc, bytes, 1);
|
|
1009
|
+
big_endian_1.packUintTo4Bytes(section.len, bytes, 5);
|
|
1010
|
+
return bytes;
|
|
1011
|
+
}
|
|
1012
|
+
else {
|
|
1013
|
+
const bytes = Buffer.allocUnsafe(13);
|
|
1014
|
+
bytes[0] = 0x22;
|
|
1015
|
+
big_endian_1.packUintTo8Bytes(section.ofsInSrc, bytes, 1);
|
|
1016
|
+
big_endian_1.packUintTo4Bytes(section.len, bytes, 9);
|
|
1017
|
+
return bytes;
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
})(payloadV2 || (payloadV2 = {}));
|
|
1021
|
+
Object.freeze(payloadV2);
|
|
1022
|
+
Object.freeze(exports);
|