From 04c83115283f226d4e071bbe9e09d390eb27a57b Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 9 Sep 2025 08:47:26 +0200 Subject: [PATCH 1/6] BUG: Walking logic in check() is broken. Moving aligning into source, removing macros --- freelist/Makefile | 4 +-- freelist/freelist.c | 59 ++++++++++++++++++++++++++++++--------------- freelist/freelist.h | 14 +---------- freelist/hexdump.c | 35 +++++++++++++++++++++++++++ freelist/hexdump.h | 8 ++++++ freelist/main.c | 41 ++++++++++++++++++++++--------- 6 files changed, 115 insertions(+), 46 deletions(-) create mode 100644 freelist/hexdump.c create mode 100644 freelist/hexdump.h diff --git a/freelist/Makefile b/freelist/Makefile index f2e1377..fa96c24 100644 --- a/freelist/Makefile +++ b/freelist/Makefile @@ -1,10 +1,8 @@ CC = gcc CFLAGS = -Wall -O2 -CFLAGS += -DFREELIST_ALIGN - TARGET = main.elf -SRC = main.c freelist.c +SRC = main.c freelist.c hexdump.c #LDFLAGS = diff --git a/freelist/freelist.c b/freelist/freelist.c index f9dc531..62bd1a3 100644 --- a/freelist/freelist.c +++ b/freelist/freelist.c @@ -6,19 +6,27 @@ #include #include -struct FreeListBlock { +struct __attribute__((packed)) FreeListBlock { struct FreeListBlock *next; }; -/* Initialize the FreeList */ -int fl_init(FreeList *fl, uintptr_t start, uintptr_t end, size_t itemsize) { - size_t size = ALIGN(itemsize); +/* Align to nearest multiple of align */ +static inline size_t align_up_to(size_t n, size_t align) { + return (n + align - 1) & ~(align - 1); +} - if (!fl || end <= start) - return EXIT_FAILURE; +/* Initialize the FreeList */ +int fl_init(FreeList *fl, uintptr_t start, size_t size_bytes, size_t itemsize) { + /* Fiddle around according to your need, (void *) alignment seems to be enough, + * but MAX_ALIGN_T is also an option. Especially for allocator implementation. */ + size_t size = align_up_to(itemsize, sizeof(void *)); + + if (size < sizeof(FreeListBlock) || !fl) { + return 0; + } fl->start = start; - fl->end = end; + fl->end = start + size_bytes; fl->size = size; fl->allocated = 0; @@ -28,10 +36,10 @@ int fl_init(FreeList *fl, uintptr_t start, uintptr_t end, size_t itemsize) { block = block->next; } - block->next = NULL; - + block->next = NULL; /* Last block */ fl->free = (FreeListBlock *)start; - return EXIT_SUCCESS; + + return 1; } /* Allocate some memory from the FreeList */ @@ -41,7 +49,7 @@ void *fl_alloc(FreeList *fl) { FreeListBlock *m = fl->free; - fl->free = fl->free->next; + fl->free = fl->free->next; /* May be null, which is fine */ fl->allocated++; memset((void *)m, 0, sizeof(FreeListBlock)); @@ -50,17 +58,16 @@ void *fl_alloc(FreeList *fl) { /* Return some memory to the FreeList */ int fl_free(FreeList *fl, void *ptr) { - if (!fl_is_managed(fl, ptr)) { - return EXIT_FAILURE; /* We cant free memory we do not own */ - } + if (!fl_is_managed(fl, ptr)) + return 0; /* We cant free memory we do not own */ - FreeListBlock *block = (FreeListBlock *)(uintptr_t)ptr; + FreeListBlock *block = (FreeListBlock *)ptr; - block->next = fl->free; + block->next = fl->free; /* May be null, which is fine */ fl->free = block; fl->allocated--; - return EXIT_SUCCESS; + return 1; } /* Returns how many slots are occupied */ @@ -80,7 +87,13 @@ size_t fl_capacity(FreeList *fl) { /* Check if a piece of memory is managed by the FreeList */ int fl_is_managed(FreeList *fl, void *ptr) { - return ((uintptr_t)ptr >= fl->start && (uintptr_t)ptr < fl->end); + uintptr_t p = (uintptr_t)ptr; + + if (p < fl->start || p >= fl->end) { + return 0; // outside pool + } + + return ((p - fl->start) % fl->size) == 0; // aligned to block } /* Returns the ratio of metadata versus data as a scalar in range 0..1 */ @@ -94,8 +107,16 @@ size_t fl_check(FreeList *fl) { FreeListBlock *cursor = fl->free; while (cursor->next != NULL) { - avail++; cursor = cursor->next; + + if (!fl_is_managed(fl, cursor)) { + printf("Unused memory at: %zX\n", (size_t)cursor->next); + printf("Min memory at: %zX\n", (size_t)fl->start); + printf("Max memory at: %zX\n", (size_t)fl->end); + return 0; + } + + avail++; } return avail; diff --git a/freelist/freelist.h b/freelist/freelist.h index 015ee33..ed7949c 100644 --- a/freelist/freelist.h +++ b/freelist/freelist.h @@ -5,18 +5,6 @@ #include #include -/* Align to nearest multiple of align */ -static inline size_t align_up_to(size_t n, size_t align) { - return (n + align - 1) & ~(align - 1); -} - -/* Fiddle these around according to your need. Delete or check makefile */ -#ifdef FREELIST_ALIGN -#define ALIGN(x) (align_up_to(x, sizeof(void *))) -#else -#define ALIGN(x) (x) -#endif - typedef struct FreeListBlock FreeListBlock; typedef struct { @@ -27,7 +15,7 @@ typedef struct { size_t allocated; } FreeList; -int fl_init(FreeList *fl, uintptr_t start, uintptr_t end, size_t itemsize); +int fl_init(FreeList *fl, uintptr_t start, size_t size_bytes, size_t itemsize); int fl_free(FreeList *fl, void *ptr); int fl_is_managed(FreeList *fl, void *ptr); void *fl_alloc(FreeList *fl); diff --git a/freelist/hexdump.c b/freelist/hexdump.c new file mode 100644 index 0000000..36db76e --- /dev/null +++ b/freelist/hexdump.c @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +void hexdump(const void *data, size_t size) { + const unsigned char *p = (const unsigned char *)data; + size_t i, j; + + for (i = 0; i < size; i += 16) { + // Print offset + printf("%08zx ", i); + + // Print hex bytes + for (j = 0; j < 16; j++) { + if (i + j < size) { + printf("%02x ", p[i + j]); + } else { + printf(" "); // padding for incomplete lines + } + if (j == 7) + printf(" "); // extra space in middle + } + + printf(" |"); + + // Print ASCII characters + for (j = 0; j < 16 && i + j < size; j++) { + unsigned char c = p[i + j]; + printf("%c", isprint(c) ? c : '.'); + } + + printf("|\n"); + } +} diff --git a/freelist/hexdump.h b/freelist/hexdump.h new file mode 100644 index 0000000..d01a907 --- /dev/null +++ b/freelist/hexdump.h @@ -0,0 +1,8 @@ +#ifndef HEXDUMP_H +#define HEXDUMP_H + +#include + +void hexdump(const void *data, size_t size); + +#endif // HEXDUMP_H diff --git a/freelist/main.c b/freelist/main.c index 0fde246..dfc4105 100644 --- a/freelist/main.c +++ b/freelist/main.c @@ -1,4 +1,5 @@ #include "freelist.h" +#include "hexdump.h" #include #include #include @@ -17,26 +18,40 @@ void printvec(const Vec3 *v) { printf("Vec3: (%d, %d, %d)\n", v->x, v->y, v->z); } -#define BUFFER_SIZE (4096) +#define BUFFER_SIZE (128) int main() { FreeList fl; const char *mem = malloc(BUFFER_SIZE); + memset((void *)mem, 0, BUFFER_SIZE); + + if (!fl_init(&fl, (uintptr_t)mem, BUFFER_SIZE, sizeof(Vec3))) { + printf("Allocation error\n"); + exit(EXIT_FAILURE); + } + + hexdump(mem, BUFFER_SIZE); - fl_init(&fl, (uintptr_t)mem, (uintptr_t)mem + BUFFER_SIZE, sizeof(Vec3)); const size_t cap = fl_capacity(&fl); printf("Item size: %lu\n", sizeof(Vec3)); + printf("FreeList Capacity: %lu\n", cap); printf("Buffer size: %d\n", BUFFER_SIZE); + printf("Freelist Blocksize: %lu\n", fl.size); printf("Space utilization internal: %.2f%%\n", 100.0 * fl_utilization(&fl, sizeof(Vec3))); printf("Space utilization external: %.2f%%\n", 100.0 * ((float)fl.size * cap) / BUFFER_SIZE); assert(fl_available(&fl) == cap); + if (!fl_check(&fl)) + printf("Check failed!\n"); + Vec3 *a = fl_alloc(&fl); Vec3 *b = fl_alloc(&fl); Vec3 *c = fl_alloc(&fl); + hexdump(mem, BUFFER_SIZE); + memset(a, 0x23, sizeof(Vec3)); memset(b, 0x24, sizeof(Vec3)); memset(c, 0x25, sizeof(Vec3)); @@ -49,18 +64,18 @@ int main() { printvec(b); printvec(c); - assert(fl_check(&fl) == cap - 3); - assert(fl_capacity(&fl) == fl_available(&fl) + 3); + // assert(fl_check(&fl) == cap - 3); + // assert(fl_capacity(&fl) == fl_available(&fl) + 3); printf("Available: %zu of %zu\n", fl_available(&fl), fl_capacity(&fl)); - assert(fl_free(&fl, a) == EXIT_SUCCESS); - assert(fl_free(&fl, b) == EXIT_SUCCESS); - assert(fl_free(&fl, c) == EXIT_SUCCESS); + assert(fl_free(&fl, a)); + assert(fl_free(&fl, b)); + assert(fl_free(&fl, c)); printf("Available: %zu of %zu\n", fl_available(&fl), fl_capacity(&fl)); - assert(fl_check(&fl) == fl_capacity(&fl) - fl_allocated(&fl)); + // assert(fl_check(&fl) == fl_capacity(&fl) - fl_allocated(&fl)); assert(fl_allocated(&fl) == 0); /* All memory is free here */ @@ -72,6 +87,10 @@ int main() { ptr_buf[i] = (uintptr_t)fl_alloc(&fl); } + /* Write some data to the last element to catch out of bounds writes */ + size_t last_elem = fl_capacity(&fl) - 1; + *(Vec3 *)ptr_buf[last_elem] = *a; + assert(0 == fl_available(&fl)); assert(fl_allocated(&fl) == fl_capacity(&fl)); assert(fl_check(&fl) == 0); @@ -81,9 +100,9 @@ int main() { fl_free(&fl, (void *)ptr_buf[i]); } - assert(cap == fl_available(&fl)); - assert(fl_allocated(&fl) == 0); - assert(fl_check(&fl) == cap); + // assert(cap == fl_available(&fl)); + // assert(fl_allocated(&fl) == 0); + // assert(fl_check(&fl) == cap); printf("All tests passed!\n"); return 0; From dc2bfe10a8f6f2fbb52b646186c5624b979f0a55 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 9 Sep 2025 10:05:29 +0200 Subject: [PATCH 2/6] Uppercase hexdup --- freelist/hexdump.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freelist/hexdump.c b/freelist/hexdump.c index 36db76e..cdf2121 100644 --- a/freelist/hexdump.c +++ b/freelist/hexdump.c @@ -14,7 +14,7 @@ void hexdump(const void *data, size_t size) { // Print hex bytes for (j = 0; j < 16; j++) { if (i + j < size) { - printf("%02x ", p[i + j]); + printf("%02X ", p[i + j]); } else { printf(" "); // padding for incomplete lines } From 9eb7b17a65e8a337e7efd169fc1444f748d7c516 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 9 Sep 2025 10:05:44 +0200 Subject: [PATCH 3/6] Reintroduce all tests --- freelist/main.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/freelist/main.c b/freelist/main.c index dfc4105..79a39dd 100644 --- a/freelist/main.c +++ b/freelist/main.c @@ -46,6 +46,8 @@ int main() { if (!fl_check(&fl)) printf("Check failed!\n"); + printf("Check: %zu\n", fl_check(&fl)); + Vec3 *a = fl_alloc(&fl); Vec3 *b = fl_alloc(&fl); Vec3 *c = fl_alloc(&fl); @@ -64,8 +66,8 @@ int main() { printvec(b); printvec(c); - // assert(fl_check(&fl) == cap - 3); - // assert(fl_capacity(&fl) == fl_available(&fl) + 3); + assert(fl_check(&fl) == cap - 3); + assert(fl_capacity(&fl) == fl_available(&fl) + 3); printf("Available: %zu of %zu\n", fl_available(&fl), fl_capacity(&fl)); @@ -75,7 +77,7 @@ int main() { printf("Available: %zu of %zu\n", fl_available(&fl), fl_capacity(&fl)); - // assert(fl_check(&fl) == fl_capacity(&fl) - fl_allocated(&fl)); + assert(fl_check(&fl) == fl_capacity(&fl) - fl_allocated(&fl)); assert(fl_allocated(&fl) == 0); /* All memory is free here */ @@ -93,6 +95,8 @@ int main() { assert(0 == fl_available(&fl)); assert(fl_allocated(&fl) == fl_capacity(&fl)); + + printf("Check: %zu\n", fl_check(&fl)); assert(fl_check(&fl) == 0); /* Return it all */ @@ -100,9 +104,9 @@ int main() { fl_free(&fl, (void *)ptr_buf[i]); } - // assert(cap == fl_available(&fl)); - // assert(fl_allocated(&fl) == 0); - // assert(fl_check(&fl) == cap); + assert(cap == fl_available(&fl)); + assert(fl_allocated(&fl) == 0); + assert(fl_check(&fl) == cap); printf("All tests passed!\n"); return 0; From f97505d8ba8deaf88b6925ea9b24025aa391e6d6 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 9 Sep 2025 10:06:03 +0200 Subject: [PATCH 4/6] Freelist now uses offsets instead of raw pointers instead of header --- freelist/freelist.c | 54 +++++++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/freelist/freelist.c b/freelist/freelist.c index 62bd1a3..7a31c38 100644 --- a/freelist/freelist.c +++ b/freelist/freelist.c @@ -7,7 +7,7 @@ #include struct __attribute__((packed)) FreeListBlock { - struct FreeListBlock *next; + uint16_t next_offset; }; /* Align to nearest multiple of align */ @@ -15,15 +15,31 @@ static inline size_t align_up_to(size_t n, size_t align) { return (n + align - 1) & ~(align - 1); } +// Convert pointer -> 1-based offset (0 means NULL) +static inline uint32_t ptr_to_offset(FreeList *fl, void *ptr) { + if (!ptr) + return 0; // NULL maps to 0 + uintptr_t diff = (uintptr_t)ptr - fl->start; + return (uint32_t)(diff / fl->size) + 1; +} + +// Convert 1-based offset -> pointer (0 means NULL) +static inline void *offset_to_ptr(FreeList *fl, uint32_t offset) { + if (offset == 0) + return NULL; // 0 = invalid/null + return (void *)(fl->start + (uintptr_t)(offset - 1) * fl->size); +} + /* Initialize the FreeList */ int fl_init(FreeList *fl, uintptr_t start, size_t size_bytes, size_t itemsize) { /* Fiddle around according to your need, (void *) alignment seems to be enough, * but MAX_ALIGN_T is also an option. Especially for allocator implementation. */ size_t size = align_up_to(itemsize, sizeof(void *)); - if (size < sizeof(FreeListBlock) || !fl) { + assert(sizeof(FreeListBlock) == 2); + + if (size < sizeof(FreeListBlock) || !fl) return 0; - } fl->start = start; fl->end = start + size_bytes; @@ -31,12 +47,13 @@ int fl_init(FreeList *fl, uintptr_t start, size_t size_bytes, size_t itemsize) { fl->allocated = 0; FreeListBlock *block = (FreeListBlock *)start; - for (size_t i = 0; i < fl_capacity(fl); i++) { - block->next = (FreeListBlock *)((void *)block + size); - block = block->next; + + for (size_t offset = 1; offset < fl_capacity(fl) + 1; offset++) { + block->next_offset = (int32_t)offset; + block = offset_to_ptr(fl, offset); } - block->next = NULL; /* Last block */ + block->next_offset = 0; /* Last block */ fl->free = (FreeListBlock *)start; return 1; @@ -49,7 +66,7 @@ void *fl_alloc(FreeList *fl) { FreeListBlock *m = fl->free; - fl->free = fl->free->next; /* May be null, which is fine */ + fl->free = offset_to_ptr(fl, fl->free->next_offset); /* May be null, which is fine */ fl->allocated++; memset((void *)m, 0, sizeof(FreeListBlock)); @@ -63,7 +80,7 @@ int fl_free(FreeList *fl, void *ptr) { FreeListBlock *block = (FreeListBlock *)ptr; - block->next = fl->free; /* May be null, which is fine */ + block->next_offset = ptr_to_offset(fl, fl->free); /* May be null, which is fine */ fl->free = block; fl->allocated--; @@ -106,17 +123,16 @@ size_t fl_check(FreeList *fl) { int avail = 0; FreeListBlock *cursor = fl->free; - while (cursor->next != NULL) { - cursor = cursor->next; - - if (!fl_is_managed(fl, cursor)) { - printf("Unused memory at: %zX\n", (size_t)cursor->next); - printf("Min memory at: %zX\n", (size_t)fl->start); - printf("Max memory at: %zX\n", (size_t)fl->end); - return 0; - } - + while (cursor) { avail++; + + if (!fl_is_managed(fl, cursor) || avail > fl_capacity(fl)) + return 0; + + if (cursor->next_offset == 0) + break; + + cursor = offset_to_ptr(fl, cursor->next_offset); } return avail; From 8fb583b57f4390d5c8809541cc12bf90653c1abc Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 9 Sep 2025 10:19:12 +0200 Subject: [PATCH 5/6] Unused headers, remove some specific asserts, some reference notes on wiping block --- freelist/freelist.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/freelist/freelist.c b/freelist/freelist.c index 7a31c38..7b181ae 100644 --- a/freelist/freelist.c +++ b/freelist/freelist.c @@ -1,10 +1,4 @@ #include "freelist.h" -#include "stddef.h" -#include -#include -#include -#include -#include struct __attribute__((packed)) FreeListBlock { uint16_t next_offset; @@ -36,8 +30,6 @@ int fl_init(FreeList *fl, uintptr_t start, size_t size_bytes, size_t itemsize) { * but MAX_ALIGN_T is also an option. Especially for allocator implementation. */ size_t size = align_up_to(itemsize, sizeof(void *)); - assert(sizeof(FreeListBlock) == 2); - if (size < sizeof(FreeListBlock) || !fl) return 0; @@ -69,7 +61,14 @@ void *fl_alloc(FreeList *fl) { fl->free = offset_to_ptr(fl, fl->free->next_offset); /* May be null, which is fine */ fl->allocated++; - memset((void *)m, 0, sizeof(FreeListBlock)); + /* Wipe it before sending it out, could use memset + * here, or even wiping the entire block */ + m->next_offset = 0; + + /* For reference: */ + // memset((void *)m, 0, fl->size); /* Wipe entire block */ + // memset((void *)m, 0, sizeof(FreeListBlock)); /* Wipe only header */ + return ((void *)m); } From dcfd4c01ffa2a0c7469b8d20608c0e0daa35bd51 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 9 Sep 2025 11:00:25 +0200 Subject: [PATCH 6/6] More rigorous testing and helpful comments --- freelist/main.c | 83 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 63 insertions(+), 20 deletions(-) diff --git a/freelist/main.c b/freelist/main.c index 79a39dd..ffb2068 100644 --- a/freelist/main.c +++ b/freelist/main.c @@ -18,20 +18,42 @@ void printvec(const Vec3 *v) { printf("Vec3: (%d, %d, %d)\n", v->x, v->y, v->z); } +/* For testing crc poly 0x04C11DB7 */ +uint32_t crc32(const uint8_t *data, size_t length); + #define BUFFER_SIZE (128) int main() { - FreeList fl; - const char *mem = malloc(BUFFER_SIZE); - memset((void *)mem, 0, BUFFER_SIZE); + FreeList fl; + + /* Here we split our malloc'ed memory into three parts, we pass one of + * them to our freelist, and keep the other two (head/tail) to check for + * overwrites: + * + * | Headblock | Freelist block | Tailblock | + * | 1x | 2x | 1x | + */ + void *headblock = malloc(BUFFER_SIZE * 2); + void *tailblock = headblock + (BUFFER_SIZE + BUFFER_SIZE / 2); + void *mem = headblock + (BUFFER_SIZE / 2); + + /* Set these buffers to something known and randomize the buffer we give to the freelist */ + memset(mem, arc4random(), BUFFER_SIZE); + memset(headblock, 0xAB, BUFFER_SIZE / 2); + memset(tailblock, 0xF0, BUFFER_SIZE / 2); + + /* Store these for reference at the end of the testing */ + const uint32_t tail_crc = crc32(tailblock, BUFFER_SIZE / 2); + const uint32_t head_crc = crc32(headblock, BUFFER_SIZE / 2); + + /* Display a nice hexdump so you can see the layout */ + hexdump(headblock, BUFFER_SIZE * 2); if (!fl_init(&fl, (uintptr_t)mem, BUFFER_SIZE, sizeof(Vec3))) { - printf("Allocation error\n"); + printf("Freelist failed to initialize!\n"); exit(EXIT_FAILURE); } - hexdump(mem, BUFFER_SIZE); - const size_t cap = fl_capacity(&fl); printf("Item size: %lu\n", sizeof(Vec3)); @@ -42,23 +64,17 @@ int main() { printf("Space utilization external: %.2f%%\n", 100.0 * ((float)fl.size * cap) / BUFFER_SIZE); assert(fl_available(&fl) == cap); - - if (!fl_check(&fl)) - printf("Check failed!\n"); - - printf("Check: %zu\n", fl_check(&fl)); + assert(fl_check(&fl) == cap); Vec3 *a = fl_alloc(&fl); Vec3 *b = fl_alloc(&fl); Vec3 *c = fl_alloc(&fl); - hexdump(mem, BUFFER_SIZE); + memset(a, 0xCA, sizeof(Vec3)); + memset(b, 0xCB, sizeof(Vec3)); + memset(c, 0xCC, sizeof(Vec3)); - memset(a, 0x23, sizeof(Vec3)); - memset(b, 0x24, sizeof(Vec3)); - memset(c, 0x25, sizeof(Vec3)); - - *a = vec3_new(12, 13, 435); + *a = vec3_new(0xAAAA, 0xAAAA, 0xAAAA); *b = vec3_new(192, 199, 435); *c = vec3_new(432, 11, 435); @@ -91,12 +107,10 @@ int main() { /* Write some data to the last element to catch out of bounds writes */ size_t last_elem = fl_capacity(&fl) - 1; - *(Vec3 *)ptr_buf[last_elem] = *a; + memset((void *)ptr_buf[last_elem], 0x42, sizeof(Vec3)); assert(0 == fl_available(&fl)); assert(fl_allocated(&fl) == fl_capacity(&fl)); - - printf("Check: %zu\n", fl_check(&fl)); assert(fl_check(&fl) == 0); /* Return it all */ @@ -104,10 +118,39 @@ int main() { fl_free(&fl, (void *)ptr_buf[i]); } + free(ptr_buf); + + /* All slots are free here */ assert(cap == fl_available(&fl)); assert(fl_allocated(&fl) == 0); assert(fl_check(&fl) == cap); + /* Check so our head and tail blocks are intact */ + assert(crc32(headblock, BUFFER_SIZE / 2) == head_crc); + assert(crc32(tailblock, BUFFER_SIZE / 2) == tail_crc); + + /* Hexdump the heap, free the block and declare victory */ + hexdump(headblock, BUFFER_SIZE * 2); + free(headblock); printf("All tests passed!\n"); return 0; } + +uint32_t crc32(const uint8_t *data, size_t length) { + uint32_t crc = 0xFFFFFFFF; + uint32_t poly = 0x04C11DB7; + + for (size_t i = 0; i < length; i++) { + crc ^= ((uint32_t)data[i]) << 24; + + for (int j = 0; j < 8; j++) { + if (crc & 0x80000000) { + crc = (crc << 1) ^ poly; + } else { + crc <<= 1; + } + } + } + + return crc ^ 0xFFFFFFFF; +}