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: 12 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,20 @@ jobs:
uses: actions/checkout@v4

- name: Install dependencies
run: sudo apt-get install -y clang
run: sudo apt-get install -y clang valgrind

- name: Build
run: make

- name: Run tests
run: make test_all
run: make test_all

- name: Run tests with ASan
run: make asan

- name: Run tests with Valgrind
run: |
for test in build/test_*; do
echo "Running valgrind on $test..."
valgrind --leak-check=full --show-leak-kinds=all --error-exitcode=1 $test
done
120 changes: 120 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# Contributing to struccs

Thanks for your interest in contributing. struccs is a handwritten, dependency-free
C library built from scratch with a focus on learning, memory management, and
type-agnostic design. Contributions are welcome as long as they align with that philosophy.

---

## Use of AI Tools

AI tools (Claude, Codex, Cursor) have been used in a limited capacity during development:

- Discussing ideas, design decisions, and tradeoffs
- Code review and feedback on existing implementations
- Mechanical refactoring tasks (e.g. find-and-replace style changes)
- Documentation suggestions

All core implementations - data structures, algorithms, memory management, and API
design — were written by hand without AI generation. This is intentional; the primary
goal of struccs is deep learning and genuine understanding of systems-level C programming.

Contributors are expected to follow the same standard. AI-generated core logic or
implementations will not be accepted.

---

## Project Philosophy

struccs is built on a few core principles:

- No dependencies — no standard container libraries, no third party code
- Generic by design — structures work with any data type via `void*` and function pointers
- Memory conscious — every allocation has a corresponding free, ownership is always clear
- Readable — code should be understandable to someone learning C, not just experts

Keep these in mind when contributing.

---

## Getting Started

```bash
git clone https://github.com/brendanddev/struccs.git
cd struccs
make
make test_all
```

See [README.md](README.md) for full build and usage instructions.

---

## Adding a New Structure

Follow this checklist when adding a new data structure:

1. Create `include/structure_name.h` — public API and struct definition
2. Create `src/structure_name.c` — implementation
3. Create `tests/test_structure_name.c` — test suite
4. Add a test target to the `Makefile`
5. Add the structure to `test_all` in the `Makefile`
6. Update the structures table in `README.md`
7. Add the header to `struccs.h` if an umbrella header exists

---

## Code Style

**Naming**
- Functions follow the `structure_operation` pattern — `ga_append`, `bst_insert`, `ht_remove`
- Structs use PascalCase — `GenericArray`, `BinarySearchTree`
- Internal/static helpers are lowercase with underscores — `shift_left`, `resize`

**Comments**
- Comment *why*, not *what* — avoid restating what the code already says
- Public headers get a brief docstring per function
- Implementation files get a file header comment, nothing more unless the logic is non-obvious

**Memory**
- Every allocation must have a corresponding free
- Ownership must be clear — document who is responsible for freeing data passed via `void*`
- Always handle allocation failure — never assume `malloc` succeeds

---

## Testing

Every new structure needs a test file in `tests/` covering:

- Initialization
- Basic operations
- Edge cases (empty structure, single element, duplicates where relevant)
- Cleanup — no memory leaks

Tests use the macros defined in `tests/test_utils.h`:

```c
ASSERT_EQ(actual, expected);
ASSERT_TRUE(expr);
ASSERT_FALSE(expr);
ASSERT_NULL(ptr);
ASSERT_NOT_NULL(ptr);
TEST(function_name);
```

---

## Submitting Changes

- Fork the repo and create a branch for your change
- Keep PRs focused — one structure or fix per PR
- Make sure `make test_all` passes before submitting
- Update documentation if your change affects the public API

---

## License

By contributing, you agree that your contributions will be licensed under the
same MIT License that covers this project.
33 changes: 27 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
# Project
PROJECT = struccs

# Compiler and flags
CC = clang
CFLAGS = -Wall -Wextra -I include
TESTFLAGS = $(CFLAGS) -I tests
CFLAGS_ASAN = -Wall -Wextra -I include -fsanitize=address,undefined -g

# Directories
# Directories and source files
SRC_DIR = src
TEST_DIR = tests
BUILD_DIR = build

# Source files
SRCS = $(wildcard $(SRC_DIR)/*.c)

# Static library
Expand Down Expand Up @@ -67,8 +65,23 @@ test_set: $(BUILD_DIR)
$(CC) $(TESTFLAGS) $(SRC_DIR)/set.c $(SRC_DIR)/hash_table.c $(TEST_DIR)/test_set.c -o $(BUILD_DIR)/test_set
./$(BUILD_DIR)/test_set

# Run all tests
test_all: test_bt test_bst test_ga test_ll test_ht test_stack test_queue test_heap test_set
test_mp: $(BUILD_DIR)
$(CC) $(TESTFLAGS) $(SRC_DIR)/memory_pool.c $(TEST_DIR)/test_mp.c -o $(BUILD_DIR)/test_mp
./$(BUILD_DIR)/test_mp

test_str: $(BUILD_DIR)
$(CC) $(TESTFLAGS) $(SRC_DIR)/str.c $(TEST_DIR)/test_str.c -o $(BUILD_DIR)/test_str
./$(BUILD_DIR)/test_str

test_result: $(BUILD_DIR)
$(CC) $(TESTFLAGS) $(SRC_DIR)/result.c $(TEST_DIR)/test_result.c -o $(BUILD_DIR)/test_result
./$(BUILD_DIR)/test_result

test_option: $(BUILD_DIR)
$(CC) $(TESTFLAGS) $(SRC_DIR)/option.c $(TEST_DIR)/test_option.c -o $(BUILD_DIR)/test_option
./$(BUILD_DIR)/test_option

test_all: test_bt test_bst test_ga test_ll test_ht test_stack test_queue test_heap test_set test_mp test_str test_result test_option

sandbox: $(LIB)
$(CC) $(CFLAGS) sandbox/scratch.c \
Expand All @@ -77,6 +90,14 @@ sandbox: $(LIB)
-g -fsanitize=address \
-o $(BUILD_DIR)/sandbox && ./$(BUILD_DIR)/sandbox

asan: $(BUILD_DIR)
@for src in $(TEST_DIR)/test_*.c; do \
name=$$(basename $$src .c); \
$(CC) $(CFLAGS_ASAN) $(SRCS) $$src -o $(BUILD_DIR)/$${name}_asan; \
echo "Running $${name}_asan..."; \
./$(BUILD_DIR)/$${name}_asan; \
done

# Clean build artifacts
clean:
rm -rf $(BUILD_DIR)
46 changes: 33 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
# struccs

A lightweight, generic C library of fundamental data structures — built from scratch with a focus on memory management, performance, and type-agnostic design.
A lightweight, generic C library, built from scratch, providing data structures, common types, and low-level memory tools. No dependencies, no standard container libraries. Just `void*`, function pointers, and manual memory management.

> Version 0.10.8

No dependencies. No standard container libraries. Just `void*`, function pointers, and manual memory management.
> Version 0.14.0

---

## Why struccs?

Most C developers end up rewriting the same data structures over and over, a linked list here, a hash table there, each tied to one specific type. **struccs** gives you a set of generic, reusable structures that work with *any* data type via `void*` pointers and user-supplied function pointers (for comparison, hashing, freeing, etc.), so you write the logic once and reuse it everywhere.

It's also a deep dive into how these structures work under the hood, useful if you're learning C, data structures, or memory management.
C gives you the building blocks but none of the infrastructure. struccs fills that gap — generic data structures, common types, and memory tools, all handwritten, all dependency-free, all designed to work together.

---

Expand Down Expand Up @@ -65,6 +61,20 @@ For full examples per structure, see [docs/USAGE.md](docs/USAGE.md).

Each structure ships with its own header and source file, and can be used independently or together via the umbrella header.

## Types

| Type | Description | Header |
|-----------|--------------------------------------------|------------|
| `Result` | Operation result wrapping a value or error | `result.h` |
| `Option` | Wrapper representing an optional value | `option.h` |
| `String` | Dynamic, resizable string type | `str.h` |

## Memory Management

| Component | Description | Header |
|----------------|-------------------------------------------------------------|------------------|
| `MemoryPool` | Fixed-size block allocator backed by a pre-allocated buffer | `memory_pool.h` |

---

## Usage
Expand Down Expand Up @@ -100,12 +110,12 @@ Each structure has its own test target (`test_ll`, `test_bt`, `test_hash`, etc.)
## Project Structure

struccs/
├── include/ # Public headers
├── src/ # Implementations
├── tests/ # Test suites
├── docs/ # Documentation
│ ├── USAGE.md # Usage examples for each structure
│ ├── API.md # Full API reference
├── include/ # Public headers
├── src/ # Implementations
├── tests/ # Test suites
├── docs/ # Documentation
│ ├── USAGE.md # Usage examples for each structure
│ ├── API.md # Full API reference
│ └── PERFORMANCE.md # Benchmarks & complexity notes
├── Makefile
├── LICENSE
Expand All @@ -121,6 +131,16 @@ struccs/

---

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md) for full guidelines on:
- Project design principles (no dependencies, generic by design, memory-conscious)
- Code style and structure conventions
- Testing requirements
- Pull request process

---

## License

MIT License — see [LICENSE](LICENSE) for details.
42 changes: 28 additions & 14 deletions include/binary_tree.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
/**
* binary_tree.h
* Header for the generic complete binary tree implementation, backed by an array.
* Brendan Dileo - 2025
*/
// binary_tree.h
// Header for the generic complete binary tree implementation, backed by an array.
// Brendan Dileo - 2025

#ifndef BINARY_TREE_H
#define BINARY_TREE_H
Expand All @@ -11,32 +9,48 @@
#include <stdlib.h>
#include <stdbool.h>

/**
* Defines the BinaryTree struct type.
* Elements are stored in a flat array using index-based parent/child relationships.
*/
// Elements are stored in a flat array, parent/child links are derived from
// array indices (left = 2i+1, right = 2i+2), so the tree is always complete.
typedef struct BinaryTree {
int length; // Number of elements in the tree
int capacity; // Capacity of the trees underlying array
void *elements; // Pointer to the memory where the elements are stored
size_t element_size; // Size of the elements stored in the tree
int length; // number of elements in use
int capacity; // slots allocated in the backing array
void *elements;
size_t element_size;
} BinaryTree;


// Allocates an empty tree for elements of element_size bytes. Caller frees with
// bt_discard. Returns NULL on allocation failure.
struct BinaryTree* bt_create(size_t element_size);
// Appends value at the next free slot, growing the backing array if full.
// Copies element_size bytes from value.
void bt_insert(struct BinaryTree *tree, void *value);
// Removes the element at index by overwriting it with the last element, so
// neither insertion order nor tree shape below index is preserved.
void bt_remove(struct BinaryTree *tree, int index);
// Copies the element at index into out. Returns false if index is out of range.
bool bt_get(struct BinaryTree *tree, int index, void *out);
// Overwrites the element at index with value. Returns false if index is out of range.
bool bt_set(struct BinaryTree *tree, int index, void *value);
// Returns true if any element compares equal (comparator returns 0) to value. O(n).
bool bt_contains(struct BinaryTree *tree, void *value, int (*comparator)(void*, void*));
// Returns the index of the first element equal to value, or -1 if absent. O(n).
int bt_find(struct BinaryTree *tree, void *value, int (*comparator)(void*, void*));
// Returns height in edges: root-only tree = 0, empty tree = -1.
int bt_height(struct BinaryTree *tree);
// Returns the number of leaf nodes (nodes with no left child).
int bt_leaves(struct BinaryTree *tree);
// Returns true if the tree holds no elements.
bool bt_isempty(struct BinaryTree *tree);
// Returns the number of elements in the tree.
int bt_size(struct BinaryTree *tree);
// Returns the current capacity of the backing array.
int bt_capacity(struct BinaryTree *tree);
// Resets length to 0; the backing array is kept and reused by later inserts.
void bt_clear(struct BinaryTree *tree);
// Prints the tree rotated 90 degrees (root at the left) using print_fn per element.
void bt_print(struct BinaryTree *tree, void (*print_fn)(void*));
// Frees the backing array and the tree struct.
void bt_discard(struct BinaryTree *tree);

#endif
#endif
Loading
Loading