Skip to content

Commit 76fe731

Browse files
committed
chore: switched out dataview to uint8arrays
1 parent 00233d1 commit 76fe731

File tree

8 files changed

+264
-114
lines changed

8 files changed

+264
-114
lines changed

src/Generator.ts

+5-18
Original file line numberDiff line numberDiff line change
@@ -4,36 +4,29 @@ import * as errors from './errors';
44
import * as utils from './utils';
55
import * as constants from './constants';
66

7-
// Computes the checksum by summing up all the bytes in the header
8-
function computeChecksum(header: Uint8Array): number {
9-
return header.reduce((sum, byte) => sum + byte, 0);
10-
}
11-
127
function generateHeader(
138
filePath: string,
149
type: EntryType,
1510
stat: FileStat,
1611
): Uint8Array {
1712
// TODO: implement long-file-name headers
1813
if (filePath.length < 1 || filePath.length > 255) {
19-
throw new errors.ErrorVirtualTarInvalidFileName(
14+
throw new errors.ErrorTarGeneratorInvalidFileName(
2015
'The file name must be longer than 1 character and shorter than 255 characters',
2116
);
2217
}
2318

2419
// As the size does not matter for directories, it can be undefined. However,
2520
// if the header is being generated for a file, then it needs to have a valid
26-
// size. This guard checks that.
21+
// size.
2722
if (stat.size == null && type === EntryType.FILE) {
28-
throw new errors.ErrorVirtualTarInvalidStat('Size must be set for files');
23+
throw new errors.ErrorTarGeneratorInvalidStat('Size must be set for files');
2924
}
3025
const size = type === EntryType.FILE ? stat.size : 0;
3126

3227
// The time can be undefined, which would be referring to epoch 0.
3328
const time = utils.dateToUnixTime(stat.mtime ?? new Date());
3429

35-
// Make sure to initialise the header with zeros to avoid writing nullish
36-
// blocks.
3730
const header = new Uint8Array(constants.BLOCK_SIZE);
3831

3932
// The TAR headers follow this structure
@@ -112,13 +105,7 @@ function generateHeader(
112105
);
113106

114107
// The checksum is calculated as the sum of all bytes in the header. It is
115-
// padded using ASCII spaces, as we currently don't have all the data yet.
116-
utils.writeBytesToArray(
117-
header,
118-
utils.pad('', HeaderSize.CHECKSUM, ' '),
119-
HeaderOffset.CHECKSUM,
120-
HeaderSize.CHECKSUM,
121-
);
108+
// left blank for later calculation.
122109

123110
// The type of file is written as a single byte in the header.
124111
utils.writeBytesToArray(
@@ -174,7 +161,7 @@ function generateHeader(
174161
);
175162

176163
// Updating with the new checksum
177-
const checksum = computeChecksum(header);
164+
const checksum = utils.calculateChecksum(header);
178165

179166
// Note the extra space in the padding for the checksum value. It is
180167
// intentionally placed there. The padding for checksum is ASCII spaces

src/Parser.ts

+58-24
Original file line numberDiff line numberDiff line change
@@ -5,50 +5,86 @@ import * as constants from './constants';
55
import * as errors from './errors';
66
import * as utils from './utils';
77

8-
function parseHeader(view: DataView): HeaderToken {
9-
// TODO: confirm integrity by checking against checksum
10-
const filePath = utils.parseFilePath(view);
8+
function parseHeader(array: Uint8Array): HeaderToken {
9+
// Validate header by checking checksum and magic string
10+
const headerChecksum = utils.extractOctal(
11+
array,
12+
HeaderOffset.CHECKSUM,
13+
HeaderSize.CHECKSUM,
14+
);
15+
const calculatedChecksum = utils.calculateChecksum(array);
16+
17+
if (headerChecksum !== calculatedChecksum) {
18+
throw new errors.ErrorTarParserInvalidHeader(
19+
`Expected checksum to be ${calculatedChecksum} but received ${headerChecksum}`,
20+
);
21+
}
22+
23+
const ustarMagic = utils.extractString(
24+
array,
25+
HeaderOffset.USTAR_NAME,
26+
HeaderSize.USTAR_NAME,
27+
);
28+
if (ustarMagic !== constants.USTAR_NAME) {
29+
throw new errors.ErrorTarParserInvalidHeader(
30+
`Expected ustar magic to be '${constants.USTAR_NAME}', got '${ustarMagic}'`,
31+
);
32+
}
33+
34+
const ustarVersion = utils.extractString(
35+
array,
36+
HeaderOffset.USTAR_VERSION,
37+
HeaderSize.USTAR_VERSION,
38+
);
39+
if (ustarVersion !== constants.USTAR_VERSION) {
40+
throw new errors.ErrorTarParserInvalidHeader(
41+
`Expected ustar version to be '${constants.USTAR_VERSION}', got '${ustarVersion}'`,
42+
);
43+
}
44+
45+
// Extract the relevant metadata from the header
46+
const filePath = utils.parseFilePath(array);
1147
const fileSize = utils.extractOctal(
12-
view,
48+
array,
1349
HeaderOffset.FILE_SIZE,
1450
HeaderSize.FILE_SIZE,
1551
);
1652
const fileMtime = new Date(
17-
utils.extractOctal(view, HeaderOffset.FILE_MTIME, HeaderSize.FILE_MTIME) *
53+
utils.extractOctal(array, HeaderOffset.FILE_MTIME, HeaderSize.FILE_MTIME) *
1854
1000,
1955
);
2056
const fileMode = utils.extractOctal(
21-
view,
57+
array,
2258
HeaderOffset.FILE_MODE,
2359
HeaderSize.FILE_MODE,
2460
);
2561
const ownerGid = utils.extractOctal(
26-
view,
62+
array,
2763
HeaderOffset.OWNER_GID,
2864
HeaderSize.OWNER_GID,
2965
);
3066
const ownerUid = utils.extractOctal(
31-
view,
67+
array,
3268
HeaderOffset.OWNER_UID,
3369
HeaderSize.OWNER_UID,
3470
);
3571
const ownerName = utils.extractString(
36-
view,
72+
array,
3773
HeaderOffset.OWNER_NAME,
3874
HeaderSize.OWNER_NAME,
3975
);
4076
const ownerGroupName = utils.extractString(
41-
view,
77+
array,
4278
HeaderOffset.OWNER_GROUPNAME,
4379
HeaderSize.OWNER_GROUPNAME,
4480
);
4581
const ownerUserName = utils.extractString(
46-
view,
82+
array,
4783
HeaderOffset.OWNER_USERNAME,
4884
HeaderSize.OWNER_USERNAME,
4985
);
5086
const fileType =
51-
utils.extractString(view, HeaderOffset.TYPE_FLAG, HeaderSize.TYPE_FLAG) ===
87+
utils.extractString(array, HeaderOffset.TYPE_FLAG, HeaderSize.TYPE_FLAG) ===
5288
EntryType.FILE
5389
? 'file'
5490
: 'directory';
@@ -68,11 +104,11 @@ function parseHeader(view: DataView): HeaderToken {
68104
};
69105
}
70106

71-
function parseData(view: DataView, remainingBytes: number): DataToken {
107+
function parseData(array: Uint8Array, remainingBytes: number): DataToken {
72108
if (remainingBytes > 512) {
73-
return { type: 'data', data: utils.extractBytes(view) };
109+
return { type: 'data', data: utils.extractBytes(array) };
74110
} else {
75-
const data = utils.extractBytes(view, 0, remainingBytes);
111+
const data = utils.extractBytes(array, 0, remainingBytes);
76112
return { type: 'data', data: data };
77113
}
78114
}
@@ -83,29 +119,27 @@ class Parser {
83119

84120
write(data: Uint8Array) {
85121
if (data.byteLength !== constants.BLOCK_SIZE) {
86-
throw new errors.ErrorVirtualTarBlockSize(
122+
throw new errors.ErrorTarParserBlockSize(
87123
`Expected block size to be ${constants.BLOCK_SIZE} bytes but received ${data.byteLength} bytes`,
88124
);
89125
}
90126

91-
const view = new DataView(data.buffer, 0, constants.BLOCK_SIZE);
92-
93127
switch (this.state) {
94128
case ParserState.ENDED: {
95-
throw new errors.ErrorVirtualTarEndOfArchive(
129+
throw new errors.ErrorTarParserEndOfArchive(
96130
'Archive has already ended',
97131
);
98132
}
99133

100134
case ParserState.READY: {
101135
// Check if we need to parse the end-of-archive marker
102-
if (utils.checkNullView(view)) {
136+
if (utils.isNullBlock(data)) {
103137
this.state = ParserState.NULL;
104138
return;
105139
}
106140

107141
// Set relevant state if the header corresponds to a file
108-
const headerToken = parseHeader(view);
142+
const headerToken = parseHeader(data);
109143
if (headerToken.fileType === 'file') {
110144
this.state = ParserState.DATA;
111145
this.remainingBytes = headerToken.fileSize;
@@ -114,18 +148,18 @@ class Parser {
114148
}
115149

116150
case ParserState.DATA: {
117-
const parsedData = parseData(view, this.remainingBytes);
151+
const parsedData = parseData(data, this.remainingBytes);
118152
this.remainingBytes -= 512;
119153
if (this.remainingBytes < 0) this.state = ParserState.READY;
120154
return parsedData;
121155
}
122156

123157
case ParserState.NULL: {
124-
if (utils.checkNullView(view)) {
158+
if (utils.isNullBlock(data)) {
125159
this.state = ParserState.ENDED;
126160
return { type: 'end' } as EndToken;
127161
} else {
128-
throw new errors.ErrorVirtualTarEndOfArchive(
162+
throw new errors.ErrorTarParserEndOfArchive(
129163
'Received garbage data after first end marker',
130164
);
131165
}

src/constants.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
export const BLOCK_SIZE = 512;
2-
export const USTAR_NAME = 'ustar\0';
2+
export const USTAR_NAME = 'ustar';
33
export const USTAR_VERSION = '00';

src/errors.ts

+25-15
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,49 @@
11
import { AbstractError } from '@matrixai/errors';
22

3-
class ErrorVirtualTar<T> extends AbstractError<T> {
3+
class ErrorTar<T> extends AbstractError<T> {
44
static description = 'VirtualTar errors';
55
}
66

7-
class ErrorVirtualTarUndefinedBehaviour<T> extends ErrorVirtualTar<T> {
7+
class ErrorVirtualTarUndefinedBehaviour<T> extends ErrorTar<T> {
88
static description = 'You should never see this error';
99
}
1010

11-
class ErrorVirtualTarInvalidFileName<T> extends ErrorVirtualTar<T> {
12-
static description = 'The provided file name is invalid';
11+
class ErrorTarGenerator<T> extends ErrorTar<T> {
12+
static description = 'VirtualTar genereator errors';
1313
}
1414

15-
class ErrorVirtualTarInvalidHeader<T> extends ErrorVirtualTar<T> {
16-
static description = 'The header has invalid data';
15+
class ErrorTarGeneratorInvalidFileName<T> extends ErrorTarGenerator<T> {
16+
static description = 'The provided file name is invalid';
1717
}
1818

19-
class ErrorVirtualTarInvalidStat<T> extends ErrorVirtualTar<T> {
19+
class ErrorTarGeneratorInvalidStat<T> extends ErrorTarGenerator<T> {
2020
static description = 'The stat contains invalid data';
2121
}
2222

23-
class ErrorVirtualTarBlockSize<T> extends ErrorVirtualTar<T> {
23+
class ErrorTarParser<T> extends ErrorTar<T> {
24+
static description = 'VirtualTar parsing errors';
25+
}
26+
27+
class ErrorTarParserInvalidHeader<T> extends ErrorTarParser<T> {
28+
static description = 'The checksum did not match the header';
29+
}
30+
31+
class ErrorTarParserBlockSize<T> extends ErrorTarParser<T> {
2432
static description = 'The block size is incorrect';
2533
}
2634

27-
class ErrorVirtualTarEndOfArchive<T> extends ErrorVirtualTar<T> {
35+
class ErrorTarParserEndOfArchive<T> extends ErrorTarParser<T> {
2836
static description = 'No data can come after an end-of-archive marker';
2937
}
3038

3139
export {
32-
ErrorVirtualTar,
40+
ErrorTar,
41+
ErrorTarGenerator,
3342
ErrorVirtualTarUndefinedBehaviour,
34-
ErrorVirtualTarInvalidFileName,
35-
ErrorVirtualTarInvalidHeader,
36-
ErrorVirtualTarInvalidStat,
37-
ErrorVirtualTarBlockSize,
38-
ErrorVirtualTarEndOfArchive,
43+
ErrorTarGeneratorInvalidFileName,
44+
ErrorTarGeneratorInvalidStat,
45+
ErrorTarParser,
46+
ErrorTarParserInvalidHeader,
47+
ErrorTarParserBlockSize,
48+
ErrorTarParserEndOfArchive,
3949
};

0 commit comments

Comments
 (0)