assafvayner HF Staff commited on
Commit
c9619ed
·
1 Parent(s): 7a92a7e
src/lib/components/FileUpload.svelte CHANGED
@@ -1,12 +1,8 @@
1
  <script lang="ts">
2
- import { createEventDispatcher } from "svelte";
3
 
4
- const dispatch = createEventDispatcher<{
5
- fileSelected: { file: File; type: "xorb" | "shard" };
6
- }>();
7
-
8
- let xorbDragActive = false;
9
- let shardDragActive = false;
10
  let xorbFileInput: HTMLInputElement;
11
  let shardFileInput: HTMLInputElement;
12
 
@@ -49,7 +45,7 @@
49
  }
50
 
51
  function handleFile(file: File, type: "xorb" | "shard") {
52
- dispatch("fileSelected", { file, type });
53
  }
54
 
55
  function openFileDialog(type: "xorb" | "shard") {
@@ -69,11 +65,11 @@
69
  <div
70
  class="upload-area xorb-area"
71
  class:drag-active={xorbDragActive}
72
- on:dragover={(e) => handleDragOver(e, "xorb")}
73
- on:dragleave={() => handleDragLeave("xorb")}
74
- on:drop={(e) => handleDrop(e, "xorb")}
75
- on:click={() => openFileDialog("xorb")}
76
- on:keydown={(e) => e.key === "Enter" && openFileDialog("xorb")}
77
  role="button"
78
  tabindex="0"
79
  >
@@ -92,11 +88,11 @@
92
  <div
93
  class="upload-area shard-area"
94
  class:drag-active={shardDragActive}
95
- on:dragover={(e) => handleDragOver(e, "shard")}
96
- on:dragleave={() => handleDragLeave("shard")}
97
- on:drop={(e) => handleDrop(e, "shard")}
98
- on:click={() => openFileDialog("shard")}
99
- on:keydown={(e) => e.key === "Enter" && openFileDialog("shard")}
100
  role="button"
101
  tabindex="0"
102
  >
@@ -113,14 +109,14 @@
113
  <input
114
  bind:this={xorbFileInput}
115
  type="file"
116
- on:change={(e) => handleFileInput(e, "xorb")}
117
  style="display: none;"
118
  />
119
 
120
  <input
121
  bind:this={shardFileInput}
122
  type="file"
123
- on:change={(e) => handleFileInput(e, "shard")}
124
  style="display: none;"
125
  />
126
  </div>
 
1
  <script lang="ts">
2
+ const { fileSelected } = $props();
3
 
4
+ let xorbDragActive = $state(false);
5
+ let shardDragActive = $state(false);
 
 
 
 
6
  let xorbFileInput: HTMLInputElement;
7
  let shardFileInput: HTMLInputElement;
8
 
 
45
  }
46
 
47
  function handleFile(file: File, type: "xorb" | "shard") {
48
+ fileSelected({ file, type });
49
  }
50
 
51
  function openFileDialog(type: "xorb" | "shard") {
 
65
  <div
66
  class="upload-area xorb-area"
67
  class:drag-active={xorbDragActive}
68
+ ondragover={(e) => handleDragOver(e, "xorb")}
69
+ ondragleave={() => handleDragLeave("xorb")}
70
+ ondrop={(e) => handleDrop(e, "xorb")}
71
+ onclick={() => openFileDialog("xorb")}
72
+ onkeydown={(e) => e.key === "Enter" && openFileDialog("xorb")}
73
  role="button"
74
  tabindex="0"
75
  >
 
88
  <div
89
  class="upload-area shard-area"
90
  class:drag-active={shardDragActive}
91
+ ondragover={(e) => handleDragOver(e, "shard")}
92
+ ondragleave={() => handleDragLeave("shard")}
93
+ ondrop={(e) => handleDrop(e, "shard")}
94
+ onclick={() => openFileDialog("shard")}
95
+ onkeydown={(e) => e.key === "Enter" && openFileDialog("shard")}
96
  role="button"
97
  tabindex="0"
98
  >
 
109
  <input
110
  bind:this={xorbFileInput}
111
  type="file"
112
+ onchange={(e) => handleFileInput(e, "xorb")}
113
  style="display: none;"
114
  />
115
 
116
  <input
117
  bind:this={shardFileInput}
118
  type="file"
119
+ onchange={(e) => handleFileInput(e, "shard")}
120
  style="display: none;"
121
  />
122
  </div>
src/lib/components/ShardViewer.svelte CHANGED
@@ -143,10 +143,10 @@
143
  <tr
144
  class="file-row"
145
  class:expanded={expandedFiles.has(fileIndex)}
146
- on:click={() => toggleFileExpansion(fileIndex)}
147
  role="button"
148
  tabindex="0"
149
- on:keydown={(e) =>
150
  e.key === "Enter" && toggleFileExpansion(fileIndex)}
151
  >
152
  <td class="hash" title={formatHash(fileInfo.header.file_hash)}>
 
143
  <tr
144
  class="file-row"
145
  class:expanded={expandedFiles.has(fileIndex)}
146
+ onclick={() => toggleFileExpansion(fileIndex)}
147
  role="button"
148
  tabindex="0"
149
+ onkeydown={(e) =>
150
  e.key === "Enter" && toggleFileExpansion(fileIndex)}
151
  >
152
  <td class="hash" title={formatHash(fileInfo.header.file_hash)}>
src/lib/parsers.ts CHANGED
@@ -6,7 +6,6 @@ import type {
6
  ChunkHeader,
7
  ShardData,
8
  MerkleHash,
9
- HMACKey,
10
  MDBShardFileHeader,
11
  MDBShardFileFooter,
12
  FileDataSequenceHeader,
@@ -70,11 +69,11 @@ export class BinaryReader {
70
  }
71
 
72
  readHash(): MerkleHash {
73
- return { data: this.readBytes(32) };
74
- }
75
-
76
- readHMACKey(): HMACKey {
77
- return { data: this.readBytes(32) };
78
  }
79
 
80
  readString(length: number): string {
@@ -108,25 +107,23 @@ function arraysEqual(a: Uint8Array, b: Uint8Array): boolean {
108
  }
109
 
110
  function isBookendHash(hash: MerkleHash): boolean {
111
- // Bookend hash is all 0xFF bytes
112
- return hash.data.every((byte) => byte === 0xff);
113
- }
114
-
115
- function reorderHash(hash: MerkleHash): Array<number> {
116
- const data = new Array(32);
117
- let start = 0;
118
- for (let i = 0; i < 32; i++) {
119
- if (i % 8 === 0) {
120
- start += 8;
121
- }
122
- data.push(hash.data[start - (i % 8) - 1]);
123
- }
124
- return data;
125
  }
126
 
127
- function formatHash(hash: MerkleHash): string {
128
- return reorderHash(hash)
129
- .map((b) => b.toString(16).padStart(2, "0"))
 
 
 
 
 
 
 
 
 
 
130
  .join("");
131
  }
132
 
@@ -214,7 +211,7 @@ function parseShardFile(data: Uint8Array): ShardData {
214
  // Skip first buffer (48 bytes)
215
  reader.readBytes(48);
216
 
217
- const chunk_hash_hmac_key = reader.readHMACKey();
218
  const shard_creation_timestamp = Number(reader.readUint64LE());
219
  const shard_key_expiry = Number(reader.readUint64LE());
220
 
@@ -408,5 +405,3 @@ export function formatHashShort(hash: MerkleHash): string {
408
  const fullHash = formatHash(hash);
409
  return fullHash.substring(0, 16) + "...";
410
  }
411
-
412
- export { formatHash };
 
6
  ChunkHeader,
7
  ShardData,
8
  MerkleHash,
 
9
  MDBShardFileHeader,
10
  MDBShardFileFooter,
11
  FileDataSequenceHeader,
 
69
  }
70
 
71
  readHash(): MerkleHash {
72
+ const u64_0 = this.readUint64LE();
73
+ const u64_1 = this.readUint64LE();
74
+ const u64_2 = this.readUint64LE();
75
+ const u64_3 = this.readUint64LE();
76
+ return { data: [u64_0, u64_1, u64_2, u64_3] };
77
  }
78
 
79
  readString(length: number): string {
 
107
  }
108
 
109
  function isBookendHash(hash: MerkleHash): boolean {
110
+ // Bookend hash is all 0xFF bytes (all 64-bit values should be 0xFFFFFFFFFFFFFFFF)
111
+ return hash.data.every((value) => value === 0xffffffffffffffffn);
 
 
 
 
 
 
 
 
 
 
 
 
112
  }
113
 
114
+ export function formatHash(hash: MerkleHash): string {
115
+ // Convert each 64-bit integer to little-endian byte representation
116
+ return hash.data
117
+ .map((value) => {
118
+ // Convert bigint to 8 bytes in little-endian order
119
+ const bytes = [];
120
+ let temp = value;
121
+ for (let i = 0; i < 8; i++) {
122
+ bytes.push(Number(temp & 0xffn));
123
+ temp = temp >> 8n;
124
+ }
125
+ return bytes.map((b) => b.toString(16).padStart(2, "0")).join("");
126
+ })
127
  .join("");
128
  }
129
 
 
211
  // Skip first buffer (48 bytes)
212
  reader.readBytes(48);
213
 
214
+ const chunk_hash_hmac_key = reader.readHash();
215
  const shard_creation_timestamp = Number(reader.readUint64LE());
216
  const shard_key_expiry = Number(reader.readUint64LE());
217
 
 
405
  const fullHash = formatHash(hash);
406
  return fullHash.substring(0, 16) + "...";
407
  }
 
 
src/lib/types.ts CHANGED
@@ -1,11 +1,7 @@
1
  // Type definitions for xorb and shard file formats
2
 
3
  export interface MerkleHash {
4
- data: Uint8Array; // 32 bytes
5
- }
6
-
7
- export interface HMACKey {
8
- data: Uint8Array; // 32 bytes
9
  }
10
 
11
  // === XORB Types ===
@@ -34,7 +30,7 @@ export interface MDBShardFileFooter {
34
  version: number;
35
  file_info_offset: number;
36
  cas_info_offset: number;
37
- chunk_hash_hmac_key: HMACKey;
38
  shard_creation_timestamp: number;
39
  shard_key_expiry: number;
40
  footer_offset: number;
 
1
  // Type definitions for xorb and shard file formats
2
 
3
  export interface MerkleHash {
4
+ data: [bigint, bigint, bigint, bigint]; // 4 x 64-bit unsigned integers (32 bytes total)
 
 
 
 
5
  }
6
 
7
  // === XORB Types ===
 
30
  version: number;
31
  file_info_offset: number;
32
  cas_info_offset: number;
33
+ chunk_hash_hmac_key: MerkleHash;
34
  shard_creation_timestamp: number;
35
  shard_key_expiry: number;
36
  footer_offset: number;
src/routes/+page.svelte CHANGED
@@ -9,21 +9,21 @@
9
  let parsedData: ParsedFileMetadata | null = null;
10
  let loading = false;
11
 
12
- async function handleFileSelected(
13
- event: CustomEvent<{ file: File; type: "xorb" | "shard" }>
14
- ) {
 
15
  loading = true;
16
  parsedData = null;
17
 
18
  try {
19
- const { file, type } = event.detail;
20
- const result = await parseFile(file, type);
21
- parsedData = result;
22
  } catch (error) {
23
  parsedData = {
24
- type: event.detail.type,
25
- filename: event.detail.file.name,
26
- fileSize: event.detail.file.size,
27
  data: {} as any,
28
  error:
29
  error instanceof Error ? error.message : "Unknown error occurred",
@@ -56,7 +56,7 @@
56
  structure
57
  </p>
58
  {#if parsedData}
59
- <button class="reset-btn" on:click={resetView}>
60
  ← Upload New File
61
  </button>
62
  {/if}
@@ -84,7 +84,7 @@
84
  />
85
  {/if}
86
  {:else}
87
- <FileUpload on:fileSelected={handleFileSelected} />
88
 
89
  <div class="info-section">
90
  <h2>Supported File Types</h2>
 
9
  let parsedData: ParsedFileMetadata | null = null;
10
  let loading = false;
11
 
12
+ async function handleFileSelected(event: {
13
+ file: File;
14
+ type: "xorb" | "shard";
15
+ }) {
16
  loading = true;
17
  parsedData = null;
18
 
19
  try {
20
+ const { file, type } = event;
21
+ parsedData = await parseFile(file, type);
 
22
  } catch (error) {
23
  parsedData = {
24
+ type: event.type,
25
+ filename: event.file.name,
26
+ fileSize: event.file.size,
27
  data: {} as any,
28
  error:
29
  error instanceof Error ? error.message : "Unknown error occurred",
 
56
  structure
57
  </p>
58
  {#if parsedData}
59
+ <button class="reset-btn" onclick={resetView}>
60
  ← Upload New File
61
  </button>
62
  {/if}
 
84
  />
85
  {/if}
86
  {:else}
87
+ <FileUpload fileSelected={handleFileSelected} />
88
 
89
  <div class="info-section">
90
  <h2>Supported File Types</h2>