Spaces:
Sleeping
Sleeping
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>
|