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.
Files changed (61) hide show
  1. package/build/api-defs/files.d.ts +8 -7
  2. package/build/core/asmail/inbox/attachments/fs.d.ts +2 -2
  3. package/build/core/asmail/inbox/attachments/fs.js +3 -2
  4. package/build/core/asmail/msg/common.d.ts +2 -2
  5. package/build/core/asmail/msg/common.js +3 -2
  6. package/build/core/asmail/msg/opener.d.ts +2 -2
  7. package/build/core/asmail/msg/opener.js +3 -2
  8. package/build/core/asmail/msg/packer.d.ts +2 -2
  9. package/build/core/asmail/msg/packer.js +7 -6
  10. package/build/core/storage/index.js +2 -2
  11. package/build/core/storage/local/obj-files.js +8 -5
  12. package/build/core/storage/local/obj-status.js +4 -3
  13. package/build/core/storage/synced/obj-status.js +4 -3
  14. package/build/ipc-via-protobuf/bytes.js +1 -1
  15. package/build/lib-client/3nstorage/xsp-fs/attrs.d.ts +28 -0
  16. package/build/lib-client/3nstorage/xsp-fs/attrs.js +337 -0
  17. package/build/lib-client/3nstorage/xsp-fs/common.d.ts +1 -1
  18. package/build/lib-client/3nstorage/xsp-fs/common.js +3 -2
  19. package/build/lib-client/3nstorage/xsp-fs/file-node.d.ts +31 -18
  20. package/build/lib-client/3nstorage/xsp-fs/file-node.js +130 -118
  21. package/build/lib-client/3nstorage/xsp-fs/file.d.ts +0 -1
  22. package/build/lib-client/3nstorage/xsp-fs/file.js +14 -49
  23. package/build/lib-client/3nstorage/xsp-fs/folder-node-serialization.d.ts +1 -1
  24. package/build/lib-client/3nstorage/xsp-fs/folder-node-serialization.js +95 -91
  25. package/build/lib-client/3nstorage/xsp-fs/folder-node.d.ts +19 -24
  26. package/build/lib-client/3nstorage/xsp-fs/folder-node.js +133 -190
  27. package/build/lib-client/3nstorage/xsp-fs/fs.d.ts +3 -4
  28. package/build/lib-client/3nstorage/xsp-fs/fs.js +18 -21
  29. package/build/lib-client/3nstorage/xsp-fs/link-node.d.ts +13 -8
  30. package/build/lib-client/3nstorage/xsp-fs/link-node.js +46 -38
  31. package/build/lib-client/3nstorage/xsp-fs/node-in-fs.d.ts +17 -35
  32. package/build/lib-client/3nstorage/xsp-fs/node-in-fs.js +34 -127
  33. package/build/lib-client/3nstorage/xsp-fs/node-persistence.d.ts +57 -0
  34. package/build/lib-client/3nstorage/xsp-fs/node-persistence.js +182 -0
  35. package/build/lib-client/3nstorage/xsp-fs/xsp-payload-v1.d.ts +3 -0
  36. package/build/lib-client/3nstorage/xsp-fs/xsp-payload-v1.js +87 -0
  37. package/build/lib-client/3nstorage/xsp-fs/xsp-payload-v2.d.ts +6 -0
  38. package/build/lib-client/3nstorage/xsp-fs/xsp-payload-v2.js +1022 -0
  39. package/build/lib-client/cryptor/cryptor-wasm.js +1 -1
  40. package/build/lib-client/cryptor/cryptor.wasm +0 -0
  41. package/build/lib-client/local-files/device-fs.js +7 -3
  42. package/build/lib-client/objs-on-disk/file-writing-proc.js +3 -2
  43. package/build/lib-client/objs-on-disk/obj-on-disk.js +8 -7
  44. package/build/lib-common/big-endian.d.ts +0 -24
  45. package/build/lib-common/big-endian.js +16 -78
  46. package/build/lib-common/exceptions/file.js +5 -1
  47. package/build/lib-common/obj-streaming/sink-utils.d.ts +0 -4
  48. package/build/lib-common/obj-streaming/sink-utils.js +4 -70
  49. package/build/lib-common/objs-on-disk/file-layout.js +2 -1
  50. package/build/lib-common/objs-on-disk/obj-file.js +2 -2
  51. package/build/lib-common/objs-on-disk/utils.js +2 -1
  52. package/build/lib-common/objs-on-disk/v1-obj-file-format.js +2 -1
  53. package/package.json +2 -2
  54. package/build/lib-client/files/file-attrs.d.ts +0 -76
  55. package/build/lib-client/files/file-attrs.js +0 -549
  56. package/build/lib-client/files/file-layout.d.ts +0 -56
  57. package/build/lib-client/files/file-layout.js +0 -456
  58. package/build/lib-client/files/file-sink.d.ts +0 -33
  59. package/build/lib-client/files/file-sink.js +0 -173
  60. package/build/lib-client/files/file-source.d.ts +0 -19
  61. 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);