Spaces:
Running
Running
coyotte508
commited on
Commit
•
b5430db
1
Parent(s):
e5bb1dc
✨ Check zip format
Browse files- dduf-content/tokenizer_2/{spiece.model → spiece.gguf} +0 -0
- file-64.dduf +0 -0
- file.dduf +0 -0
- package.json +2 -1
- src/lib/check-dduf.ts +99 -2
- src/lib/check-filename.ts +16 -0
- src/routes/+page.svelte +13 -3
dduf-content/tokenizer_2/{spiece.model → spiece.gguf}
RENAMED
File without changes
|
file-64.dduf
CHANGED
Binary files a/file-64.dduf and b/file-64.dduf differ
|
|
file.dduf
CHANGED
Binary files a/file.dduf and b/file.dduf differ
|
|
package.json
CHANGED
@@ -9,7 +9,8 @@
|
|
9 |
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
10 |
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
11 |
"format": "prettier --write .",
|
12 |
-
"lint": "prettier --check . && eslint ."
|
|
|
13 |
},
|
14 |
"devDependencies": {
|
15 |
"@eslint/compat": "^1.2.3",
|
|
|
9 |
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
10 |
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
11 |
"format": "prettier --write .",
|
12 |
+
"lint": "prettier --check . && eslint .",
|
13 |
+
"gen": "cd dduf-content && zip -r -0 ../file.dduf . && zip -r -0 -fz ../file-64.dduf ."
|
14 |
},
|
15 |
"devDependencies": {
|
16 |
"@eslint/compat": "^1.2.3",
|
src/lib/check-dduf.ts
CHANGED
@@ -1,3 +1,4 @@
|
|
|
|
1 |
import { WebBlob } from './WebBlob';
|
2 |
|
3 |
export async function* checkDduf(url: string): AsyncGenerator<string> {
|
@@ -7,9 +8,105 @@ export async function* checkDduf(url: string): AsyncGenerator<string> {
|
|
7 |
|
8 |
// DDUF is a zip file, uncompressed.
|
9 |
|
10 |
-
const
|
11 |
|
12 |
-
const view = new DataView(
|
13 |
|
14 |
let index = view.byteLength - 22;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
}
|
|
|
1 |
+
import { checkFilename } from './check-filename';
|
2 |
import { WebBlob } from './WebBlob';
|
3 |
|
4 |
export async function* checkDduf(url: string): AsyncGenerator<string> {
|
|
|
8 |
|
9 |
// DDUF is a zip file, uncompressed.
|
10 |
|
11 |
+
const last100kB = await blob.slice(blob.size - 100000, blob.size).arrayBuffer();
|
12 |
|
13 |
+
const view = new DataView(last100kB);
|
14 |
|
15 |
let index = view.byteLength - 22;
|
16 |
+
let found = false;
|
17 |
+
|
18 |
+
while (index >= 0) {
|
19 |
+
if (view.getUint32(index, true) === 0x06054b50) {
|
20 |
+
found = true;
|
21 |
+
break;
|
22 |
+
}
|
23 |
+
|
24 |
+
index--;
|
25 |
+
}
|
26 |
+
|
27 |
+
if (!found) {
|
28 |
+
throw new Error('DDUF footer not found in last 100kB of file');
|
29 |
+
}
|
30 |
+
|
31 |
+
yield 'DDUF footer found at offset ' + (blob.size - last100kB.byteLength + index);
|
32 |
+
|
33 |
+
const diskNumber = view.getUint16(index + 4, true);
|
34 |
+
|
35 |
+
if (diskNumber === 0xffff) {
|
36 |
+
throw new Error('Spanned archives (ZIP64) not yet supported');
|
37 |
+
}
|
38 |
+
|
39 |
+
if (diskNumber !== 0) {
|
40 |
+
throw new Error('Multi-disk archives not supported');
|
41 |
+
}
|
42 |
+
|
43 |
+
const fileCount = view.getUint16(index + 10, true);
|
44 |
+
const centralDirSize = view.getUint32(index + 12, true);
|
45 |
+
const centralDirOffset = view.getUint32(index + 16, true);
|
46 |
+
|
47 |
+
yield 'File count: ' + fileCount;
|
48 |
+
yield 'Central directory size: ' + centralDirSize;
|
49 |
+
yield 'Central directory offset: ' + centralDirOffset;
|
50 |
+
|
51 |
+
const centralDir =
|
52 |
+
centralDirOffset > blob.size - last100kB.byteLength
|
53 |
+
? last100kB.slice(
|
54 |
+
centralDirOffset - (blob.size - last100kB.byteLength),
|
55 |
+
centralDirOffset - (blob.size - last100kB.byteLength) + centralDirSize
|
56 |
+
)
|
57 |
+
: await blob.slice(centralDirOffset, centralDirOffset + centralDirSize).arrayBuffer();
|
58 |
+
|
59 |
+
const centralDirView = new DataView(centralDir);
|
60 |
+
let offset = 0;
|
61 |
+
|
62 |
+
for (let i = 0; i < fileCount; i++) {
|
63 |
+
if (centralDirView.getUint32(offset + 0, true) !== 0x02014b50) {
|
64 |
+
throw new Error('Invalid central directory file header');
|
65 |
+
}
|
66 |
+
|
67 |
+
if (offset + 46 > centralDir.byteLength) {
|
68 |
+
throw new Error('Unexpected end of central directory');
|
69 |
+
}
|
70 |
+
|
71 |
+
const compressionMethod = centralDirView.getUint16(offset + 10, true);
|
72 |
+
|
73 |
+
if (compressionMethod !== 0) {
|
74 |
+
throw new Error('Unsupported compression method: ' + compressionMethod);
|
75 |
+
}
|
76 |
+
|
77 |
+
const size = centralDirView.getUint32(offset + 24, true);
|
78 |
+
const compressedSize = centralDirView.getUint32(offset + 20, true);
|
79 |
+
|
80 |
+
if (size !== compressedSize) {
|
81 |
+
throw new Error('Compressed size and size differ');
|
82 |
+
}
|
83 |
+
|
84 |
+
const filenameLength = centralDirView.getUint16(offset + 28, true);
|
85 |
+
const fileName = new TextDecoder().decode(
|
86 |
+
new Uint8Array(centralDir, offset + 46, filenameLength)
|
87 |
+
);
|
88 |
+
|
89 |
+
yield 'File ' + i;
|
90 |
+
yield 'File name: ' + fileName;
|
91 |
+
yield 'File size: ' + size;
|
92 |
+
|
93 |
+
checkFilename(fileName);
|
94 |
+
|
95 |
+
const fileDiskNumber = centralDirView.getUint16(34, true);
|
96 |
+
|
97 |
+
if (fileDiskNumber !== 0) {
|
98 |
+
throw new Error('Multi-disk archives not supported');
|
99 |
+
}
|
100 |
+
|
101 |
+
const extraFieldLength = centralDirView.getUint16(offset + 30, true);
|
102 |
+
const commentLength = centralDirView.getUint16(offset + 32, true);
|
103 |
+
|
104 |
+
const filePosition = centralDirView.getUint32(offset + 42, true);
|
105 |
+
|
106 |
+
yield 'File position in archive: ' + filePosition;
|
107 |
+
|
108 |
+
offset += 46 + filenameLength + extraFieldLength + commentLength;
|
109 |
+
}
|
110 |
+
|
111 |
+
yield 'All files checked';
|
112 |
}
|
src/lib/check-filename.ts
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export function checkFilename(filename: string) {
|
2 |
+
if (
|
3 |
+
!filename.endsWith('.safetensors') &&
|
4 |
+
!filename.endsWith('.json') &&
|
5 |
+
!filename.endsWith('.gguf') &&
|
6 |
+
!filename.endsWith('/')
|
7 |
+
) {
|
8 |
+
throw new Error('Files must have a .safetensors, .gguf or .json extension');
|
9 |
+
}
|
10 |
+
|
11 |
+
const split = filename.split('/');
|
12 |
+
|
13 |
+
if (split.length > 2) {
|
14 |
+
throw new Error('Files must be only one level deep, not more');
|
15 |
+
}
|
16 |
+
}
|
src/routes/+page.svelte
CHANGED
@@ -1,16 +1,22 @@
|
|
1 |
<script lang="ts">
|
2 |
-
import { goto } from '$app/navigation';
|
3 |
import { checkDduf } from '$lib/check-dduf';
|
4 |
|
5 |
let url = 'https://huggingface.co/spaces/coyotte508/dduf-check/resolve/main/file.dduf';
|
6 |
let output = '';
|
|
|
7 |
|
8 |
async function handleSubmit(event: Event) {
|
9 |
event.preventDefault();
|
10 |
output = 'Checking...';
|
|
|
11 |
|
12 |
-
|
13 |
-
|
|
|
|
|
|
|
|
|
|
|
14 |
}
|
15 |
}
|
16 |
</script>
|
@@ -35,5 +41,9 @@
|
|
35 |
<textarea class="w-full rounded-md border border-gray-300 p-2" rows="10" readonly
|
36 |
>{output}</textarea
|
37 |
>
|
|
|
|
|
|
|
|
|
38 |
</form>
|
39 |
</div>
|
|
|
1 |
<script lang="ts">
|
|
|
2 |
import { checkDduf } from '$lib/check-dduf';
|
3 |
|
4 |
let url = 'https://huggingface.co/spaces/coyotte508/dduf-check/resolve/main/file.dduf';
|
5 |
let output = '';
|
6 |
+
let error = '';
|
7 |
|
8 |
async function handleSubmit(event: Event) {
|
9 |
event.preventDefault();
|
10 |
output = 'Checking...';
|
11 |
+
error = '';
|
12 |
|
13 |
+
try {
|
14 |
+
for await (const str of checkDduf(url)) {
|
15 |
+
output += '\n' + str;
|
16 |
+
}
|
17 |
+
} catch (e) {
|
18 |
+
console.error(e);
|
19 |
+
error = (e as Error).message;
|
20 |
}
|
21 |
}
|
22 |
</script>
|
|
|
41 |
<textarea class="w-full rounded-md border border-gray-300 p-2" rows="10" readonly
|
42 |
>{output}</textarea
|
43 |
>
|
44 |
+
|
45 |
+
{#if error}
|
46 |
+
<p class="text-red-500">{error}</p>
|
47 |
+
{/if}
|
48 |
</form>
|
49 |
</div>
|