quickwin 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/README.en.md +204 -204
  2. package/README.md +203 -203
  3. package/examples/PdfCanvas.tsx +83 -83
  4. package/examples/exec_server.js +25 -3
  5. package/examples/exec_server.ts +35 -17
  6. package/examples/pdf_preview2.ts +375 -375
  7. package/examples/pdf_viewer.tsx +279 -279
  8. package/examples/test_gallery.tsx +285 -285
  9. package/examples/test_react_lazy.tsx +63 -63
  10. package/examples/test_react_listbox.tsx +71 -71
  11. package/examples/test_root_window.tsx +41 -41
  12. package/examples/test_tab.tsx +75 -75
  13. package/examples/tray_demo.tsx +81 -81
  14. package/lib/assert.ts +6 -6
  15. package/lib/cache-utils.ts +40 -40
  16. package/lib/dumpRects.ts +25 -25
  17. package/lib/fetch.ts +502 -502
  18. package/lib/http-common.ts +399 -399
  19. package/lib/http-server.ts +424 -424
  20. package/lib/polyfill.ts +171 -171
  21. package/lib/react-qw/components/index.ts +17 -17
  22. package/lib/react-qw/index.ts +90 -90
  23. package/lib/react-qw/jsx.d.ts +34 -34
  24. package/lib/react-qw/layout.ts +111 -111
  25. package/lib/react-qw/props.ts +36 -36
  26. package/lib/react-qw/reconciler.ts +373 -373
  27. package/lib/stream.ts +21 -21
  28. package/lib/text-codec.ts +171 -171
  29. package/lib/text-measure.ts +54 -54
  30. package/lib/url.ts +201 -201
  31. package/lib/vendor/web-streams/ponyfill.d.mts +829 -829
  32. package/lib/vendor/web-streams/ponyfill.mjs +8 -8
  33. package/lib/websocket.js +8 -0
  34. package/lib/websocket.ts +563 -555
  35. package/package.json +50 -44
  36. package/qwin-nowasm-x86.exe +0 -0
  37. package/qwin-nowasm.exe +0 -0
  38. package/qwin-x86.exe +0 -0
  39. package/qwin.exe +0 -0
  40. package/test/run.ts +96 -96
  41. package/test/test_basic.ts +9 -9
  42. package/test/test_basic_fetch.ts +33 -33
  43. package/test/test_brotli.ts +52 -52
  44. package/test/test_fetch_cache.ts +187 -187
  45. package/test/test_ffi.ts +174 -174
  46. package/test/test_frame_encoding.ts +132 -132
  47. package/test/test_helper.ts +80 -80
  48. package/test/test_http_import.ts +74 -74
  49. package/test/test_http_server.ts +136 -136
  50. package/test/test_import_dir.ts +5 -5
  51. package/test/test_ipv6.ts +129 -129
  52. package/test/test_meta_url.ts +4 -4
  53. package/test/test_mupdf_render.ts +74 -74
  54. package/test/test_mupdf_twice.ts +81 -81
  55. package/test/test_mupdf_wasm.ts +30 -30
  56. package/test/test_net_event.ts +55 -55
  57. package/test/test_net_fetch.ts +238 -238
  58. package/test/test_net_websocket.ts +164 -164
  59. package/test/test_polyfill.ts +159 -159
  60. package/test/test_url.ts +183 -183
  61. package/test/test_wasm_basic.ts +73 -73
  62. package/test/test_wasm_import_global.ts +45 -45
  63. package/test/test_wasm_sjlj.ts +125 -125
  64. package/test/test_wasm_types.ts +124 -124
  65. package/test/test_worker.ts +24 -24
  66. package/test/test_worker_fetch.ts +27 -27
  67. package/test/test_worker_net.ts +54 -54
  68. package/test/test_worker_wasm_concurrent.ts +45 -45
  69. package/test/worker_fetch.ts +13 -13
  70. package/test/worker_helper.ts +1 -1
  71. package/test/worker_http.ts +20 -20
  72. package/test/worker_import_lib.ts +13 -13
  73. package/test/worker_module.ts +17 -17
  74. package/test/worker_wasm_concurrent.ts +22 -22
  75. package/tsconfig.json +27 -27
  76. package/vendor/mupdf-wasm/mupdf-wasm.d.ts +571 -571
  77. package/vendor/mupdf-wasm/mupdf-wasm.js +2749 -2749
  78. package/vendor/mupdf-wasm/mupdf.d.ts +939 -939
  79. package/vendor/mupdf-wasm/mupdf.js +3317 -3317
package/test/test_ffi.ts CHANGED
@@ -1,174 +1,174 @@
1
- import * as std from 'std'
2
- import * as win from 'win'
3
- import * as ffi from 'ffi'
4
- import { Tester } from './test_helper.js'
5
-
6
- function decodeWideAtPtr(ptr: number): string {
7
- if (!ptr) return ''
8
- const chars: number[] = []
9
- let pos = ptr
10
- while (true) {
11
- const low = ffi.readByte(pos)
12
- const high = ffi.readByte(pos + 1)
13
- const ch = low + high * 256
14
- if (ch === 0) break
15
- chars.push(ch)
16
- pos += 2
17
- }
18
- return String.fromCharCode(...chars)
19
- }
20
-
21
- function readPtr(dv: DataView, offset: number): number {
22
- const low = dv.getUint32(offset, true)
23
- const high = dv.getUint32(offset + 4, true)
24
- return low + high * 4294967296
25
- }
26
-
27
- const STATUS_FLAGS: [number, string][] = [
28
- [0x00000001, 'PAUSED'],
29
- [0x00000002, 'ERROR'],
30
- [0x00000004, 'PENDING_DELETION'],
31
- [0x00000008, 'PAPER_JAM'],
32
- [0x00000010, 'PAPER_OUT'],
33
- [0x00000020, 'MANUAL_FEED'],
34
- [0x00000040, 'PAPER_PROBLEM'],
35
- [0x00000080, 'OFFLINE'],
36
- [0x00000100, 'IO_ACTIVE'],
37
- [0x00000200, 'BUSY'],
38
- [0x00000400, 'PRINTING'],
39
- [0x00000800, 'OUTPUT_BIN_FULL'],
40
- [0x00001000, 'NOT_AVAILABLE'],
41
- [0x00002000, 'WAITING'],
42
- [0x00004000, 'PROCESSING'],
43
- [0x00008000, 'INITIALIZING'],
44
- [0x00010000, 'WARMING_UP'],
45
- [0x00020000, 'TONER_LOW'],
46
- [0x00040000, 'NO_TONER'],
47
- [0x00080000, 'PAGE_PUNT'],
48
- [0x00100000, 'USER_INTERVENTION'],
49
- [0x00200000, 'OUT_OF_MEMORY'],
50
- [0x00400000, 'DOOR_OPEN'],
51
- [0x00800000, 'SERVER_UNKNOWN'],
52
- [0x01000000, 'POWER_SAVE'],
53
- [0x02000000, 'SERVER_OFFLINE'],
54
- [0x04000000, 'DRIVER_UPDATE'],
55
- ];
56
-
57
- function formatStatus(status: number): string {
58
- if (status === 0) return 'OK'
59
- const parts: string[] = []
60
- for (const [flag, name] of STATUS_FLAGS) {
61
- if (status & flag) parts.push(name)
62
- }
63
- return parts.join('|') || 'UNKNOWN'
64
- }
65
-
66
- export const suite = {
67
- name: 'ffi',
68
- run: (t: Tester) => {
69
- t.section('EnumPrintersW')
70
- const hWinspool = win.LoadLibrary('winspool.drv')
71
- t.checkTrue('LoadLibrary("winspool.drv") succeeds', hWinspool !== null)
72
- if (!hWinspool) return
73
-
74
- const enumPrinters = win.GetProcAddress(hWinspool, 'EnumPrintersW')
75
- t.checkTrue('GetProcAddress("EnumPrintersW") succeeds', enumPrinters !== null)
76
- if (!enumPrinters) return
77
-
78
- const flags = 0x06
79
- const level = 2
80
- const neededBuf = new Uint32Array(new ArrayBuffer(4))
81
- const returnedBuf = new Uint32Array(new ArrayBuffer(4))
82
-
83
- const ret1 = ffi.ffiCall(
84
- enumPrinters,
85
- [
86
- ffi.FFI_TYPE_UINT32,
87
- ffi.FFI_TYPE_POINTER,
88
- ffi.FFI_TYPE_UINT32,
89
- ffi.FFI_TYPE_POINTER,
90
- ffi.FFI_TYPE_UINT32,
91
- ffi.FFI_TYPE_POINTER,
92
- ffi.FFI_TYPE_POINTER,
93
- ],
94
- [
95
- flags,
96
- null,
97
- level,
98
- null,
99
- 0,
100
- neededBuf.buffer,
101
- returnedBuf.buffer,
102
- ],
103
- ffi.FFI_TYPE_SINT32
104
- )
105
- std.printf(' first call: ret=%d needed=%d returned=%d\n', ret1, neededBuf[0], returnedBuf[0])
106
- t.checkTrue('pcbNeeded > 0', neededBuf[0]! > 0)
107
- if (neededBuf[0]! <= 0) return
108
-
109
- const printerBuf = new ArrayBuffer(neededBuf[0]!)
110
- const ret2 = ffi.ffiCall(
111
- enumPrinters,
112
- [
113
- ffi.FFI_TYPE_UINT32,
114
- ffi.FFI_TYPE_POINTER,
115
- ffi.FFI_TYPE_UINT32,
116
- ffi.FFI_TYPE_POINTER,
117
- ffi.FFI_TYPE_UINT32,
118
- ffi.FFI_TYPE_POINTER,
119
- ffi.FFI_TYPE_POINTER,
120
- ],
121
- [
122
- flags,
123
- null,
124
- level,
125
- printerBuf,
126
- neededBuf[0]!,
127
- neededBuf.buffer,
128
- returnedBuf.buffer,
129
- ],
130
- ffi.FFI_TYPE_SINT32
131
- )
132
- t.checkTrue('EnumPrintersW succeeds', ret2 !== 0)
133
- if (ret2 === 0) return
134
-
135
- std.printf(' printers found: %d\n', returnedBuf[0])
136
-
137
- if (returnedBuf[0]! > 0) {
138
- const dv = new DataView(printerBuf)
139
- const structSize = 136
140
-
141
- for (let i = 0; i < returnedBuf[0]! && i < 20; i++) {
142
- const off = i * structSize
143
- const name = decodeWideAtPtr(readPtr(dv, off + 8))
144
- const port = decodeWideAtPtr(readPtr(dv, off + 24))
145
- const driver = decodeWideAtPtr(readPtr(dv, off + 32))
146
- const location = decodeWideAtPtr(readPtr(dv, off + 48))
147
- const comment = decodeWideAtPtr(readPtr(dv, off + 40))
148
- const status = dv.getUint32(off + 124, true)
149
- std.printf(' [%d] %s\n', i + 1, name)
150
- std.printf(' port: %s\n', port)
151
- std.printf(' driver: %s\n', driver)
152
- if (location) std.printf(' location: %s\n', location)
153
- if (comment) std.printf(' comment: %s\n', comment)
154
- std.printf(' status: 0x%08X (%s)\n', status, formatStatus(status))
155
- }
156
- } else {
157
- std.printf(' no printers installed (skip)\n')
158
- }
159
-
160
- t.section('pointer arg type check')
161
- let threw = false
162
- try {
163
- ffi.ffiCall(
164
- enumPrinters,
165
- [ffi.FFI_TYPE_POINTER] as any,
166
- [123] as any,
167
- ffi.FFI_TYPE_VOID
168
- )
169
- } catch (e) {
170
- threw = true
171
- }
172
- t.checkTrue('non-ArrayBuffer pointer arg throws TypeError', threw)
173
- }
174
- }
1
+ import * as std from 'std'
2
+ import * as win from 'win'
3
+ import * as ffi from 'ffi'
4
+ import { Tester } from './test_helper.js'
5
+
6
+ function decodeWideAtPtr(ptr: number): string {
7
+ if (!ptr) return ''
8
+ const chars: number[] = []
9
+ let pos = ptr
10
+ while (true) {
11
+ const low = ffi.readByte(pos)
12
+ const high = ffi.readByte(pos + 1)
13
+ const ch = low + high * 256
14
+ if (ch === 0) break
15
+ chars.push(ch)
16
+ pos += 2
17
+ }
18
+ return String.fromCharCode(...chars)
19
+ }
20
+
21
+ function readPtr(dv: DataView, offset: number): number {
22
+ const low = dv.getUint32(offset, true)
23
+ const high = dv.getUint32(offset + 4, true)
24
+ return low + high * 4294967296
25
+ }
26
+
27
+ const STATUS_FLAGS: [number, string][] = [
28
+ [0x00000001, 'PAUSED'],
29
+ [0x00000002, 'ERROR'],
30
+ [0x00000004, 'PENDING_DELETION'],
31
+ [0x00000008, 'PAPER_JAM'],
32
+ [0x00000010, 'PAPER_OUT'],
33
+ [0x00000020, 'MANUAL_FEED'],
34
+ [0x00000040, 'PAPER_PROBLEM'],
35
+ [0x00000080, 'OFFLINE'],
36
+ [0x00000100, 'IO_ACTIVE'],
37
+ [0x00000200, 'BUSY'],
38
+ [0x00000400, 'PRINTING'],
39
+ [0x00000800, 'OUTPUT_BIN_FULL'],
40
+ [0x00001000, 'NOT_AVAILABLE'],
41
+ [0x00002000, 'WAITING'],
42
+ [0x00004000, 'PROCESSING'],
43
+ [0x00008000, 'INITIALIZING'],
44
+ [0x00010000, 'WARMING_UP'],
45
+ [0x00020000, 'TONER_LOW'],
46
+ [0x00040000, 'NO_TONER'],
47
+ [0x00080000, 'PAGE_PUNT'],
48
+ [0x00100000, 'USER_INTERVENTION'],
49
+ [0x00200000, 'OUT_OF_MEMORY'],
50
+ [0x00400000, 'DOOR_OPEN'],
51
+ [0x00800000, 'SERVER_UNKNOWN'],
52
+ [0x01000000, 'POWER_SAVE'],
53
+ [0x02000000, 'SERVER_OFFLINE'],
54
+ [0x04000000, 'DRIVER_UPDATE'],
55
+ ];
56
+
57
+ function formatStatus(status: number): string {
58
+ if (status === 0) return 'OK'
59
+ const parts: string[] = []
60
+ for (const [flag, name] of STATUS_FLAGS) {
61
+ if (status & flag) parts.push(name)
62
+ }
63
+ return parts.join('|') || 'UNKNOWN'
64
+ }
65
+
66
+ export const suite = {
67
+ name: 'ffi',
68
+ run: (t: Tester) => {
69
+ t.section('EnumPrintersW')
70
+ const hWinspool = win.LoadLibrary('winspool.drv')
71
+ t.checkTrue('LoadLibrary("winspool.drv") succeeds', hWinspool !== null)
72
+ if (!hWinspool) return
73
+
74
+ const enumPrinters = win.GetProcAddress(hWinspool, 'EnumPrintersW')
75
+ t.checkTrue('GetProcAddress("EnumPrintersW") succeeds', enumPrinters !== null)
76
+ if (!enumPrinters) return
77
+
78
+ const flags = 0x06
79
+ const level = 2
80
+ const neededBuf = new Uint32Array(new ArrayBuffer(4))
81
+ const returnedBuf = new Uint32Array(new ArrayBuffer(4))
82
+
83
+ const ret1 = ffi.ffiCall(
84
+ enumPrinters,
85
+ [
86
+ ffi.FFI_TYPE_UINT32,
87
+ ffi.FFI_TYPE_POINTER,
88
+ ffi.FFI_TYPE_UINT32,
89
+ ffi.FFI_TYPE_POINTER,
90
+ ffi.FFI_TYPE_UINT32,
91
+ ffi.FFI_TYPE_POINTER,
92
+ ffi.FFI_TYPE_POINTER,
93
+ ],
94
+ [
95
+ flags,
96
+ null,
97
+ level,
98
+ null,
99
+ 0,
100
+ neededBuf.buffer,
101
+ returnedBuf.buffer,
102
+ ],
103
+ ffi.FFI_TYPE_SINT32
104
+ )
105
+ std.printf(' first call: ret=%d needed=%d returned=%d\n', ret1, neededBuf[0], returnedBuf[0])
106
+ t.checkTrue('pcbNeeded > 0', neededBuf[0]! > 0)
107
+ if (neededBuf[0]! <= 0) return
108
+
109
+ const printerBuf = new ArrayBuffer(neededBuf[0]!)
110
+ const ret2 = ffi.ffiCall(
111
+ enumPrinters,
112
+ [
113
+ ffi.FFI_TYPE_UINT32,
114
+ ffi.FFI_TYPE_POINTER,
115
+ ffi.FFI_TYPE_UINT32,
116
+ ffi.FFI_TYPE_POINTER,
117
+ ffi.FFI_TYPE_UINT32,
118
+ ffi.FFI_TYPE_POINTER,
119
+ ffi.FFI_TYPE_POINTER,
120
+ ],
121
+ [
122
+ flags,
123
+ null,
124
+ level,
125
+ printerBuf,
126
+ neededBuf[0]!,
127
+ neededBuf.buffer,
128
+ returnedBuf.buffer,
129
+ ],
130
+ ffi.FFI_TYPE_SINT32
131
+ )
132
+ t.checkTrue('EnumPrintersW succeeds', ret2 !== 0)
133
+ if (ret2 === 0) return
134
+
135
+ std.printf(' printers found: %d\n', returnedBuf[0])
136
+
137
+ if (returnedBuf[0]! > 0) {
138
+ const dv = new DataView(printerBuf)
139
+ const structSize = 136
140
+
141
+ for (let i = 0; i < returnedBuf[0]! && i < 20; i++) {
142
+ const off = i * structSize
143
+ const name = decodeWideAtPtr(readPtr(dv, off + 8))
144
+ const port = decodeWideAtPtr(readPtr(dv, off + 24))
145
+ const driver = decodeWideAtPtr(readPtr(dv, off + 32))
146
+ const location = decodeWideAtPtr(readPtr(dv, off + 48))
147
+ const comment = decodeWideAtPtr(readPtr(dv, off + 40))
148
+ const status = dv.getUint32(off + 124, true)
149
+ std.printf(' [%d] %s\n', i + 1, name)
150
+ std.printf(' port: %s\n', port)
151
+ std.printf(' driver: %s\n', driver)
152
+ if (location) std.printf(' location: %s\n', location)
153
+ if (comment) std.printf(' comment: %s\n', comment)
154
+ std.printf(' status: 0x%08X (%s)\n', status, formatStatus(status))
155
+ }
156
+ } else {
157
+ std.printf(' no printers installed (skip)\n')
158
+ }
159
+
160
+ t.section('pointer arg type check')
161
+ let threw = false
162
+ try {
163
+ ffi.ffiCall(
164
+ enumPrinters,
165
+ [ffi.FFI_TYPE_POINTER] as any,
166
+ [123] as any,
167
+ ffi.FFI_TYPE_VOID
168
+ )
169
+ } catch (e) {
170
+ threw = true
171
+ }
172
+ t.checkTrue('non-ArrayBuffer pointer arg throws TypeError', threw)
173
+ }
174
+ }
@@ -1,132 +1,132 @@
1
- import * as std from 'std'
2
- import { Tester } from './test_helper.js'
3
-
4
- export const suite = {
5
- name: 'wasm-frame-encoding',
6
- run: (t: Tester) => {
7
- function createFrameHeader(payloadLen: number): Uint8Array {
8
- let headerSize = payloadLen < 126 ? 6 : (payloadLen < 65536 ? 8 : 14)
9
- let buf = new ArrayBuffer(headerSize)
10
- let view = new Uint8Array(buf)
11
- let off = 0
12
- view[off++] = 0x81
13
- if (payloadLen < 126) {
14
- view[off++] = 0x80 | payloadLen
15
- } else if (payloadLen < 65536) {
16
- view[off++] = 0x80 | 126
17
- view[off++] = (payloadLen >> 8) & 0xFF
18
- view[off++] = payloadLen & 0xFF
19
- } else {
20
- view[off++] = 0x80 | 127
21
- const len = payloadLen
22
- const hi = Math.floor(len / 0x100000000) >>> 0
23
- const lo = len >>> 0
24
- view[off++] = (hi >> 24) & 0xFF
25
- view[off++] = (hi >> 16) & 0xFF
26
- view[off++] = (hi >> 8) & 0xFF
27
- view[off++] = hi & 0xFF
28
- view[off++] = (lo >> 24) & 0xFF
29
- view[off++] = (lo >> 16) & 0xFF
30
- view[off++] = (lo >> 8) & 0xFF
31
- view[off++] = lo & 0xFF
32
- }
33
- return view
34
- }
35
-
36
- function tryParseFrameLen(data: Uint8Array): number | null {
37
- if (data.length < 2) return null
38
- const b1 = data[1]!
39
- let payloadLen = b1 & 0x7F
40
- let offset = 2
41
- if (payloadLen === 126) {
42
- if (data.length < 4) return null
43
- payloadLen = (data[2]! << 8) | data[3]!
44
- } else if (payloadLen === 127) {
45
- if (data.length < 10) return null
46
- let hi = 0, lo = 0
47
- for (let i = 0; i < 4; i++) hi = (hi * 256 + data[offset + i]!) >>> 0
48
- for (let i = 4; i < 8; i++) lo = (lo * 256 + data[offset + i]!) >>> 0
49
- payloadLen = hi * 0x100000000 + lo
50
- }
51
- return payloadLen
52
- }
53
-
54
- function assert(name: string, ok: boolean): void {
55
- if (ok) { t.ok++; std.printf(' PASS: %s\n', name) }
56
- else { t.fail++; std.printf(' FAIL: %s\n', name) }
57
- }
58
-
59
- // small frame (100 bytes)
60
- t.section('inline length (<126)')
61
- let h = createFrameHeader(100)
62
- assert('length=100', h[1] === (0x80 | 100))
63
- assert('parse 100', tryParseFrameLen(h) === 100)
64
-
65
- // 1 (edge case)
66
- h = createFrameHeader(1)
67
- assert('length=1', h[1] === (0x80 | 1))
68
- assert('parse 1', tryParseFrameLen(h) === 1)
69
-
70
- // 125 (max inline)
71
- h = createFrameHeader(125)
72
- assert('length=125', h[1] === (0x80 | 125))
73
- assert('parse 125', tryParseFrameLen(h) === 125)
74
-
75
- // 126 (enters 16-bit extended)
76
- t.section('16-bit extended length (126–65535)')
77
- h = createFrameHeader(126)
78
- assert('126 marker', h[1] === (0x80 | 126))
79
- assert('126 value', h[2] === 0 && h[3] === 126)
80
- assert('parse 126', tryParseFrameLen(h) === 126)
81
-
82
- // 65535 (max 16-bit)
83
- h = createFrameHeader(65535)
84
- assert('65535 marker', h[1] === (0x80 | 126))
85
- assert('65535 hi byte', h[2] === 0xFF)
86
- assert('65535 lo byte', h[3] === 0xFF)
87
- assert('parse 65535', tryParseFrameLen(h) === 65535)
88
-
89
- // 65536 (enters 64-bit extended)
90
- t.section('64-bit extended length (>=65536)')
91
- h = createFrameHeader(65536)
92
- assert('65536 marker', h[1] === (0x80 | 127))
93
- assert('parse 65536', tryParseFrameLen(h) === 65536)
94
-
95
- // 66000
96
- h = createFrameHeader(66000)
97
- assert('66000 marker', h[1] === (0x80 | 127))
98
- assert('parse 66000', tryParseFrameLen(h) === 66000)
99
-
100
- // 70000 — verify exact bytes
101
- h = createFrameHeader(70000)
102
- assert('70000 marker', h[1] === (0x80 | 127))
103
- // 70000 = 0x11170, big-endian 8 bytes: 00 00 00 00 00 01 11 70
104
- assert('70000 bytes big-endian',
105
- h[2] === 0x00 && h[3] === 0x00 && h[4] === 0x00 && h[5] === 0x00 &&
106
- h[6] === 0x00 && h[7] === 0x01 && h[8] === 0x11 && h[9] === 0x70)
107
- assert('parse 70000', tryParseFrameLen(h) === 70000)
108
-
109
- // Large value near 2^32 boundary
110
- t.section('large values')
111
- h = createFrameHeader(0xABCD1234)
112
- assert('0xABCD1234 marker', h[1] === (0x80 | 127))
113
- assert('parse 0xABCD1234', tryParseFrameLen(h) === 0xABCD1234)
114
- assert('MSB non-zero',
115
- h[2] === 0x00 && h[3] === 0x00 && h[4] === 0x00 && h[5] === 0x00 &&
116
- h[6] === 0xAB && h[7] === 0xCD && h[8] === 0x12 && h[9] === 0x34)
117
-
118
- // 0x100000001 (just above 2^32)
119
- h = createFrameHeader(0x100000001)
120
- assert('2^32+1 parse', tryParseFrameLen(h) === 0x100000001)
121
- assert('2^32+1 bytes',
122
- h[2] === 0x00 && h[3] === 0x00 && h[4] === 0x00 && h[5] === 0x01 &&
123
- h[6] === 0x00 && h[7] === 0x00 && h[8] === 0x00 && h[9] === 0x01)
124
-
125
- // Full 8-byte roundtrip: 0x0123456789ABCDEF
126
- // This exceeds Number's safe integer range (2^53) slightly,
127
- // but our test value 0x0123456789ABCDEF = 81985529216486895 < 2^53 ✓
128
- const bigVal = 0x0123456789ABCDEF
129
- h = createFrameHeader(bigVal)
130
- assert('big val parse', tryParseFrameLen(h) === bigVal)
131
- }
132
- }
1
+ import * as std from 'std'
2
+ import { Tester } from './test_helper.js'
3
+
4
+ export const suite = {
5
+ name: 'wasm-frame-encoding',
6
+ run: (t: Tester) => {
7
+ function createFrameHeader(payloadLen: number): Uint8Array {
8
+ let headerSize = payloadLen < 126 ? 6 : (payloadLen < 65536 ? 8 : 14)
9
+ let buf = new ArrayBuffer(headerSize)
10
+ let view = new Uint8Array(buf)
11
+ let off = 0
12
+ view[off++] = 0x81
13
+ if (payloadLen < 126) {
14
+ view[off++] = 0x80 | payloadLen
15
+ } else if (payloadLen < 65536) {
16
+ view[off++] = 0x80 | 126
17
+ view[off++] = (payloadLen >> 8) & 0xFF
18
+ view[off++] = payloadLen & 0xFF
19
+ } else {
20
+ view[off++] = 0x80 | 127
21
+ const len = payloadLen
22
+ const hi = Math.floor(len / 0x100000000) >>> 0
23
+ const lo = len >>> 0
24
+ view[off++] = (hi >> 24) & 0xFF
25
+ view[off++] = (hi >> 16) & 0xFF
26
+ view[off++] = (hi >> 8) & 0xFF
27
+ view[off++] = hi & 0xFF
28
+ view[off++] = (lo >> 24) & 0xFF
29
+ view[off++] = (lo >> 16) & 0xFF
30
+ view[off++] = (lo >> 8) & 0xFF
31
+ view[off++] = lo & 0xFF
32
+ }
33
+ return view
34
+ }
35
+
36
+ function tryParseFrameLen(data: Uint8Array): number | null {
37
+ if (data.length < 2) return null
38
+ const b1 = data[1]!
39
+ let payloadLen = b1 & 0x7F
40
+ let offset = 2
41
+ if (payloadLen === 126) {
42
+ if (data.length < 4) return null
43
+ payloadLen = (data[2]! << 8) | data[3]!
44
+ } else if (payloadLen === 127) {
45
+ if (data.length < 10) return null
46
+ let hi = 0, lo = 0
47
+ for (let i = 0; i < 4; i++) hi = (hi * 256 + data[offset + i]!) >>> 0
48
+ for (let i = 4; i < 8; i++) lo = (lo * 256 + data[offset + i]!) >>> 0
49
+ payloadLen = hi * 0x100000000 + lo
50
+ }
51
+ return payloadLen
52
+ }
53
+
54
+ function assert(name: string, ok: boolean): void {
55
+ if (ok) { t.ok++; std.printf(' PASS: %s\n', name) }
56
+ else { t.fail++; std.printf(' FAIL: %s\n', name) }
57
+ }
58
+
59
+ // small frame (100 bytes)
60
+ t.section('inline length (<126)')
61
+ let h = createFrameHeader(100)
62
+ assert('length=100', h[1] === (0x80 | 100))
63
+ assert('parse 100', tryParseFrameLen(h) === 100)
64
+
65
+ // 1 (edge case)
66
+ h = createFrameHeader(1)
67
+ assert('length=1', h[1] === (0x80 | 1))
68
+ assert('parse 1', tryParseFrameLen(h) === 1)
69
+
70
+ // 125 (max inline)
71
+ h = createFrameHeader(125)
72
+ assert('length=125', h[1] === (0x80 | 125))
73
+ assert('parse 125', tryParseFrameLen(h) === 125)
74
+
75
+ // 126 (enters 16-bit extended)
76
+ t.section('16-bit extended length (126–65535)')
77
+ h = createFrameHeader(126)
78
+ assert('126 marker', h[1] === (0x80 | 126))
79
+ assert('126 value', h[2] === 0 && h[3] === 126)
80
+ assert('parse 126', tryParseFrameLen(h) === 126)
81
+
82
+ // 65535 (max 16-bit)
83
+ h = createFrameHeader(65535)
84
+ assert('65535 marker', h[1] === (0x80 | 126))
85
+ assert('65535 hi byte', h[2] === 0xFF)
86
+ assert('65535 lo byte', h[3] === 0xFF)
87
+ assert('parse 65535', tryParseFrameLen(h) === 65535)
88
+
89
+ // 65536 (enters 64-bit extended)
90
+ t.section('64-bit extended length (>=65536)')
91
+ h = createFrameHeader(65536)
92
+ assert('65536 marker', h[1] === (0x80 | 127))
93
+ assert('parse 65536', tryParseFrameLen(h) === 65536)
94
+
95
+ // 66000
96
+ h = createFrameHeader(66000)
97
+ assert('66000 marker', h[1] === (0x80 | 127))
98
+ assert('parse 66000', tryParseFrameLen(h) === 66000)
99
+
100
+ // 70000 — verify exact bytes
101
+ h = createFrameHeader(70000)
102
+ assert('70000 marker', h[1] === (0x80 | 127))
103
+ // 70000 = 0x11170, big-endian 8 bytes: 00 00 00 00 00 01 11 70
104
+ assert('70000 bytes big-endian',
105
+ h[2] === 0x00 && h[3] === 0x00 && h[4] === 0x00 && h[5] === 0x00 &&
106
+ h[6] === 0x00 && h[7] === 0x01 && h[8] === 0x11 && h[9] === 0x70)
107
+ assert('parse 70000', tryParseFrameLen(h) === 70000)
108
+
109
+ // Large value near 2^32 boundary
110
+ t.section('large values')
111
+ h = createFrameHeader(0xABCD1234)
112
+ assert('0xABCD1234 marker', h[1] === (0x80 | 127))
113
+ assert('parse 0xABCD1234', tryParseFrameLen(h) === 0xABCD1234)
114
+ assert('MSB non-zero',
115
+ h[2] === 0x00 && h[3] === 0x00 && h[4] === 0x00 && h[5] === 0x00 &&
116
+ h[6] === 0xAB && h[7] === 0xCD && h[8] === 0x12 && h[9] === 0x34)
117
+
118
+ // 0x100000001 (just above 2^32)
119
+ h = createFrameHeader(0x100000001)
120
+ assert('2^32+1 parse', tryParseFrameLen(h) === 0x100000001)
121
+ assert('2^32+1 bytes',
122
+ h[2] === 0x00 && h[3] === 0x00 && h[4] === 0x00 && h[5] === 0x01 &&
123
+ h[6] === 0x00 && h[7] === 0x00 && h[8] === 0x00 && h[9] === 0x01)
124
+
125
+ // Full 8-byte roundtrip: 0x0123456789ABCDEF
126
+ // This exceeds Number's safe integer range (2^53) slightly,
127
+ // but our test value 0x0123456789ABCDEF = 81985529216486895 < 2^53 ✓
128
+ const bigVal = 0x0123456789ABCDEF
129
+ h = createFrameHeader(bigVal)
130
+ assert('big val parse', tryParseFrameLen(h) === bigVal)
131
+ }
132
+ }