@@ -26,33 +26,99 @@ export class WASMLoader {
2626 /**
2727 * Load and instantiate the WASM module
2828 */
29- static async initialize ( ) : Promise < void > {
29+ static async initialize ( onProgress ?: ( percent : number ) => void ) : Promise < void > {
3030 if ( this . instance ) return ;
3131
3232 try {
33- // Try to load WASM binary
34- const wasmBuffer = await this . loadWASMBuffer ( ) ;
35-
36- // Compile the module
37- this . module = await WebAssembly . compile ( wasmBuffer ) ;
38-
39- // Instantiate with imports
40- this . instance = await WebAssembly . instantiate ( this . module , {
33+ const imports = {
4134 env : {
4235 // Add any required imports here
4336 abort : ( ) => { throw new Error ( 'WASM abort called' ) ; }
4437 }
45- } ) ;
38+ } ;
39+
40+ // Report initial progress
41+ onProgress ?.( 0 ) ;
42+
43+ // Try streaming compilation first (faster)
44+ if ( typeof WebAssembly . instantiateStreaming === 'function' && typeof fetch !== 'undefined' ) {
45+ try {
46+ const wasmUrl = await this . getWASMUrl ( ) ;
47+ onProgress ?.( 10 ) ; // Fetching
48+
49+ const response = await fetch ( wasmUrl ) ;
50+
51+ if ( response . ok ) {
52+ onProgress ?.( 50 ) ; // Compiling
53+ const result = await WebAssembly . instantiateStreaming ( response , imports ) ;
54+ this . module = result . module ;
55+ this . instance = result . instance ;
56+ this . exports = this . instance . exports as unknown as WASMExports ;
57+ this . updateMemoryView ( ) ;
58+ onProgress ?.( 100 ) ; // Complete
59+ return ;
60+ }
61+ } catch ( streamError ) {
62+ console . warn ( 'Streaming compilation failed, falling back to ArrayBuffer:' , streamError ) ;
63+ }
64+ }
65+
66+ // Fallback to ArrayBuffer compilation
67+ onProgress ?.( 20 ) ; // Loading buffer
68+ const wasmBuffer = await this . loadWASMBuffer ( ) ;
69+ onProgress ?.( 60 ) ; // Compiling
70+
71+ // Use compileStreaming if available and we have a Response
72+ if ( typeof Response !== 'undefined' && typeof WebAssembly . compileStreaming === 'function' ) {
73+ try {
74+ const response = new Response ( wasmBuffer , {
75+ headers : { 'Content-Type' : 'application/wasm' }
76+ } ) ;
77+ this . module = await WebAssembly . compileStreaming ( response ) ;
78+ } catch {
79+ // Fallback to regular compile
80+ this . module = await WebAssembly . compile ( wasmBuffer ) ;
81+ }
82+ } else {
83+ this . module = await WebAssembly . compile ( wasmBuffer ) ;
84+ }
85+
86+ onProgress ?.( 90 ) ; // Instantiating
87+
88+ // Instantiate with imports
89+ this . instance = await WebAssembly . instantiate ( this . module , imports ) ;
4690
4791 this . exports = this . instance . exports as unknown as WASMExports ;
4892 this . updateMemoryView ( ) ;
93+ onProgress ?.( 100 ) ; // Complete
4994
5095 } catch ( error ) {
5196 console . error ( 'Failed to initialize WASM:' , error ) ;
5297 throw new Error ( `WASM initialization failed: ${ error } ` ) ;
5398 }
5499 }
55100
101+ /**
102+ * Get WASM URL for streaming compilation
103+ */
104+ private static async getWASMUrl ( ) : Promise < string > {
105+ // In browser environment
106+ if ( typeof window !== 'undefined' && window . location ) {
107+ return new URL ( '/src/media/wasm/image-metadata.wasm' , window . location . href ) . href ;
108+ }
109+
110+ // In Node.js environment
111+ if ( typeof process !== 'undefined' && process . versions ?. node ) {
112+ const __filename = fileURLToPath ( import . meta. url ) ;
113+ const __dirname = dirname ( __filename ) ;
114+ const wasmPath = join ( __dirname , 'image-metadata.wasm' ) ;
115+ return `file://${ wasmPath } ` ;
116+ }
117+
118+ // Fallback
119+ return '/src/media/wasm/image-metadata.wasm' ;
120+ }
121+
56122 /**
57123 * Load WASM buffer - tries multiple methods
58124 */
@@ -131,29 +197,52 @@ export class WASMLoader {
131197 }
132198
133199 /**
134- * Copy data to WASM memory
200+ * Copy data to WASM memory with optimization for large images
135201 */
136202 static copyToWASM ( data : Uint8Array ) : number {
137203 if ( ! this . exports || ! this . memoryView ) {
138204 throw new Error ( 'WASM not initialized' ) ;
139205 }
140206
207+ // For very large images, consider sampling instead of processing full image
208+ const MAX_IMAGE_SIZE = 50 * 1024 * 1024 ; // 50MB limit
209+ let processData = data ;
210+
211+ if ( data . length > MAX_IMAGE_SIZE ) {
212+ console . warn ( `Image too large (${ data . length } bytes), will process only metadata` ) ;
213+ // For metadata extraction, we only need the header
214+ processData = data . slice ( 0 , 65536 ) ; // First 64KB should contain all metadata
215+ }
216+
141217 // Check if memory needs to grow
142- const requiredSize = data . length ;
218+ const requiredSize = processData . length + 4096 ; // Add buffer for alignment
143219 const currentSize = this . memoryView . length ;
144220
145221 if ( requiredSize > currentSize ) {
146222 // Grow memory (in pages of 64KB)
147223 const pagesNeeded = Math . ceil ( ( requiredSize - currentSize ) / 65536 ) ;
148- this . exports . memory . grow ( pagesNeeded ) ;
149- this . updateMemoryView ( ) ;
224+ try {
225+ this . exports . memory . grow ( pagesNeeded ) ;
226+ this . updateMemoryView ( ) ;
227+ } catch ( error ) {
228+ throw new Error ( `Failed to allocate memory: ${ error } . Required: ${ requiredSize } bytes` ) ;
229+ }
150230 }
151231
152232 // Allocate memory in WASM
153- const ptr = this . exports . malloc ( data . length ) ;
233+ const ptr = this . exports . malloc ( processData . length ) ;
234+
235+ if ( ptr === 0 ) {
236+ throw new Error ( 'Failed to allocate memory in WASM' ) ;
237+ }
154238
155239 // Copy data
156- this . memoryView ! . set ( data , ptr ) ;
240+ try {
241+ this . memoryView ! . set ( processData , ptr ) ;
242+ } catch ( error ) {
243+ this . exports . free ( ptr ) ;
244+ throw new Error ( `Failed to copy data to WASM memory: ${ error } ` ) ;
245+ }
157246
158247 return ptr ;
159248 }
0 commit comments