@loaders.gl/zip 4.2.0-alpha.4 → 4.2.0-alpha.6

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 (56) hide show
  1. package/dist/dist.dev.js +919 -523
  2. package/dist/dist.min.js +25 -0
  3. package/dist/filesystems/IndexedArchive.js +26 -12
  4. package/dist/filesystems/zip-filesystem.d.ts +2 -2
  5. package/dist/filesystems/zip-filesystem.d.ts.map +1 -1
  6. package/dist/filesystems/zip-filesystem.js +125 -88
  7. package/dist/hash-file-utility.d.ts +1 -1
  8. package/dist/hash-file-utility.d.ts.map +1 -1
  9. package/dist/hash-file-utility.js +85 -42
  10. package/dist/index.cjs +64 -128
  11. package/dist/index.cjs.map +7 -0
  12. package/dist/index.d.ts +12 -12
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +4 -1
  15. package/dist/lib/tar/header.d.ts +1 -1
  16. package/dist/lib/tar/header.d.ts.map +1 -1
  17. package/dist/lib/tar/header.js +69 -33
  18. package/dist/lib/tar/tar.d.ts +1 -1
  19. package/dist/lib/tar/tar.d.ts.map +1 -1
  20. package/dist/lib/tar/tar.js +124 -106
  21. package/dist/lib/tar/types.js +3 -1
  22. package/dist/lib/tar/utils.js +45 -18
  23. package/dist/parse-zip/cd-file-header.d.ts +1 -1
  24. package/dist/parse-zip/cd-file-header.d.ts.map +1 -1
  25. package/dist/parse-zip/cd-file-header.js +239 -177
  26. package/dist/parse-zip/end-of-central-directory.js +247 -158
  27. package/dist/parse-zip/local-file-header.d.ts +1 -1
  28. package/dist/parse-zip/local-file-header.d.ts.map +1 -1
  29. package/dist/parse-zip/local-file-header.js +143 -102
  30. package/dist/parse-zip/search-from-the-end.js +27 -13
  31. package/dist/parse-zip/zip-composition.js +142 -92
  32. package/dist/parse-zip/zip64-info-generation.js +64 -41
  33. package/dist/tar-builder.d.ts +1 -1
  34. package/dist/tar-builder.d.ts.map +1 -1
  35. package/dist/tar-builder.js +32 -29
  36. package/dist/zip-loader.js +52 -41
  37. package/dist/zip-writer.js +40 -40
  38. package/package.json +11 -7
  39. package/src/filesystems/zip-filesystem.ts +4 -0
  40. package/dist/filesystems/IndexedArchive.js.map +0 -1
  41. package/dist/filesystems/zip-filesystem.js.map +0 -1
  42. package/dist/hash-file-utility.js.map +0 -1
  43. package/dist/index.js.map +0 -1
  44. package/dist/lib/tar/header.js.map +0 -1
  45. package/dist/lib/tar/tar.js.map +0 -1
  46. package/dist/lib/tar/types.js.map +0 -1
  47. package/dist/lib/tar/utils.js.map +0 -1
  48. package/dist/parse-zip/cd-file-header.js.map +0 -1
  49. package/dist/parse-zip/end-of-central-directory.js.map +0 -1
  50. package/dist/parse-zip/local-file-header.js.map +0 -1
  51. package/dist/parse-zip/search-from-the-end.js.map +0 -1
  52. package/dist/parse-zip/zip-composition.js.map +0 -1
  53. package/dist/parse-zip/zip64-info-generation.js.map +0 -1
  54. package/dist/tar-builder.js.map +0 -1
  55. package/dist/zip-loader.js.map +0 -1
  56. package/dist/zip-writer.js.map +0 -1
@@ -1,6 +1,10 @@
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
1
4
  import { compareArrayBuffers, concatenateArrayBuffers } from '@loaders.gl/loader-utils';
2
5
  import { parseEoCDRecord } from "./end-of-central-directory.js";
3
6
  import { createZip64Info, setFieldToNumber } from "./zip64-info-generation.js";
7
+ // offsets accroding to https://en.wikipedia.org/wiki/ZIP_(file_format)
4
8
  const CD_COMPRESSED_SIZE_OFFSET = 20n;
5
9
  const CD_UNCOMPRESSED_SIZE_OFFSET = 24n;
6
10
  const CD_FILE_NAME_LENGTH_OFFSET = 28n;
@@ -9,191 +13,249 @@ const CD_START_DISK_OFFSET = 32n;
9
13
  const CD_LOCAL_HEADER_OFFSET_OFFSET = 42n;
10
14
  const CD_FILE_NAME_OFFSET = 46n;
11
15
  export const signature = new Uint8Array([0x50, 0x4b, 0x01, 0x02]);
16
+ /**
17
+ * Parses central directory file header of zip file
18
+ * @param headerOffset - offset in the archive where header starts
19
+ * @param buffer - buffer containing whole array
20
+ * @returns Info from the header
21
+ */
12
22
  export const parseZipCDFileHeader = async (headerOffset, file) => {
13
- const magicBytes = await file.slice(headerOffset, headerOffset + 4n);
14
- if (!compareArrayBuffers(magicBytes, signature.buffer)) {
15
- return null;
16
- }
17
- const compressedSize = BigInt(await file.getUint32(headerOffset + CD_COMPRESSED_SIZE_OFFSET));
18
- const uncompressedSize = BigInt(await file.getUint32(headerOffset + CD_UNCOMPRESSED_SIZE_OFFSET));
19
- const extraFieldLength = await file.getUint16(headerOffset + CD_EXTRA_FIELD_LENGTH_OFFSET);
20
- const startDisk = BigInt(await file.getUint16(headerOffset + CD_START_DISK_OFFSET));
21
- const fileNameLength = await file.getUint16(headerOffset + CD_FILE_NAME_LENGTH_OFFSET);
22
- const filenameBytes = await file.slice(headerOffset + CD_FILE_NAME_OFFSET, headerOffset + CD_FILE_NAME_OFFSET + BigInt(fileNameLength));
23
- const fileName = new TextDecoder().decode(filenameBytes);
24
- const extraOffset = headerOffset + CD_FILE_NAME_OFFSET + BigInt(fileNameLength);
25
- const oldFormatOffset = await file.getUint32(headerOffset + CD_LOCAL_HEADER_OFFSET_OFFSET);
26
- const localHeaderOffset = BigInt(oldFormatOffset);
27
- const extraField = new DataView(await file.slice(extraOffset, extraOffset + BigInt(extraFieldLength)));
28
- const zip64data = {
29
- uncompressedSize,
30
- compressedSize,
31
- localHeaderOffset,
32
- startDisk
33
- };
34
- const res = findZip64DataInExtra(zip64data, extraField);
35
- return {
36
- ...zip64data,
37
- ...res,
38
- extraFieldLength,
39
- fileNameLength,
40
- fileName,
41
- extraOffset
42
- };
23
+ const magicBytes = await file.slice(headerOffset, headerOffset + 4n);
24
+ if (!compareArrayBuffers(magicBytes, signature.buffer)) {
25
+ return null;
26
+ }
27
+ const compressedSize = BigInt(await file.getUint32(headerOffset + CD_COMPRESSED_SIZE_OFFSET));
28
+ const uncompressedSize = BigInt(await file.getUint32(headerOffset + CD_UNCOMPRESSED_SIZE_OFFSET));
29
+ const extraFieldLength = await file.getUint16(headerOffset + CD_EXTRA_FIELD_LENGTH_OFFSET);
30
+ const startDisk = BigInt(await file.getUint16(headerOffset + CD_START_DISK_OFFSET));
31
+ const fileNameLength = await file.getUint16(headerOffset + CD_FILE_NAME_LENGTH_OFFSET);
32
+ const filenameBytes = await file.slice(headerOffset + CD_FILE_NAME_OFFSET, headerOffset + CD_FILE_NAME_OFFSET + BigInt(fileNameLength));
33
+ const fileName = new TextDecoder().decode(filenameBytes);
34
+ const extraOffset = headerOffset + CD_FILE_NAME_OFFSET + BigInt(fileNameLength);
35
+ const oldFormatOffset = await file.getUint32(headerOffset + CD_LOCAL_HEADER_OFFSET_OFFSET);
36
+ const localHeaderOffset = BigInt(oldFormatOffset);
37
+ const extraField = new DataView(await file.slice(extraOffset, extraOffset + BigInt(extraFieldLength)));
38
+ // looking for info that might be also be in zip64 extra field
39
+ const zip64data = {
40
+ uncompressedSize,
41
+ compressedSize,
42
+ localHeaderOffset,
43
+ startDisk
44
+ };
45
+ const res = findZip64DataInExtra(zip64data, extraField);
46
+ return {
47
+ ...zip64data,
48
+ ...res,
49
+ extraFieldLength,
50
+ fileNameLength,
51
+ fileName,
52
+ extraOffset
53
+ };
43
54
  };
55
+ /**
56
+ * Create iterator over files of zip archive
57
+ * @param fileProvider - file provider that provider random access to the file
58
+ */
44
59
  export async function* makeZipCDHeaderIterator(fileProvider) {
45
- const {
46
- cdStartOffset
47
- } = await parseEoCDRecord(fileProvider);
48
- let cdHeader = await parseZipCDFileHeader(cdStartOffset, fileProvider);
49
- while (cdHeader) {
50
- yield cdHeader;
51
- cdHeader = await parseZipCDFileHeader(cdHeader.extraOffset + BigInt(cdHeader.extraFieldLength), fileProvider);
52
- }
60
+ const { cdStartOffset } = await parseEoCDRecord(fileProvider);
61
+ let cdHeader = await parseZipCDFileHeader(cdStartOffset, fileProvider);
62
+ while (cdHeader) {
63
+ yield cdHeader;
64
+ cdHeader = await parseZipCDFileHeader(cdHeader.extraOffset + BigInt(cdHeader.extraFieldLength), fileProvider);
65
+ }
53
66
  }
54
- const getUint16 = function () {
55
- return (arguments.length <= 0 ? undefined : arguments[0]) + (arguments.length <= 1 ? undefined : arguments[1]) * 16;
67
+ /**
68
+ * returns the number written in the provided bytes
69
+ * @param bytes two bytes containing the number
70
+ * @returns the number written in the provided bytes
71
+ */
72
+ const getUint16 = (...bytes) => {
73
+ return bytes[0] + bytes[1] * 16;
56
74
  };
75
+ /**
76
+ * reads all nesessary data from zip64 record in the extra data
77
+ * @param zip64data values that might be in zip64 record
78
+ * @param extraField full extra data
79
+ * @returns data read from zip64
80
+ */
57
81
  const findZip64DataInExtra = (zip64data, extraField) => {
58
- const zip64dataList = findExpectedData(zip64data);
59
- const zip64DataRes = {};
60
- if (zip64dataList.length > 0) {
61
- const zip64chunkSize = zip64dataList.reduce((sum, curr) => sum + curr.length, 0);
62
- const offsetInExtraData = new Uint8Array(extraField.buffer).findIndex((_val, i, arr) => getUint16(arr[i], arr[i + 1]) === 0x0001 && getUint16(arr[i + 2], arr[i + 3]) === zip64chunkSize);
63
- let bytesRead = 0;
64
- for (const note of zip64dataList) {
65
- const offset = bytesRead;
66
- zip64DataRes[note.name] = extraField.getBigUint64(offsetInExtraData + 4 + offset, true);
67
- bytesRead = offset + note.length;
82
+ const zip64dataList = findExpectedData(zip64data);
83
+ const zip64DataRes = {};
84
+ if (zip64dataList.length > 0) {
85
+ // total length of data in zip64 notation in bytes
86
+ const zip64chunkSize = zip64dataList.reduce((sum, curr) => sum + curr.length, 0);
87
+ // we're looking for the zip64 nontation header (0x0001)
88
+ // and a size field with a correct value next to it
89
+ const offsetInExtraData = new Uint8Array(extraField.buffer).findIndex((_val, i, arr) => getUint16(arr[i], arr[i + 1]) === 0x0001 &&
90
+ getUint16(arr[i + 2], arr[i + 3]) === zip64chunkSize);
91
+ // then we read all the nesessary fields from the zip64 data
92
+ let bytesRead = 0;
93
+ for (const note of zip64dataList) {
94
+ const offset = bytesRead;
95
+ zip64DataRes[note.name] = extraField.getBigUint64(offsetInExtraData + 4 + offset, true);
96
+ bytesRead = offset + note.length;
97
+ }
68
98
  }
69
- }
70
- return zip64DataRes;
99
+ return zip64DataRes;
71
100
  };
72
- const findExpectedData = zip64data => {
73
- const zip64dataList = [];
74
- if (zip64data.uncompressedSize === BigInt(0xffffffff)) {
75
- zip64dataList.push({
76
- name: 'uncompressedSize',
77
- length: 8
78
- });
79
- }
80
- if (zip64data.compressedSize === BigInt(0xffffffff)) {
81
- zip64dataList.push({
82
- name: 'compressedSize',
83
- length: 8
84
- });
85
- }
86
- if (zip64data.localHeaderOffset === BigInt(0xffffffff)) {
87
- zip64dataList.push({
88
- name: 'localHeaderOffset',
89
- length: 8
90
- });
91
- }
92
- if (zip64data.startDisk === BigInt(0xffffffff)) {
93
- zip64dataList.push({
94
- name: 'startDisk',
95
- length: 4
96
- });
97
- }
98
- return zip64dataList;
101
+ /**
102
+ * frind data that's expected to be in zip64
103
+ * @param zip64data values that might be in zip64 record
104
+ * @returns zip64 data description
105
+ */
106
+ const findExpectedData = (zip64data) => {
107
+ // We define fields that should be in zip64 data
108
+ const zip64dataList = [];
109
+ if (zip64data.uncompressedSize === BigInt(0xffffffff)) {
110
+ zip64dataList.push({ name: 'uncompressedSize', length: 8 });
111
+ }
112
+ if (zip64data.compressedSize === BigInt(0xffffffff)) {
113
+ zip64dataList.push({ name: 'compressedSize', length: 8 });
114
+ }
115
+ if (zip64data.localHeaderOffset === BigInt(0xffffffff)) {
116
+ zip64dataList.push({ name: 'localHeaderOffset', length: 8 });
117
+ }
118
+ if (zip64data.startDisk === BigInt(0xffffffff)) {
119
+ zip64dataList.push({ name: 'startDisk', length: 4 });
120
+ }
121
+ return zip64dataList;
99
122
  };
123
+ /**
124
+ * generates cd header for the file
125
+ * @param options info that can be placed into cd header
126
+ * @returns buffer with header
127
+ */
100
128
  export function generateCDHeader(options) {
101
- const optionsToUse = {
102
- ...options,
103
- fnlength: options.fileName.length,
104
- extraLength: 0
105
- };
106
- let zip64header = new ArrayBuffer(0);
107
- const optionsToZip64 = {};
108
- if (optionsToUse.offset >= 0xffffffff) {
109
- optionsToZip64.offset = optionsToUse.offset;
110
- optionsToUse.offset = BigInt(0xffffffff);
111
- }
112
- if (optionsToUse.length >= 0xffffffff) {
113
- optionsToZip64.size = optionsToUse.length;
114
- optionsToUse.length = 0xffffffff;
115
- }
116
- if (Object.keys(optionsToZip64).length) {
117
- zip64header = createZip64Info(optionsToZip64);
118
- optionsToUse.extraLength = zip64header.byteLength;
119
- }
120
- const header = new DataView(new ArrayBuffer(Number(CD_FILE_NAME_OFFSET)));
121
- for (const field of ZIP_HEADER_FIELDS) {
122
- var _ref, _optionsToUse, _field$name;
123
- setFieldToNumber(header, field.size, field.offset, (_ref = (_optionsToUse = optionsToUse[(_field$name = field.name) !== null && _field$name !== void 0 ? _field$name : '']) !== null && _optionsToUse !== void 0 ? _optionsToUse : field.default) !== null && _ref !== void 0 ? _ref : 0);
124
- }
125
- const encodedName = new TextEncoder().encode(optionsToUse.fileName);
126
- const resHeader = concatenateArrayBuffers(header.buffer, encodedName, zip64header);
127
- return resHeader;
129
+ const optionsToUse = {
130
+ ...options,
131
+ fnlength: options.fileName.length,
132
+ extraLength: 0
133
+ };
134
+ let zip64header = new ArrayBuffer(0);
135
+ const optionsToZip64 = {};
136
+ if (optionsToUse.offset >= 0xffffffff) {
137
+ optionsToZip64.offset = optionsToUse.offset;
138
+ optionsToUse.offset = BigInt(0xffffffff);
139
+ }
140
+ if (optionsToUse.length >= 0xffffffff) {
141
+ optionsToZip64.size = optionsToUse.length;
142
+ optionsToUse.length = 0xffffffff;
143
+ }
144
+ if (Object.keys(optionsToZip64).length) {
145
+ zip64header = createZip64Info(optionsToZip64);
146
+ optionsToUse.extraLength = zip64header.byteLength;
147
+ }
148
+ const header = new DataView(new ArrayBuffer(Number(CD_FILE_NAME_OFFSET)));
149
+ for (const field of ZIP_HEADER_FIELDS) {
150
+ setFieldToNumber(header, field.size, field.offset, optionsToUse[field.name ?? ''] ?? field.default ?? 0);
151
+ }
152
+ const encodedName = new TextEncoder().encode(optionsToUse.fileName);
153
+ const resHeader = concatenateArrayBuffers(header.buffer, encodedName, zip64header);
154
+ return resHeader;
128
155
  }
129
- const ZIP_HEADER_FIELDS = [{
130
- offset: 0,
131
- size: 4,
132
- default: new DataView(signature.buffer).getUint32(0, true)
133
- }, {
134
- offset: 4,
135
- size: 2,
136
- default: 45
137
- }, {
138
- offset: 6,
139
- size: 2,
140
- default: 45
141
- }, {
142
- offset: 8,
143
- size: 2,
144
- default: 0
145
- }, {
146
- offset: 10,
147
- size: 2,
148
- default: 0
149
- }, {
150
- offset: 12,
151
- size: 2,
152
- default: 0
153
- }, {
154
- offset: 14,
155
- size: 2,
156
- default: 0
157
- }, {
158
- offset: 16,
159
- size: 4,
160
- name: 'crc32'
161
- }, {
162
- offset: 20,
163
- size: 4,
164
- name: 'length'
165
- }, {
166
- offset: 24,
167
- size: 4,
168
- name: 'length'
169
- }, {
170
- offset: 28,
171
- size: 2,
172
- name: 'fnlength'
173
- }, {
174
- offset: 30,
175
- size: 2,
176
- default: 0,
177
- name: 'extraLength'
178
- }, {
179
- offset: 32,
180
- size: 2,
181
- default: 0
182
- }, {
183
- offset: 34,
184
- size: 2,
185
- default: 0
186
- }, {
187
- offset: 36,
188
- size: 2,
189
- default: 0
190
- }, {
191
- offset: 38,
192
- size: 4,
193
- default: 0
194
- }, {
195
- offset: 42,
196
- size: 4,
197
- name: 'offset'
198
- }];
199
- //# sourceMappingURL=cd-file-header.js.map
156
+ /** Fields map */
157
+ const ZIP_HEADER_FIELDS = [
158
+ // Central directory file header signature = 0x02014b50
159
+ {
160
+ offset: 0,
161
+ size: 4,
162
+ default: new DataView(signature.buffer).getUint32(0, true)
163
+ },
164
+ // Version made by
165
+ {
166
+ offset: 4,
167
+ size: 2,
168
+ default: 45
169
+ },
170
+ // Version needed to extract (minimum)
171
+ {
172
+ offset: 6,
173
+ size: 2,
174
+ default: 45
175
+ },
176
+ // General purpose bit flag
177
+ {
178
+ offset: 8,
179
+ size: 2,
180
+ default: 0
181
+ },
182
+ // Compression method
183
+ {
184
+ offset: 10,
185
+ size: 2,
186
+ default: 0
187
+ },
188
+ // File last modification time
189
+ {
190
+ offset: 12,
191
+ size: 2,
192
+ default: 0
193
+ },
194
+ // File last modification date
195
+ {
196
+ offset: 14,
197
+ size: 2,
198
+ default: 0
199
+ },
200
+ // CRC-32 of uncompressed data
201
+ {
202
+ offset: 16,
203
+ size: 4,
204
+ name: 'crc32'
205
+ },
206
+ // Compressed size (or 0xffffffff for ZIP64)
207
+ {
208
+ offset: 20,
209
+ size: 4,
210
+ name: 'length'
211
+ },
212
+ // Uncompressed size (or 0xffffffff for ZIP64)
213
+ {
214
+ offset: 24,
215
+ size: 4,
216
+ name: 'length'
217
+ },
218
+ // File name length (n)
219
+ {
220
+ offset: 28,
221
+ size: 2,
222
+ name: 'fnlength'
223
+ },
224
+ // Extra field length (m)
225
+ {
226
+ offset: 30,
227
+ size: 2,
228
+ default: 0,
229
+ name: 'extraLength'
230
+ },
231
+ // File comment length (k)
232
+ {
233
+ offset: 32,
234
+ size: 2,
235
+ default: 0
236
+ },
237
+ // Disk number where file starts (or 0xffff for ZIP64)
238
+ {
239
+ offset: 34,
240
+ size: 2,
241
+ default: 0
242
+ },
243
+ // Internal file attributes
244
+ {
245
+ offset: 36,
246
+ size: 2,
247
+ default: 0
248
+ },
249
+ // External file attributes
250
+ {
251
+ offset: 38,
252
+ size: 4,
253
+ default: 0
254
+ },
255
+ // Relative offset of local file header
256
+ {
257
+ offset: 42,
258
+ size: 4,
259
+ name: 'offset'
260
+ }
261
+ ];