From 3411a4d84603dab17200f7caea850673129c6e8b Mon Sep 17 00:00:00 2001 From: hanez Date: Wed, 26 Nov 2025 20:36:40 +0100 Subject: [PATCH] Added CRC32 and CRC32C classes to stdlib. (0.31.0) --- CMakeLists.txt | 2 +- README.md | 2 + examples/crc32_example.fun | 54 +++++++++++++ examples/crc32c_example.fun | 54 +++++++++++++ lib/crypt/crc32.fun | 156 ++++++++++++++++++++++++++++++++++++ lib/crypt/crc32c.fun | 156 ++++++++++++++++++++++++++++++++++++ lib/io/pcsc.fun | 3 +- 7 files changed, 425 insertions(+), 2 deletions(-) create mode 100644 examples/crc32_example.fun create mode 100644 examples/crc32c_example.fun create mode 100644 lib/crypt/crc32.fun create mode 100644 lib/crypt/crc32c.fun diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c81c1c..b17909b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.10) -project(fun VERSION 0.30.0 LANGUAGES C) +project(fun VERSION 0.31.0 LANGUAGES C) set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD_REQUIRED ON) diff --git a/README.md b/README.md index 0d9dd59..13548b4 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,8 @@ Fun is and will ever be 100% free under the terms of the [Apache-2.0 License](ht - [PCRE2](https://pcre2project.github.io/pcre2/) support builtin for Perl-Compatible Regular Expressions (optional) ☑ - [SQLite](https://sqlite.org/) support builtin (optional) ☐ - [Tk](https://www.tcl-lang.org/) support builtin for GUI application development (optional) ☐ +- [XML](https://www.w3.org/XML/) support builtin using [libxml2](https://gitlab.gnome.org/GNOME/libxml2/-/wikis/home) (optional) ☐ +- [YAML](https://yaml.org/) support builtin using [libfyaml](https://github.com/pantoniou/libfyaml) (optional) ☐ ☑ = Done / ☐ = Planned or in progress. diff --git a/examples/crc32_example.fun b/examples/crc32_example.fun new file mode 100644 index 0000000..830cbab --- /dev/null +++ b/examples/crc32_example.fun @@ -0,0 +1,54 @@ +#!/usr/bin/env fun + +/* + * This file is part of the Fun programming language. + * https://fun-lang.xyz/ + * + * Copyright 2025 Johannes Findeisen + * Licensed under the terms of the Apache-2.0 license. + * https://opensource.org/license/apache-2-0 + * + * Added: 2025-11-26 + */ + +// Example: Using the CRC32 class from lib/crypt/crc32.fun + +include + +print("-- CRC32 example --") + +c = CRC32() + +// 1) Known test vector: "123456789" -> cbf43926 +msg = "123456789" +crc1 = c.crc32_str(msg) +print("Input (ASCII): " + msg) +print("CRC32: " + crc1) // expected: cbf43926 + +print("") + +// 2) Same data provided as hex string +hex_msg = "313233343536373839" // hex for "123456789" +crc2 = c.crc32_hex(hex_msg) +print("Input (hex): " + hex_msg) +print("CRC32: " + crc2) // expected: cbf43926 + +print("") + +// 3) Another quick demo +other = "Fun language" +crc3 = c.crc32_str(other) +print("Input (ASCII): " + other) +print("CRC32: " + crc3) + +/* Expected output: +-- CRC32 example -- +Input (ASCII): 123456789 +CRC32: cbf43926 + +Input (hex): 313233343536373839 +CRC32: cbf43926 + +Input (ASCII): Fun language +CRC32: d7d83272 +*/ diff --git a/examples/crc32c_example.fun b/examples/crc32c_example.fun new file mode 100644 index 0000000..dfbb8c5 --- /dev/null +++ b/examples/crc32c_example.fun @@ -0,0 +1,54 @@ +#!/usr/bin/env fun + +/* + * This file is part of the Fun programming language. + * https://fun-lang.xyz/ + * + * Copyright 2025 Johannes Findeisen + * Licensed under the terms of the Apache-2.0 license. + * https://opensource.org/license/apache-2-0 + * + * Added: 2025-11-26 + */ + +// Example: Using the CRC32C class from lib/crypt/crc32c.fun + +include + +print("-- CRC32C example --") + +c = CRC32C() + +// 1) Known test vector: "123456789" -> e3069283 +msg = "123456789" +crc1 = c.crc32c_str(msg) +print("Input (ASCII): " + msg) +print("CRC32C: " + crc1) // expected: e3069283 + +print("") + +// 2) Same data provided as hex string +hex_msg = "313233343536373839" // hex for "123456789" +crc2 = c.crc32c_hex(hex_msg) +print("Input (hex): " + hex_msg) +print("CRC32C: " + crc2) // expected: e3069283 + +print("") + +// 3) Another quick demo +other = "Fun language" +crc3 = c.crc32c_str(other) +print("Input (ASCII): " + other) +print("CRC32C: " + crc3) + +/* Expected output: +-- CRC32C example -- +Input (ASCII): 123456789 +CRC32C: e3069283 + +Input (hex): 313233343536373839 +CRC32C: e3069283 + +Input (ASCII): Fun language +CRC32C: c0158b58 +*/ diff --git a/lib/crypt/crc32.fun b/lib/crypt/crc32.fun new file mode 100644 index 0000000..0759a90 --- /dev/null +++ b/lib/crypt/crc32.fun @@ -0,0 +1,156 @@ +/* + * This file is part of the Fun programming language. + * https://fun-lang.xyz/ + * + * Copyright 2025 Johannes Findeisen + * Licensed under the terms of the Apache-2.0 license. + * https://opensource.org/license/apache-2-0 + * + * Added: 2025-11-26 + */ + +// lib/crypt/crc32.fun +// Pure Fun implementation of CRC-32 (IEEE 802.3) operating on hex-string input. +// Reflected polynomial: 0xEDB88320 +// Initial value: 0xFFFFFFFF, Final XOR: 0xFFFFFFFF +// +// Public API (class methods): +// crc32_hex(hexStr) -> 8-char lowercase hex string +// crc32_str(str) -> 8-char lowercase string (ASCII input) +// +// Example: +// // "123456789" CRC32 is cbf43926 +// c = CRC32() +// print(c.crc32_str("123456789")) + +#include + +class CRC32() + // 32-bit helpers + fun u32(this, x) + m = 4294967296 + while x < 0 + x = x + m + while x >= m + x = x - m + return x + + fun shr32(this, x, s) + return shr(this.u32(x), s) + + fun shl32(this, x, s) + return shl(this.u32(x), s) + + fun xor32(this, a, b) + return bxor(this.u32(a), this.u32(b)) + + fun and32(this, a, b) + return band(this.u32(a), this.u32(b)) + + // hex helpers (mirroring style from lib/crypt/md5.fun) + 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, "") + + fun u32_to_hex8(this, n) + // big-endian printing (MSB first), common for CRC displays + b3 = this.and32(this.shr32(n, 24), 255) + b2 = this.and32(this.shr32(n, 16), 255) + b1 = this.and32(this.shr32(n, 8), 255) + b0 = this.and32(n, 255) + return this.bytes_to_hex([b3, b2, b1, b0]) + + // Bitwise update (no table needed) using reflected polynomial 0xEDB88320 + POLY = 3988292384 // 0xEDB88320 + + // Compute CRC32 over byte array, return u32 value (bitwise, reflected) + fun crc32_bytes_value(this, bytes) + crc = 4294967295 // 0xFFFFFFFF + i = 0 + n = len(bytes) + while i < n + crc = this.xor32(crc, bytes[i]) + j = 0 + while j < 8 + if (this.and32(crc, 1) == 1) + crc = this.xor32(this.shr32(crc, 1), this.POLY) + else + crc = this.shr32(crc, 1) + j = j + 1 + i = i + 1 + return this.xor32(crc, 4294967295) // final XOR + + // Public: compute CRC32 of hex string of bytes, return 8-char hex + fun crc32_hex(this, hexStr) + bytes = this.from_hex(hexStr) + v = this.crc32_bytes_value(bytes) + return this.u32_to_hex8(v) + + // Convenience: compute CRC32 of ASCII string + fun crc32_str(this, str) + bytes = string_to_bytes_ascii(str) + v = this.crc32_bytes_value(bytes) + return this.u32_to_hex8(v) diff --git a/lib/crypt/crc32c.fun b/lib/crypt/crc32c.fun new file mode 100644 index 0000000..8a9cbd0 --- /dev/null +++ b/lib/crypt/crc32c.fun @@ -0,0 +1,156 @@ +/* + * This file is part of the Fun programming language. + * https://fun-lang.xyz/ + * + * Copyright 2025 Johannes Findeisen + * Licensed under the terms of the Apache-2.0 license. + * https://opensource.org/license/apache-2-0 + * + * Added: 2025-11-26 + */ + +// lib/crypt/crc32c.fun +// Pure Fun implementation of CRC-32C (Castagnoli) operating on hex-string input. +// Polynomial (reflected): 0x82F63B78 +// Initial value: 0xFFFFFFFF, Final XOR: 0xFFFFFFFF +// +// Public API (class methods): +// crc32c_hex(hexStr) -> 8-char lowercase hex string +// crc32c_str(str) -> 8-char lowercase string (ASCII input) +// +// Example: +// // "123456789" in ASCII is 313233343536373839 in hex, CRC32C is e3069283 +// c = CRC32C() +// print(c.crc32c_hex("313233343536373839")) + +#include + +class CRC32C() + // 32-bit helpers + fun u32(this, x) + m = 4294967296 + while x < 0 + x = x + m + while x >= m + x = x - m + return x + + fun shr32(this, x, s) + return shr(this.u32(x), s) + + fun shl32(this, x, s) + return shl(this.u32(x), s) + + fun xor32(this, a, b) + return bxor(this.u32(a), this.u32(b)) + + fun and32(this, a, b) + return band(this.u32(a), this.u32(b)) + + // hex helpers (mirroring style from lib/crypt/md5.fun) + 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, "") + + fun u32_to_hex8(this, n) + // big-endian printing (MSB first), common for CRC displays + b3 = this.and32(this.shr32(n, 24), 255) + b2 = this.and32(this.shr32(n, 16), 255) + b1 = this.and32(this.shr32(n, 8), 255) + b0 = this.and32(n, 255) + return this.bytes_to_hex([b3, b2, b1, b0]) + + // Bitwise update (no table needed) using reflected polynomial 0x82F63B78 + POLY = 2197175160 // 0x82F63B78 + + // Compute CRC32C over byte array, return u32 value (bitwise, reflected) + fun crc32c_bytes_value(this, bytes) + crc = 4294967295 // 0xFFFFFFFF + i = 0 + n = len(bytes) + while i < n + crc = this.xor32(crc, bytes[i]) + j = 0 + while j < 8 + if (this.and32(crc, 1) == 1) + crc = this.xor32(this.shr32(crc, 1), this.POLY) + else + crc = this.shr32(crc, 1) + j = j + 1 + i = i + 1 + return this.xor32(crc, 4294967295) // final XOR + + // Public: compute CRC32C of hex string of bytes, return 8-char hex + fun crc32c_hex(this, hexStr) + bytes = this.from_hex(hexStr) + v = this.crc32c_bytes_value(bytes) + return this.u32_to_hex8(v) + + // Convenience: compute CRC32C of ASCII string + fun crc32c_str(this, str) + bytes = string_to_bytes_ascii(str) + v = this.crc32c_bytes_value(bytes) + return this.u32_to_hex8(v) diff --git a/lib/io/pcsc.fun b/lib/io/pcsc.fun index 022226b..feb9263 100644 --- a/lib/io/pcsc.fun +++ b/lib/io/pcsc.fun @@ -79,6 +79,7 @@ class PCSC() return m */ +// This class is in a very early stage of development. class PCSC() fun get_readers(this) ctx = pcsc_establish() @@ -109,7 +110,7 @@ class PCSC() m["sw2"] = -1 m["code"] = -2 return m - // Actually selecting the second reader hardcode. This class is not in a very early stage of development. + // Actually selecting the second reader hardcoded. handle = pcsc_connect(ctx, readers[1]) apdu = this.hex_to_bytes(hex_apdu) res = pcsc_transmit(handle, apdu)