Skip to content

Commit 7736870

Browse files
committed
feat: split VirtualTar to VirtualTarParser and VirtualTarGenerator
1 parent b5a9174 commit 7736870

16 files changed

+1033
-950
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@matrixai/js-virtualtar",
3-
"version": "0.0.1",
3+
"version": "1.16.3",
44
"author": "Matrix AI",
55
"contributors": [
66
{

src/Generator.ts

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { FileType, FileStat } from './types';
2-
import { GeneratorState, HeaderSize } from './types';
2+
import { GeneratorState } from './types';
33
import * as constants from './constants';
44
import * as errors from './errors';
55
import * as utils from './utils';
@@ -106,18 +106,21 @@ class Generator {
106106
);
107107
}
108108

109-
if (stat?.uname != null && stat?.uname.length > HeaderSize.OWNER_USERNAME) {
109+
if (
110+
stat?.uname != null &&
111+
stat?.uname.length > constants.HEADER_SIZE.OWNER_USERNAME
112+
) {
110113
throw new errors.ErrorVirtualTarGeneratorInvalidStat(
111-
`The username must not exceed ${HeaderSize.OWNER_USERNAME} bytes`,
114+
`The username must not exceed ${constants.HEADER_SIZE.OWNER_USERNAME} bytes`,
112115
);
113116
}
114117

115118
if (
116119
stat?.gname != null &&
117-
stat?.gname.length > HeaderSize.OWNER_GROUPNAME
120+
stat?.gname.length > constants.HEADER_SIZE.OWNER_GROUPNAME
118121
) {
119122
throw new errors.ErrorVirtualTarGeneratorInvalidStat(
120-
`The groupname must not exceed ${HeaderSize.OWNER_GROUPNAME} bytes`,
123+
`The groupname must not exceed ${constants.HEADER_SIZE.OWNER_GROUPNAME} bytes`,
121124
);
122125
}
123126

@@ -129,16 +132,16 @@ class Generator {
129132
}
130133

131134
// Write the relevant sections in the header with the provided data
132-
utils.writeUstarMagic(header);
133-
utils.writeFileType(header, type);
134135
utils.writeFilePath(header, filePath);
135136
utils.writeFileMode(header, stat.mode);
136137
utils.writeOwnerUid(header, stat.uid);
137138
utils.writeOwnerGid(header, stat.gid);
138-
utils.writeOwnerUserName(header, stat.uname);
139-
utils.writeOwnerGroupName(header, stat.gname);
140139
utils.writeFileSize(header, stat.size);
141140
utils.writeFileMtime(header, stat.mtime);
141+
utils.writeFileType(header, type);
142+
utils.writeUstarMagic(header);
143+
utils.writeOwnerUserName(header, stat.uname);
144+
utils.writeOwnerGroupName(header, stat.gname);
142145

143146
// The checksum can only be calculated once the entire header has been
144147
// written. This is why the checksum is calculated and written at the end.
@@ -307,18 +310,17 @@ class Generator {
307310
switch (this.state) {
308311
case GeneratorState.HEADER:
309312
this.state = GeneratorState.NULL;
310-
break;
313+
return new Uint8Array(constants.BLOCK_SIZE);
311314
case GeneratorState.NULL:
312315
this.state = GeneratorState.ENDED;
313-
break;
316+
return new Uint8Array(constants.BLOCK_SIZE);
314317
default:
315318
throw new errors.ErrorVirtualTarGeneratorInvalidState(
316319
`Expected state ${GeneratorState[GeneratorState.HEADER]} or ${
317320
GeneratorState[GeneratorState.NULL]
318321
} but got ${GeneratorState[this.state]}`,
319322
);
320323
}
321-
return new Uint8Array(constants.BLOCK_SIZE);
322324
}
323325
}
324326

src/Parser.ts

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -84,41 +84,41 @@ class Parser {
8484
protected state: ParserState = ParserState.HEADER;
8585
protected remainingBytes = 0;
8686

87-
protected parseHeader(array: Uint8Array): TokenHeader {
87+
protected parseHeader(header: Uint8Array): TokenHeader {
8888
// Validate header by checking checksum and magic string
89-
const headerChecksum = utils.decodeChecksum(array);
90-
const calculatedChecksum = utils.calculateChecksum(array);
89+
const headerChecksum = utils.decodeChecksum(header);
90+
const calculatedChecksum = utils.calculateChecksum(header);
9191

9292
if (headerChecksum !== calculatedChecksum) {
9393
throw new errors.ErrorVirtualTarParserInvalidHeader(
9494
`Expected checksum to be ${calculatedChecksum} but received ${headerChecksum}`,
9595
);
9696
}
9797

98-
const ustarMagic = utils.decodeUstarMagic(array);
98+
const ustarMagic = utils.decodeUstarMagic(header);
9999
if (ustarMagic !== constants.USTAR_NAME) {
100100
throw new errors.ErrorVirtualTarParserInvalidHeader(
101101
`Expected ustar magic to be '${constants.USTAR_NAME}', got '${ustarMagic}'`,
102102
);
103103
}
104104

105-
const ustarVersion = utils.decodeUstarVersion(array);
105+
const ustarVersion = utils.decodeUstarVersion(header);
106106
if (ustarVersion !== constants.USTAR_VERSION) {
107107
throw new errors.ErrorVirtualTarParserInvalidHeader(
108108
`Expected ustar version to be '${constants.USTAR_VERSION}', got '${ustarVersion}'`,
109109
);
110110
}
111111

112112
// Extract the relevant metadata from the header
113-
const filePath = utils.decodeFilePath(array);
114-
const fileSize = utils.decodeFileSize(array);
115-
const fileMtime = utils.decodeFileMtime(array);
116-
const fileMode = utils.decodeFileMode(array);
117-
const ownerUid = utils.decodeOwnerUid(array);
118-
const ownerGid = utils.decodeOwnerGid(array);
119-
const ownerUserName = utils.decodeOwnerUserName(array);
120-
const ownerGroupName = utils.decodeOwnerGroupName(array);
121-
const fileType = utils.decodeFileType(array);
113+
const filePath = utils.decodeFilePath(header);
114+
const fileMode = utils.decodeFileMode(header);
115+
const ownerUid = utils.decodeOwnerUid(header);
116+
const ownerGid = utils.decodeOwnerGid(header);
117+
const fileSize = utils.decodeFileSize(header);
118+
const fileMtime = utils.decodeFileMtime(header);
119+
const fileType = utils.decodeFileType(header);
120+
const ownerUserName = utils.decodeOwnerUserName(header);
121+
const ownerGroupName = utils.decodeOwnerGroupName(header);
122122

123123
return {
124124
type: 'header',
@@ -212,6 +212,12 @@ class Parser {
212212
this.state = ParserState.DATA;
213213
this.remainingBytes = headerToken.fileSize;
214214
}
215+
216+
// Only the file header and the extended header can potentially have
217+
// additional data blocks following them. This needs to be tracked in
218+
// the parser state. Directory headers don't have this issue and doesn't
219+
// need any additional processing.
220+
215221
return headerToken;
216222
}
217223

@@ -225,7 +231,7 @@ class Parser {
225231
case ParserState.NULL: {
226232
if (utils.isNullBlock(data)) {
227233
this.state = ParserState.ENDED;
228-
return { type: 'end' } as TokenEnd;
234+
return { type: 'end' };
229235
} else {
230236
throw new errors.ErrorVirtualTarParserEndOfArchive(
231237
'Received garbage data after first end marker',
@@ -234,7 +240,7 @@ class Parser {
234240
}
235241

236242
default:
237-
utils.never('Unexpected state');
243+
utils.never(`Unexpected state: ${this.state}`);
238244
}
239245
}
240246
}

0 commit comments

Comments
 (0)