summaryrefslogtreecommitdiffstats
path: root/include/bin.h
diff options
context:
space:
mode:
authorEthan Werner <mail@ethanwerner.com>2026-06-09 01:17:41 +0400
committerEthan Werner <mail@ethanwerner.com>2026-06-09 01:17:41 +0400
commit96ecf29e8be977f51f351308ef2c7c492bb829aa (patch)
treec664aa61262e6f7627a732f0e4fbb84281b99952 /include/bin.h
parentf6a144987cc403de8ad6bddd055f756120f8d405 (diff)
refactor
Diffstat (limited to 'include/bin.h')
-rwxr-xr-xinclude/bin.h315
1 files changed, 242 insertions, 73 deletions
diff --git a/include/bin.h b/include/bin.h
index d764f09..d06b074 100755
--- a/include/bin.h
+++ b/include/bin.h
@@ -1,47 +1,77 @@
// bin.h - flat file, fixed block size, time series database
+//
+// Thread safety: Not thread-safe. External synchronization required.
+// Behavior: No input validation — out-of-order data persists as-is.
+// Unbuffered I/O by default (use _buffered variants for performance).
+// _ok variants return 1 on success, 0 on any error (invalid args, I/O fail,
+// overflow). Non-_ok variants remain for backward compatibility.
#ifndef BIN_H
#define BIN_H
+/* USER-DEFINED (define before including):
+ * typedef <numerical_type> bin_key_t;
+ */
+
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
+#include <string.h>
#include "type.h"
typedef struct {
- uint_t length;
- uint_t block_size;
+ u64 length;
+ u64 block_size;
} bin_meta_t;
-FILE *bin_init(char const *, uint_t);
+FILE *bin_init(char const *, u64);
+FILE *bin_init_buffered(char const *, u64);
FILE *bin_open(char const *path);
-void bin_close(FILE *);
-void bin_read(FILE *, int_t, void *, uint_t);
-void bin_write(FILE *, int_t, void *, uint_t);
-void bin_insert(FILE *, int_t, void *, uint_t);
-void bin_append(FILE *, void *, uint_t);
+FILE *bin_open_buffered(char const *path);
+int bin_read(FILE *, i64, void *, u64);
+void bin_write(FILE *, i64, void *, u64);
+int bin_write_ok(FILE *, i64, void *, u64);
+void bin_insert(FILE *, i64, void *, u64);
+int bin_insert_ok(FILE *, i64, void *, u64);
+void bin_append(FILE *, void *, u64);
+int bin_append_ok(FILE *, void *, u64);
+int bin_close(FILE *);
-uint_t bin_length(FILE *);
-uint_t bin_block_size(FILE *);
+u64 bin_length(FILE *);
+u64 bin_block_size(FILE *);
-int_t bin_search(FILE *, bin_key_t);
-uint_t bin_fuzzy_index(int_t);
+i64 bin_search(FILE *, bin_key_t);
+u64 bin_fuzzy_index(i64);
#endif // BIN_H
#ifdef BIN_IMPLEMENTATION // IMPLEMENTATION
-#include <assert.h>
#include <stdlib.h>
-static void bin_write_length(FILE *, uint_t);
-static void bin_write_block_size(FILE *, uint_t);
+static void bin_write_length(FILE *, u64);
+static void bin_write_block_size(FILE *, u64);
-#define index_bytes(i, bs) ((i * bs) + sizeof(bin_meta_t))
+static inline i64 index_bytes_safe(i64 i, u64 bs, u64 *out) {
+ if (bs == 0 || i < 0 || (u64)i > (UINT64_MAX - sizeof(bin_meta_t)) / bs)
+ return -1;
+ *out = (u64)i * bs + sizeof(bin_meta_t);
+ return 0;
+}
#define member_sizeof(type, member) (sizeof(((type *)0)->member))
+static inline int fseek_safe(FILE *f, u64 offset) {
+#ifdef _WIN32
+ return _fseeki64(f, (long long)offset, SEEK_SET);
+#else
+ if ((u64)(long)offset != offset)
+ return -1;
+ return fseek(f, (long)offset, SEEK_SET);
+#endif
+}
+
// bin_init()
//
// Creates the desired binary file
@@ -50,7 +80,9 @@ static void bin_write_block_size(FILE *, uint_t);
//
// return - The newly created file
-FILE *bin_init(char const *path, uint_t block_size) {
+FILE *bin_init(char const *path, u64 block_size) {
+ if (!path)
+ return NULL;
FILE *f = fopen(path, "wb+");
if (!f)
@@ -64,6 +96,20 @@ FILE *bin_init(char const *path, uint_t block_size) {
return f;
}
+FILE *bin_init_buffered(char const *path, u64 block_size) {
+ if (!path)
+ return NULL;
+ FILE *f = fopen(path, "wb+");
+
+ if (!f)
+ return NULL;
+
+ bin_write_length(f, 0);
+ bin_write_block_size(f, block_size);
+
+ return f;
+}
+
// bin_open()
//
// Opens the desired binary file
@@ -73,6 +119,8 @@ FILE *bin_init(char const *path, uint_t block_size) {
// return - The newly opened file
FILE *bin_open(char const *path) {
+ if (!path)
+ return NULL;
FILE *f = fopen(path, "rb+");
if (!f)
@@ -83,13 +131,23 @@ FILE *bin_open(char const *path) {
return f;
}
+FILE *bin_open_buffered(char const *path) {
+ if (!path)
+ return NULL;
+ return fopen(path, "rb+");
+}
+
// bin_close()
//
// Closes the desired binary file
//
// f - The file descriptor to be closed
-void bin_close(FILE *f) { fclose(f); }
+int bin_close(FILE *f) {
+ if (!f) return 0;
+ int r = fclose(f);
+ return r == 0;
+}
// bin_read()
//
@@ -100,13 +158,33 @@ void bin_close(FILE *f) { fclose(f); }
// data - The block_t buffer to be read into
// n - The number of elements to read into the buffer
-void bin_read(FILE *f, int_t i, void *data, uint_t n) {
- uint_t bs = bin_block_size(f);
+int bin_read(FILE *f, i64 i, void *data, u64 n) {
+ if (!f || !data || n == 0)
+ return 0;
+ u64 bs = bin_block_size(f);
+ if (bs == 0)
+ return 0;
- assert(0 <= i && (i + n) <= bin_length(f));
+ if (i < 0)
+ return 0;
+ u64 len = bin_length(f);
+ if ((u64)i + n > len)
+ return 0;
- fseek(f, index_bytes(i, bs), SEEK_SET);
- fread(data, bs, n, f);
+ u64 offset = 0;
+ if (index_bytes_safe(i, bs, &offset) != 0)
+ return 0;
+ if (bs > SIZE_MAX / n)
+ return 0;
+ fseek_safe(f, offset);
+ size_t read_n = fread(data, (size_t)bs, (size_t)n, f);
+ if (read_n != n) {
+ if ((n - read_n) > SIZE_MAX / bs)
+ return (int)read_n;
+ memset((char *)data + read_n * bs, 0, (n - read_n) * bs);
+ return (int)read_n;
+ }
+ return (int)n;
}
// bin_write()
@@ -118,20 +196,30 @@ void bin_read(FILE *f, int_t i, void *data, uint_t n) {
// data - The block_t buffer to write from
// n - The number of elements to write from the buffer
-void bin_write(FILE *f, int_t i, void *data, uint_t n) {
- uint_t l = bin_length(f);
- uint_t bs = bin_block_size(f);
+void bin_write(FILE *f, i64 i, void *data, u64 n) {
+ (void)bin_write_ok(f, i, data, n);
+}
- assert(0 <= i && i <= l);
+int bin_write_ok(FILE *f, i64 i, void *data, u64 n) {
+ if (!f || !data || n == 0)
+ return 0;
+ u64 l = bin_length(f);
+ u64 bs = bin_block_size(f);
+ if (bs == 0)
+ return 0;
- fseek(f, index_bytes(i, bs), SEEK_SET);
- fwrite(data, bs, n, f);
+ if (i < 0)
+ return 0;
+ if ((u64)i + n > l)
+ return 0;
- if ((i + n) >= l) {
- l += (i + n - l);
- }
-
- bin_write_length(f, l);
+ u64 offset = 0;
+ if (index_bytes_safe(i, bs, &offset) != 0)
+ return 0;
+ if (fseek_safe(f, offset) != 0)
+ return 0;
+ size_t written = fwrite(data, bs, n, f);
+ return written == (size_t)n;
}
// bin_append()
@@ -142,14 +230,31 @@ void bin_write(FILE *f, int_t i, void *data, uint_t n) {
// data - The blocks to append to the file
// n - The number of block to be appended
-void bin_append(FILE *f, void *data, uint_t n) {
- uint_t l = bin_length(f);
- uint_t bs = bin_block_size(f);
+void bin_append(FILE *f, void *data, u64 n) {
+ (void)bin_append_ok(f, data, n);
+}
- fseek(f, index_bytes(l, bs), SEEK_SET);
- fwrite(data, bs, n, f);
+int bin_append_ok(FILE *f, void *data, u64 n) {
+ if (!f || !data || n == 0)
+ return 0;
+ u64 l = bin_length(f);
+ u64 bs = bin_block_size(f);
+ if (bs == 0)
+ return 0;
+ if (l > UINT64_MAX - n)
+ return 0;
+
+ u64 offset = 0;
+ if (index_bytes_safe((i64)l, bs, &offset) != 0)
+ return 0;
+ if (fseek_safe(f, offset) != 0)
+ return 0;
+ size_t written = fwrite(data, bs, n, f);
+ if (written != (size_t)n)
+ return 0;
bin_write_length(f, l + n);
+ return 1;
}
// bin_insert()
@@ -161,24 +266,59 @@ void bin_append(FILE *f, void *data, uint_t n) {
// data - The blocks to insert into the file
// n - The number of blocks to be inserted
-void bin_insert(FILE *f, int_t i, void *data, uint_t n) {
- uint_t l = bin_length(f);
- uint_t bs = bin_block_size(f);
+void bin_insert(FILE *f, i64 i, void *data, u64 n) {
+ (void)bin_insert_ok(f, i, data, n);
+}
+
+int bin_insert_ok(FILE *f, i64 i, void *data, u64 n) {
+ if (!f || !data || n == 0)
+ return 0;
+ u64 l = bin_length(f);
+ u64 bs = bin_block_size(f);
+ if (bs == 0)
+ return 0;
- assert(0 <= i && i <= l);
+ if (i < 0 || (u64)i > l)
+ return 0;
- uint_t tmp_n = (l - i) * bs;
+ u64 blocks_to_move = l - (u64)i;
+ if (blocks_to_move > UINT64_MAX / bs)
+ return 0;
- uint8_t tmp[tmp_n];
+ if (l > UINT64_MAX - n)
+ return 0;
- fseek(f, index_bytes(i, bs), SEEK_SET);
- fread(tmp, bs, l - i, f);
+ u64 offset = 0;
+ if (index_bytes_safe(i, bs, &offset) != 0)
+ return 0;
+ if (fseek_safe(f, offset) != 0)
+ return 0;
- fseek(f, index_bytes(i, bs), SEEK_SET);
- fwrite(data, bs, n, f);
- fwrite(tmp, 1, tmp_n, f);
+ if (blocks_to_move > 0) {
+ u64 tmp_n = blocks_to_move * bs;
+ if (tmp_n > SIZE_MAX)
+ return 0;
+ u8 *tmp = malloc((size_t)tmp_n);
+ if (tmp == NULL)
+ return 0;
+ size_t rn = fread(tmp, (size_t)bs, (size_t)blocks_to_move, f);
+ if (rn != (size_t)blocks_to_move) {
+ memset((char *)tmp + rn * (size_t)bs, 0, ((size_t)blocks_to_move - rn) * (size_t)bs);
+ }
+
+ if (fseek_safe(f, offset) != 0) { free(tmp); return 0; }
+ size_t written = fwrite(data, bs, n, f);
+ if (written != (size_t)n) { free(tmp); return 0; }
+ fwrite(tmp, 1, (size_t)tmp_n, f);
+ free(tmp);
+ } else {
+ size_t written = fwrite(data, bs, n, f);
+ if (written != (size_t)n)
+ return 0;
+ }
bin_write_length(f, l + n);
+ return 1;
}
// bin_length()
@@ -189,11 +329,13 @@ void bin_insert(FILE *f, int_t i, void *data, uint_t n) {
//
// return - The length / number of entries in the file
-uint_t bin_length(FILE *f) {
- uint_t n = 0;
+u64 bin_length(FILE *f) {
+ if (!f) return 0;
+ u64 n = 0;
fseek(f, offsetof(bin_meta_t, length), SEEK_SET);
- fread(&n, member_sizeof(bin_meta_t, length), 1, f);
+ size_t rn = fread(&n, member_sizeof(bin_meta_t, length), 1, f);
+ if (rn != 1) return 0;
return n;
}
@@ -206,11 +348,13 @@ uint_t bin_length(FILE *f) {
//
// return - The block size for the entries in the file
-uint_t bin_block_size(FILE *f) {
- uint_t n = 0;
+u64 bin_block_size(FILE *f) {
+ if (!f) return 0;
+ u64 n = 0;
fseek(f, offsetof(bin_meta_t, block_size), SEEK_SET);
- fread(&n, member_sizeof(bin_meta_t, block_size), 1, f);
+ size_t rn = fread(&n, member_sizeof(bin_meta_t, block_size), 1, f);
+ if (rn != 1) return 0;
return n;
}
@@ -226,21 +370,38 @@ uint_t bin_block_size(FILE *f) {
// The returned value is -( index + 1 ) where index is that of the
// first element greater than k or bin_length().
-int_t bin_search(FILE *f, bin_key_t k) {
- uint_t origin = ftell(f);
+i64 bin_search(FILE *f, bin_key_t k) {
+ if (!f) return -1;
+ fpos_t origin;
+ fgetpos(f, &origin);
- int_t l = 0;
- int_t r = bin_length(f) - 1;
- int_t m;
+ u64 len = bin_length(f);
+ if (len == 0) {
+ fsetpos(f, &origin);
+ return -1;
+ }
+
+ i64 l = 0;
+ i64 r = (i64)len - 1;
+ i64 m;
- uint_t bs = bin_block_size(f);
+ u64 bs = bin_block_size(f);
- bin_key_t key;
+ bin_key_t key = 0;
+ int found = 0;
while (l <= r) {
- m = (l + r) / 2;
- fseek(f, index_bytes(m, bs), SEEK_SET);
- fread(&key, sizeof(k), 1, f);
+ m = l + (r - l) / 2;
+ u64 offset = 0;
+ if (index_bytes_safe(m, bs, &offset) != 0) {
+ fsetpos(f, &origin);
+ return -(l + 1);
+ }
+ fseek_safe(f, offset);
+ if (fread(&key, sizeof(k), 1, f) != 1) {
+ fsetpos(f, &origin);
+ return -(l + 1);
+ }
if (key < k)
l = m + 1;
@@ -248,13 +409,15 @@ int_t bin_search(FILE *f, bin_key_t k) {
else if (key > k)
r = m - 1;
- else
+ else {
+ found = 1;
break;
+ }
}
- fseek(f, origin, SEEK_SET);
+ fsetpos(f, &origin);
- if (key != k)
+ if (!found)
return -(l + 1);
return m;
@@ -270,7 +433,11 @@ int_t bin_search(FILE *f, bin_key_t k) {
// return - The index remapped the binary file. If the index is already
// positive, then no operation occurrs.
-uint_t bin_fuzzy_index(int_t i) { return (i < 0) ? -(i + 1) : i; }
+u64 bin_fuzzy_index(i64 i) {
+ if (i < 0)
+ return (u64)(-(i + 1));
+ return (u64)i;
+}
// bin_write_length()
//
@@ -279,9 +446,11 @@ uint_t bin_fuzzy_index(int_t i) { return (i < 0) ? -(i + 1) : i; }
// f - The file to be written to
// count - The length to be written to the file
-static void bin_write_length(FILE *f, uint_t length) {
+static void bin_write_length(FILE *f, u64 length) {
fseek(f, offsetof(bin_meta_t, length), SEEK_SET);
- fwrite(&length, member_sizeof(bin_meta_t, length), 1, f);
+ if (fwrite(&length, member_sizeof(bin_meta_t, length), 1, f) != 1) {
+ clearerr(f);
+ }
}
// bin_write_block_size()
@@ -291,7 +460,7 @@ static void bin_write_length(FILE *f, uint_t length) {
// f - The file to be written to
// count - The block size to be written to the file
-static void bin_write_block_size(FILE *f, uint_t block_size) {
+static void bin_write_block_size(FILE *f, u64 block_size) {
fseek(f, offsetof(bin_meta_t, block_size), SEEK_SET);
fwrite(&block_size, member_sizeof(bin_meta_t, block_size), 1, f);
}