00
:
00
:
00
:
00
Corso SEO AI - Usa SEOEMAIL al checkout per il 30% di sconto

Memoria e Tipi

Memoria Lineare

La memoria di WebAssembly è un blocco contiguo di byte (linear memory) che può essere letto e scritto sia da Wasm che da JavaScript. Non esiste un heap strutturato o garbage collector nel modello base di Wasm.

WebAssembly.Memory

// Creare memoria: 1 pagina iniziale (64 KB), massimo 10 pagine (640 KB)
const memory = new WebAssembly.Memory({ initial: 1, maximum: 10 });

// Passarla come import al modulo
const importObject = {
  env: { memory },
};

// Accedere al buffer
const buffer = new Uint8Array(memory.buffer);
buffer[0] = 255;

In WAT, la memoria si dichiara internamente o si importa:

(module
  ;; Memoria interna: 1 pagina iniziale
  (memory 1)

  ;; Funzione che scrive il valore 42 all'offset 0
  (func (export "write42")
    i32.const 0      ;; offset
    i32.const 42     ;; valore
    i32.store)       ;; scrive 4 byte (i32) all'offset 0

  ;; Funzione che legge dall'offset 0
  (func (export "read") (result i32)
    i32.const 0
    i32.load)
)

Crescita della Memoria

La memoria può crescere a runtime, ma non può mai ridursi.

const memory = new WebAssembly.Memory({ initial: 1, maximum: 100 });
console.log(memory.buffer.byteLength); // 65536 (64 KB)

memory.grow(2); // Aggiunge 2 pagine (128 KB)
console.log(memory.buffer.byteLength); // 196608 (192 KB)

Attenzione: dopo memory.grow(), il vecchio ArrayBuffer viene detached. Devi riacquisire il riferimento con memory.buffer.

let view = new Uint8Array(memory.buffer);
memory.grow(1);
// view è ora INVALIDO
view = new Uint8Array(memory.buffer); // riacquisire

In WAT, si usa l’istruzione memory.grow:

(func (export "growBy") (param $pages i32) (result i32)
  local.get $pages
  memory.grow)   ;; Restituisce la dimensione precedente, o -1 se fallisce

SharedArrayBuffer e Threading

Per Wasm multi-thread, si usa SharedArrayBuffer:

const sharedMemory = new WebAssembly.Memory({
  initial: 1,
  maximum: 10,
  shared: true, // Richiede COOP/COEP headers nel server
});

// Il buffer è un SharedArrayBuffer
const view = new Int32Array(sharedMemory.buffer);

// Operazioni atomiche
Atomics.store(view, 0, 100);
Atomics.load(view, 0); // 100
Atomics.add(view, 0, 5); // atomically aggiunge 5

Tipi Numerici di Wasm

WebAssembly ha solo quattro tipi numerici:

Tipo Descrizione Dimensione
i32 Intero 32-bit 4 byte
i64 Intero 64-bit 8 byte
f32 Float IEEE 754 singola precisione 4 byte
f64 Float IEEE 754 doppia precisione 8 byte

Non esistono stringhe, booleani, array o oggetti nativi. Tutto deve essere codificato tramite questi tipi base e la memoria lineare.

(func (export "tipi") (param $a i32) (param $b i64) (param $c f32) (param $d f64)
  ;; i32: usato per interi, booleani, puntatori a memoria
  ;; i64: interi grandi (attenzione: JS usa BigInt per i64)
  ;; f32: float a singola precisione
  ;; f64: equivalente a Number di JS
  nop)

Passare Stringhe tra JS e Wasm

Wasm non ha un tipo stringa nativo. Le stringhe vanno scritte in memoria e passate come puntatore + lunghezza.

// Scrivere una stringa nella memoria Wasm
function writeString(memory, string, offset) {
  const encoder = new TextEncoder();
  const bytes = encoder.encode(string);
  const view = new Uint8Array(memory.buffer);
  view.set(bytes, offset);
  return bytes.length;
}

// Leggere una stringa dalla memoria Wasm
function readString(memory, offset, length) {
  const decoder = new TextDecoder();
  const view = new Uint8Array(memory.buffer, offset, length);
  return decoder.decode(view);
}

// Uso con un modulo
const { instance } = await WebAssembly.instantiateStreaming(
  fetch("string_processor.wasm"),
  { env: { memory } }
);

const len = writeString(memory, "Ciao Wasm!", 0);
instance.exports.processString(0, len); // passa puntatore e lunghezza
const result = readString(memory, 256, instance.exports.getResultLen());

TypedArrays come Vista sulla Memoria

I TypedArray di JavaScript offrono viste tipizzate sullo stesso buffer di memoria:

const memory = instance.exports.memory;

// Diverse viste sullo stesso buffer
const bytes  = new Uint8Array(memory.buffer);    // byte singoli
const ints   = new Int32Array(memory.buffer);     // interi 32-bit
const floats = new Float64Array(memory.buffer);   // float 64-bit

// Scrivere un float64 all'offset 0
floats[0] = 3.14159;

// Lo stesso dato letto come byte
console.log(bytes[0], bytes[1], bytes[2], bytes[3]); // byte grezzi del float

// Attenzione all'allineamento!
// Int32Array[i] corrisponde a byte offset i*4
ints[0] = 1000;
console.log(bytes[0]); // 232 (byte meno significativo di 1000)

L’allineamento è fondamentale: un i32.load all’offset 3 (non multiplo di 4) funziona ma può essere più lento su alcune piattaforme.