/*
██████ ██ ▄████▄ ██ ██ ▄████▄ █████▄ ██████ ▄████▄ ██ ██ ███ ██
██▄▄ ██ ██▄▄██ ██▄▄██ ██ ██ ██▄▄██▄ ██ ██ ██ ██ ▄█▄ ██ ██ ▀▄██ ▀
██ ██████ ██ ██ ▀██▀ ▀████▀ ██ ██ ██ ▀████▀ ▀██▀██▀ ██ ██ ▄
██ ██ ██ ██████ ██████ ██ ██ ██████ ▄████ █████▄ ██████ ██████ ███ ██
██████ ██ ██ ██ ██████ ██▄▄ ██ ▄▄▄ ██▄▄██▄ ██▄▄ ██▄▄ ██ ▀▄██
██ ██ ██ ██ ██ ██ ██ ██▄▄▄▄ ▀███▀ ██ ██ ██▄▄▄▄ ██▄▄▄▄ ██ ██
█████▄ ██ ██ ███ ██ █████▄ ██ ██ ██████ ██████ ▄████▄ ███ ██
██▄▄██▄ ██ ██ ██ ▀▄██ ██▄▄██ ██ ██ ██ ██ ██ ██ ██ ▀▄██
██ ██ ▀████▀ ██ ██ ██▄▄█▀ ▀████▀ ██ ██ ▀████▀ ██ ██
*/
/*
this example creates a registry where the key is a city and the value is the
population
*/
#include <stdio.h>
#include "registry.h"
int main() {
// the key is always a string. specify the type of the value here.
// in a real scenario, a struct would be more useful. i am using an int here
// for simplicity
struct registry* reg = registry_init(sizeof(int));
// i make an int variable here so i can dereference it later
// when you add a value to a registry, it copies the bytes of the value, it
// does NOT copy the pointer. therefore, i can change this variable and the
// value in the registry will remain the same
int buf;
// nyc
buf = 8600000; /* 8.6 million */
registry_add(reg, "new_york_city", &buf);
// los angeles
buf = 3900000; /* 3.9 million */
registry_add(reg, "los_angeles", &buf);
// seattle
buf = 800000; /* 800 thousand */
registry_add(reg, "seattle", &buf);
// london
buf = 9000000; /* 9 million */
registry_add(reg, "london", &buf);
// tokyo
buf = 14000000; /* 14 million */
registry_add(reg, "tokyo", &buf);
// now we will get the population for london
// this function returns NULL if the key is invalid, but for simplicity we
// will not check that here
printf("Population of London: %i\n", *(int*)registry_ktov(reg, "london"));
// output: Population of London: 9000000
// if you have a registry where the values are structs with fields on the
// heap, remember to free those fields before calling registry_cleanup()
registry_cleanup(reg);
return 0;
}
/*
██████ ██ ▄████▄ ██ ██ ▄████▄ █████▄ ██████ ▄████▄ ██ ██ ███ ██
██▄▄ ██ ██▄▄██ ██▄▄██ ██ ██ ██▄▄██▄ ██ ██ ██ ██ ▄█▄ ██ ██ ▀▄██ ▀
██ ██████ ██ ██ ▀██▀ ▀████▀ ██ ██ ██ ▀████▀ ▀██▀██▀ ██ ██ ▄
██ ██ ██ ██████ ██████ ██ ██ ██████ ▄████ █████▄ ██████ ██████ ███ ██
██████ ██ ██ ██ ██████ ██▄▄ ██ ▄▄▄ ██▄▄██▄ ██▄▄ ██▄▄ ██ ▀▄██
██ ██ ██ ██ ██ ██ ██ ██▄▄▄▄ ▀███▀ ██ ██ ██▄▄▄▄ ██▄▄▄▄ ██ ██
█████▄ ██ ██ ███ ██ █████▄ ██ ██ ██████ ██████ ▄████▄ ███ ██
██▄▄██▄ ██ ██ ██ ▀▄██ ██▄▄██ ██ ██ ██ ██ ██ ██ ██ ▀▄██
██ ██ ▀████▀ ██ ██ ██▄▄█▀ ▀████▀ ██ ██ ▀████▀ ██ ██
*/
/*
registry.h - part of the registry.c library by Colin Melican (Kolin63)
https://github.com/kolin63/registry.c
Under the MIT License
Copyright (c) 2026 Colin Melican
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef COLIN_REGISTRY_H_
#define COLIN_REGISTRY_H_
// an interface to a single registry. none of these fields should be manually
// written to. to read from the keys or the values, either manually reading or
// using the functions are ok
struct registry {
// amount of keys / values
int length;
// size of value type in bytes
int val_size;
// array of keys
char** keys;
// value data. continguous in memory
void* values;
};
// puts a new registry on the heap. registry_cleanup() must be called when it
// is done being used
struct registry* registry_init(int val_size);
// frees allocated memory for a registry. if the registry contains structs with
// data on the heap, those fields must be freed before calling this function
void registry_cleanup(struct registry* reg);
// adds a key and a value. returns -1 if the key already exists
int registry_add(struct registry* reg, const char* key, const void* val);
// index to value. no bounds checking
void* registry_itov(struct registry* reg, int i);
// index to value. returns NULL on error
void* registry_itov_safe(struct registry* reg, int i);
// index to key. no bounds checking
char* registry_itok(struct registry* reg, int i);
// index to key. returns NULL on error
char* registry_itok_safe(struct registry* reg, int i);
// key to index. returns -1 if the key doesn't exist
int registry_ktoi(struct registry* reg, const char* key);
// key to value. returns NULL on error
void* registry_ktov(struct registry* reg, const char* key);
#endif
/*
██████ ██ ▄████▄ ██ ██ ▄████▄ █████▄ ██████ ▄████▄ ██ ██ ███ ██
██▄▄ ██ ██▄▄██ ██▄▄██ ██ ██ ██▄▄██▄ ██ ██ ██ ██ ▄█▄ ██ ██ ▀▄██ ▀
██ ██████ ██ ██ ▀██▀ ▀████▀ ██ ██ ██ ▀████▀ ▀██▀██▀ ██ ██ ▄
██ ██ ██ ██████ ██████ ██ ██ ██████ ▄████ █████▄ ██████ ██████ ███ ██
██████ ██ ██ ██ ██████ ██▄▄ ██ ▄▄▄ ██▄▄██▄ ██▄▄ ██▄▄ ██ ▀▄██
██ ██ ██ ██ ██ ██ ██ ██▄▄▄▄ ▀███▀ ██ ██ ██▄▄▄▄ ██▄▄▄▄ ██ ██
█████▄ ██ ██ ███ ██ █████▄ ██ ██ ██████ ██████ ▄████▄ ███ ██
██▄▄██▄ ██ ██ ██ ▀▄██ ██▄▄██ ██ ██ ██ ██ ██ ██ ██ ▀▄██
██ ██ ▀████▀ ██ ██ ██▄▄█▀ ▀████▀ ██ ██ ▀████▀ ██ ██
*/
/*
registry.c - part of the registry.c library by Colin Melican (Kolin63)
https://github.com/kolin63/registry.c
Under the MIT License
Copyright (c) 2026 Colin Melican
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "registry.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct registry* registry_init(int val_size) {
struct registry* reg = malloc(sizeof(struct registry));
reg->length = 0;
reg->val_size = val_size;
reg->keys = NULL;
reg->values = NULL;
return reg;
}
void registry_cleanup(struct registry* reg) {
for (size_t i = 0; i < reg->length; i++) {
free(reg->keys[i]);
}
free(reg->keys);
free(reg->values);
free(reg);
}
int registry_add(struct registry* reg, const char* key, const void* val) {
// do a binary search to find insertion index
// 0 2 4 6 8
// ^
// 5 (insert at index 3)
int insert_index = 0;
{
int left = 0;
int right = reg->length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
int cmp = strcmp(reg->keys[mid], key);
if (cmp < 0) {
left = mid + 1;
} else if (cmp > 0) {
right = mid - 1;
} else {
fprintf(stderr, "Key already exists in registry: %s\n", key);
return -1;
}
}
insert_index = left + (right - left) / 2;
}
// move keys and values to make room
// 0 2 4 _ 6 8
// ^
// 5
// first keys
{
reg->keys = realloc(reg->keys, (reg->length + 1) * sizeof(char*));
char** src = reg->keys + insert_index;
char** dest = src + 1;
const size_t n = (reg->length - insert_index) * sizeof(char*);
memmove(dest, src, n);
}
// then values
{
reg->values = realloc(reg->values, (reg->length + 1) * reg->val_size);
void* src = reg->values + insert_index * reg->val_size;
void* dest = src + reg->val_size;
const size_t n = (reg->length - insert_index) * reg->val_size;
memmove(dest, src, n);
}
// move in new key
{
char* new_key = malloc(strlen(key) + 1);
strcpy(new_key, key);
*(reg->keys + insert_index) = new_key;
}
// move in new value
{
const void* src = val;
void* dest = reg->values + insert_index * reg->val_size;
const size_t n = reg->val_size;
memcpy(dest, src, n);
}
reg->length++;
return 0;
}
void* registry_itov(struct registry* reg, int i) {
return reg->values + i * reg->val_size;
}
void* registry_itov_safe(struct registry* reg, int i) {
if (i < 0 || i >= reg->length) {
return NULL;
} else {
return reg->values + i * reg->val_size;
}
}
char* registry_itok(struct registry* reg, int i) { return reg->keys[i]; }
char* registry_itok_safe(struct registry* reg, int i) {
if (i < 0 || i >= reg->length) {
return NULL;
} else {
return reg->keys[i];
}
}
int registry_ktoi(struct registry* reg, const char* key) {
int left = 0;
int right = reg->length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
int cmp = strcmp(reg->keys[mid], key);
if (cmp < 0) {
left = mid + 1;
} else if (cmp > 0) {
right = mid - 1;
} else {
return mid;
}
}
return -1;
}
void* registry_ktov(struct registry* reg, const char* key) {
int i = registry_ktoi(reg, key);
if (i < 0) {
return NULL;
} else {
return registry_itov(reg, i);
}
}