commit e0afcc26555255a5e7dfccb3233337c0d14d2507
Author: finwo <finwo@pm.me>
Date: Wed, 18 Mar 2026 19:36:32 +0100
Project init
Diffstat:
11 files changed, 421 insertions(+), 0 deletions(-)
diff --git a/.dep.export b/.dep.export
@@ -0,0 +1 @@
+include/finwo/crc16-xmodem.h src/crc16-xmodem.h
diff --git a/.editorconfig b/.editorconfig
@@ -0,0 +1,13 @@
+# 2d0e4a3f-ac19-430c-bf1b-46c68651ce21
+root = true
+
+[*]
+end_of_line = lf
+insert_final_newline = true
+charset = utf-8
+indent_size = 2
+indent_style = space
+trim_trailing_whitespace = true
+
+[Makefile*]
+indent_style = tab
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,4 @@
+/lib/
+*.o
+/test
+/benchmark
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
@@ -0,0 +1,6 @@
+<!-- 46b43825-f791-485e-9445-415ee7bbbf2d -->
+# Contributor Code of Conduct
+
+This project adheres to No Code of Conduct. We are all adults. We accept anyone's contributions. Nothing else matters.
+
+For more information please visit the [No Code of Conduct](https://github.com/domgetter/NCoC) homepage.
diff --git a/LICENSE.md b/LICENSE.md
@@ -0,0 +1,34 @@
+Copyright (c) 2026 finwo
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to use, copy,
+modify, and distribute the Software, subject to the following conditions:
+
+ 1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions, and the following disclaimer.
+
+ 2. Redistributions in binary form, or any public offering of the Software
+ (including hosted or managed services), must reproduce the above copyright
+ notice, this list of conditions, and the following disclaimer in the
+ documentation and/or other materials provided.
+
+ 3. Any redistribution or public offering of the Software must clearly attribute
+ the Software to the original copyright holder, reference this License, and
+ include a link to the official project repository or website.
+
+ 4. The Software may not be renamed, rebranded, or marketed in a manner that
+ implies it is an independent or proprietary product. Derivative works must
+ clearly state that they are based on the Software.
+
+ 5. Modifications to copies of the Software must carry prominent notices stating
+ that changes were made, the nature of the modifications, and the date of the
+ modifications.
+
+Any violation of these conditions terminates the permissions granted herein.
+
+THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/Makefile b/Makefile
@@ -0,0 +1,34 @@
+SRC=$(wildcard src/*.c)
+
+INCLUDES?=
+INCLUDES+=-Isrc
+
+CFLAGS?=-Wall -std=c99 -O2
+
+ifeq ($(origin SRC),undefined)
+-include config.mk
+endif
+
+CFLAGS+=$(INCLUDES)
+CFLAGS+=-D_DEFAULT_SOURCE
+
+OBJ=$(SRC:.c=.o)
+
+BIN=test
+
+default: $(BIN)
+
+$(BIN): $(OBJ) test.o
+ $(CC) $(CFLAGS) $(OBJ) test.o -o $@
+
+.PHONY: clean check
+clean:
+ rm -f $(OBJ)
+ rm -f test.o
+ rm -f $(BIN)
+
+check: $(BIN)
+ ./$(BIN)
+
+README.md: src/crc16-xmodem.h
+ stddoc < src/crc16-xmodem.h > README.md
diff --git a/README.md b/README.md
@@ -0,0 +1,80 @@
+crc16-xmodem
+=============
+
+Generate CRC-16/XMODEM codes consistently
+
+Why
+---
+
+While a fairly simple task, I was implementing this throughout multiple projects
+of mine. Now if there's a bug, speed improvement to be had, or simple because
+the C standard changes, I want to be able to update it in 1 location
+instead of having to fix it in every individual project.
+
+TL;DR; I'm lazy, and want a single version of the code
+
+Installation
+------------
+
+```sh
+dep add finwo/crc16-xmodem
+dep install
+```
+
+After that, simply add `include lib/.dep/config.mk` in your makefile and include
+the header file by adding `#include "finwo/crc16-xmodem.h"`.
+
+Usage
+-----
+
+```c
+#include "finwo/crc16-xmodem.h"
+
+uint8_t message[] = "Hi!\0\0";
+
+// Output 12797 (0x31FD)
+uint16_t crc = crc16_xmodem(message, 4);
+crc16_xmodem_b(message, 4, &message[4]);
+
+// Now if you want to check if your message has travelled the network without errors:
+if (crc16_xmodem(message, 6) != 0) {
+ printf("Message got corrupted in transit\n");
+}
+```
+
+Features
+--------
+
+- Consistent CRC-16/XMODEM implementation
+- ANSI C (C99)
+- Fast lookup-table based calculation
+
+API
+---
+
+### crc16_xmodem(data, length)
+
+Calculate the CRC-16/XMODEM checksum of the given data
+
+```c
+uint16_t crc16_xmodem(const uint8_t *data, size_t length);
+```
+
+### crc16_xmodem_b(data, length, out)
+
+Calculate the CRC-16/XMODEM checksum and write it as big-endian bytes
+
+```c
+void crc16_xmodem_b(const uint8_t *data, size_t length, uint8_t *out);
+```
+
+Testing
+-------
+
+If you want to run the library's tests, simply run `make test` to compile
+the testing binary, and then `./test` to run the actual tests.
+
+License
+-------
+
+crc16-xmodem source code is available under the [FGPL license](LICENSE.md)
diff --git a/config.mk b/config.mk
@@ -0,0 +1 @@
+SRC+={{module.dirname}}/src/crc16-xmodem.c
diff --git a/src/crc16-xmodem.c b/src/crc16-xmodem.c
@@ -0,0 +1,55 @@
+#include "crc16-xmodem.h"
+
+#define polynomial 0x1021
+#define init 0x0000
+#define xorout 0x0000
+
+static const uint16_t crc_table[256] = {
+ 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
+ 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
+ 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
+ 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
+ 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
+ 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
+ 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
+ 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
+ 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
+ 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
+ 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
+ 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
+ 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
+ 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
+ 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
+ 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
+ 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
+ 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
+ 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
+ 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
+ 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
+ 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
+ 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
+ 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
+ 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
+ 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
+ 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
+ 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
+ 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
+ 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
+ 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
+ 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
+};
+
+uint16_t crc16_xmodem(const uint8_t *data, size_t length) {
+ uint16_t result = init;
+ for (size_t i = 0; i < length; i++) {
+ uint8_t index = (result >> 8) ^ data[i];
+ result = (result << 8) ^ crc_table[index];
+ }
+ return result ^ xorout;
+}
+
+void crc16_xmodem_b(const uint8_t *data, size_t length, uint8_t *out) {
+ uint16_t crc = crc16_xmodem(data, length);
+ out[0] = (crc >> 8) & 0xFF;
+ out[1] = crc & 0xFF;
+}
diff --git a/src/crc16-xmodem.h b/src/crc16-xmodem.h
@@ -0,0 +1,85 @@
+#ifndef __FINWO_CRC16_XMODEM_H__
+#define __FINWO_CRC16_XMODEM_H__
+
+/// crc16-xmodem
+/// =============
+///
+/// Generate CRC-16/XMODEM codes consistently
+///
+/// Why
+/// ---
+///
+/// While a fairly simple task, I was implementing this throughout multiple projects
+/// of mine. Now if there's a bug, speed improvement to be had, or simple because
+/// the C standard changes, I want to be able to update it in 1 location
+/// instead of having to fix it in every individual project.
+///
+/// TL;DR; I'm lazy, and want a single version of the code
+///
+/// Usage
+/// -----
+///
+/// ```c
+/// #include "finwo/crc16-xmodem.h"
+///
+/// uint8_t message[] = "Hi!\0\0";
+///
+/// // Output 12797 (0x31FD)
+/// uint16_t crc = crc16_xmodem(message, 4);
+/// crc16_xmodem_b(message, 4, &message[4]);
+///
+/// // Now if you want to check if your message has travelled the network without errors:
+/// if (crc16_xmodem(message, 6) != 0) {
+/// printf("Message got corrupted in transit\n");
+/// }
+/// ```
+///
+/// Features
+/// --------
+///
+/// - Consistent CRC-16/XMODEM implementation
+/// - ANSI C (C99)
+/// - Fast lookup-table based calculation
+///
+/// API
+/// ---
+
+#include <stdint.h>
+#include <stddef.h>
+
+/// <details>
+/// <summary>crc16_xmodem(data, length)</summary>
+///
+/// Calculate the CRC-16/XMODEM checksum of the given data
+///
+/// ```c
+/// uint16_t crc16_xmodem(const uint8_t *data, size_t length);
+/// ```
+///
+/// </details>
+uint16_t crc16_xmodem(const uint8_t *data, size_t length);
+
+/// <details>
+/// <summary>crc16_xmodem_b(data, length, out)</summary>
+///
+/// Calculate the CRC-16/XMODEM checksum and write it as big-endian bytes
+///
+/// ```c
+/// void crc16_xmodem_b(const uint8_t *data, size_t length, uint8_t *out);
+/// ```
+///
+/// </details>
+void crc16_xmodem_b(const uint8_t *data, size_t length, uint8_t *out);
+
+/// Testing
+/// -------
+///
+/// If you want to run the library's tests, simply run `make test` to compile
+/// the testing binary, and then `./test` to run the actual tests.
+///
+/// License
+/// -------
+///
+/// crc16-xmodem source code is available under the MIT-0 license.
+
+#endif
diff --git a/test.c b/test.c
@@ -0,0 +1,108 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "crc16-xmodem.h"
+
+static int tests_run = 0;
+static int tests_passed = 0;
+
+#define ASSERT(cond, msg) do { \
+ tests_run++; \
+ if (cond) { \
+ tests_passed++; \
+ printf(" PASS: %s\n", msg); \
+ } else { \
+ printf(" FAIL: %s\n", msg); \
+ } \
+} while(0)
+
+#define RUN(name) do { \
+ printf("Running %s...\n", #name); \
+ name(); \
+} while(0)
+
+void test_crc16_xmodem_basic() {
+ uint8_t message[] = "Hi!\0\0";
+
+ uint16_t crc = crc16_xmodem(message, 3);
+ ASSERT(crc == 12797, "'Hi!' produces good known-value numerically");
+ ASSERT(crc == 0x31FD, "'Hi!' produces 0x31FD");
+
+ uint8_t out[2];
+ crc16_xmodem_b(message, 3, out);
+ ASSERT(out[0] == 0x31 && out[1] == 0xFD, "'Hi!' produces 0x31FD as bytes");
+}
+
+void test_crc16_xmodem_known_values() {
+ struct {
+ const char *input;
+ uint16_t expected;
+ } known[] = {
+ {"Hello World\0\0" , 0x992A},
+ {"Hello World!\0\0" , 0x0CD3},
+ {"Pizza Calzone\0\0", 0x795B},
+ };
+
+ for (size_t i = 0; i < sizeof(known) / sizeof(known[0]); i++) {
+ size_t len = strlen(known[i].input);
+ uint16_t crc = crc16_xmodem((const uint8_t *)known[i].input, len);
+ char buf[256];
+ snprintf(buf, sizeof(buf), "known value '%s' produces correct output", known[i].input);
+ ASSERT(crc == known[i].expected, buf);
+ }
+}
+
+void test_crc16_xmodem_inverse() {
+ struct {
+ const char *input;
+ uint16_t expected;
+ } known[] = {
+ {"Hello World\0\0" , 0x992A},
+ {"Hello World!\0\0" , 0x0CD3},
+ {"Pizza Calzone\0\0", 0x795B},
+ };
+
+ for (size_t i = 0; i < sizeof(known) / sizeof(known[0]); i++) {
+ size_t len = strlen(known[i].input);
+ uint8_t *nb = malloc(len + 2);
+ memcpy(nb, known[i].input, len);
+
+ crc16_xmodem_b(nb, len, &nb[len]);
+
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "inverse '%s' resolves to 0 when no errors introduced", known[i].input);
+ ASSERT(crc16_xmodem(nb, len + 2) == 0, buf);
+
+ nb[0]++;
+ snprintf(buf, sizeof(buf), "inverse '%s' resolves to non-0 with increment error", known[i].input);
+ ASSERT(crc16_xmodem(nb, len + 2) != 0, buf);
+ nb[0]--;
+
+ nb[1] ^= 0x02;
+ snprintf(buf, sizeof(buf), "inverse '%s' resolves to non-0 with 1 bit-flip", known[i].input);
+ ASSERT(crc16_xmodem(nb, len + 2) != 0, buf);
+ nb[1] ^= 0x02;
+
+ nb[1] ^= 0x04;
+ snprintf(buf, sizeof(buf), "inverse '%s' resolves to non-0 with 2 consecutive bit-flips", known[i].input);
+ ASSERT(crc16_xmodem(nb, len + 2) != 0, buf);
+
+ free(nb);
+ }
+}
+
+int main() {
+ printf("CRC16-XMODEM Tests\n");
+ printf("==================\n\n");
+
+ RUN(test_crc16_xmodem_basic);
+ RUN(test_crc16_xmodem_known_values);
+ RUN(test_crc16_xmodem_inverse);
+
+ printf("\n------------------\n");
+ printf("Results: %d/%d tests passed\n", tests_passed, tests_run);
+
+ return tests_passed == tests_run ? 0 : 1;
+}