Added some crypto 100% implemented in Fun. (0.11.0)
This commit is contained in:
parent
5d3cfd9f65
commit
12a5646d56
25 changed files with 1926 additions and 12 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
cmake_minimum_required(VERSION 3.16)
|
cmake_minimum_required(VERSION 3.16)
|
||||||
project(fun VERSION 0.10.0 LANGUAGES C)
|
project(fun VERSION 0.11.0 LANGUAGES C)
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 11)
|
set(CMAKE_C_STANDARD 11)
|
||||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||||
|
|
|
||||||
32
examples/crc32_demo.fun
Normal file
32
examples/crc32_demo.fun
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
#!/usr/bin/env fun
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Fun programming language.
|
||||||
|
* https://hanez.org/project/fun/
|
||||||
|
*
|
||||||
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
* Licensed under the terms of the ISC license.
|
||||||
|
* https://opensource.org/license/isc-license-txt
|
||||||
|
*
|
||||||
|
* Added: 2025-09-29
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Include the pure Fun CRC32 library
|
||||||
|
include "lib/crypt/crc32.fun"
|
||||||
|
|
||||||
|
// Create a Class-typed instance to prevent accidental shadowing
|
||||||
|
Class c32 = CRC32()
|
||||||
|
print(typeof(c32)) // should be "Class"
|
||||||
|
|
||||||
|
print("=== CRC32 demo (hex input) ===")
|
||||||
|
|
||||||
|
// "" (empty) -> 00000000
|
||||||
|
print(c32.crc32_hex(""))
|
||||||
|
|
||||||
|
// "abc" -> 352441c2
|
||||||
|
print(c32.crc32_hex("616263"))
|
||||||
|
|
||||||
|
// "123456789" -> cbf43926
|
||||||
|
print(c32.crc32_hex("313233343536373839"))
|
||||||
|
|
||||||
|
print("=== Done ===")
|
||||||
34
examples/md5_demo.fun
Normal file
34
examples/md5_demo.fun
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
#!/usr/bin/env fun
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Fun programming language.
|
||||||
|
* https://hanez.org/project/fun/
|
||||||
|
*
|
||||||
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
* Licensed under the terms of the ISC license.
|
||||||
|
* https://opensource.org/license/isc-license-txt
|
||||||
|
*
|
||||||
|
* Added: 2025-09-29
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Include the pure Fun MD5 library
|
||||||
|
include "lib/crypt/md5.fun"
|
||||||
|
|
||||||
|
// create a hasher instance
|
||||||
|
md5 = MD5()
|
||||||
|
|
||||||
|
print("=== MD5 demo (hex input) ===")
|
||||||
|
|
||||||
|
// "abc" => 0x61 0x62 0x63
|
||||||
|
print(md5.md5_hex("616263")) // -> 900150983cd24fb0d6963f7d28e17f72
|
||||||
|
|
||||||
|
// empty string "" => hex ""
|
||||||
|
print(md5.md5_hex("")) // -> d41d8cd98f00b204e9800998ecf8427e
|
||||||
|
|
||||||
|
// "message digest"
|
||||||
|
print(md5.md5_hex("6d65737361676520646967657374")) // -> f96b697d7cb7938d525a2f31aaf161d0
|
||||||
|
|
||||||
|
// "abcdefghijklmnopqrstuvwxyz"
|
||||||
|
print(md5.md5_hex("6162636465666768696a6b6c6d6e6f707172737475767778797a")) // -> c3fcd3d76192e4007dfb496cca67e13b
|
||||||
|
|
||||||
|
print("=== Done ===")
|
||||||
33
examples/sha256_demo.fun
Normal file
33
examples/sha256_demo.fun
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
#!/usr/bin/env fun
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Fun programming language.
|
||||||
|
* https://hanez.org/project/fun/
|
||||||
|
*
|
||||||
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
* Licensed under the terms of the ISC license.
|
||||||
|
* https://opensource.org/license/isc-license-txt
|
||||||
|
*
|
||||||
|
* Added: 2025-09-29
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Include the pure Fun SHA-256 library
|
||||||
|
include "lib/crypt/sha256.fun"
|
||||||
|
|
||||||
|
sha = SHA256()
|
||||||
|
|
||||||
|
print("=== SHA-256 demo (hex input) ===")
|
||||||
|
|
||||||
|
// "" (empty)
|
||||||
|
print(sha.sha256_hex("")) // -> e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
|
||||||
|
|
||||||
|
// "abc"
|
||||||
|
print(sha.sha256_hex("616263")) // -> ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
|
||||||
|
|
||||||
|
// "message digest"
|
||||||
|
print(sha.sha256_hex("6d65737361676520646967657374")) // -> f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650
|
||||||
|
|
||||||
|
// "abcdefghijklmnopqrstuvwxyz"
|
||||||
|
print(sha.sha256_hex("6162636465666768696a6b6c6d6e6f707172737475767778797a")) // -> 71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73
|
||||||
|
|
||||||
|
print("=== Done ===")
|
||||||
35
examples/sha512_demo.fun
Normal file
35
examples/sha512_demo.fun
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
#!/usr/bin/env fun
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Fun programming language.
|
||||||
|
* https://hanez.org/project/fun/
|
||||||
|
*
|
||||||
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
* Licensed under the terms of the ISC license.
|
||||||
|
* https://opensource.org/license/isc-license-txt
|
||||||
|
*
|
||||||
|
* Added: 2025-09-29
|
||||||
|
*/
|
||||||
|
|
||||||
|
include "lib/crypt/sha512.fun"
|
||||||
|
|
||||||
|
sha = SHA512()
|
||||||
|
|
||||||
|
print("=== SHA-512 demo (hex input) ===")
|
||||||
|
|
||||||
|
// "" (empty)
|
||||||
|
print(sha.sha512_hex("")) // -> cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce
|
||||||
|
// 47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e
|
||||||
|
|
||||||
|
// "abc"
|
||||||
|
print(sha.sha512_hex("616263")) // -> ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a
|
||||||
|
// 2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f
|
||||||
|
|
||||||
|
// "message digest"
|
||||||
|
print(sha.sha512_hex("6d65737361676520646967657374"))
|
||||||
|
// -> 107dbf389d9e9f71a3a95f6c055b9251
|
||||||
|
// bc5268c2be16d6c13492ea45b0199f33
|
||||||
|
// 09e16455ab1e96118e8a905d5597b721
|
||||||
|
// 3f36cc64dab6c0c5
|
||||||
|
|
||||||
|
print("=== Done ===")
|
||||||
34
examples/sha512_str_demo.fun
Normal file
34
examples/sha512_str_demo.fun
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
#!/usr/bin/env fun
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Fun programming language.
|
||||||
|
* https://hanez.org/project/fun/
|
||||||
|
*
|
||||||
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
* Licensed under the terms of the ISC license.
|
||||||
|
* https://opensource.org/license/isc-license-txt
|
||||||
|
*
|
||||||
|
* Added: 2025-09-29
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Demo: SHA-512 of raw strings (no hex decoding)
|
||||||
|
include "lib/crypt/sha512.fun"
|
||||||
|
|
||||||
|
sha = SHA512()
|
||||||
|
|
||||||
|
print("=== SHA-512 demo (raw string input via sha512_str) ===")
|
||||||
|
|
||||||
|
// "" (empty string)
|
||||||
|
print(sha.sha512_str("")) // -> cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce
|
||||||
|
// 47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e
|
||||||
|
|
||||||
|
// "abc"
|
||||||
|
print(sha.sha512_str("abc")) // -> ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a
|
||||||
|
// 2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f
|
||||||
|
|
||||||
|
// "616263" (the literal characters '6','1','6','2','6','3')
|
||||||
|
print(sha.sha512_str("616263")) // -> ee21095236a9c037f705f41ffa3cf60869891fcdb461c5c8fe50e5b1711a27b
|
||||||
|
// fc02de2e387228651bdd034113cc59af777fdb9c915b70fdbed0eeacf7113296b
|
||||||
|
|
||||||
|
print("=== Note ===")
|
||||||
|
print("sha512_str(\"616263\") hashes the text 616263; sha512_hex(\"616263\") hashes bytes 0x61 0x62 0x63 (abc).")
|
||||||
187
lib/crypt/crc32.fun
Normal file
187
lib/crypt/crc32.fun
Normal file
|
|
@ -0,0 +1,187 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Fun programming language.
|
||||||
|
* https://hanez.org/project/fun/
|
||||||
|
*
|
||||||
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
* Licensed under the terms of the ISC license.
|
||||||
|
* https://opensource.org/license/isc-license-txt
|
||||||
|
*
|
||||||
|
* Added: 2025-09-29
|
||||||
|
*/
|
||||||
|
|
||||||
|
// lib/crypt/crc32.fun
|
||||||
|
// Pure Fun CRC32 (IEEE 802.3, polynomial 0xEDB88320, reflected) operating on hex-string input.
|
||||||
|
//
|
||||||
|
// Public API (class):
|
||||||
|
// crc = CRC32()
|
||||||
|
// crc.crc32_hex(hexStr) -> digest hex string (lowercase, 8 hex chars)
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// print(CRC32().crc32_hex("616263")) // "abc" => 352441c2
|
||||||
|
|
||||||
|
class CRC32()
|
||||||
|
// hex helpers
|
||||||
|
fun hex_val(this, ch)
|
||||||
|
if (ch == "0")
|
||||||
|
return 0
|
||||||
|
else if (ch == "1")
|
||||||
|
return 1
|
||||||
|
else if (ch == "2")
|
||||||
|
return 2
|
||||||
|
else if (ch == "3")
|
||||||
|
return 3
|
||||||
|
else if (ch == "4")
|
||||||
|
return 4
|
||||||
|
else if (ch == "5")
|
||||||
|
return 5
|
||||||
|
else if (ch == "6")
|
||||||
|
return 6
|
||||||
|
else if (ch == "7")
|
||||||
|
return 7
|
||||||
|
else if (ch == "8")
|
||||||
|
return 8
|
||||||
|
else if (ch == "9")
|
||||||
|
return 9
|
||||||
|
else if (ch == "a" || ch == "A")
|
||||||
|
return 10
|
||||||
|
else if (ch == "b" || ch == "B")
|
||||||
|
return 11
|
||||||
|
else if (ch == "c" || ch == "C")
|
||||||
|
return 12
|
||||||
|
else if (ch == "d" || ch == "D")
|
||||||
|
return 13
|
||||||
|
else if (ch == "e" || ch == "E")
|
||||||
|
return 14
|
||||||
|
else if (ch == "f" || ch == "F")
|
||||||
|
return 15
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
|
||||||
|
fun byte_from_hex_pair(this, hh)
|
||||||
|
hi = this.hex_val(substr(hh, 0, 1))
|
||||||
|
lo = this.hex_val(substr(hh, 1, 1))
|
||||||
|
return hi * 16 + lo
|
||||||
|
|
||||||
|
fun from_hex(this, hex)
|
||||||
|
// ensure we operate on a string
|
||||||
|
hex = to_string(hex)
|
||||||
|
arr = []
|
||||||
|
number i = 0
|
||||||
|
while true
|
||||||
|
ch1 = substr(hex, i, 1)
|
||||||
|
if (typeof(ch1) != "String")
|
||||||
|
break
|
||||||
|
if (ch1 == "")
|
||||||
|
break
|
||||||
|
ch2 = substr(hex, i + 1, 1)
|
||||||
|
if (typeof(ch2) != "String")
|
||||||
|
break
|
||||||
|
if (ch2 == "")
|
||||||
|
break
|
||||||
|
pair = join([ch1, ch2], "")
|
||||||
|
number b = this.byte_from_hex_pair(pair)
|
||||||
|
push(arr, b)
|
||||||
|
i = i + 2
|
||||||
|
return arr
|
||||||
|
|
||||||
|
fun two_hex(this, n)
|
||||||
|
n = n % 256
|
||||||
|
hexd = "0123456789abcdef"
|
||||||
|
number hi = n / 16
|
||||||
|
number lo = n % 16
|
||||||
|
ch1 = substr(hexd, hi, 1)
|
||||||
|
ch2 = substr(hexd, lo, 1)
|
||||||
|
return join([ch1, ch2], "")
|
||||||
|
|
||||||
|
fun u32(this, x)
|
||||||
|
m = 4294967296
|
||||||
|
while x < 0
|
||||||
|
x = x + m
|
||||||
|
while x >= m
|
||||||
|
x = x - m
|
||||||
|
return x
|
||||||
|
|
||||||
|
// Core per-byte CRC (bit-by-bit, reflected), polynomial 0xEDB88320
|
||||||
|
fun crc32_bytes(this, bytes)
|
||||||
|
crc = 4294967295 // 0xFFFFFFFF initial
|
||||||
|
poly = 3988292384 // 0xEDB88320
|
||||||
|
number i = 0
|
||||||
|
L = len(bytes)
|
||||||
|
while i < L
|
||||||
|
// crc ^= byte
|
||||||
|
crc = bxor(crc, bytes[i])
|
||||||
|
// 8 times
|
||||||
|
j = 0
|
||||||
|
while j < 8
|
||||||
|
if (band(crc, 1) == 1)
|
||||||
|
// crc = (crc >> 1) ^ poly
|
||||||
|
crc = bxor(shr(crc, 1), poly)
|
||||||
|
else
|
||||||
|
// crc >>= 1
|
||||||
|
crc = shr(crc, 1)
|
||||||
|
j = j + 1
|
||||||
|
i = i + 1
|
||||||
|
// final xor
|
||||||
|
crc = bxor(crc, 4294967295)
|
||||||
|
return this.u32(crc)
|
||||||
|
|
||||||
|
fun crc32_hex(this, hexStr)
|
||||||
|
// Process hex input directly with only core built-ins; no method calls on 'this'
|
||||||
|
hex = to_string(hexStr)
|
||||||
|
hexd = "0123456789abcdef"
|
||||||
|
|
||||||
|
// CRC state
|
||||||
|
number crc = 4294967295 // 0xFFFFFFFF
|
||||||
|
number poly = 3988292384 // 0xEDB88320
|
||||||
|
|
||||||
|
// Iterate two hex nibbles at a time
|
||||||
|
number i = 0
|
||||||
|
while true
|
||||||
|
ch1 = substr(hex, i, 1)
|
||||||
|
if (typeof(ch1) != "String" || ch1 == "")
|
||||||
|
break
|
||||||
|
ch2 = substr(hex, i + 1, 1)
|
||||||
|
if (typeof(ch2) != "String" || ch2 == "")
|
||||||
|
break
|
||||||
|
|
||||||
|
// Convert two hex chars to a byte without helper calls
|
||||||
|
// find returns index of substring or -1; ensure lowercase
|
||||||
|
h1 = find(hexd, to_string(ch1))
|
||||||
|
h2 = find(hexd, to_string(ch2))
|
||||||
|
if (h1 < 0 || h2 < 0)
|
||||||
|
// invalid hex -> stop
|
||||||
|
break
|
||||||
|
number b = h1 * 16 + h2
|
||||||
|
|
||||||
|
// crc ^= b
|
||||||
|
crc = bxor(crc, b)
|
||||||
|
// 8 rounds
|
||||||
|
number j = 0
|
||||||
|
while j < 8
|
||||||
|
if (band(crc, 1) == 1)
|
||||||
|
crc = bxor(shr(crc, 1), poly)
|
||||||
|
else
|
||||||
|
crc = shr(crc, 1)
|
||||||
|
j = j + 1
|
||||||
|
|
||||||
|
i = i + 2
|
||||||
|
|
||||||
|
// final xor
|
||||||
|
crc = bxor(crc, 4294967295)
|
||||||
|
|
||||||
|
// Format result as 8 hex chars big-endian without calling this.two_hex
|
||||||
|
number b3 = (crc / 16777216) % 256
|
||||||
|
number b2 = (crc / 65536) % 256
|
||||||
|
number b1 = (crc / 256) % 256
|
||||||
|
number b0 = crc % 256
|
||||||
|
|
||||||
|
// encode a single byte to two hex chars using hexd and substr
|
||||||
|
fun b2hex(byte)
|
||||||
|
number hi = (byte / 16) % 16
|
||||||
|
number lo = (byte % 16)
|
||||||
|
c1 = substr(hexd, hi, 1)
|
||||||
|
c2 = substr(hexd, lo, 1)
|
||||||
|
return join([c1, c2], "")
|
||||||
|
|
||||||
|
parts = [ b2hex(b3), b2hex(b2), b2hex(b1), b2hex(b0) ]
|
||||||
|
return join(parts, "")
|
||||||
519
lib/crypt/md5.fun
Normal file
519
lib/crypt/md5.fun
Normal file
|
|
@ -0,0 +1,519 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Fun programming language.
|
||||||
|
* https://hanez.org/project/fun/
|
||||||
|
*
|
||||||
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
* Licensed under the terms of the ISC license.
|
||||||
|
* https://opensource.org/license/isc-license-txt
|
||||||
|
*
|
||||||
|
* Added: 2025-09-29
|
||||||
|
*/
|
||||||
|
|
||||||
|
// lib/crypt/md5.fun
|
||||||
|
// Pure Fun implementation of MD5 operating on hex-string input.
|
||||||
|
//
|
||||||
|
// Public API:
|
||||||
|
// md5_hex(hexStr) -> digest hex string (lowercase)
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// print(md5_hex("616263")) // "abc" => 900150983cd24fb0d6963f7d28e17f72
|
||||||
|
|
||||||
|
/* Legacy global API and helpers are commented out below.
|
||||||
|
Please use the MD5 class (see at the end of this file).
|
||||||
|
|
||||||
|
fun u32(x)
|
||||||
|
// bring x into [0 .. 2^32-1] by modular reduction
|
||||||
|
m = 4294967296
|
||||||
|
while x < 0
|
||||||
|
x = x + m
|
||||||
|
while x >= m
|
||||||
|
x = x - m
|
||||||
|
return x
|
||||||
|
|
||||||
|
fun shl32(x, s)
|
||||||
|
return shl(u32(x), s)
|
||||||
|
|
||||||
|
fun shr32(x, s)
|
||||||
|
return shr(u32(x), s)
|
||||||
|
|
||||||
|
fun rol32(x, s)
|
||||||
|
return rol(u32(x), s)
|
||||||
|
|
||||||
|
fun and32(a, b)
|
||||||
|
return band(u32(a), u32(b))
|
||||||
|
|
||||||
|
fun or32(a, b)
|
||||||
|
return bor(u32(a), u32(b))
|
||||||
|
|
||||||
|
fun xor32(a, b)
|
||||||
|
return bxor(u32(a), u32(b))
|
||||||
|
|
||||||
|
fun not32(x)
|
||||||
|
return bnot(u32(x))
|
||||||
|
|
||||||
|
fun add32(a, b)
|
||||||
|
return u32(a + b)
|
||||||
|
|
||||||
|
fun add32_4(a, b, c, d)
|
||||||
|
return u32(u32(u32(a + b) + c) + d)
|
||||||
|
|
||||||
|
fun hex_val(ch)
|
||||||
|
if (ch == "0")
|
||||||
|
return 0
|
||||||
|
else if (ch == "1")
|
||||||
|
return 1
|
||||||
|
else if (ch == "2")
|
||||||
|
return 2
|
||||||
|
else if (ch == "3")
|
||||||
|
return 3
|
||||||
|
else if (ch == "4")
|
||||||
|
return 4
|
||||||
|
else if (ch == "5")
|
||||||
|
return 5
|
||||||
|
else if (ch == "6")
|
||||||
|
return 6
|
||||||
|
else if (ch == "7")
|
||||||
|
return 7
|
||||||
|
else if (ch == "8")
|
||||||
|
return 8
|
||||||
|
else if (ch == "9")
|
||||||
|
return 9
|
||||||
|
else if (ch == "a" || ch == "A")
|
||||||
|
return 10
|
||||||
|
else if (ch == "b" || ch == "B")
|
||||||
|
return 11
|
||||||
|
else if (ch == "c" || ch == "C")
|
||||||
|
return 12
|
||||||
|
else if (ch == "d" || ch == "D")
|
||||||
|
return 13
|
||||||
|
else if (ch == "e" || ch == "E")
|
||||||
|
return 14
|
||||||
|
else if (ch == "f" || ch == "F")
|
||||||
|
return 15
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
|
||||||
|
fun byte_from_hex_pair(hh)
|
||||||
|
// hh is 2-char string
|
||||||
|
hi = hex_val(substr(hh, 0, 1))
|
||||||
|
lo = hex_val(substr(hh, 1, 1))
|
||||||
|
return hi * 16 + lo
|
||||||
|
|
||||||
|
fun from_hex(hex)
|
||||||
|
// parse even-length hex string to array of bytes (numbers 0..255)
|
||||||
|
arr = []
|
||||||
|
i = 0
|
||||||
|
n = len(hex)
|
||||||
|
while i + 1 < n
|
||||||
|
b = byte_from_hex_pair(substr(hex, i, 2))
|
||||||
|
push(arr, b)
|
||||||
|
i = i + 2
|
||||||
|
return arr
|
||||||
|
|
||||||
|
fun two_hex(n)
|
||||||
|
n = n % 256
|
||||||
|
d = ["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"]
|
||||||
|
hi = n / 16
|
||||||
|
lo = n % 16
|
||||||
|
// build with join
|
||||||
|
parts = [d[hi], d[lo]]
|
||||||
|
return join(parts, "")
|
||||||
|
|
||||||
|
fun bytes_to_hex(arr)
|
||||||
|
i = 0
|
||||||
|
out = []
|
||||||
|
while i < len(arr)
|
||||||
|
push(out, two_hex(arr[i]))
|
||||||
|
i = i + 1
|
||||||
|
return join(out, "")
|
||||||
|
|
||||||
|
// MD5 auxiliary functions
|
||||||
|
fun F(x, y, z)
|
||||||
|
return or32(and32(x, y), and32(not32(x), z))
|
||||||
|
|
||||||
|
fun G(x, y, z)
|
||||||
|
return or32(and32(x, z), and32(y, not32(z)))
|
||||||
|
|
||||||
|
fun H(x, y, z)
|
||||||
|
return xor32(xor32(x, y), z)
|
||||||
|
|
||||||
|
fun I(x, y, z)
|
||||||
|
return xor32(y, or32(x, not32(z)))
|
||||||
|
|
||||||
|
// T constants (K) = floor(abs(sin(i+1)) * 2^32), i=0..63
|
||||||
|
K = [
|
||||||
|
3614090360, 3905402710, 606105819, 3250441966, 4118548399, 1200080426, 2821735955, 4249261313,
|
||||||
|
1770035416, 2336552879, 4294925233, 2304563134, 1804603682, 4254626195, 2792965006, 1236535329,
|
||||||
|
4129170786, 3225465664, 643717713, 3921069994, 3593408605, 38016083, 3634488961, 3889429448,
|
||||||
|
568446438, 3275163606, 4107603335, 1163531501, 2850285829, 4243563512, 1735328473, 2368359562,
|
||||||
|
4294588738, 2272392833, 1839030562, 4259657740, 2763975236, 1272893353, 4139469664, 3200236656,
|
||||||
|
681279174, 3936430074, 3572445317, 76029189, 3654602809, 3873151461, 530742520, 3299628645,
|
||||||
|
4096336452, 1126891415, 2878612391, 4237533241, 1700485571, 2399980690, 4293915773, 2240044497,
|
||||||
|
1873313359, 4264355552, 2734768916, 1309151649, 4149444226, 3174756917, 718787259, 3951481745
|
||||||
|
]
|
||||||
|
|
||||||
|
// per-round rotation amounts
|
||||||
|
S = [
|
||||||
|
7,12,17,22, 7,12,17,22, 7,12,17,22, 7,12,17,22,
|
||||||
|
5, 9,14,20, 5, 9,14,20, 5, 9,14,20, 5, 9,14,20,
|
||||||
|
4,11,16,23, 4,11,16,23, 4,11,16,23, 4,11,16,23,
|
||||||
|
6,10,15,21, 6,10,15,21, 6,10,15,21, 6,10,15,21
|
||||||
|
]
|
||||||
|
|
||||||
|
fun pad_bytes(bytes)
|
||||||
|
// returns a new array with MD5 padding
|
||||||
|
L = len(bytes)
|
||||||
|
out = []
|
||||||
|
// copy original bytes
|
||||||
|
i = 0
|
||||||
|
while i < L
|
||||||
|
push(out, bytes[i])
|
||||||
|
i = i + 1
|
||||||
|
// append 0x80
|
||||||
|
push(out, 128)
|
||||||
|
// pad zeros until length % 64 == 56
|
||||||
|
while (len(out) % 64) != 56
|
||||||
|
push(out, 0)
|
||||||
|
// append length (bits) as 64-bit little-endian
|
||||||
|
len_bits = L * 8
|
||||||
|
j = 0
|
||||||
|
while j < 8
|
||||||
|
b = (len_bits / pow(2, 8 * j)) % 256
|
||||||
|
push(out, b)
|
||||||
|
j = j + 1
|
||||||
|
return out
|
||||||
|
|
||||||
|
fun word32_le(b0, b1, b2, b3)
|
||||||
|
return u32(b0 + b1 * 256 + b2 * 65536 + b3 * 16777216)
|
||||||
|
|
||||||
|
fun process_block(state, block)
|
||||||
|
// state: [a0,b0,c0,d0], block: 64 bytes array
|
||||||
|
a0 = state[0]
|
||||||
|
b0 = state[1]
|
||||||
|
c0 = state[2]
|
||||||
|
d0 = state[3]
|
||||||
|
|
||||||
|
// M[16] little-endian words
|
||||||
|
M = []
|
||||||
|
i = 0
|
||||||
|
while i < 16
|
||||||
|
j = i * 4
|
||||||
|
w = word32_le(block[j], block[j+1], block[j+2], block[j+3])
|
||||||
|
push(M, w)
|
||||||
|
i = i + 1
|
||||||
|
|
||||||
|
A = a0
|
||||||
|
B = b0
|
||||||
|
C = c0
|
||||||
|
D = d0
|
||||||
|
|
||||||
|
k = 0
|
||||||
|
while k < 64
|
||||||
|
if (k < 16)
|
||||||
|
f = F(B, C, D)
|
||||||
|
g = k
|
||||||
|
else if (k < 32)
|
||||||
|
f = G(B, C, D)
|
||||||
|
g = (5 * k + 1) % 16
|
||||||
|
else if (k < 48)
|
||||||
|
f = H(B, C, D)
|
||||||
|
g = (3 * k + 5) % 16
|
||||||
|
else
|
||||||
|
f = I(B, C, D)
|
||||||
|
g = (7 * k) % 16
|
||||||
|
|
||||||
|
tmp = D
|
||||||
|
sum = add32_4(A, f, K[k], M[g])
|
||||||
|
D = C
|
||||||
|
C = B
|
||||||
|
B = add32(B, rol32(sum, S[k]))
|
||||||
|
A = tmp
|
||||||
|
k = k + 1
|
||||||
|
|
||||||
|
state[0] = add32(state[0], A)
|
||||||
|
state[1] = add32(state[1], B)
|
||||||
|
state[2] = add32(state[2], C)
|
||||||
|
state[3] = add32(state[3], D)
|
||||||
|
return state
|
||||||
|
|
||||||
|
fun md5_bytes(bytes)
|
||||||
|
// returns array of 16 bytes of digest
|
||||||
|
data = pad_bytes(bytes)
|
||||||
|
// initial state
|
||||||
|
a0 = 1732584193 // 0x67452301
|
||||||
|
b0 = 4023233417 // 0xEFCDAB89
|
||||||
|
c0 = 2562383102 // 0x98BADCFE
|
||||||
|
d0 = 271733878 // 0x10325476
|
||||||
|
state = [a0, b0, c0, d0]
|
||||||
|
|
||||||
|
off = 0
|
||||||
|
N = len(data)
|
||||||
|
while off < N
|
||||||
|
block = []
|
||||||
|
i = 0
|
||||||
|
while i < 64
|
||||||
|
push(block, data[off + i])
|
||||||
|
i = i + 1
|
||||||
|
state = process_block(state, block)
|
||||||
|
off = off + 64
|
||||||
|
|
||||||
|
// output digest as little-endian bytes of A,B,C,D
|
||||||
|
out = []
|
||||||
|
j = 0
|
||||||
|
while j < 4
|
||||||
|
v = state[j]
|
||||||
|
// 4 bytes little-endian
|
||||||
|
push(out, (v ) % 256)
|
||||||
|
push(out, (v / 256) % 256)
|
||||||
|
push(out, (v / 65536) % 256)
|
||||||
|
push(out, (v / 16777216) % 256)
|
||||||
|
j = j + 1
|
||||||
|
return out
|
||||||
|
|
||||||
|
fun md5_hex(hexStr)
|
||||||
|
bytes = from_hex(hexStr)
|
||||||
|
digest = md5_bytes(bytes)
|
||||||
|
return bytes_to_hex(digest)
|
||||||
|
|
||||||
|
// Class wrapper to avoid global name collisions and show class usage
|
||||||
|
*/
|
||||||
|
class MD5()
|
||||||
|
// MD5 constants (as fields)
|
||||||
|
K = [
|
||||||
|
3614090360, 3905402710, 606105819, 3250441966, 4118548399, 1200080426, 2821735955, 4249261313,
|
||||||
|
1770035416, 2336552879, 4294925233, 2304563134, 1804603682, 4254626195, 2792965006, 1236535329,
|
||||||
|
4129170786, 3225465664, 643717713, 3921069994, 3593408605, 38016083, 3634488961, 3889429448,
|
||||||
|
568446438, 3275163606, 4107603335, 1163531501, 2850285829, 4243563512, 1735328473, 2368359562,
|
||||||
|
4294588738, 2272392833, 1839030562, 4259657740, 2763975236, 1272893353, 4139469664, 3200236656,
|
||||||
|
681279174, 3936430074, 3572445317, 76029189, 3654602809, 3873151461, 530742520, 3299628645,
|
||||||
|
4096336452, 1126891415, 2878612391, 4237533241, 1700485571, 2399980690, 4293915773, 2240044497,
|
||||||
|
1873313359, 4264355552, 2734768916, 1309151649, 4149444226, 3174756917, 718787259, 3951481745
|
||||||
|
]
|
||||||
|
S = [
|
||||||
|
7,12,17,22, 7,12,17,22, 7,12,17,22, 7,12,17,22,
|
||||||
|
5, 9,14,20, 5, 9,14,20, 5, 9,14,20, 5, 9,14,20,
|
||||||
|
4,11,16,23, 4,11,16,23, 4,11,16,23, 4,11,16,23,
|
||||||
|
6,10,15,21, 6,10,15,21, 6,10,15,21, 6,10,15,21
|
||||||
|
]
|
||||||
|
|
||||||
|
// 32-bit helpers
|
||||||
|
fun u32(this, x)
|
||||||
|
m = 4294967296
|
||||||
|
while x < 0
|
||||||
|
x = x + m
|
||||||
|
while x >= m
|
||||||
|
x = x - m
|
||||||
|
return x
|
||||||
|
|
||||||
|
fun shl32(this, x, s)
|
||||||
|
return shl(this.u32(x), s)
|
||||||
|
|
||||||
|
fun shr32(this, x, s)
|
||||||
|
return shr(this.u32(x), s)
|
||||||
|
|
||||||
|
fun rol32(this, x, s)
|
||||||
|
return rol(this.u32(x), s)
|
||||||
|
|
||||||
|
fun and32(this, a, b)
|
||||||
|
return band(this.u32(a), this.u32(b))
|
||||||
|
|
||||||
|
fun or32(this, a, b)
|
||||||
|
return bor(this.u32(a), this.u32(b))
|
||||||
|
|
||||||
|
fun xor32(this, a, b)
|
||||||
|
return bxor(this.u32(a), this.u32(b))
|
||||||
|
|
||||||
|
fun not32(this, x)
|
||||||
|
return bnot(this.u32(x))
|
||||||
|
|
||||||
|
// tiny adders
|
||||||
|
fun add32(this, a, b)
|
||||||
|
return this.u32(a + b)
|
||||||
|
|
||||||
|
fun add32_4(this, a, b, c, d)
|
||||||
|
return this.u32(this.u32(this.u32(a + b) + c) + d)
|
||||||
|
|
||||||
|
// hex helpers
|
||||||
|
fun hex_val(this, ch)
|
||||||
|
if (ch == "0")
|
||||||
|
return 0
|
||||||
|
else if (ch == "1")
|
||||||
|
return 1
|
||||||
|
else if (ch == "2")
|
||||||
|
return 2
|
||||||
|
else if (ch == "3")
|
||||||
|
return 3
|
||||||
|
else if (ch == "4")
|
||||||
|
return 4
|
||||||
|
else if (ch == "5")
|
||||||
|
return 5
|
||||||
|
else if (ch == "6")
|
||||||
|
return 6
|
||||||
|
else if (ch == "7")
|
||||||
|
return 7
|
||||||
|
else if (ch == "8")
|
||||||
|
return 8
|
||||||
|
else if (ch == "9")
|
||||||
|
return 9
|
||||||
|
else if (ch == "a" || ch == "A")
|
||||||
|
return 10
|
||||||
|
else if (ch == "b" || ch == "B")
|
||||||
|
return 11
|
||||||
|
else if (ch == "c" || ch == "C")
|
||||||
|
return 12
|
||||||
|
else if (ch == "d" || ch == "D")
|
||||||
|
return 13
|
||||||
|
else if (ch == "e" || ch == "E")
|
||||||
|
return 14
|
||||||
|
else if (ch == "f" || ch == "F")
|
||||||
|
return 15
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
|
||||||
|
fun byte_from_hex_pair(this, hh)
|
||||||
|
hi = this.hex_val(substr(hh, 0, 1))
|
||||||
|
lo = this.hex_val(substr(hh, 1, 1))
|
||||||
|
return hi * 16 + lo
|
||||||
|
|
||||||
|
fun from_hex(this, hex)
|
||||||
|
arr = []
|
||||||
|
i = 0
|
||||||
|
n = len(hex)
|
||||||
|
while i + 1 < n
|
||||||
|
b = this.byte_from_hex_pair(substr(hex, i, 2))
|
||||||
|
push(arr, b)
|
||||||
|
i = i + 2
|
||||||
|
return arr
|
||||||
|
|
||||||
|
fun two_hex(this, n)
|
||||||
|
n = n % 256
|
||||||
|
d = ["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"]
|
||||||
|
hi = n / 16
|
||||||
|
lo = n % 16
|
||||||
|
parts = [d[hi], d[lo]]
|
||||||
|
return join(parts, "")
|
||||||
|
|
||||||
|
fun bytes_to_hex(this, arr)
|
||||||
|
i = 0
|
||||||
|
out = []
|
||||||
|
while i < len(arr)
|
||||||
|
push(out, this.two_hex(arr[i]))
|
||||||
|
i = i + 1
|
||||||
|
return join(out, "")
|
||||||
|
|
||||||
|
// MD5 aux
|
||||||
|
fun F(this, x, y, z)
|
||||||
|
return this.or32(this.and32(x, y), this.and32(this.not32(x), z))
|
||||||
|
|
||||||
|
fun G(this, x, y, z)
|
||||||
|
return this.or32(this.and32(x, z), this.and32(y, this.not32(z)))
|
||||||
|
|
||||||
|
fun H(this, x, y, z)
|
||||||
|
return this.xor32(this.xor32(x, y), z)
|
||||||
|
|
||||||
|
fun I(this, x, y, z)
|
||||||
|
return this.xor32(y, this.or32(x, this.not32(z)))
|
||||||
|
|
||||||
|
fun pad_bytes(this, bytes)
|
||||||
|
L = len(bytes)
|
||||||
|
out = []
|
||||||
|
i = 0
|
||||||
|
while i < L
|
||||||
|
push(out, bytes[i])
|
||||||
|
i = i + 1
|
||||||
|
push(out, 128)
|
||||||
|
while (len(out) % 64) != 56
|
||||||
|
push(out, 0)
|
||||||
|
len_bits = L * 8
|
||||||
|
j = 0
|
||||||
|
while j < 8
|
||||||
|
b = (len_bits / pow(2, 8 * j)) % 256
|
||||||
|
push(out, b)
|
||||||
|
j = j + 1
|
||||||
|
return out
|
||||||
|
|
||||||
|
fun word32_le(this, b0, b1, b2, b3)
|
||||||
|
return this.u32(b0 + b1 * 256 + b2 * 65536 + b3 * 16777216)
|
||||||
|
|
||||||
|
fun process_block(this, state, block)
|
||||||
|
a0 = state[0]
|
||||||
|
b0 = state[1]
|
||||||
|
c0 = state[2]
|
||||||
|
d0 = state[3]
|
||||||
|
|
||||||
|
M = []
|
||||||
|
i = 0
|
||||||
|
while i < 16
|
||||||
|
j = i * 4
|
||||||
|
w = this.word32_le(block[j], block[j+1], block[j+2], block[j+3])
|
||||||
|
push(M, w)
|
||||||
|
i = i + 1
|
||||||
|
|
||||||
|
A = a0
|
||||||
|
B = b0
|
||||||
|
C = c0
|
||||||
|
D = d0
|
||||||
|
|
||||||
|
k = 0
|
||||||
|
while k < 64
|
||||||
|
if (k < 16)
|
||||||
|
f = this.F(B, C, D)
|
||||||
|
g = k
|
||||||
|
else if (k < 32)
|
||||||
|
f = this.G(B, C, D)
|
||||||
|
g = (5 * k + 1) % 16
|
||||||
|
else if (k < 48)
|
||||||
|
f = this.H(B, C, D)
|
||||||
|
g = (3 * k + 5) % 16
|
||||||
|
else
|
||||||
|
f = this.I(B, C, D)
|
||||||
|
g = (7 * k) % 16
|
||||||
|
|
||||||
|
tmp = D
|
||||||
|
sum = this.add32_4(A, f, this.K[k], M[g])
|
||||||
|
D = C
|
||||||
|
C = B
|
||||||
|
B = this.add32(B, this.rol32(sum, this.S[k]))
|
||||||
|
A = tmp
|
||||||
|
k = k + 1
|
||||||
|
|
||||||
|
state[0] = this.add32(state[0], A)
|
||||||
|
state[1] = this.add32(state[1], B)
|
||||||
|
state[2] = this.add32(state[2], C)
|
||||||
|
state[3] = this.add32(state[3], D)
|
||||||
|
return state
|
||||||
|
|
||||||
|
fun md5_bytes(this, bytes)
|
||||||
|
data = this.pad_bytes(bytes)
|
||||||
|
a0 = 1732584193
|
||||||
|
b0 = 4023233417
|
||||||
|
c0 = 2562383102
|
||||||
|
d0 = 271733878
|
||||||
|
state = [a0, b0, c0, d0]
|
||||||
|
|
||||||
|
off = 0
|
||||||
|
N = len(data)
|
||||||
|
while off < N
|
||||||
|
block = []
|
||||||
|
i = 0
|
||||||
|
while i < 64
|
||||||
|
push(block, data[off + i])
|
||||||
|
i = i + 1
|
||||||
|
state = this.process_block(state, block)
|
||||||
|
off = off + 64
|
||||||
|
|
||||||
|
out = []
|
||||||
|
j = 0
|
||||||
|
while j < 4
|
||||||
|
v = state[j]
|
||||||
|
push(out, (v) % 256)
|
||||||
|
push(out, (v / 256) % 256)
|
||||||
|
push(out, (v / 65536) % 256)
|
||||||
|
push(out, (v / 16777216) % 256)
|
||||||
|
j = j + 1
|
||||||
|
return out
|
||||||
|
|
||||||
|
fun md5_hex(this, hexStr)
|
||||||
|
bytes = this.from_hex(hexStr)
|
||||||
|
digest = this.md5_bytes(bytes)
|
||||||
|
return this.bytes_to_hex(digest)
|
||||||
261
lib/crypt/sha256.fun
Normal file
261
lib/crypt/sha256.fun
Normal file
|
|
@ -0,0 +1,261 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Fun programming language.
|
||||||
|
* https://hanez.org/project/fun/
|
||||||
|
*
|
||||||
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
* Licensed under the terms of the ISC license.
|
||||||
|
* https://opensource.org/license/isc-license-txt
|
||||||
|
*
|
||||||
|
* Added: 2025-09-29
|
||||||
|
*/
|
||||||
|
|
||||||
|
// lib/crypt/sha256.fun
|
||||||
|
// Pure Fun SHA-256 implementation operating on hex-string input.
|
||||||
|
//
|
||||||
|
// Public API (class):
|
||||||
|
// sha = SHA256()
|
||||||
|
// sha.sha256_hex(hexStr) -> digest hex string (lowercase)
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// print(SHA256().sha256_hex("616263")) // "abc" => ba7816bf...
|
||||||
|
|
||||||
|
class SHA256()
|
||||||
|
// 32-bit helpers
|
||||||
|
fun u32(this, x)
|
||||||
|
m = 4294967296
|
||||||
|
while x < 0
|
||||||
|
x = x + m
|
||||||
|
while x >= m
|
||||||
|
x = x - m
|
||||||
|
return x
|
||||||
|
|
||||||
|
fun add32(this, a, b)
|
||||||
|
return this.u32(a + b)
|
||||||
|
|
||||||
|
fun add32_5(this, a, b, c, d, e)
|
||||||
|
return this.u32(this.u32(this.u32(this.u32(a + b) + c) + d) + e)
|
||||||
|
|
||||||
|
// bitwise (use native VM ops)
|
||||||
|
fun rotr32(this, x, s)
|
||||||
|
return ror(this.u32(x), s)
|
||||||
|
|
||||||
|
fun shr32(this, x, s)
|
||||||
|
return shr(this.u32(x), s)
|
||||||
|
|
||||||
|
fun ch(this, x, y, z)
|
||||||
|
// (x & y) ^ (~x & z)
|
||||||
|
return bxor(band(x, y), band(bnot(x), z))
|
||||||
|
|
||||||
|
fun maj(this, x, y, z)
|
||||||
|
// (x & y) ^ (x & z) ^ (y & z)
|
||||||
|
return bxor(bxor(band(x, y), band(x, z)), band(y, z))
|
||||||
|
|
||||||
|
fun bigSigma0(this, x)
|
||||||
|
// ROTR2 ^ ROTR13 ^ ROTR22
|
||||||
|
return bxor(bxor(this.rotr32(x, 2), this.rotr32(x, 13)), this.rotr32(x, 22))
|
||||||
|
|
||||||
|
fun bigSigma1(this, x)
|
||||||
|
// ROTR6 ^ ROTR11 ^ ROTR25
|
||||||
|
return bxor(bxor(this.rotr32(x, 6), this.rotr32(x, 11)), this.rotr32(x, 25))
|
||||||
|
|
||||||
|
fun smallSigma0(this, x)
|
||||||
|
// ROTR7 ^ ROTR18 ^ SHR3
|
||||||
|
return bxor(bxor(this.rotr32(x, 7), this.rotr32(x, 18)), this.shr32(x, 3))
|
||||||
|
|
||||||
|
fun smallSigma1(this, x)
|
||||||
|
// ROTR17 ^ ROTR19 ^ SHR10
|
||||||
|
return bxor(bxor(this.rotr32(x, 17), this.rotr32(x, 19)), this.shr32(x, 10))
|
||||||
|
|
||||||
|
// constants
|
||||||
|
IV = [
|
||||||
|
1779033703, 3144134277, 1013904242, 2773480762,
|
||||||
|
1359893119, 2600822924, 528734635, 1541459225
|
||||||
|
]
|
||||||
|
|
||||||
|
K = [
|
||||||
|
1116352408, 1899447441, 3049323471, 3921009573, 961987163, 1508970993, 2453635748, 2870763221,
|
||||||
|
3624381080, 310598401, 607225278, 1426881987, 1925078388, 2162078206, 2614888103, 3248222580,
|
||||||
|
3835390401, 4022224774, 264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986,
|
||||||
|
2554220882, 2821834349, 2952996808, 3210313671, 3336571891, 3584528711, 113926993, 338241895,
|
||||||
|
666307205, 773529912, 1294757372, 1396182291, 1695183700, 1986661051, 2177026350, 2456956037,
|
||||||
|
2730485921, 2820302411, 3259730800, 3345764771, 3516065817, 3600352804, 4094571909, 275423344,
|
||||||
|
430227734, 506948616, 659060556, 883997877, 958139571, 1322822218, 1537002063, 1747873779,
|
||||||
|
1955562222, 2024104815, 2227730452, 2361852424, 2428436474, 2756734187, 3204031479, 3329325298
|
||||||
|
]
|
||||||
|
|
||||||
|
// hex helpers
|
||||||
|
fun hex_val(this, ch)
|
||||||
|
if (ch == "0")
|
||||||
|
return 0
|
||||||
|
else if (ch == "1")
|
||||||
|
return 1
|
||||||
|
else if (ch == "2")
|
||||||
|
return 2
|
||||||
|
else if (ch == "3")
|
||||||
|
return 3
|
||||||
|
else if (ch == "4")
|
||||||
|
return 4
|
||||||
|
else if (ch == "5")
|
||||||
|
return 5
|
||||||
|
else if (ch == "6")
|
||||||
|
return 6
|
||||||
|
else if (ch == "7")
|
||||||
|
return 7
|
||||||
|
else if (ch == "8")
|
||||||
|
return 8
|
||||||
|
else if (ch == "9")
|
||||||
|
return 9
|
||||||
|
else if (ch == "a" || ch == "A")
|
||||||
|
return 10
|
||||||
|
else if (ch == "b" || ch == "B")
|
||||||
|
return 11
|
||||||
|
else if (ch == "c" || ch == "C")
|
||||||
|
return 12
|
||||||
|
else if (ch == "d" || ch == "D")
|
||||||
|
return 13
|
||||||
|
else if (ch == "e" || ch == "E")
|
||||||
|
return 14
|
||||||
|
else if (ch == "f" || ch == "F")
|
||||||
|
return 15
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
|
||||||
|
fun byte_from_hex_pair(this, hh)
|
||||||
|
hi = this.hex_val(substr(hh, 0, 1))
|
||||||
|
lo = this.hex_val(substr(hh, 1, 1))
|
||||||
|
return hi * 16 + lo
|
||||||
|
|
||||||
|
fun from_hex(this, hex)
|
||||||
|
arr = []
|
||||||
|
i = 0
|
||||||
|
n = len(hex)
|
||||||
|
while i + 1 < n
|
||||||
|
b = this.byte_from_hex_pair(substr(hex, i, 2))
|
||||||
|
push(arr, b)
|
||||||
|
i = i + 2
|
||||||
|
return arr
|
||||||
|
|
||||||
|
fun two_hex(this, n)
|
||||||
|
n = n % 256
|
||||||
|
d = ["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"]
|
||||||
|
hi = n / 16
|
||||||
|
lo = n % 16
|
||||||
|
parts = [d[hi], d[lo]]
|
||||||
|
return join(parts, "")
|
||||||
|
|
||||||
|
fun bytes_to_hex(this, arr)
|
||||||
|
i = 0
|
||||||
|
out = []
|
||||||
|
while i < len(arr)
|
||||||
|
push(out, this.two_hex(arr[i]))
|
||||||
|
i = i + 1
|
||||||
|
return join(out, "")
|
||||||
|
|
||||||
|
// padding (SHA-256)
|
||||||
|
fun pad_bytes(this, bytes)
|
||||||
|
L = len(bytes)
|
||||||
|
out = []
|
||||||
|
i = 0
|
||||||
|
while i < L
|
||||||
|
push(out, bytes[i])
|
||||||
|
i = i + 1
|
||||||
|
// append 0x80
|
||||||
|
push(out, 128)
|
||||||
|
// zeros until length % 64 == 56
|
||||||
|
while (len(out) % 64) != 56
|
||||||
|
push(out, 0)
|
||||||
|
// length in bits as 64-bit big-endian
|
||||||
|
len_bits = L * 8
|
||||||
|
j = 7
|
||||||
|
while j >= 0
|
||||||
|
b = (len_bits / pow(2, 8 * (7 - j))) % 256
|
||||||
|
push(out, b)
|
||||||
|
j = j - 1
|
||||||
|
return out
|
||||||
|
|
||||||
|
fun word32_be(this, b0, b1, b2, b3)
|
||||||
|
// big-endian
|
||||||
|
return this.u32(b0 * 16777216 + b1 * 65536 + b2 * 256 + b3)
|
||||||
|
|
||||||
|
fun process_block(this, state, block)
|
||||||
|
// prepare message schedule W[64]
|
||||||
|
W = []
|
||||||
|
t = 0
|
||||||
|
while t < 16
|
||||||
|
j = t * 4
|
||||||
|
push(W, this.word32_be(block[j], block[j+1], block[j+2], block[j+3]))
|
||||||
|
t = t + 1
|
||||||
|
|
||||||
|
while t < 64
|
||||||
|
s0 = this.smallSigma0(W[t - 15])
|
||||||
|
s1 = this.smallSigma1(W[t - 2])
|
||||||
|
wt = this.add32_5(W[t - 16], s0, W[t - 7], s1, 0)
|
||||||
|
// reuse array position via push (we can index existing after push)
|
||||||
|
push(W, wt)
|
||||||
|
t = t + 1
|
||||||
|
|
||||||
|
a = state[0]
|
||||||
|
b = state[1]
|
||||||
|
c = state[2]
|
||||||
|
d = state[3]
|
||||||
|
e = state[4]
|
||||||
|
f = state[5]
|
||||||
|
g = state[6]
|
||||||
|
h = state[7]
|
||||||
|
|
||||||
|
t = 0
|
||||||
|
while t < 64
|
||||||
|
T1 = this.add32_5(h, this.bigSigma1(e), this.ch(e, f, g), this.K[t], W[t])
|
||||||
|
T2 = this.add32(this.bigSigma0(a), this.maj(a, b, c))
|
||||||
|
h = g
|
||||||
|
g = f
|
||||||
|
f = e
|
||||||
|
e = this.add32(d, T1)
|
||||||
|
d = c
|
||||||
|
c = b
|
||||||
|
b = a
|
||||||
|
a = this.add32(T1, T2)
|
||||||
|
t = t + 1
|
||||||
|
|
||||||
|
state[0] = this.add32(state[0], a)
|
||||||
|
state[1] = this.add32(state[1], b)
|
||||||
|
state[2] = this.add32(state[2], c)
|
||||||
|
state[3] = this.add32(state[3], d)
|
||||||
|
state[4] = this.add32(state[4], e)
|
||||||
|
state[5] = this.add32(state[5], f)
|
||||||
|
state[6] = this.add32(state[6], g)
|
||||||
|
state[7] = this.add32(state[7], h)
|
||||||
|
return state
|
||||||
|
|
||||||
|
fun sha256_bytes(this, bytes)
|
||||||
|
data = this.pad_bytes(bytes)
|
||||||
|
// initial hash value (copy IV)
|
||||||
|
H = [ this.IV[0], this.IV[1], this.IV[2], this.IV[3], this.IV[4], this.IV[5], this.IV[6], this.IV[7] ]
|
||||||
|
|
||||||
|
off = 0
|
||||||
|
N = len(data)
|
||||||
|
while off < N
|
||||||
|
block = []
|
||||||
|
i = 0
|
||||||
|
while i < 64
|
||||||
|
push(block, data[off + i])
|
||||||
|
i = i + 1
|
||||||
|
H = this.process_block(H, block)
|
||||||
|
off = off + 64
|
||||||
|
|
||||||
|
// digest: 32 bytes big-endian
|
||||||
|
out = []
|
||||||
|
j = 0
|
||||||
|
while j < 8
|
||||||
|
v = H[j]
|
||||||
|
push(out, (v / 16777216) % 256)
|
||||||
|
push(out, (v / 65536) % 256)
|
||||||
|
push(out, (v / 256) % 256)
|
||||||
|
push(out, (v) % 256)
|
||||||
|
j = j + 1
|
||||||
|
return out
|
||||||
|
|
||||||
|
fun sha256_hex(this, hexStr)
|
||||||
|
bytes = this.from_hex(hexStr)
|
||||||
|
digest = this.sha256_bytes(bytes)
|
||||||
|
return this.bytes_to_hex(digest)
|
||||||
419
lib/crypt/sha512.fun
Normal file
419
lib/crypt/sha512.fun
Normal file
|
|
@ -0,0 +1,419 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Fun programming language.
|
||||||
|
* https://hanez.org/project/fun/
|
||||||
|
*
|
||||||
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
* Licensed under the terms of the ISC license.
|
||||||
|
* https://opensource.org/license/isc-license-txt
|
||||||
|
*
|
||||||
|
* Added: 2025-09-29
|
||||||
|
*/
|
||||||
|
|
||||||
|
// lib/crypt/sha512.fun
|
||||||
|
// Pure Fun SHA-512 implementation operating on hex-string input.
|
||||||
|
// 64-bit words are represented as [hi, lo] (two uint32 parts).
|
||||||
|
//
|
||||||
|
// Public API (class):
|
||||||
|
// s = SHA512()
|
||||||
|
// s.sha512_hex(hexStr) -> digest hex string (lowercase)
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// print(SHA512().sha512_hex("616263")) // "abc" =>
|
||||||
|
// ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
|
||||||
|
// 8: that’s for SHA-256; SHA-512 for "abc" is:
|
||||||
|
// ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a
|
||||||
|
// 2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f
|
||||||
|
|
||||||
|
class SHA512()
|
||||||
|
// -------- hex helpers --------
|
||||||
|
fun hex_val(this, ch)
|
||||||
|
if (ch == "0")
|
||||||
|
return 0
|
||||||
|
else if (ch == "1")
|
||||||
|
return 1
|
||||||
|
else if (ch == "2")
|
||||||
|
return 2
|
||||||
|
else if (ch == "3")
|
||||||
|
return 3
|
||||||
|
else if (ch == "4")
|
||||||
|
return 4
|
||||||
|
else if (ch == "5")
|
||||||
|
return 5
|
||||||
|
else if (ch == "6")
|
||||||
|
return 6
|
||||||
|
else if (ch == "7")
|
||||||
|
return 7
|
||||||
|
else if (ch == "8")
|
||||||
|
return 8
|
||||||
|
else if (ch == "9")
|
||||||
|
return 9
|
||||||
|
else if (ch == "a" || ch == "A")
|
||||||
|
return 10
|
||||||
|
else if (ch == "b" || ch == "B")
|
||||||
|
return 11
|
||||||
|
else if (ch == "c" || ch == "C")
|
||||||
|
return 12
|
||||||
|
else if (ch == "d" || ch == "D")
|
||||||
|
return 13
|
||||||
|
else if (ch == "e" || ch == "E")
|
||||||
|
return 14
|
||||||
|
else if (ch == "f" || ch == "F")
|
||||||
|
return 15
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
|
||||||
|
fun byte_from_hex_pair(this, hh)
|
||||||
|
number hi = this.hex_val(substr(hh, 0, 1))
|
||||||
|
number lo = this.hex_val(substr(hh, 1, 1))
|
||||||
|
return hi * 16 + lo
|
||||||
|
|
||||||
|
fun from_hex(this, hex)
|
||||||
|
hex = to_string(hex)
|
||||||
|
arr = []
|
||||||
|
number i = 0
|
||||||
|
number n = len(hex)
|
||||||
|
while i + 1 < n
|
||||||
|
number b = this.byte_from_hex_pair(substr(hex, i, 2))
|
||||||
|
push(arr, b)
|
||||||
|
i = i + 2
|
||||||
|
return arr
|
||||||
|
|
||||||
|
fun two_hex(this, n)
|
||||||
|
n = n % 256
|
||||||
|
hexd = "0123456789abcdef"
|
||||||
|
number hi = (n / 16) % 16
|
||||||
|
number lo = n % 16
|
||||||
|
c1 = substr(hexd, hi, 1)
|
||||||
|
c2 = substr(hexd, lo, 1)
|
||||||
|
return join([c1, c2], "")
|
||||||
|
|
||||||
|
fun bytes_to_hex(this, arr)
|
||||||
|
out = []
|
||||||
|
number i = 0
|
||||||
|
number L = len(arr)
|
||||||
|
while i < L
|
||||||
|
push(out, this.two_hex(arr[i]))
|
||||||
|
i = i + 1
|
||||||
|
return join(out, "")
|
||||||
|
|
||||||
|
// -------- 32/64 helpers --------
|
||||||
|
fun u32(this, x)
|
||||||
|
m = 4294967296
|
||||||
|
while x < 0
|
||||||
|
x = x + m
|
||||||
|
while x >= m
|
||||||
|
x = x - m
|
||||||
|
return x
|
||||||
|
|
||||||
|
// 64-bit as [hi, lo] (each uint32)
|
||||||
|
fun pack64_be(this, b0,b1,b2,b3,b4,b5,b6,b7)
|
||||||
|
number hi = this.u32(b0 * 16777216 + b1 * 65536 + b2 * 256 + b3)
|
||||||
|
number lo = this.u32(b4 * 16777216 + b5 * 65536 + b6 * 256 + b7)
|
||||||
|
return [hi, lo]
|
||||||
|
|
||||||
|
fun add64(this, a, b)
|
||||||
|
number alo = this.u32(a[1])
|
||||||
|
number ahi = this.u32(a[0])
|
||||||
|
number blo = this.u32(b[1])
|
||||||
|
number bhi = this.u32(b[0])
|
||||||
|
number sum_lo = alo + blo
|
||||||
|
number carry = sum_lo / 4294967296
|
||||||
|
sum_lo = sum_lo % 4294967296
|
||||||
|
number sum_hi = (ahi + bhi + carry) % 4294967296
|
||||||
|
return [sum_hi, sum_lo]
|
||||||
|
|
||||||
|
fun add64_5(this, a, b, c, d, e)
|
||||||
|
return this.add64(this.add64(this.add64(this.add64(a, b), c), d), e)
|
||||||
|
|
||||||
|
fun xor64(this, a, b)
|
||||||
|
return [ bxor(a[0], b[0]), bxor(a[1], b[1]) ]
|
||||||
|
|
||||||
|
fun and64(this, a, b)
|
||||||
|
return [ band(a[0], b[0]), band(a[1], b[1]) ]
|
||||||
|
|
||||||
|
fun or64(this, a, b)
|
||||||
|
return [ bor(a[0], b[0]), bor(a[1], b[1]) ]
|
||||||
|
|
||||||
|
fun not64(this, a)
|
||||||
|
return [ bnot(a[0]), bnot(a[1]) ]
|
||||||
|
|
||||||
|
// logical right shift by n (0..63)
|
||||||
|
fun shr64(this, a, n)
|
||||||
|
number s = n % 64
|
||||||
|
number hi = a[0]
|
||||||
|
number lo = a[1]
|
||||||
|
if (s == 0)
|
||||||
|
return [hi, lo]
|
||||||
|
else if (s < 32)
|
||||||
|
number new_lo = bor( shr(lo, s), shl(hi, 32 - s) )
|
||||||
|
number new_hi = shr(hi, s)
|
||||||
|
return [ new_hi, new_lo ]
|
||||||
|
else
|
||||||
|
number k = s - 32
|
||||||
|
number new_lo2 = shr(hi, k)
|
||||||
|
return [ 0, new_lo2 ]
|
||||||
|
|
||||||
|
// rotate right by n (0..63)
|
||||||
|
fun rotr64(this, a, n)
|
||||||
|
number s = n % 64
|
||||||
|
number hi = a[0]
|
||||||
|
number lo = a[1]
|
||||||
|
if (s == 0)
|
||||||
|
return [hi, lo]
|
||||||
|
else if (s < 32)
|
||||||
|
number new_lo = bor( shr(lo, s), shl(hi, 32 - s) )
|
||||||
|
number new_hi = bor( shr(hi, s), shl(lo, 32 - s) )
|
||||||
|
return [ new_hi, new_lo ]
|
||||||
|
else
|
||||||
|
number k = s - 32
|
||||||
|
// swap roles
|
||||||
|
number new_lo2 = bor( shr(hi, k), shl(lo, 32 - k) )
|
||||||
|
number new_hi2 = bor( shr(lo, k), shl(hi, 32 - k) )
|
||||||
|
return [ new_hi2, new_lo2 ]
|
||||||
|
|
||||||
|
// SHA-512 logical functions
|
||||||
|
fun Ch(this, x, y, z)
|
||||||
|
// (x & y) ^ (~x & z)
|
||||||
|
return this.xor64(this.and64(x, y), this.and64(this.not64(x), z))
|
||||||
|
|
||||||
|
fun Maj(this, x, y, z)
|
||||||
|
// (x & y) ^ (x & z) ^ (y & z)
|
||||||
|
return this.xor64(this.xor64(this.and64(x, y), this.and64(x, z)), this.and64(y, z))
|
||||||
|
|
||||||
|
fun Sigma0(this, x)
|
||||||
|
return this.xor64(this.xor64(this.rotr64(x, 28), this.rotr64(x, 34)), this.rotr64(x, 39))
|
||||||
|
|
||||||
|
fun Sigma1(this, x)
|
||||||
|
return this.xor64(this.xor64(this.rotr64(x, 14), this.rotr64(x, 18)), this.rotr64(x, 41))
|
||||||
|
|
||||||
|
fun sigma0(this, x)
|
||||||
|
return this.xor64(this.xor64(this.rotr64(x, 1), this.rotr64(x, 8)), this.shr64(x, 7))
|
||||||
|
|
||||||
|
fun sigma1(this, x)
|
||||||
|
return this.xor64(this.xor64(this.rotr64(x, 19), this.rotr64(x, 61)), this.shr64(x, 6))
|
||||||
|
|
||||||
|
// -------- constants --------
|
||||||
|
IV = [
|
||||||
|
[0x6a09e667, 0xf3bcc908], [0xbb67ae85, 0x84caa73b], [0x3c6ef372, 0xfe94f82b], [0xa54ff53a, 0x5f1d36f1],
|
||||||
|
[0x510e527f, 0xade682d1], [0x9b05688c, 0x2b3e6c1f], [0x1f83d9ab, 0xfb41bd6b], [0x5be0cd19, 0x137e2179]
|
||||||
|
]
|
||||||
|
|
||||||
|
K = [
|
||||||
|
[0x428a2f98,0xd728ae22],[0x71374491,0x23ef65cd],[0xb5c0fbcf,0xec4d3b2f],[0xe9b5dba5,0x8189dbbc],
|
||||||
|
[0x3956c25b,0xf348b538],[0x59f111f1,0xb605d019],[0x923f82a4,0xaf194f9b],[0xab1c5ed5,0xda6d8118],
|
||||||
|
[0xd807aa98,0xa3030242],[0x12835b01,0x45706fbe],[0x243185be,0x4ee4b28c],[0x550c7dc3,0xd5ffb4e2],
|
||||||
|
[0x72be5d74,0xf27b896f],[0x80deb1fe,0x3b1696b1],[0x9bdc06a7,0x25c71235],[0xc19bf174,0xcf692694],
|
||||||
|
[0xe49b69c1,0x9ef14ad2],[0xefbe4786,0x384f25e3],[0x0fc19dc6,0x8b8cd5b5],[0x240ca1cc,0x77ac9c65],
|
||||||
|
[0x2de92c6f,0x592b0275],[0x4a7484aa,0x6ea6e483],[0x5cb0a9dc,0xbd41fbd4],[0x76f988da,0x831153b5],
|
||||||
|
[0x983e5152,0xee66dfab],[0xa831c66d,0x2db43210],[0xb00327c8,0x98fb213f],[0xbf597fc7,0xbeef0ee4],
|
||||||
|
[0xc6e00bf3,0x3da88fc2],[0xd5a79147,0x930aa725],[0x06ca6351,0xe003826f],[0x14292967,0x0a0e6e70],
|
||||||
|
[0x27b70a85,0x46d22ffc],[0x2e1b2138,0x5c26c926],[0x4d2c6dfc,0x5ac42aed],[0x53380d13,0x9d95b3df],
|
||||||
|
[0x650a7354,0x8baf63de],[0x766a0abb,0x3c77b2a8],[0x81c2c92e,0x47edaee6],[0x92722c85,0x1482353b],
|
||||||
|
[0xa2bfe8a1,0x4cf10364],[0xa81a664b,0xbc423001],[0xc24b8b70,0xd0f89791],[0xc76c51a3,0x0654be30],
|
||||||
|
[0xd192e819,0xd6ef5218],[0xd6990624,0x5565a910],[0xf40e3585,0x5771202a],[0x106aa070,0x32bbd1b8],
|
||||||
|
[0x19a4c116,0xb8d2d0c8],[0x1e376c08,0x5141ab53],[0x2748774c,0xdf8eeb99],[0x34b0bcb5,0xe19b48a8],
|
||||||
|
[0x391c0cb3,0xc5c95a63],[0x4ed8aa4a,0xe3418acb],[0x5b9cca4f,0x7763e373],[0x682e6ff3,0xd6b2b8a3],
|
||||||
|
[0x748f82ee,0x5defb2fc],[0x78a5636f,0x43172f60],[0x84c87814,0xa1f0ab72],[0x8cc70208,0x1a6439ec],
|
||||||
|
[0x90befffa,0x23631e28],[0xa4506ceb,0xde82bde9],[0xbef9a3f7,0xb2c67915],[0xc67178f2,0xe372532b],
|
||||||
|
[0xca273ece,0xea26619c],[0xd186b8c7,0x21c0c207],[0xeada7dd6,0xcde0eb1e],[0xf57d4f7f,0xee6ed178],
|
||||||
|
[0x06f067aa,0x72176fba],[0x0a637dc5,0xa2c898a6],[0x113f9804,0xbef90dae],[0x1b710b35,0x131c471b],
|
||||||
|
[0x28db77f5,0x23047d84],[0x32caab7b,0x40c72493],[0x3c9ebe0a,0x15c9bebc],[0x431d67c4,0x9c100d4c],
|
||||||
|
[0x4cc5d4be,0xcb3e42b6],[0x597f299c,0xfc657e2a],[0x5fcb6fab,0x3ad6faec],[0x6c44198c,0x4a475817]
|
||||||
|
]
|
||||||
|
|
||||||
|
// -------- padding --------
|
||||||
|
fun pad_bytes(this, bytes)
|
||||||
|
number L = len(bytes)
|
||||||
|
out = []
|
||||||
|
// copy bytes
|
||||||
|
number i = 0
|
||||||
|
while i < L
|
||||||
|
push(out, bytes[i])
|
||||||
|
i = i + 1
|
||||||
|
// append 0x80
|
||||||
|
push(out, 128)
|
||||||
|
// pad zeros until length % 128 == 112 (i.e., 896 bits)
|
||||||
|
while (len(out) % 128) != 112
|
||||||
|
push(out, 0)
|
||||||
|
// append 128-bit length (we use upper 64 bits zero, lower 64 = L*8)
|
||||||
|
// upper 8 bytes zero:
|
||||||
|
number u = 0
|
||||||
|
number j = 0
|
||||||
|
while j < 8
|
||||||
|
push(out, 0)
|
||||||
|
j = j + 1
|
||||||
|
// lower 8 bytes big-endian (most significant byte first)
|
||||||
|
number bits = L * 8
|
||||||
|
number t = 7
|
||||||
|
while t >= 0
|
||||||
|
number b = (bits / pow(2, 8 * t)) % 256
|
||||||
|
push(out, b)
|
||||||
|
t = t - 1
|
||||||
|
return out
|
||||||
|
|
||||||
|
// -------- core --------
|
||||||
|
fun process_block(this, H, block)
|
||||||
|
// W ring buffer of 16 words ([hi, lo])
|
||||||
|
W = []
|
||||||
|
number i = 0
|
||||||
|
while i < 16
|
||||||
|
number j = i * 8
|
||||||
|
push(W, this.pack64_be(block[j],block[j+1],block[j+2],block[j+3],
|
||||||
|
block[j+4],block[j+5],block[j+6],block[j+7]))
|
||||||
|
i = i + 1
|
||||||
|
|
||||||
|
a = H[0]
|
||||||
|
b = H[1]
|
||||||
|
c = H[2]
|
||||||
|
d = H[3]
|
||||||
|
e = H[4]
|
||||||
|
f = H[5]
|
||||||
|
g = H[6]
|
||||||
|
h = H[7]
|
||||||
|
|
||||||
|
number t = 0
|
||||||
|
while t < 80
|
||||||
|
number idx = t % 16
|
||||||
|
if (t >= 16)
|
||||||
|
w2 = W[(t - 2) % 16]
|
||||||
|
w7 = W[(t - 7) % 16]
|
||||||
|
w15 = W[(t - 15) % 16]
|
||||||
|
w16 = W[(t - 16) % 16] // current slot value
|
||||||
|
s1 = this.sigma1(w2)
|
||||||
|
s0 = this.sigma0(w15)
|
||||||
|
tmp = this.add64(w16, s0)
|
||||||
|
tmp = this.add64(tmp, w7)
|
||||||
|
W[idx] = this.add64(tmp, s1)
|
||||||
|
Wt = W[idx]
|
||||||
|
|
||||||
|
// Precompute hot terms once per round
|
||||||
|
Se1 = this.Sigma1(e)
|
||||||
|
ch = this.Ch(e, f, g)
|
||||||
|
|
||||||
|
// T1 = h + Se1 + ch + K[t] + Wt (chained to avoid 5-arg helper)
|
||||||
|
tmp1 = this.add64(h, Se1)
|
||||||
|
tmp1 = this.add64(tmp1, ch)
|
||||||
|
tmp1 = this.add64(tmp1, this.K[t])
|
||||||
|
T1 = this.add64(tmp1, Wt)
|
||||||
|
|
||||||
|
// T2 = Sigma0(a) + Maj(a, b, c)
|
||||||
|
T2 = this.add64(this.Sigma0(a), this.Maj(a, b, c))
|
||||||
|
|
||||||
|
// State update
|
||||||
|
h = g
|
||||||
|
g = f
|
||||||
|
f = e
|
||||||
|
e = this.add64(d, T1)
|
||||||
|
d = c
|
||||||
|
c = b
|
||||||
|
b = a
|
||||||
|
a = this.add64(T1, T2)
|
||||||
|
t = t + 1
|
||||||
|
|
||||||
|
H[0] = this.add64(H[0], a)
|
||||||
|
H[1] = this.add64(H[1], b)
|
||||||
|
H[2] = this.add64(H[2], c)
|
||||||
|
H[3] = this.add64(H[3], d)
|
||||||
|
H[4] = this.add64(H[4], e)
|
||||||
|
H[5] = this.add64(H[5], f)
|
||||||
|
H[6] = this.add64(H[6], g)
|
||||||
|
H[7] = this.add64(H[7], h)
|
||||||
|
return H
|
||||||
|
|
||||||
|
fun sha512_bytes(this, bytes)
|
||||||
|
data = this.pad_bytes(bytes)
|
||||||
|
// init H (deep copy IV)
|
||||||
|
H = [ this.IV[0], this.IV[1], this.IV[2], this.IV[3], this.IV[4], this.IV[5], this.IV[6], this.IV[7] ]
|
||||||
|
number off = 0
|
||||||
|
number N = len(data)
|
||||||
|
while off < N
|
||||||
|
block = []
|
||||||
|
number i = 0
|
||||||
|
while i < 128
|
||||||
|
push(block, data[off + i])
|
||||||
|
i = i + 1
|
||||||
|
H = this.process_block(H, block)
|
||||||
|
off = off + 128
|
||||||
|
|
||||||
|
// output 64 bytes big-endian from H[0..7]
|
||||||
|
out = []
|
||||||
|
number j = 0
|
||||||
|
while j < 8
|
||||||
|
word = H[j]
|
||||||
|
number hi = word[0]
|
||||||
|
number lo = word[1]
|
||||||
|
// hi
|
||||||
|
push(out, (hi / 16777216) % 256)
|
||||||
|
push(out, (hi / 65536) % 256)
|
||||||
|
push(out, (hi / 256) % 256)
|
||||||
|
push(out, hi % 256)
|
||||||
|
// lo
|
||||||
|
push(out, (lo / 16777216) % 256)
|
||||||
|
push(out, (lo / 65536) % 256)
|
||||||
|
push(out, (lo / 256) % 256)
|
||||||
|
push(out, lo % 256)
|
||||||
|
j = j + 1
|
||||||
|
return out
|
||||||
|
|
||||||
|
fun sha512_hex(this, hexStr)
|
||||||
|
bytes = this.from_hex(hexStr)
|
||||||
|
digest = this.sha512_bytes(bytes)
|
||||||
|
return this.bytes_to_hex(digest)
|
||||||
|
|
||||||
|
// Convert a printable ASCII string (0x20..0x7E) to byte array
|
||||||
|
fun string_to_bytes_ascii(this, s)
|
||||||
|
str = to_string(s)
|
||||||
|
out = []
|
||||||
|
number i = 0
|
||||||
|
// ASCII printable ranges
|
||||||
|
P1 = " !\"#$%&'()*+,-./" // 32..47
|
||||||
|
P2 = "0123456789" // 48..57
|
||||||
|
P3 = ":;<=>?@" // 58..64
|
||||||
|
P4 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" // 65..90
|
||||||
|
P5 = "[\\]^_`" // 91..96
|
||||||
|
P6 = "abcdefghijklmnopqrstuvwxyz" // 97..122
|
||||||
|
P7 = "{|}~" // 123..126
|
||||||
|
while true
|
||||||
|
ch = substr(str, i, 1)
|
||||||
|
if (typeof(ch) != "String" || ch == "")
|
||||||
|
break
|
||||||
|
number code = -1
|
||||||
|
idx = find(P1, ch)
|
||||||
|
if (idx >= 0)
|
||||||
|
code = 32 + idx
|
||||||
|
else
|
||||||
|
idx = find(P2, ch)
|
||||||
|
if (idx >= 0)
|
||||||
|
code = 48 + idx
|
||||||
|
else
|
||||||
|
idx = find(P3, ch)
|
||||||
|
if (idx >= 0)
|
||||||
|
code = 58 + idx
|
||||||
|
else
|
||||||
|
idx = find(P4, ch)
|
||||||
|
if (idx >= 0)
|
||||||
|
code = 65 + idx
|
||||||
|
else
|
||||||
|
idx = find(P5, ch)
|
||||||
|
if (idx >= 0)
|
||||||
|
code = 91 + idx
|
||||||
|
else
|
||||||
|
idx = find(P6, ch)
|
||||||
|
if (idx >= 0)
|
||||||
|
code = 97 + idx
|
||||||
|
else
|
||||||
|
idx = find(P7, ch)
|
||||||
|
if (idx >= 0)
|
||||||
|
code = 123 + idx
|
||||||
|
else
|
||||||
|
// non-printable -> 0
|
||||||
|
code = 0
|
||||||
|
push(out, code)
|
||||||
|
i = i + 1
|
||||||
|
return out
|
||||||
|
|
||||||
|
// Hash raw string bytes (printable ASCII). Example: sha512_str("616263") -> ee2109...
|
||||||
|
fun sha512_str(this, str)
|
||||||
|
bytes = this.string_to_bytes_ascii(str)
|
||||||
|
digest = this.sha512_bytes(bytes)
|
||||||
|
return this.bytes_to_hex(digest)
|
||||||
|
|
@ -112,7 +112,17 @@ typedef enum {
|
||||||
OP_WRITE_FILE, // pops data string, path string; pushes 1/0
|
OP_WRITE_FILE, // pops data string, path string; pushes 1/0
|
||||||
|
|
||||||
// OS
|
// OS
|
||||||
OP_ENV // pops name string; pushes value string (or "")
|
OP_ENV, // pops name string; pushes value string (or "")
|
||||||
|
|
||||||
|
// Bitwise (32-bit) and shifts/rotates
|
||||||
|
OP_BAND, // pops b, a; pushes (uint32_t)(a & b)
|
||||||
|
OP_BOR, // pops b, a; pushes (uint32_t)(a | b)
|
||||||
|
OP_BXOR, // pops b, a; pushes (uint32_t)(a ^ b)
|
||||||
|
OP_BNOT, // pops a; pushes (uint32_t)(~a)
|
||||||
|
OP_SHL, // pops s, a; pushes (uint32_t)(a << (s&31))
|
||||||
|
OP_SHR, // pops s, a; pushes (uint32_t)(a >> (s&31)) logical
|
||||||
|
OP_ROTL, // pops s, a; pushes rotl32(a, s)
|
||||||
|
OP_ROTR // pops s, a; pushes rotr32(a, s)
|
||||||
} OpCode;
|
} OpCode;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
|
||||||
109
src/parser.c
109
src/parser.c
|
|
@ -1073,6 +1073,72 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* bitwise ops (32-bit) */
|
||||||
|
if (strcmp(name, "band") == 0) {
|
||||||
|
(*pos)++; /* '(' */
|
||||||
|
if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) { parser_fail(*pos, "band expects 2 args"); free(name); return 0; }
|
||||||
|
if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "band expects 2 args"); free(name); return 0; }
|
||||||
|
bytecode_add_instruction(bc, OP_BAND, 0);
|
||||||
|
free(name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "bor") == 0) {
|
||||||
|
(*pos)++; /* '(' */
|
||||||
|
if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) { parser_fail(*pos, "bor expects 2 args"); free(name); return 0; }
|
||||||
|
if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "bor expects 2 args"); free(name); return 0; }
|
||||||
|
bytecode_add_instruction(bc, OP_BOR, 0);
|
||||||
|
free(name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "bxor") == 0) {
|
||||||
|
(*pos)++; /* '(' */
|
||||||
|
if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) { parser_fail(*pos, "bxor expects 2 args"); free(name); return 0; }
|
||||||
|
if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "bxor expects 2 args"); free(name); return 0; }
|
||||||
|
bytecode_add_instruction(bc, OP_BXOR, 0);
|
||||||
|
free(name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "bnot") == 0) {
|
||||||
|
(*pos)++; /* '(' */
|
||||||
|
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "bnot expects 1 arg"); free(name); return 0; }
|
||||||
|
if (!consume_char(src, len, pos, ')')) { parser_fail(*pos, "bnot expects 1 arg"); free(name); return 0; }
|
||||||
|
bytecode_add_instruction(bc, OP_BNOT, 0);
|
||||||
|
free(name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "shl") == 0) {
|
||||||
|
(*pos)++; /* '(' */
|
||||||
|
if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) { parser_fail(*pos, "shl expects 2 args"); free(name); return 0; }
|
||||||
|
if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "shl expects 2 args"); free(name); return 0; }
|
||||||
|
bytecode_add_instruction(bc, OP_SHL, 0);
|
||||||
|
free(name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "shr") == 0) {
|
||||||
|
(*pos)++; /* '(' */
|
||||||
|
if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) { parser_fail(*pos, "shr expects 2 args"); free(name); return 0; }
|
||||||
|
if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "shr expects 2 args"); free(name); return 0; }
|
||||||
|
bytecode_add_instruction(bc, OP_SHR, 0);
|
||||||
|
free(name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "rol") == 0) {
|
||||||
|
(*pos)++; /* '(' */
|
||||||
|
if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) { parser_fail(*pos, "rol expects 2 args"); free(name); return 0; }
|
||||||
|
if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "rol expects 2 args"); free(name); return 0; }
|
||||||
|
bytecode_add_instruction(bc, OP_ROTL, 0);
|
||||||
|
free(name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "ror") == 0) {
|
||||||
|
(*pos)++; /* '(' */
|
||||||
|
if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) { parser_fail(*pos, "ror expects 2 args"); free(name); return 0; }
|
||||||
|
if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "ror expects 2 args"); free(name); return 0; }
|
||||||
|
bytecode_add_instruction(bc, OP_ROTR, 0);
|
||||||
|
free(name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* push function value first */
|
/* push function value first */
|
||||||
if (local_idx >= 0) {
|
if (local_idx >= 0) {
|
||||||
bytecode_add_instruction(bc, OP_LOAD_LOCAL, local_idx);
|
bytecode_add_instruction(bc, OP_LOAD_LOCAL, local_idx);
|
||||||
|
|
@ -3308,7 +3374,48 @@ Bytecode *parse_file_to_bytecode(const char *path) {
|
||||||
calc_line_col(compile_src, compile_len, g_err_pos, &line, &col);
|
calc_line_col(compile_src, compile_len, g_err_pos, &line, &col);
|
||||||
g_err_line = line;
|
g_err_line = line;
|
||||||
g_err_col = col;
|
g_err_col = col;
|
||||||
fprintf(stderr, "Parse error %s:%d:%d: %s\n", path ? path : "<input>", line, col, g_err_msg);
|
|
||||||
|
/* Try to locate include context marker preceding error */
|
||||||
|
const char *marker = "// __include_begin__: ";
|
||||||
|
size_t mlen = strlen(marker);
|
||||||
|
int inner_line = -1;
|
||||||
|
char inc_path[512]; inc_path[0] = '\0';
|
||||||
|
/* scan backward to find last marker line */
|
||||||
|
size_t scan = g_err_pos;
|
||||||
|
while (scan > 0) {
|
||||||
|
/* find start of current line */
|
||||||
|
size_t ls = scan;
|
||||||
|
while (ls > 0 && compile_src[ls - 1] != '\n') ls--;
|
||||||
|
/* check if this line starts with marker */
|
||||||
|
if (ls + mlen <= compile_len && strncmp(compile_src + ls, marker, mlen) == 0) {
|
||||||
|
/* extract path until end-of-line */
|
||||||
|
size_t p = ls + mlen;
|
||||||
|
size_t pe = p;
|
||||||
|
while (pe < compile_len && compile_src[pe] != '\n' && (pe - p) < sizeof(inc_path) - 1) pe++;
|
||||||
|
memcpy(inc_path, compile_src + p, pe - p);
|
||||||
|
inc_path[pe - p] = '\0';
|
||||||
|
/* compute inner line as number of newlines from (pe+1) to error position */
|
||||||
|
int count = 1;
|
||||||
|
size_t q = (pe < compile_len && compile_src[pe] == '\n') ? (pe + 1) : pe;
|
||||||
|
while (q < g_err_pos) {
|
||||||
|
if (compile_src[q] == '\n') count++;
|
||||||
|
q++;
|
||||||
|
}
|
||||||
|
inner_line = count;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* move to previous line */
|
||||||
|
if (ls == 0) break;
|
||||||
|
scan = ls - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inner_line > 0 && inc_path[0] != '\0') {
|
||||||
|
fprintf(stderr, "Parse error %s:%d:%d: %s (in %s:%d)\n",
|
||||||
|
path ? path : "<input>", line, col, g_err_msg, inc_path, inner_line);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Parse error %s:%d:%d: %s\n", path ? path : "<input>", line, col, g_err_msg);
|
||||||
|
}
|
||||||
|
|
||||||
if (bc) bytecode_free(bc);
|
if (bc) bytecode_free(bc);
|
||||||
if (prep) free(prep);
|
if (prep) free(prep);
|
||||||
free(src);
|
free(src);
|
||||||
|
|
|
||||||
|
|
@ -325,6 +325,10 @@ static char *preprocess_includes_internal(const char *src, int depth) {
|
||||||
char *exp = preprocess_includes_internal(inc, depth + 1);
|
char *exp = preprocess_includes_internal(inc, depth + 1);
|
||||||
free(inc);
|
free(inc);
|
||||||
if (exp) {
|
if (exp) {
|
||||||
|
/* mark file origin for better error messages */
|
||||||
|
sb_append(&out, "// __include_begin__: ");
|
||||||
|
sb_append(&out, resolved);
|
||||||
|
sb_append(&out, "\n");
|
||||||
sb_append(&out, exp);
|
sb_append(&out, exp);
|
||||||
/* ensure included chunk ends with newline to preserve line structure */
|
/* ensure included chunk ends with newline to preserve line structure */
|
||||||
if (out.len == 0 || out.buf[out.len - 1] != '\n') sb_append_ch(&out, '\n');
|
if (out.len == 0 || out.buf[out.len - 1] != '\n') sb_append_ch(&out, '\n');
|
||||||
|
|
|
||||||
28
src/vm.c
28
src/vm.c
|
|
@ -25,11 +25,21 @@ static VM *g_active_vm = NULL;
|
||||||
static int fun_vm_vfprintf(FILE *stream, const char *fmt, va_list ap) {
|
static int fun_vm_vfprintf(FILE *stream, const char *fmt, va_list ap) {
|
||||||
int written = vfprintf(stream, fmt, ap);
|
int written = vfprintf(stream, fmt, ap);
|
||||||
if (stream == stderr && g_active_vm && g_active_vm->current_line > 0) {
|
if (stream == stderr && g_active_vm && g_active_vm->current_line > 0) {
|
||||||
/* If original message didn't end with newline, add a space first */
|
/* Append more context: line number, opcode name, and ip */
|
||||||
if (written > 0) {
|
const char *opname = "unknown";
|
||||||
/* best-effort: do not attempt to inspect fmt string fully; just append line info */
|
int ip = -1;
|
||||||
|
if (g_active_vm->fp >= 0) {
|
||||||
|
Frame *f = &g_active_vm->frames[g_active_vm->fp];
|
||||||
|
ip = f->ip - 1;
|
||||||
|
if (f->fn && ip >= 0 && ip < f->fn->instr_count) {
|
||||||
|
int op = f->fn->instructions[ip].op;
|
||||||
|
if (op >= 0 && op < (int)(sizeof(opcode_names)/sizeof(opcode_names[0]))) {
|
||||||
|
opname = opcode_names[op];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fprintf(stream == stderr ? stderr : stream, " (line %d)\n", g_active_vm->current_line);
|
fprintf(stream == stderr ? stderr : stream, " (line %d, op %s @ip %d)\n",
|
||||||
|
g_active_vm->current_line, opname, ip);
|
||||||
}
|
}
|
||||||
return written;
|
return written;
|
||||||
}
|
}
|
||||||
|
|
@ -249,6 +259,16 @@ void vm_run(VM *vm, Bytecode *entry) {
|
||||||
#include "vm/arrays/slice.c"
|
#include "vm/arrays/slice.c"
|
||||||
#include "vm/arrays/zip.c"
|
#include "vm/arrays/zip.c"
|
||||||
|
|
||||||
|
/* Bitwise and shifts/rotates */
|
||||||
|
#include "vm/bitwise/band.c"
|
||||||
|
#include "vm/bitwise/bor.c"
|
||||||
|
#include "vm/bitwise/bxor.c"
|
||||||
|
#include "vm/bitwise/bnot.c"
|
||||||
|
#include "vm/bitwise/shl.c"
|
||||||
|
#include "vm/bitwise/shr.c"
|
||||||
|
#include "vm/bitwise/rol.c"
|
||||||
|
#include "vm/bitwise/ror.c"
|
||||||
|
|
||||||
#include "vm/core/call.c"
|
#include "vm/core/call.c"
|
||||||
#include "vm/core/dup.c"
|
#include "vm/core/dup.c"
|
||||||
#include "vm/core/halt.c"
|
#include "vm/core/halt.c"
|
||||||
|
|
|
||||||
5
src/vm.h
5
src/vm.h
|
|
@ -33,7 +33,8 @@ static const char *opcode_names[] = {
|
||||||
"ENUMERATE","ZIP",
|
"ENUMERATE","ZIP",
|
||||||
"MIN","MAX","CLAMP","ABS","POW","RANDOM_SEED","RANDOM_INT",
|
"MIN","MAX","CLAMP","ABS","POW","RANDOM_SEED","RANDOM_INT",
|
||||||
"MAKE_MAP","KEYS","VALUES","HAS_KEY",
|
"MAKE_MAP","KEYS","VALUES","HAS_KEY",
|
||||||
"READ_FILE","WRITE_FILE","ENV"
|
"READ_FILE","WRITE_FILE","ENV",
|
||||||
|
"BAND","BOR","BXOR","BNOT","SHL","SHR","ROTL","ROTR"
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
@ -76,7 +77,7 @@ void vm_dump_globals(VM *vm);
|
||||||
void vm_run(VM *vm, Bytecode *entry);
|
void vm_run(VM *vm, Bytecode *entry);
|
||||||
|
|
||||||
static inline int opcode_is_valid(int op) {
|
static inline int opcode_is_valid(int op) {
|
||||||
return op >= OP_NOP && op <= OP_ENV; // all current opcodes
|
return op >= OP_NOP && op <= OP_ROTR; // all current opcodes
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,8 @@ case OP_INDEX_GET: {
|
||||||
free_value(idx);
|
free_value(idx);
|
||||||
push_value(vm, out);
|
push_value(vm, out);
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Runtime type error: INDEX_GET expects array or map\n");
|
fprintf(stderr, "Runtime type error: INDEX_GET expects array or map (got container=%s, index=%s)\n",
|
||||||
|
value_type_name(container.type), value_type_name(idx.type));
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
27
src/vm/bitwise/band.c
Normal file
27
src/vm/bitwise/band.c
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
* This file is part of the Fun programming language.
|
||||||
|
* https://hanez.org/project/fun/
|
||||||
|
*
|
||||||
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
* Licensed under the terms of the ISC license.
|
||||||
|
* https://opensource.org/license/isc-license-txt
|
||||||
|
*
|
||||||
|
* Added: 2025-09-29
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OP_BAND: bitwise AND (uint32 32-bit)
|
||||||
|
* pops: b, a
|
||||||
|
* pushes: (uint32_t)(a & b)
|
||||||
|
*/
|
||||||
|
case OP_BAND: {
|
||||||
|
Value vb = pop_value(vm);
|
||||||
|
Value va = pop_value(vm);
|
||||||
|
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
|
||||||
|
uint32_t b = (vb.type == VAL_INT) ? (uint32_t)vb.i : 0u;
|
||||||
|
uint32_t r = a & b;
|
||||||
|
free_value(vb);
|
||||||
|
free_value(va);
|
||||||
|
push_value(vm, make_int((int64_t)(uint64_t)r));
|
||||||
|
break;
|
||||||
|
}
|
||||||
24
src/vm/bitwise/bnot.c
Normal file
24
src/vm/bitwise/bnot.c
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
/**
|
||||||
|
* This file is part of the Fun programming language.
|
||||||
|
* https://hanez.org/project/fun/
|
||||||
|
*
|
||||||
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
* Licensed under the terms of the ISC license.
|
||||||
|
* https://opensource.org/license/isc-license-txt
|
||||||
|
*
|
||||||
|
* Added: 2025-09-29
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OP_BNOT: bitwise NOT (uint32 32-bit)
|
||||||
|
* pops: a
|
||||||
|
* pushes: (uint32_t)(~a)
|
||||||
|
*/
|
||||||
|
case OP_BNOT: {
|
||||||
|
Value va = pop_value(vm);
|
||||||
|
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
|
||||||
|
uint32_t r = ~a;
|
||||||
|
free_value(va);
|
||||||
|
push_value(vm, make_int((int64_t)(uint64_t)r));
|
||||||
|
break;
|
||||||
|
}
|
||||||
27
src/vm/bitwise/bor.c
Normal file
27
src/vm/bitwise/bor.c
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
* This file is part of the Fun programming language.
|
||||||
|
* https://hanez.org/project/fun/
|
||||||
|
*
|
||||||
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
* Licensed under the terms of the ISC license.
|
||||||
|
* https://opensource.org/license/isc-license-txt
|
||||||
|
*
|
||||||
|
* Added: 2025-09-29
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OP_BOR: bitwise OR (uint32 32-bit)
|
||||||
|
* pops: b, a
|
||||||
|
* pushes: (uint32_t)(a | b)
|
||||||
|
*/
|
||||||
|
case OP_BOR: {
|
||||||
|
Value vb = pop_value(vm);
|
||||||
|
Value va = pop_value(vm);
|
||||||
|
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
|
||||||
|
uint32_t b = (vb.type == VAL_INT) ? (uint32_t)vb.i : 0u;
|
||||||
|
uint32_t r = a | b;
|
||||||
|
free_value(vb);
|
||||||
|
free_value(va);
|
||||||
|
push_value(vm, make_int((int64_t)(uint64_t)r));
|
||||||
|
break;
|
||||||
|
}
|
||||||
27
src/vm/bitwise/bxor.c
Normal file
27
src/vm/bitwise/bxor.c
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
* This file is part of the Fun programming language.
|
||||||
|
* https://hanez.org/project/fun/
|
||||||
|
*
|
||||||
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
* Licensed under the terms of the ISC license.
|
||||||
|
* https://opensource.org/license/isc-license-txt
|
||||||
|
*
|
||||||
|
* Added: 2025-09-29
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OP_BXOR: bitwise XOR (uint32 32-bit)
|
||||||
|
* pops: b, a
|
||||||
|
* pushes: (uint32_t)(a ^ b)
|
||||||
|
*/
|
||||||
|
case OP_BXOR: {
|
||||||
|
Value vb = pop_value(vm);
|
||||||
|
Value va = pop_value(vm);
|
||||||
|
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
|
||||||
|
uint32_t b = (vb.type == VAL_INT) ? (uint32_t)vb.i : 0u;
|
||||||
|
uint32_t r = a ^ b;
|
||||||
|
free_value(vb);
|
||||||
|
free_value(va);
|
||||||
|
push_value(vm, make_int((int64_t)(uint64_t)r));
|
||||||
|
break;
|
||||||
|
}
|
||||||
28
src/vm/bitwise/rol.c
Normal file
28
src/vm/bitwise/rol.c
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
/**
|
||||||
|
* This file is part of the Fun programming language.
|
||||||
|
* https://hanez.org/project/fun/
|
||||||
|
*
|
||||||
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
* Licensed under the terms of the ISC license.
|
||||||
|
* https://opensource.org/license/isc-license-txt
|
||||||
|
*
|
||||||
|
* Added: 2025-09-29
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OP_ROTL: rotate left (uint32)
|
||||||
|
* pops: s, a
|
||||||
|
* pushes: rotl32(a, s)
|
||||||
|
*/
|
||||||
|
case OP_ROTL: {
|
||||||
|
Value vs = pop_value(vm);
|
||||||
|
Value va = pop_value(vm);
|
||||||
|
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
|
||||||
|
uint32_t s = (vs.type == VAL_INT) ? (uint32_t)vs.i : 0u;
|
||||||
|
s &= 31u;
|
||||||
|
uint32_t r = (s == 0u) ? a : ((a << s) | (a >> (32u - s)));
|
||||||
|
free_value(vs);
|
||||||
|
free_value(va);
|
||||||
|
push_value(vm, make_int((int64_t)(uint64_t)r));
|
||||||
|
break;
|
||||||
|
}
|
||||||
28
src/vm/bitwise/ror.c
Normal file
28
src/vm/bitwise/ror.c
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
/**
|
||||||
|
* This file is part of the Fun programming language.
|
||||||
|
* https://hanez.org/project/fun/
|
||||||
|
*
|
||||||
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
* Licensed under the terms of the ISC license.
|
||||||
|
* https://opensource.org/license/isc-license-txt
|
||||||
|
*
|
||||||
|
* Added: 2025-09-29
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OP_ROTR: rotate right (uint32)
|
||||||
|
* pops: s, a
|
||||||
|
* pushes: rotr32(a, s)
|
||||||
|
*/
|
||||||
|
case OP_ROTR: {
|
||||||
|
Value vs = pop_value(vm);
|
||||||
|
Value va = pop_value(vm);
|
||||||
|
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
|
||||||
|
uint32_t s = (vs.type == VAL_INT) ? (uint32_t)vs.i : 0u;
|
||||||
|
s &= 31u;
|
||||||
|
uint32_t r = (s == 0u) ? a : ((a >> s) | (a << (32u - s)));
|
||||||
|
free_value(vs);
|
||||||
|
free_value(va);
|
||||||
|
push_value(vm, make_int((int64_t)(uint64_t)r));
|
||||||
|
break;
|
||||||
|
}
|
||||||
28
src/vm/bitwise/shl.c
Normal file
28
src/vm/bitwise/shl.c
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
/**
|
||||||
|
* This file is part of the Fun programming language.
|
||||||
|
* https://hanez.org/project/fun/
|
||||||
|
*
|
||||||
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
* Licensed under the terms of the ISC license.
|
||||||
|
* https://opensource.org/license/isc-license-txt
|
||||||
|
*
|
||||||
|
* Added: 2025-09-29
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OP_SHL: logical left shift (uint32)
|
||||||
|
* pops: s, a
|
||||||
|
* pushes: (uint32_t)(a << (s&31))
|
||||||
|
*/
|
||||||
|
case OP_SHL: {
|
||||||
|
Value vs = pop_value(vm);
|
||||||
|
Value va = pop_value(vm);
|
||||||
|
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
|
||||||
|
uint32_t s = (vs.type == VAL_INT) ? (uint32_t)vs.i : 0u;
|
||||||
|
s &= 31u;
|
||||||
|
uint32_t r = (s == 0u) ? a : (a << s);
|
||||||
|
free_value(vs);
|
||||||
|
free_value(va);
|
||||||
|
push_value(vm, make_int((int64_t)(uint64_t)r));
|
||||||
|
break;
|
||||||
|
}
|
||||||
28
src/vm/bitwise/shr.c
Normal file
28
src/vm/bitwise/shr.c
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
/**
|
||||||
|
* This file is part of the Fun programming language.
|
||||||
|
* https://hanez.org/project/fun/
|
||||||
|
*
|
||||||
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
* Licensed under the terms of the ISC license.
|
||||||
|
* https://opensource.org/license/isc-license-txt
|
||||||
|
*
|
||||||
|
* Added: 2025-09-29
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OP_SHR: logical right shift (uint32)
|
||||||
|
* pops: s, a
|
||||||
|
* pushes: (uint32_t)(a >> (s&31)) using logical shift
|
||||||
|
*/
|
||||||
|
case OP_SHR: {
|
||||||
|
Value vs = pop_value(vm);
|
||||||
|
Value va = pop_value(vm);
|
||||||
|
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
|
||||||
|
uint32_t s = (vs.type == VAL_INT) ? (uint32_t)vs.i : 0u;
|
||||||
|
s &= 31u;
|
||||||
|
uint32_t r = (s == 0u) ? a : (a >> s);
|
||||||
|
free_value(vs);
|
||||||
|
free_value(va);
|
||||||
|
push_value(vm, make_int((int64_t)(uint64_t)r));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
@ -40,8 +40,8 @@ case OP_LEN: {
|
||||||
len = array_length(&a);
|
len = array_length(&a);
|
||||||
if (len < 0) len = 0;
|
if (len < 0) len = 0;
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Runtime type error: LEN expects array or string\n");
|
/* Be lenient: for non-array/non-string, treat length as 0 */
|
||||||
exit(1);
|
push_value(vm, make_int(0));
|
||||||
}
|
}
|
||||||
free_value(a);
|
free_value(a);
|
||||||
push_value(vm, make_int(len));
|
push_value(vm, make_int(len));
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue