quickwin 1.0.2 → 1.1.1

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 (78) 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.ts +35 -35
  5. package/examples/pdf_preview2.ts +375 -375
  6. package/examples/pdf_viewer.tsx +279 -279
  7. package/examples/test_gallery.tsx +285 -285
  8. package/examples/test_react_lazy.tsx +63 -63
  9. package/examples/test_react_listbox.tsx +71 -71
  10. package/examples/test_root_window.tsx +41 -41
  11. package/examples/test_tab.tsx +75 -75
  12. package/examples/tray_demo.tsx +81 -81
  13. package/lib/assert.ts +6 -6
  14. package/lib/cache-utils.ts +40 -40
  15. package/lib/dumpRects.ts +25 -25
  16. package/lib/fetch.ts +502 -502
  17. package/lib/http-common.ts +399 -399
  18. package/lib/http-server.ts +424 -424
  19. package/lib/polyfill.ts +171 -171
  20. package/lib/react-qw/components/index.ts +17 -17
  21. package/lib/react-qw/index.ts +90 -90
  22. package/lib/react-qw/jsx.d.ts +34 -34
  23. package/lib/react-qw/layout.ts +111 -111
  24. package/lib/react-qw/props.ts +36 -36
  25. package/lib/react-qw/reconciler.ts +373 -373
  26. package/lib/stream.ts +21 -21
  27. package/lib/text-codec.ts +171 -171
  28. package/lib/text-measure.ts +54 -54
  29. package/lib/url.ts +201 -201
  30. package/lib/vendor/web-streams/ponyfill.d.mts +829 -829
  31. package/lib/vendor/web-streams/ponyfill.mjs +8 -8
  32. package/lib/websocket.js +8 -0
  33. package/lib/websocket.ts +563 -555
  34. package/package.json +50 -44
  35. package/qwin-nowasm-x86.exe +0 -0
  36. package/qwin-nowasm.exe +0 -0
  37. package/qwin-x86.exe +0 -0
  38. package/qwin.exe +0 -0
  39. package/test/run.ts +96 -96
  40. package/test/test_basic.ts +9 -9
  41. package/test/test_basic_fetch.ts +33 -33
  42. package/test/test_brotli.ts +52 -52
  43. package/test/test_fetch_cache.ts +187 -187
  44. package/test/test_ffi.ts +174 -174
  45. package/test/test_frame_encoding.ts +132 -132
  46. package/test/test_helper.ts +80 -80
  47. package/test/test_http_import.ts +74 -74
  48. package/test/test_http_server.ts +136 -136
  49. package/test/test_import_dir.ts +5 -5
  50. package/test/test_ipv6.ts +129 -129
  51. package/test/test_meta_url.ts +4 -4
  52. package/test/test_mupdf_render.ts +74 -74
  53. package/test/test_mupdf_twice.ts +81 -81
  54. package/test/test_mupdf_wasm.ts +30 -30
  55. package/test/test_net_event.ts +55 -55
  56. package/test/test_net_fetch.ts +238 -238
  57. package/test/test_net_websocket.ts +164 -164
  58. package/test/test_polyfill.ts +159 -159
  59. package/test/test_url.ts +183 -183
  60. package/test/test_wasm_basic.ts +73 -73
  61. package/test/test_wasm_import_global.ts +45 -45
  62. package/test/test_wasm_sjlj.ts +125 -125
  63. package/test/test_wasm_types.ts +124 -124
  64. package/test/test_worker.ts +24 -24
  65. package/test/test_worker_fetch.ts +27 -27
  66. package/test/test_worker_net.ts +54 -54
  67. package/test/test_worker_wasm_concurrent.ts +45 -45
  68. package/test/worker_fetch.ts +13 -13
  69. package/test/worker_helper.ts +1 -1
  70. package/test/worker_http.ts +20 -20
  71. package/test/worker_import_lib.ts +13 -13
  72. package/test/worker_module.ts +17 -17
  73. package/test/worker_wasm_concurrent.ts +22 -22
  74. package/tsconfig.json +27 -27
  75. package/vendor/mupdf-wasm/mupdf-wasm.d.ts +571 -571
  76. package/vendor/mupdf-wasm/mupdf-wasm.js +2749 -2749
  77. package/vendor/mupdf-wasm/mupdf.d.ts +939 -939
  78. 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
+ }