summaryrefslogtreecommitdiff
path: root/src/benc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/benc.c')
-rw-r--r--src/benc.c232
1 files changed, 232 insertions, 0 deletions
diff --git a/src/benc.c b/src/benc.c
new file mode 100644
index 0000000..4f61ff7
--- /dev/null
+++ b/src/benc.c
@@ -0,0 +1,232 @@
+#include "benc.h"
+#include "log.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <ctype.h>
+#include <string.h>
+
+
+#define MAX(a, b) \
+ ({ \
+ __typeof__ (a) _a = (a); \
+ __typeof__ (b) _b = (b); \
+ _a > _b ? _a : _b; \
+ })
+
+#define MIN(a, b) \
+ ({ \
+ __typeof__ (a) _a = (a); \
+ __typeof__ (b) _b = (b); \
+ _a < _b ? _a : _b; \
+ })
+
+bool readint(const char** loc, int64_t* val) {
+ char* end = NULL;
+ errno = 0;
+ *val = strtol(*loc, &end, 10);
+ *loc = end;
+
+ return errno == 0;
+}
+
+bool digit(const char c) {
+ return c >= '0' && c <= '9';
+}
+
+char* nodenames[] = {
+ "INT",
+ "STR",
+ "LIST",
+ "DICT",
+ "END",
+};
+
+void indent(int depth) {
+ for(int i = 0; i < depth; i++) {
+ printf(" ");
+ }
+}
+
+void benc_print(const struct benc_node* stream, size_t stream_len, int* depth) {
+ const struct benc_node* cursor = stream;
+ for(; cursor < stream + stream_len; cursor++) {
+ switch(cursor->type) {
+ case BNT_INT:
+ indent(*depth);
+ printf("INT %.*s\n", cursor->size, cursor->loc);
+ break;
+ case BNT_STRING:
+ indent(*depth);
+ bool allprint = true;
+ for (const char* c = cursor->loc; c < cursor->loc + cursor->size; c++) {
+ if(!isalnum(*c)) {
+ allprint = false;
+ break;
+ }
+ }
+ printf("STR ");
+ if(allprint) {
+ printf("%.*s", cursor->size, cursor->loc);
+ } else {
+ for (const unsigned char* c = (const unsigned char*)cursor->loc; c < (unsigned char*)(cursor->loc + cursor->size); c++) {
+ printf("\\x%02X", *c);
+ }
+ }
+ printf("\n");
+ break;
+ case BNT_LIST:
+ indent(*depth);
+ printf("LIST\n");
+ *depth = cursor->depth + 1;
+ break;
+ case BNT_DICT:
+ indent(*depth);
+ printf("DICT\n");
+ *depth = cursor->depth + 1;
+ break;
+ case BNT_END:
+ *depth = cursor->depth;
+ indent(*depth);
+ printf("END\n");
+ break;
+ }
+ }
+}
+
+int64_t benc_decode(const char** cursor, const char* end, int* depth, struct benc_node* stream, size_t stream_len) {
+ size_t cursor_out = 0;
+
+ while(cursor_out < stream_len) {
+ struct benc_node* node = &stream[cursor_out];
+
+ if(**cursor == 'i') {
+ node->type = BNT_INT;
+ (*cursor)++;
+ if(*cursor >= end) {
+ return -cursor_out;
+ }
+ node->loc = *cursor;
+ while(**cursor != 'e') {
+ if(**cursor != '-' && !digit(**cursor)) {
+ return -cursor_out;
+ }
+ (*cursor)++;
+ if(*cursor >= end) {
+ return -cursor_out;
+ }
+ }
+ node->size = *cursor - node->loc;
+ (*cursor)++;
+ } else if(**cursor == 'l') {
+ node->type = BNT_LIST;
+ node->depth = *depth;
+ (*depth)++;
+ node->loc = *cursor;
+ (*cursor)++;
+ if(*cursor >= end) {
+ return -cursor_out;
+ }
+ } else if(**cursor == 'd') {
+ node->type = BNT_DICT;
+ node->depth = *depth;
+ (*depth)++;
+ node->loc = *cursor;
+ (*cursor)++;
+ if(*cursor >= end) {
+ return -cursor_out;
+ }
+ } else if(**cursor == 'e') {
+ node->type = BNT_END;
+ (*depth)--;
+ node->depth = *depth;
+ node->loc = *cursor;
+ (*cursor)++;
+ } else if(digit(**cursor)) {
+ node->type = BNT_STRING;
+ int64_t val;
+ bool rc = readint(cursor, &val);
+ node->size = val;
+ if(*cursor >= end) {
+ return -cursor_out;
+ }
+ assert(rc);
+ if(**cursor != ':') {
+ return -cursor_out;
+ }
+ (*cursor)++;
+ if(*cursor >= end) {
+ return -cursor_out;
+ }
+ node->loc = *cursor;
+ (*cursor) += node->size;
+ if(*cursor > end) {
+ return -cursor_out;
+ }
+ } else {
+ return -cursor_out;
+ dbg("Failing on char \"%c\"", **cursor);
+ assert(false);
+ }
+ cursor_out++;
+ if(*depth == 0) break;
+ }
+ return cursor_out;
+}
+
+void skip_sibling(const struct benc_node** cursor, const struct benc_node* end) {
+ assert((*cursor)->type == BNT_LIST || (*cursor)->type == BNT_DICT);
+
+ if((*cursor)->type == BNT_LIST || (*cursor)->type == BNT_DICT) {
+ uint32_t tdepth = (*cursor)->depth;
+ (*cursor)++;
+ while(true) {
+ if(*cursor == end) {
+ fatal("Invalid dict");
+ }
+
+ if((*cursor)->type == BNT_END && (*cursor)->depth == tdepth) {
+ break;
+ }
+
+ (*cursor)++;
+ }
+ (*cursor)++;
+ } else {
+ (*cursor)++;
+ }
+}
+
+// All the arrays should be equal length
+ssize_t skip_to_key(const struct benc_node** cursor, const struct benc_node* end, const enum benc_nodetype* keyTypes, const char** keyValues, const size_t* keyLengths, const size_t keys) {
+ // @ROBUSTNESS: Check if keytypes are anything but strings and ints because that is not supported
+
+ while(true) {
+ if(*cursor > end-1) {
+ fatal("Invalid dict");
+ }
+
+ // Skip lists
+ if((*cursor)->type == BNT_LIST || (*cursor)->type == BNT_DICT) {
+ skip_sibling(cursor, end);
+ } else {
+ for(size_t i = 0; i < keys; i++) {
+ if((*cursor)->type == keyTypes[i] && memcmp((*cursor)->loc, keyValues[i], MIN((*cursor)->size, keyLengths[i])) == 0) {
+ return i;
+ }
+ }
+ }
+
+ (*cursor) += 2;
+
+ if((*cursor-1)->type == BNT_LIST || (*cursor-1)->type == BNT_DICT) {
+ (*cursor)--;
+ skip_sibling(cursor, end);
+ }
+
+ if((*cursor)->type == BNT_END) {
+ return -1;
+ }
+ }
+}