Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion .github/workflows/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@ jobs:
run: |
find src examples -name '*.c' -o -name '*.h' | xargs clang-format --dry-run --Werror

ruff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install ruff
run: pip install ruff

- name: Lint Python and SCons files
run: |
ruff check $(find . -path ./build -prune -o \( -name "*.py" -o -name "SConstruct" -o -name "SConscript" \) -print)

scons-test:
runs-on: ubuntu-latest
steps:
Expand All @@ -40,7 +52,7 @@ jobs:

build-deb:
runs-on: ubuntu-latest
needs: [clang-format, scons-test]
needs: [clang-format, ruff, scons-test]
steps:
- uses: actions/checkout@v4

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.1.2
2.0.1
13 changes: 13 additions & 0 deletions debian/changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
libmicrohammer (2.0.1-1) unstable; urgency=medium

* Widen HToken.len and bits_env.length from uint8_t to size_t, fixing silent
integer truncation for tokens and bit counts exceeding 255 (closes #3)
* Introduce HAMMER_ASSERT in internal.h: unconditional abort for programmer
errors that survives -DNDEBUG; replaces assert()-based guards in bitwriter.c,
glue.c, glue.h, and datastructures.c (closes #6)
* Update h_assert_type macro to abort on type mismatch via HAMMER_ASSERT
* Update token length test to verify 256-byte tokens parse correctly
* Add ruff linting of Python and SCons files to CI pipeline

-- Mahmoud Elbasiouny <melbasiouny@riversideresearch.org> Fri, 17 Apr 2026 12:00:00 -0400

libmicrohammer (2.0.0-1) unstable; urgency=medium

* Reintroduce Python, Java/JNI, and C++ language bindings
Expand Down
9 changes: 4 additions & 5 deletions src/bitwriter.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ static void h_bit_writer_reserve(HBitWriter *w, size_t nbits) {
}

void h_bit_writer_put(HBitWriter *w, uint64_t data, size_t nbits) {
assert(nbits > 0); // Less than or equal to zero makes complete nonsense
HAMMER_ASSERT(nbits > 0);

// expand size...
h_bit_writer_reserve(w, nbits);
Expand Down Expand Up @@ -90,10 +90,9 @@ void h_bit_writer_put(HBitWriter *w, uint64_t data, size_t nbits) {
}

const uint8_t *h_bit_writer_get_buffer(HBitWriter *w, size_t *len) {
assert(len != NULL);
assert(w != NULL);
// Not entirely sure how to handle a non-integral number of bytes... make it an error for now
assert(w->bit_offset == 0); // BUG: change this to some sane behaviour
HAMMER_ASSERT(w != NULL);
HAMMER_ASSERT(len != NULL);
HAMMER_ASSERT(w->bit_offset == 0);

*len = w->index;
return w->buf;
Expand Down
4 changes: 2 additions & 2 deletions src/datastructures.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ void h_slist_push(HSlist *slist, void *item) {
}

bool h_slist_find(HSlist *slist, const void *item) {
assert(item != NULL);
Comment thread
melbasiouny-riverside marked this conversation as resolved.
HAMMER_ASSERT(item != NULL);
HSlistNode *head = slist->head;
while (head != NULL) {
if (head->elem == item)
Expand All @@ -123,7 +123,7 @@ bool h_slist_find(HSlist *slist, const void *item) {
}

HSlist *h_slist_remove_all(HSlist *slist, const void *item) {
assert(item != NULL);
HAMMER_ASSERT(item != NULL);
HSlistNode *node = slist->head;
HSlistNode *prev = NULL;
while (node != NULL) {
Expand Down
30 changes: 15 additions & 15 deletions src/glue.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ HParsedToken *h_make_(HArena *arena, HTokenType type) {
}

HParsedToken *h_make(HArena *arena, HTokenType type, void *value) {
assert(type >= TT_USER);
HAMMER_ASSERT(type >= TT_USER);
HParsedToken *ret = h_make_(arena, type);
ret->user = value;
return ret;
Expand Down Expand Up @@ -129,25 +129,25 @@ HParsedToken *h_make_float(HArena *arena, float val) {

// XXX -> internal
HParsedToken *h_carray_index(const HCountedArray *a, size_t i) {
assert(i < a->used);
HAMMER_ASSERT(i < a->used);
return a->elements[i];
}

size_t h_seq_len(const HParsedToken *p) {
assert(p != NULL);
assert(p->token_type == TT_SEQUENCE);
HAMMER_ASSERT(p != NULL);
HAMMER_ASSERT(p->token_type == TT_SEQUENCE);
return p->seq->used;
}

HParsedToken **h_seq_elements(const HParsedToken *p) {
assert(p != NULL);
assert(p->token_type == TT_SEQUENCE);
HAMMER_ASSERT(p != NULL);
HAMMER_ASSERT(p->token_type == TT_SEQUENCE);
return p->seq->elements;
}

HParsedToken *h_seq_index(const HParsedToken *p, size_t i) {
assert(p != NULL);
assert(p->token_type == TT_SEQUENCE);
HAMMER_ASSERT(p != NULL);
HAMMER_ASSERT(p->token_type == TT_SEQUENCE);
return h_carray_index(p->seq, i);
}

Expand All @@ -172,17 +172,17 @@ HParsedToken *h_seq_index_vpath(const HParsedToken *p, size_t i, va_list va) {
}

void h_seq_snoc(HParsedToken *xs, const HParsedToken *x) {
assert(xs != NULL);
assert(xs->token_type == TT_SEQUENCE);
HAMMER_ASSERT(xs != NULL);
HAMMER_ASSERT(xs->token_type == TT_SEQUENCE);

h_carray_append(xs->seq, (HParsedToken *)x);
}

void h_seq_append(HParsedToken *xs, const HParsedToken *ys) {
assert(xs != NULL);
assert(xs->token_type == TT_SEQUENCE);
assert(ys != NULL);
assert(ys->token_type == TT_SEQUENCE);
HAMMER_ASSERT(xs != NULL);
HAMMER_ASSERT(xs->token_type == TT_SEQUENCE);
HAMMER_ASSERT(ys != NULL);
HAMMER_ASSERT(ys->token_type == TT_SEQUENCE);

for (size_t i = 0; i < ys->seq->used; i++)
h_carray_append(xs->seq, ys->seq->elements[i]);
Expand All @@ -191,7 +191,7 @@ void h_seq_append(HParsedToken *xs, const HParsedToken *ys) {
// Flatten nested sequences. Always returns a sequence.
// If input element is not a sequence, returns it as a singleton sequence.
const HParsedToken *h_seq_flatten(HArena *arena, const HParsedToken *p) {
assert(p != NULL);
HAMMER_ASSERT(p != NULL);

HParsedToken *ret = h_make_seq(arena);
switch (p->token_type) {
Expand Down
7 changes: 3 additions & 4 deletions src/glue.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@
#define HAMMER_GLUE__H

#include "hammer.h"

#include <assert.h>
#include "internal.h"

/**
* Grammar specification
Expand Down Expand Up @@ -218,8 +217,8 @@ HParsedToken *h_make_float(HArena *arena, float val);

/** Extract (cast) type-specific value back from HParsedTokens... */

/** Pass-through assertion that a given token has the expected type. */
#define h_assert_type(T, P) (assert(P->token_type == (HTokenType)T), P)
/** Pass-through assertion that a given token has the expected type. Aborts on mismatch. */
#define h_assert_type(T, P) (HAMMER_ASSERT((P)->token_type == (HTokenType)(T)), (P))

/** Convenience short-hand forms of h_assert_type. */
#define H_ASSERT(TYP, TOK) h_assert_type(TT_##TYP, TOK)
Expand Down
7 changes: 7 additions & 0 deletions src/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

/* "Internal" in this case means "we're not ready to commit
Expand All @@ -47,6 +48,12 @@
} while (0)
#endif

/* Unconditional assertion for programmer errors — fires in all builds, including -DNDEBUG. */
#define HAMMER_ASSERT(cond) \
((void)((cond) || \
(fprintf(stderr, "Hammer assertion failed: %s (%s:%d)\n", #cond, __FILE__, __LINE__), \
abort(), 0)))

#define HAMMER_FN_IMPL_NOARGS(rtype_t, name) \
rtype_t name(void) { return name##__m(system_allocator); } \
rtype_t name##__m(HAllocator *mm__)
Expand Down
7 changes: 4 additions & 3 deletions src/parsers/bits.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@
#include <assert.h>

struct bits_env {
uint8_t length;
size_t length;
Comment thread
GJames-RiversideResearch marked this conversation as resolved.
uint8_t signedp;
};

static HParseResult *parse_bits(void *env, HParseState *state) {
struct bits_env *env_ = env;
HParsedToken *result = a_new(HParsedToken, 1);
result->token_type = (env_->signedp ? TT_SINT : TT_UINT);
// h_read_bits takes int; cast is required by its signature
if (env_->signedp)
result->sint = h_read_bits(&state->input_stream, env_->length, true);
result->sint = h_read_bits(&state->input_stream, (int)env_->length, true);
else
result->uint = h_read_bits(&state->input_stream, env_->length, false);
result->uint = h_read_bits(&state->input_stream, (int)env_->length, false);
Comment thread
GJames-RiversideResearch marked this conversation as resolved.
result->index = 0;
result->bit_length = 0;
result->bit_offset = 0;
Expand Down
8 changes: 3 additions & 5 deletions src/parsers/token.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@

typedef struct {
uint8_t *str;
uint8_t len;
size_t len;
} HToken;

static HParseResult *parse_token(void *env, HParseState *state) {
HToken *t = (HToken *)env;
for (int i = 0; i < t->len; ++i) {
for (size_t i = 0; i < t->len; ++i) {
uint8_t chr = (uint8_t)h_read_bits(&state->input_stream, 8, false);
if (t->str[i] != chr) {
return NULL;
Expand Down Expand Up @@ -76,11 +76,9 @@ HParser *h_token(const uint8_t *str, const size_t len) {
return h_token__m(&system_allocator, str, len);
}
HParser *h_token__m(HAllocator *mm__, const uint8_t *str, const size_t len) {
// Length has to be <= 255 (uint8) as defined by HToken struct
assert(len <= UINT8_MAX);
HToken *t = h_new(HToken, 1);
uint8_t *str_cpy = h_new(uint8_t, len);
memcpy(str_cpy, str, len);
t->str = str_cpy, t->len = (uint8_t)len;
t->str = str_cpy, t->len = len;
return h_new_parser(mm__, &token_vt, t);
}
6 changes: 4 additions & 2 deletions src/sloballoc.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ struct slob {
SLOB *slobinit(void *mem, size_t size) {
SLOB *slob = mem;

assert(size >= sizeof(SLOB) + sizeof(struct block));
assert(size < UINTPTR_MAX - (uintptr_t)mem);
if (size < sizeof(SLOB) + sizeof(struct block))
return NULL;
if (size >= UINTPTR_MAX - (uintptr_t)mem)
return NULL;

slob = mem;
slob->size = size - sizeof(SLOB);
Expand Down
29 changes: 16 additions & 13 deletions tests/parsers/test_token.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,29 +38,32 @@ static void test_reshape_token(gconstpointer backend) {
}
}

// Test token.c: len not > UINT8_MAX assert
// Test token.c: tokens longer than 255 bytes are now supported
Comment thread
melbasiouny-riverside marked this conversation as resolved.
#define TOKEN_TEST_LEN 256
static void test_token_len_assert(gconstpointer backend) {
(void)backend;
HParserBackend be = (HParserBackend)GPOINTER_TO_INT(backend);

if (g_test_subprocess()) {
uint8_t expected[256];
memset(expected, 0x41, sizeof(expected));
uint8_t expected[TOKEN_TEST_LEN];
memset(expected, 0x41, sizeof(expected));

HParser *parser = h_token(expected, sizeof(expected));
HParser *parser = h_token(expected, TOKEN_TEST_LEN);
g_check_cmp_ptr(parser, !=, NULL);

// Shouldn't get here
(void)parser;
return;
h_compile(parser, be, NULL);
HParseResult *res = h_parse(parser, expected, TOKEN_TEST_LEN);
g_check_cmp_ptr(res, !=, NULL);
if (res) {
g_check_cmp_ptr(res->ast, !=, NULL);
if (res->ast && res->ast->token_type == TT_BYTES)
g_check_cmp_int(res->ast->bytes.len, ==, TOKEN_TEST_LEN);
h_parse_result_free(res);
}

g_test_trap_subprocess(NULL, 0, 0);
g_test_trap_assert_failed();
}

void register_token_tests(void) {
g_test_add_data_func("/core/parser/packrat/reshape_token", GINT_TO_POINTER(PB_PACKRAT),
test_reshape_token);

g_test_add_data_func("/core/parser/packrat/token_len_assert", GINT_TO_POINTER(PB_PACKRAT),
test_token_len_assert);
}
1 change: 0 additions & 1 deletion tests/t_bitreader.c
Original file line number Diff line number Diff line change
Expand Up @@ -234,4 +234,3 @@ void register_bitreader_tests(void) {
g_test_add_func("/core/bitreader/byte_le_fast_path", test_read_bits_byte_le_fast_path);
g_test_add_func("/core/bitreader/byte_le_slow_path", test_read_bits_byte_le_slow_path);
}

Loading