(value: any)
| 479 | } |
| 480 | |
| 481 | private writeValue(value: any): { type: number; payload: number } { |
| 482 | if (typeof value === "number") { |
| 483 | const ptr = this.alloc(8); |
| 484 | this.f64[ptr >> 3] = value; |
| 485 | return { type: TYPE_NUMBER, payload: ptr }; |
| 486 | } |
| 487 | if (value === null || value === undefined) { |
| 488 | return { type: TYPE_NULL, payload: 0 }; |
| 489 | } |
| 490 | if (value === true) return { type: TYPE_TRUE, payload: 0 }; |
| 491 | if (value === false) return { type: TYPE_FALSE, payload: 0 }; |
| 492 | |
| 493 | if (typeof value === "string") { |
| 494 | // Get current free pointer directly (bypass alloc() overhead for now) |
| 495 | const freePtrIdx = OFFSET_FREE_PTR >> 2; |
| 496 | let currentPtr = Atomics.load(this.u32, freePtrIdx); |
| 497 | |
| 498 | // Ensure alignment for the 4-byte length header |
| 499 | currentPtr = (currentPtr + 3) & ~3; |
| 500 | |
| 501 | // Calculate worst-case size (3 bytes per char for UTF-8 + 4 bytes header) |
| 502 | const maxBytes = value.length * 3 + 4; |
| 503 | |
| 504 | // Safety Check: If near end of buffer, fallback to standard alloc() (which handles GC and OOM errors properly) |
| 505 | if (currentPtr + maxBytes > this.buffer.byteLength) { |
| 506 | const encoded = this.textEncoder.encode(value); // Slow path allocation |
| 507 | const len = encoded.byteLength; |
| 508 | const ptr = this.alloc(4 + len); |
| 509 | this.u32[ptr >> 2] = len; |
| 510 | this.u8.set(encoded, ptr + 4); |
| 511 | return { type: TYPE_STRING, payload: ptr }; |
| 512 | } |
| 513 | |
| 514 | // This faster because it writes directly to shared memory but does not work in the browser because of security reasons: |
| 515 | // TextEncoder.encodeInto: Argument 2 can't be a SharedArrayBuffer or an ArrayBufferView backed by a SharedArrayBuffer |
| 516 | |
| 517 | // const { written } = this.textEncoder.encodeInto( |
| 518 | // value, |
| 519 | // this.u8.subarray(currentPtr + 4, currentPtr + maxBytes), |
| 520 | // ); |
| 521 | |
| 522 | // So alternatively we use the (slower) approach below to stay compatible with browsers |
| 523 | const encoded = this.textEncoder.encode(value); |
| 524 | const written = Math.min(encoded.length, maxBytes); |
| 525 | this.u8.set(encoded.subarray(0, written), currentPtr + 4); |
| 526 | |
| 527 | // Write actual length |
| 528 | this.u32[currentPtr >> 2] = written!; |
| 529 | |
| 530 | // Manually advance free pointer (Align to 8 bytes for future number writes) |
| 531 | const actualSize = 4 + written!; |
| 532 | const nextPtr = (currentPtr + actualSize + 7) & ~7; |
| 533 | Atomics.store(this.u32, freePtrIdx, nextPtr); |
| 534 | |
| 535 | return { type: TYPE_STRING, payload: currentPtr }; |
| 536 | } |
| 537 | |
| 538 | if (Array.isArray(value)) { |
no test coverage detected