Skip to content

Instantly share code, notes, and snippets.

@DavideGalilei
Last active March 9, 2023 12:52
Show Gist options
  • Save DavideGalilei/5cda8f3e17eb3e7e4087b824352989ca to your computer and use it in GitHub Desktop.
Save DavideGalilei/5cda8f3e17eb3e7e4087b824352989ca to your computer and use it in GitHub Desktop.
C type safe sequence
#define SEQ_H_IMPLEMENTATION
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define Seq(T) \
struct { \
T *data; \
T temp; \
size_t size; \
size_t len; \
}
#ifndef ARENA_SIZE
#define ARENA_SIZE 1024
#endif
#define Seq_status_(seq) (*(char *) &(seq).temp)
#define Seq_pop(seq, index) \
( \
(seq).data = Seq_pop_( \
(seq).data, \
&(seq).size, \
&(seq).len, \
sizeof((seq).data[0]), \
(index), \
&(seq).temp \
), \
Seq_status_(seq) \
)
#define Seq_destroy(seq) Seq_destroy_((seq).data)
#define Seq_add(seq, element) \
( \
(seq).data = Seq_add_( \
(seq).data, \
&(seq).size, \
&(seq).len, \
sizeof((seq).data[0]), \
( \
(seq).temp = (element), \
&(seq).temp) \
), \
Seq_status_(seq) \
)
void *Seq_add_(void *data, size_t *data_size, size_t *len, size_t elem_size, void *element);
void *Seq_pop_(void *data, size_t *data_size, size_t *len, size_t elem_size, size_t index, void *element);
void Seq_destroy_(void *data);
#ifdef SEQ_H_IMPLEMENTATION
void *Seq_add_(void *data, size_t *data_size, size_t *len, size_t elem_size, void *element) {
// if len(elements + 1) doesn't fit within seq.size, enlarge the arena pool
// return -1 if an error occurs
char status = 1;
if (((*len) + 1) * elem_size > (*data_size)) {
void *realloc_mem = realloc(data, *data_size + ((ARENA_SIZE) *elem_size));
if (realloc_mem == NULL) {
status = 0;
goto fail;
}
*data_size += (ARENA_SIZE) *elem_size;
data = realloc_mem;
}
// copy the element to its respective cell calculated by len(seq) * sizeof(elem)
memcpy((char *) data + (*len) * elem_size, element, elem_size);
(*len)++;
fail:
*(char *) element = status;
return data;
}
void *Seq_pop_(void *data, size_t *data_size, size_t *len, size_t elem_size, size_t index, void *element) {
// if index is out of bounds, return error
char status = 1;
if (index >= (*len)) {
status = 0;
goto fail;
}
// if the index doesn't belong to the last element, shift the elements afterwards
if (index != (*len - 1))
memmove((char *) data + index * elem_size,
(char *) data + (index * elem_size) + elem_size,
((*len) - index) * elem_size);
// if len(elements + ARENA_SIZE) fits within `seq.size - ARENA_SIZE`, shrink the arena pool
// return -1 if an error occurs
if (((*len + (ARENA_SIZE)) * elem_size) < (*data_size)) {
void *realloc_mem = realloc(data, *data_size - ((ARENA_SIZE) *elem_size));
if (realloc_mem == NULL) {
status = 0;
goto fail;
}
data = realloc_mem;
*data_size -= (ARENA_SIZE) *elem_size;
}
(*len)--;
fail:
*(char *) element = status;
return data;
}
void Seq_destroy_(void *data) {
// free the sequence data
free(data);
}
#ifdef _SEQ_DEBUG
// c && gcc -Wall -Wextra -Werror -pedantic -o test -x c -DSEQ_H_IMPLEMENTATION -DARENA_SIZE=3 -D_SEQ_DEBUG seq.h && ./test
int main(void) {
Seq(int) numbers = {0};
for (size_t i = 0; i < 20; i++) {
Seq_add(numbers, i);
if (!(i % 2)) printf("Added %ld'th element (value: %d). (Arena size: %ld)\n",
i + 1,
numbers.data[numbers.len - 1],
numbers.size);
if (i % 2) {
Seq_pop(numbers, numbers.len - 1);
printf("Pop'd last element (Arena size: %ld)\n",
numbers.size);
}
}
for (size_t i = 0; i < numbers.len; i++) {
printf("numbers[%ld] = %d\n",
i,
numbers.data[i]);
}
for (; numbers.len > 0;) {
if (Seq_pop(numbers, numbers.len - 1) == -1) {
printf("An error occurred while popping last element\n");
return (EXIT_FAILURE);
} else {
printf("Pop'd last element (len now: %ld | Arena size: %ld)\n", numbers.len, numbers.size);
}
}
}
#endif // _SEQ_DEBUG
#endif // SEQ_H_IMPLEMENTATION
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment