diff --git a/frameworks/typev/Dockerfile b/frameworks/typev/Dockerfile index 1b1ea558b..9fd8d905a 100644 --- a/frameworks/typev/Dockerfile +++ b/frameworks/typev/Dockerfile @@ -12,7 +12,7 @@ WORKDIR /app # typev VM — binary + FFI plugins (json, stdcore, stdsocket, ...). Pre-built # (-O3 -march=x86-64-v3, static liburing) and fetched from object storage RUN wget -q -O /tmp/typev.zip \ - https://typev.s3.fr-par.scw.cloud/typev-17-05-2026.zip && \ + https://typev.s3.fr-par.scw.cloud/typev-19-05-2026.zip && \ unzip -q /tmp/typev.zip -d /app && \ rm /tmp/typev.zip diff --git a/frameworks/typev/bundle/benchmark-code/src/ws.tc b/frameworks/typev/bundle/benchmark-code/src/ws.tc index 65e00633d..c5064a4c5 100644 --- a/frameworks/typev/bundle/benchmark-code/src/ws.tc +++ b/frameworks/typev/bundle/benchmark-code/src/ws.tc @@ -3,13 +3,9 @@ // frame echo loop: read masked client frames, echo them unmasked. from std.socket import tcp_write, tcp_read +from std.internal.socket import socket from response import writeBytes -// Low 32 bits mask — SHA-1 is 32-bit arithmetic; typev `uint` is 64-bit. -let local const M32: uint = 4294967295u - -let local const B64: byte[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".bytes() -let local const WS_GUID: byte[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11".bytes() let local const WS_101: byte[] = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: ".bytes() let local const WS_CRLF2: byte[] = "\r\n\r\n".bytes() let local const WS_BAD: byte[] = "HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\nConnection: close\r\n\r\n".bytes() @@ -36,136 +32,11 @@ local fn wsSend(fd: int, b: byte[], len: uint) -> bool { return true } -// 32-bit rotate-left. -local fn rotl32(x: uint, n: uint) -> uint { - return ((x << n) | (x >> (32u - n))) & M32 -} - -// SHA-1 of data[0..n) — returns the 20-byte digest. -local fn sha1(data: byte[], n: uint) -> byte[] { - let padded: uint = ((n + 8u) / 64u + 1u) * 64u - let msg: byte[] = new byte[](padded) - foreach i: uint in 0u, n { - msg[i] = data[i] - } - msg[n] = 128 as byte - foreach i: uint in n + 1u, padded { - msg[i] = 0 as byte - } - let bits: uint = n * 8u - foreach i: uint in 0u, 8u { - msg[padded - 1u - i] = ((bits >> (8u * i)) & 255u) as byte - } - - let h0: uint = 1732584193u - let h1: uint = 4023233417u - let h2: uint = 2562383102u - let h3: uint = 271733878u - let h4: uint = 3285377520u - let w: uint[] = new uint[](80) - let blocks: uint = padded / 64u - - foreach b: uint in 0u, blocks { - let base: uint = b * 64u - foreach t: uint in 0u, 16u { - let o: uint = base + t * 4u - w[t] = (((msg[o] as uint) << 24u) | ((msg[o + 1u] as uint) << 16u) - | ((msg[o + 2u] as uint) << 8u) | (msg[o + 3u] as uint)) & M32 - } - foreach t: uint in 16u, 80u { - w[t] = rotl32((w[t - 3u] ^ w[t - 8u] ^ w[t - 14u] ^ w[t - 16u]) & M32, 1u) - } - let a: uint = h0 - let bb: uint = h1 - let c: uint = h2 - let d: uint = h3 - let e: uint = h4 - foreach t: uint in 0u, 80u { - let f: uint = 0u - let k: uint = 0u - if t < 20u { - f = (bb & c) | ((M32 ^ bb) & d) - k = 1518500249u - } else if t < 40u { - f = bb ^ c ^ d - k = 1859775393u - } else if t < 60u { - f = (bb & c) | (bb & d) | (c & d) - k = 2400959708u - } else { - f = bb ^ c ^ d - k = 3395469782u - } - let tt: uint = (rotl32(a, 5u) + (f & M32) + e + k + w[t]) & M32 - e = d - d = c - c = rotl32(bb, 30u) - bb = a - a = tt - } - h0 = (h0 + a) & M32 - h1 = (h1 + bb) & M32 - h2 = (h2 + c) & M32 - h3 = (h3 + d) & M32 - h4 = (h4 + e) & M32 - } - - let digest: byte[] = new byte[](20) - let hs: uint[] = [h0, h1, h2, h3, h4] - foreach i: uint in 0u, 5u { - let v: uint = hs[i] - digest[i * 4u] = ((v >> 24u) & 255u) as byte - digest[i * 4u + 1u] = ((v >> 16u) & 255u) as byte - digest[i * 4u + 2u] = ((v >> 8u) & 255u) as byte - digest[i * 4u + 3u] = (v & 255u) as byte - } - return digest -} - -// Base64-encode data[0..n). -local fn b64encode(data: byte[], n: uint) -> byte[] { - let outLen: uint = ((n + 2u) / 3u) * 4u - let out: byte[] = new byte[](outLen) - let i: uint = 0u - let o: uint = 0u - while i + 3u <= n { - let x: uint = ((data[i] as uint) << 16u) | ((data[i + 1u] as uint) << 8u) | (data[i + 2u] as uint) - out[o] = B64[(x >> 18u) & 63u] - out[o + 1u] = B64[(x >> 12u) & 63u] - out[o + 2u] = B64[(x >> 6u) & 63u] - out[o + 3u] = B64[x & 63u] - i = i + 3u - o = o + 4u - } - let rem: uint = n - i - if rem == 1u { - let x: uint = (data[i] as uint) << 16u - out[o] = B64[(x >> 18u) & 63u] - out[o + 1u] = B64[(x >> 12u) & 63u] - out[o + 2u] = 61 as byte - out[o + 3u] = 61 as byte - } else if rem == 2u { - let x: uint = ((data[i] as uint) << 16u) | ((data[i + 1u] as uint) << 8u) - out[o] = B64[(x >> 18u) & 63u] - out[o + 1u] = B64[(x >> 12u) & 63u] - out[o + 2u] = B64[(x >> 6u) & 63u] - out[o + 3u] = 61 as byte - } - return out -} - -// Sec-WebSocket-Accept = base64(SHA1(key + GUID)). +// Sec-WebSocket-Accept = base64(SHA1(key + GUID)) — computed natively. local fn wsAccept(key: byte[], keyStart: uint, keyEnd: uint) -> byte[] { - let keyLen: uint = keyEnd - keyStart - let concat: byte[] = new byte[](keyLen + WS_GUID.length) - foreach i: uint in 0u, keyLen { - concat[i] = key[keyStart + i] - } - foreach i: uint in 0u, WS_GUID.length { - concat[keyLen + i] = WS_GUID[i] - } - let digest: byte[] = sha1(concat, concat.length) - return b64encode(digest, 20u) + let out: byte[] = new byte[](28) + socket.socket_ws_accept(key, keyStart, keyEnd - keyStart, out) + return out } // Send the 101 upgrade response; `scratch` is the caller's output buffer. diff --git a/frameworks/typev/bundle/benchmark-code/stdlib/internal/socket.tc b/frameworks/typev/bundle/benchmark-code/stdlib/internal/socket.tc index 84e2522a8..11133663f 100644 --- a/frameworks/typev/bundle/benchmark-code/stdlib/internal/socket.tc +++ b/frameworks/typev/bundle/benchmark-code/stdlib/internal/socket.tc @@ -234,4 +234,11 @@ extern socket from "stdsocket" = { * Returns the number of complete request terminators found. */ fn count_http_terminators(buf: uint[], n: uint) -> uint + + /** + * WebSocket Sec-WebSocket-Accept = base64(SHA1(key + GUID)), written as + * 28 bytes into `out`. Native — the whole handshake digest in one call. + * Returns 0, or -1 if out < 28. + */ + fn socket_ws_accept(key: uint[], start: uint, len: uint, out: uint[]) -> int } diff --git a/frameworks/typev/bundle/output.tvbc b/frameworks/typev/bundle/output.tvbc index baf128c9c..4f04b5450 100644 Binary files a/frameworks/typev/bundle/output.tvbc and b/frameworks/typev/bundle/output.tvbc differ