Skip to content

Commit 0f7dfb2

Browse files
author
Lars Kölpin-Freese
committed
feat: add offset
1 parent 6d8ca52 commit 0f7dfb2

File tree

7 files changed

+95
-39
lines changed

7 files changed

+95
-39
lines changed

src/index.spec.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,15 @@ import {
44
filetypemime,
55
filetypeextension
66
} from "./index";
7+
import * as fs from "fs";
78

89
describe("Tests the public API", () => {
10+
it("detects tar with offset", () => {
11+
const buffer = fs.readFileSync(require.resolve("./testfiles/a.tar"));
12+
const bytes = Array.prototype.slice.call(buffer, 0);
13+
expect(filetypeinfo(bytes)).toHaveLength(1);
14+
});
15+
916
it("filetypeinfo", () => {
1017
const bytes = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
1118
expect(filetypeinfo(bytes)).toHaveLength(1);

src/index.ts

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,51 @@
11
import patternTree from "./pattern-tree.snapshot";
2-
import { Leaf, Node } from "./model/tree";
2+
import { GuessedFile, Node, Tree } from "./model/tree";
3+
import { fromHex, toHex } from "./model/toHex";
34

4-
const hex = num => new Number(num).toString(16).toLowerCase();
5-
const toHex = num => `0x${hex(num).length === 1 ? "0" + hex(num) : hex(num)}`;
6-
7-
export const filetypeinfo = (bytes: number[]): Leaf[] => {
5+
export const filetypeinfo = (bytes: number[]): GuessedFile[] => {
86
let currentByteIndex = 0;
9-
let guessFile = [];
10-
let step: Node = patternTree;
7+
let tree: Tree = patternTree;
8+
let step = tree.noOffset;
9+
10+
for (const k of Object.keys(tree.offset)) {
11+
const offset = fromHex(k);
12+
const offsetExceedsFile = offset >= bytes.length;
13+
if (offsetExceedsFile) {
14+
continue;
15+
}
16+
const node = patternTree.offset[k];
17+
const guessed = walkTree(offset, bytes, node);
18+
if (guessed.length > 0) {
19+
return guessed;
20+
}
21+
}
22+
return walkTree(0, bytes, tree.noOffset);
23+
};
24+
25+
const walkTree = (
26+
index: number,
27+
bytes: number[],
28+
node: Node
29+
): GuessedFile[] => {
30+
let step: Node = node;
31+
let guessFile: GuessedFile[] = [];
1132
while (true) {
12-
const currentByte = toHex(bytes[currentByteIndex]);
33+
const currentByte = toHex(bytes[index]);
1334
if (step.bytes["?"] && !step.bytes[currentByte]) {
1435
step = step.bytes["?"];
1536
} else {
1637
step = step.bytes[currentByte];
1738
}
18-
1939
if (!step) {
2040
return guessFile;
2141
}
2242
if (step && step.matches) {
2343
guessFile = step.matches;
2444
}
25-
currentByteIndex += 1;
45+
index += 1;
2646
}
27-
return [];
2847
};
48+
2949
export default filetypeinfo;
3050

3151
export const filetypename = (bytes: any[]): string[] =>

src/model/pattern-tree.ts

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
import { createComplexTree, createNode, Info, merge } from "./tree";
2-
import { Node } from "./tree";
1+
import { createComplexNode, createNode, Info, merge, Tree } from "./tree";
2+
import { toHex } from "./toHex";
33

44
// https://en.wikipedia.org/wiki/List_of_file_signatures
55
let fileType = new Map();
6-
let tree: Node | null = null;
6+
let tree: Tree = {
7+
noOffset: null,
8+
offset: {}
9+
};
710

811
type TypeName = string;
912
type Signature = string[];
@@ -16,22 +19,40 @@ const add = (
1619
) => {
1720
fileType.set(typename, signature);
1821
if (offset) {
19-
}
20-
if (tree === null) {
21-
tree = createComplexTree(
22-
typename,
23-
signature.map(e => e.toLowerCase()),
24-
additionalInfo
25-
);
22+
const existing = tree.offset[toHex(offset)];
23+
if (!existing) {
24+
tree.offset[toHex(offset)] = createComplexNode(
25+
typename,
26+
signature.map(e => e.toLowerCase()),
27+
additionalInfo
28+
);
29+
} else {
30+
tree.offset[toHex(offset)] = merge(
31+
createNode(
32+
typename,
33+
signature.map(e => e.toLowerCase()),
34+
additionalInfo
35+
),
36+
tree.offset[toHex(offset)]
37+
);
38+
}
2639
} else {
27-
tree = merge(
28-
createNode(
40+
if (tree.noOffset === null) {
41+
tree.noOffset = createComplexNode(
2942
typename,
3043
signature.map(e => e.toLowerCase()),
3144
additionalInfo
32-
),
33-
tree
34-
);
45+
);
46+
} else {
47+
tree.noOffset = merge(
48+
createNode(
49+
typename,
50+
signature.map(e => e.toLowerCase()),
51+
additionalInfo
52+
),
53+
tree.noOffset
54+
);
55+
}
3556
}
3657
};
3758

@@ -657,4 +678,4 @@ add("mpeg", ["0x00", "0x00", "0x01", "0xB3"], {
657678

658679
add("hl2demo", ["48", "4C", "32", "44", "45", "4D", "4F"]);
659680

660-
export default (): Node | null => tree as Node | null;
681+
export default (): Tree | null => tree as Tree;

src/model/toHex.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
const hex = (num: number) => new Number(num).toString(16).toLowerCase();
2+
export const toHex = (num: number): string =>
3+
`0x${hex(num).length === 1 ? "0" + hex(num) : hex(num)}`;
4+
export const fromHex = (hex: string) => new Number(hex) as number;

src/model/tree.spec.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
import { merge, createNode, createComplexTree } from "./tree";
1+
import { merge, createNode, createComplexNode } from "./tree";
22

33
describe("tree", () => {
44
it("Creates complex node", () => {
5-
const tree = createComplexTree("mpe", ["0x00", "0x01"]);
5+
const tree = createComplexNode("mpe", ["0x00", "0x01"]);
66
expect(tree.bytes["0x00"].bytes["0x01"]).toHaveProperty("matches");
77
expect(tree.bytes["0x00"].bytes["0x01"]["matches"][0].typename).toBe("mpe");
88
});
99

1010
it("Merges trees", () => {
11-
const tree = createComplexTree("pic", ["0x00"]);
11+
const tree = createComplexNode("pic", ["0x00"]);
1212
const dba = createNode("dba", ["0x00", "0x01", "0x02", "0x03"]);
1313
const merged = merge(dba, tree);
1414
expect(merged.bytes["0x00"].matches[0].typename).toBe("pic");
@@ -19,14 +19,14 @@ describe("tree", () => {
1919
});
2020

2121
it("Merges overlapping", () => {
22-
const tree = createComplexTree("pic", ["0x00"]);
22+
const tree = createComplexNode("pic", ["0x00"]);
2323
const dba = createNode("pif", ["0x00"]);
2424
const merged = merge(dba, tree);
2525
expect(merged.bytes["0x00"].matches).toHaveLength(2);
2626
});
2727

2828
it("Merges deep overlapping", () => {
29-
const gifA = createComplexTree(
29+
const gifA = createComplexNode(
3030
"gif",
3131
["0x47", "0x49", "0x46", "0x38", "0x37", "0x61"],
3232
{ mime: "image/gif", extension: "gif" }
@@ -43,7 +43,6 @@ describe("tree", () => {
4343
);
4444
const mergeA = merge(gifB, gifA);
4545
const mergeB = merge(gifC, mergeA);
46-
console.log(JSON.stringify(mergeB));
4746
expect(
4847
mergeB.bytes["0x47"].bytes["0x49"].bytes["0x46"].bytes["0x38"].bytes[
4948
"0x37"

src/model/tree.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export type NewNode = PathlessNewNode & {
77
bytes: string[];
88
};
99

10-
export type Leaf = Info & {
10+
export type GuessedFile = Info & {
1111
typename: string;
1212
};
1313

@@ -17,13 +17,18 @@ export type Info = {
1717
};
1818

1919
export type Node = {
20-
matches?: Leaf[];
20+
matches?: GuessedFile[];
2121
bytes: {
2222
[nextbyte: string]: Node;
2323
};
2424
};
2525

26-
const toLeaf = (leaf: PathlessNewNode): Leaf => ({
26+
export type Tree = {
27+
noOffset: Node | null;
28+
offset: { [offsetByte: string]: Node };
29+
};
30+
31+
const toLeaf = (leaf: PathlessNewNode): GuessedFile => ({
2732
typename: leaf.typename,
2833
mime: leaf.info.mime,
2934
extension: leaf.info.extension
@@ -66,7 +71,7 @@ export const merge = (node: NewNode, tree: Node): Node => {
6671
if (!tree.bytes[currentByte]) {
6772
tree.bytes[currentByte] = {
6873
...tree.bytes[currentByte],
69-
...createComplexTree(node.typename, path, node.info)
74+
...createComplexNode(node.typename, path, node.info)
7075
};
7176
}
7277
return tree;
@@ -80,7 +85,7 @@ export const createNode = (
8085
return { typename, bytes, info: info ? info : {} };
8186
};
8287

83-
export const createComplexTree = (
88+
export const createComplexNode = (
8489
typename: string,
8590
bytes: string[],
8691
info?: Info
@@ -102,6 +107,6 @@ export const createComplexTree = (
102107
bytes: {}
103108
};
104109
}
105-
obj.bytes[currentKey] = createComplexTree(typename, path, info);
110+
obj.bytes[currentKey] = createComplexNode(typename, path, info);
106111
return obj;
107112
};

src/testfiles/a.tar

2.5 KB
Binary file not shown.

0 commit comments

Comments
 (0)