Created
April 12, 2020 17:43
-
-
Save gsora/9454855e5958f0cb83d73856ce51d3c9 to your computer and use it in GitHub Desktop.
extremely stupid C key-value store
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#define PREAMBLE_DELIM "#preamble#" | |
#define PREAMBLE_DELIM_LEN 11 | |
#define SPLIT_TOKEN "\n" | |
#define T PREAMBLE_DELIM"\n""a:int:1\nb:string:31\nc:bool:1\n"PREAMBLE_DELIM"\na:1234\nb:ciaociao ciao come va la vita?\n" | |
#define FIELD_SZ 128 | |
#define PREAMBLE_LINE_FMT "%[^:]:%[^:]:%lli\n" | |
#define KEY_FMT "%[^:]:" | |
#define STRING_FMT "%[^:]:%[^:]" | |
#define INTEGER_FMT "%[^:]:%lli" | |
#define BOOLEAN_FMT "%[^:]:%d" | |
enum entry_type { | |
Integer = 10, | |
Boolean = 20, | |
String = 30, | |
}; | |
typedef enum entry_type entry_type; | |
typedef struct { | |
char* name; | |
entry_type* type; | |
long long *length; | |
} preamble_token; | |
typedef struct { | |
long long length; | |
long long skip_lines; | |
preamble_token **tokens; | |
} preamble; | |
void preamble_free(preamble *p) { | |
long long i = 0; | |
for(i = 0; i < p->length; i++) { | |
preamble_token *pp = p->tokens[i]; | |
free(pp->name); | |
free(pp->type); | |
free(pp->length); | |
free(p->tokens[i]); | |
} | |
free(p->tokens); | |
free(p); | |
} | |
entry_type* string_to_type(const char* ts) { | |
entry_type t = 0; | |
if (strcmp(ts, "int") == 0) { | |
t = Integer; | |
} else if (strcmp(ts, "string") == 0) { | |
t = String; | |
} else { | |
t = Boolean; | |
} | |
entry_type *tt = malloc(1*sizeof(entry_type)); | |
tt = &t; | |
return tt; | |
} | |
preamble* parse_preamble(const char* input) { | |
char *data = malloc(strlen(input) * sizeof(char)); | |
memcpy(data, input, strlen(input)); | |
preamble *p = malloc(1*sizeof(preamble)); | |
p->length = 0; | |
char* token = strtok((char *)data, SPLIT_TOKEN); | |
short preamble_tags = 2; | |
long long skip_lines = 0; | |
while (token != NULL) { | |
skip_lines++; | |
if (strncmp(PREAMBLE_DELIM, token, PREAMBLE_DELIM_LEN) == 0) { | |
preamble_tags--; | |
if(preamble_tags == 0) { | |
free(data); | |
p->skip_lines = skip_lines; | |
return p; | |
} | |
token = strtok(NULL,SPLIT_TOKEN); | |
continue; | |
} else if(preamble_tags == 2) { | |
printf("preamble does not start with delimiter, bailing out\n"); | |
free(data); | |
return NULL; | |
} | |
if (p->length == 0) { | |
p->tokens = calloc(1, sizeof(preamble_token*)); | |
} else { | |
p->tokens = realloc(p->tokens, p->length*sizeof(preamble_token*)); | |
} | |
preamble_token *t = malloc(1*sizeof(preamble_token)); | |
t->name = malloc(FIELD_SZ*sizeof(char)); | |
t->type = malloc(1*sizeof(entry_type)); | |
t->length = malloc(1*sizeof(long long)); | |
char type_string[FIELD_SZ]; | |
sscanf(token, PREAMBLE_LINE_FMT, t->name, type_string, t->length); | |
t->name = realloc(t->name, strlen(t->name)*sizeof(char)); | |
memcpy(t->type, string_to_type(type_string), sizeof(entry_type)); | |
p->tokens[p->length++] = t; | |
token = strtok(NULL, SPLIT_TOKEN); | |
} | |
if (preamble_tags != 0) { | |
printf("failed to process preamble, encountered %d tags\n", preamble_tags); | |
preamble_free(p); | |
free(data); | |
return NULL; | |
} | |
free(data); | |
return NULL; | |
} | |
void* get_field(const char* input, preamble* preamble, const char* key, size_t key_len) { | |
char *data = malloc(strlen(input) * sizeof(char)); | |
memcpy(data, input, strlen(input)); | |
void* buffer = NULL; | |
long long length = 0; | |
char fmt[FIELD_SZ]; | |
long long i = 0; | |
for (i = 0; i < preamble->length; i++) { | |
preamble_token *pt = preamble->tokens[i]; | |
if (strncmp(pt->name, key, key_len) == 0) { | |
switch (*pt->type) { | |
case Integer: | |
buffer = malloc(1*sizeof(long long)); | |
strcpy(fmt, INTEGER_FMT); | |
length = 1; | |
break; | |
case String: | |
buffer = malloc(1*sizeof(pt->length)); | |
length = *pt->length; | |
strcpy(fmt, STRING_FMT); | |
break; | |
case Boolean: | |
buffer = malloc(1*sizeof(int)); | |
strcpy(fmt, BOOLEAN_FMT); | |
length = 1; | |
break; | |
default: | |
printf("undefined type %u\n", *pt->type); | |
break; | |
} | |
} | |
} | |
char* token = strtok((char *)data, SPLIT_TOKEN); | |
long long iter = 0; | |
while (token != NULL) { | |
if (iter++ < preamble->skip_lines) { | |
token = strtok(NULL, SPLIT_TOKEN); | |
continue; | |
} | |
char read_key[FIELD_SZ]; | |
sscanf(token, KEY_FMT, read_key); | |
if(strncmp(read_key, key, key_len) == 0) { | |
sscanf(token, fmt, read_key, buffer); | |
free(data); | |
return buffer; | |
} | |
token = strtok(NULL, SPLIT_TOKEN); | |
} | |
free(data); | |
return NULL; | |
} | |
int main(void) { | |
char *data = T; | |
preamble* p = parse_preamble(data); | |
if (p == NULL) { return 1; } | |
printf("lines to skip: %lli\n", p->skip_lines); | |
long long i = 0; | |
for(i = 0; i < p->length; i++) { | |
preamble_token *pp = p->tokens[i]; | |
printf("name %s, type %u, length %lli, pointer %p\n", pp->name, *pp->type, *pp->length, pp); | |
} | |
printf("getting field b\n"); | |
char* c = (char *)get_field(data, p, "b", 1); | |
printf("field b: %s\n", c); | |
printf("getting nonexistant field\n"); | |
char* cc = (char *)get_field(data, p, "asd", 3); | |
printf("nonexistant field: %s\n", cc); | |
preamble_free(p); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment