From 8708c99e333609fee20019da65300298a029465e Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Mon, 21 Jan 2013 14:37:29 -0500 Subject: [PATCH] add bencode lib to sources --- daemon/Makefile | 3 +- daemon/bencode.c | 561 +++++++++++++++++++++++++++++++++++++++++++++++ daemon/bencode.h | 284 ++++++++++++++++++++++++ 3 files changed, 847 insertions(+), 1 deletion(-) create mode 100644 daemon/bencode.c create mode 100644 daemon/bencode.h diff --git a/daemon/Makefile b/daemon/Makefile index 12de7f3..fa11084 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -25,7 +25,8 @@ CFLAGS+= `dpkg-buildflags --get CFLAGS` CPPFLAGS+= `dpkg-buildflags --get CPPFLAGS` LDFLAGS+= `dpkg-buildflags --get LDFLAGS` -SRCS= main.c kernel.c poller.c aux.c control.c streambuf.c call.c control_udp.c redis.c +SRCS= main.c kernel.c poller.c aux.c control.c streambuf.c call.c control_udp.c redis.c \ + bencode.c OBJS= $(SRCS:.c=.o) diff --git a/daemon/bencode.c b/daemon/bencode.c new file mode 100644 index 0000000..b0e31f4 --- /dev/null +++ b/daemon/bencode.c @@ -0,0 +1,561 @@ +#include +#include +#include +#include +#include +#include + +#include "bencode.h" + +/* set to 0 for alloc debugging, e.g. through valgrind */ +#define BENCODE_MIN_BUFFER_PIECE_LEN 512 + +struct __bencode_buffer_piece { + char *tail; + unsigned int left; + struct __bencode_buffer_piece *next; + char buf[0]; +}; + + + + + +static bencode_item_t __bencode_end_marker = { + .type = BENCODE_END_MARKER, + .iov[0].iov_base = "e", + .iov[0].iov_len = 1, + .iov_cnt = 1, + .str_len = 1, +}; + + + + + +static void __bencode_item_init(bencode_item_t *item) { + item->parent = item->child = item->sibling = NULL; +} + +static void __bencode_container_init(bencode_item_t *cont) { + cont->iov[0].iov_len = 1; + cont->iov[1].iov_base = "e"; + cont->iov[1].iov_len = 1; + cont->iov_cnt = 2; + cont->str_len = 2; +} + +static void bencode_dictionary_init(bencode_item_t *dict) { + dict->type = BENCODE_DICTIONARY; + dict->iov[0].iov_base = "d"; + __bencode_container_init(dict); +} + +static void bencode_list_init(bencode_item_t *list) { + list->type = BENCODE_LIST; + list->iov[0].iov_base = "l"; + __bencode_container_init(list); +} + +static struct __bencode_buffer_piece *__bencode_piece_new(unsigned int size) { + struct __bencode_buffer_piece *ret; + + if (size < BENCODE_MIN_BUFFER_PIECE_LEN) + size = BENCODE_MIN_BUFFER_PIECE_LEN; + ret = BENCODE_MALLOC(sizeof(*ret) + size); + if (!ret) + return NULL; + + ret->tail = ret->buf; + ret->left = size; + ret->next = NULL; + + return ret; +} + +int bencode_buffer_init(bencode_buffer_t *buf) { + buf->pieces = __bencode_piece_new(0); + if (!buf->pieces) + return -1; + return 0; +} + +static void *__bencode_alloc(bencode_buffer_t *buf, unsigned int size) { + struct __bencode_buffer_piece *piece; + void *ret; + + piece = buf->pieces; + + if (size <= piece->left) + goto alloc; + + piece = __bencode_piece_new(size); + if (!piece) + return NULL; + piece->next = buf->pieces; + buf->pieces = piece; + + assert(size <= piece->left); + +alloc: + piece->left -= size; + ret = piece->tail; + piece->tail += size; + return ret; +} + +void bencode_buffer_free(bencode_buffer_t *buf) { + struct __bencode_buffer_piece *piece, *next; + + for (piece = buf->pieces; piece; piece = next) { + next = piece->next; + BENCODE_FREE(piece); + } +} + +static bencode_item_t *__bencode_item_alloc(bencode_buffer_t *buf, unsigned int payload) { + bencode_item_t *ret; + + ret = __bencode_alloc(buf, sizeof(struct bencode_item) + payload); + if (!ret) + return NULL; + ret->buffer = buf; + __bencode_item_init(ret); + return ret; +} + +bencode_item_t *bencode_dictionary(bencode_buffer_t *buf) { + bencode_item_t *ret; + + ret = __bencode_item_alloc(buf, 0); + if (!ret) + return NULL; + bencode_dictionary_init(ret); + return ret; +} + +bencode_item_t *bencode_list(bencode_buffer_t *buf) { + bencode_item_t *ret; + + ret = __bencode_item_alloc(buf, 0); + if (!ret) + return NULL; + bencode_list_init(ret); + return ret; +} + +static void __bencode_container_add(bencode_item_t *parent, bencode_item_t *child) { + assert(child->parent == NULL); + assert(child->sibling == NULL); + + child->parent = parent; + child->sibling = parent->child; + parent->child = child; + + while (parent) { + parent->iov_cnt += child->iov_cnt; + parent->str_len += child->str_len; + parent = parent->parent; + } +} + +bencode_item_t *bencode_string_len(bencode_buffer_t *buf, const char *s, int len) { + bencode_item_t *ret; + int len_len; + + assert((len <= 99999) && (len >= 0)); + ret = __bencode_item_alloc(buf, strlen(s) + 7); + if (!ret) + return NULL; + len_len = sprintf(ret->__buf, "%d:", len); + + ret->type = BENCODE_STRING; + ret->iov[0].iov_base = ret->__buf; + ret->iov[0].iov_len = len_len; + ret->iov[1].iov_base = (void *) s; + ret->iov[1].iov_len = len; + ret->iov_cnt = 2; + ret->str_len = len_len + len; + + return ret; +} + +bencode_item_t *bencode_integer(bencode_buffer_t *buf, long long int i) { + bencode_item_t *ret; + int alen, rlen; + + alen = 8; + while (1) { + ret = __bencode_item_alloc(buf, alen + 3); + if (!ret) + return NULL; + rlen = snprintf(ret->__buf, alen, "i%llde", i); + if (rlen < alen) + break; + alen <<= 1; + } + + ret->type = BENCODE_INTEGER; + ret->iov[0].iov_base = ret->__buf; + ret->iov[0].iov_len = rlen; + ret->iov[1].iov_base = NULL; + ret->iov[1].iov_len = 0; + ret->iov_cnt = 1; + ret->str_len = rlen; + + return ret; +} + +bencode_item_t *bencode_dictionary_add_len(bencode_item_t *dict, const char *key, int keylen, bencode_item_t *val) { + bencode_item_t *str; + + assert(dict->type == BENCODE_DICTIONARY); + if (!val) + return NULL; + + str = bencode_string_len(dict->buffer, key, keylen); + if (!str) + return NULL; + __bencode_container_add(dict, str); + __bencode_container_add(dict, val); + return val; +} + +bencode_item_t *bencode_list_add(bencode_item_t *list, bencode_item_t *item) { + assert(list->type == BENCODE_LIST); + __bencode_container_add(list, item); + return item; +} + +static int __bencode_iovec_dump_rev(struct iovec *out, bencode_item_t *item) { + bencode_item_t *child; + struct iovec *orig = out; + + if (item->iov[1].iov_base) + *--out = item->iov[1]; + + child = item->child; + while (child) { + out -= __bencode_iovec_dump_rev(out, child); + child = child->sibling; + } + + assert(item->iov[0].iov_base != NULL); + *--out = item->iov[0]; + + assert((orig - out) == item->iov_cnt); + return item->iov_cnt; +} + +static int __bencode_str_dump_rev(char *out, bencode_item_t *item) { + bencode_item_t *child; + char *orig = out; + + if (item->iov[1].iov_base) { + out -= item->iov[1].iov_len; + memcpy(out, item->iov[1].iov_base, item->iov[1].iov_len); + } + + child = item->child; + while (child) { + out -= __bencode_str_dump_rev(out, child); + child = child->sibling; + } + + assert(item->iov[0].iov_base != NULL); + out -= item->iov[0].iov_len; + memcpy(out, item->iov[0].iov_base, item->iov[0].iov_len); + + assert((orig - out) == item->str_len); + return item->str_len; +} + +static int __bencode_iovec_dump(struct iovec *out, bencode_item_t *item) { + int len; + + /* sibling lists are built in reverse, so dump everything + out backwards, end to start */ + out += item->iov_cnt; + len = __bencode_iovec_dump_rev(out, item); + assert(len == item->iov_cnt); + return len; +} + +static int __bencode_str_dump(char *out, bencode_item_t *item) { + int len; + char *orig = out; + + /* sibling lists are built in reverse, so dump everything + out backwards, end to start */ + out += item->str_len; + len = __bencode_str_dump_rev(out, item); + assert(len == item->str_len); + orig[len] = '\0'; + return len; +} + +struct iovec *bencode_iovec(bencode_item_t *root, int *cnt, unsigned int head, unsigned int tail) { + struct iovec *ret; + + assert(root != NULL); + assert(cnt != NULL); + assert(root->iov_cnt > 0); + + ret = __bencode_alloc(root->buffer, sizeof(*ret) * (root->iov_cnt + head + tail)); + if (!ret) + return NULL; + *cnt = __bencode_iovec_dump(ret + head, root); + return ret; +} + +char *bencode_collapse(bencode_item_t *root, int *len) { + char *ret; + int l; + + assert(root != NULL); + assert(root->str_len > 0); + + ret = __bencode_alloc(root->buffer, root->str_len + 1); + if (!ret) + return NULL; + l = __bencode_str_dump(ret, root); + if (len) + *len = l; + return ret; +} + +char *bencode_collapse_dup(bencode_item_t *root, int *len) { + char *ret; + int l; + + assert(root != NULL); + assert(root->str_len > 0); + + ret = BENCODE_MALLOC(root->str_len + 1); + if (!ret) + return NULL; + + l = __bencode_str_dump(ret, root); + if (len) + *len = l; + return ret; +} + +static bencode_item_t *bencode_decode_dictionary(bencode_buffer_t *buf, const char *s, int len) { + bencode_item_t *ret, *item; + + if (*s != 'd') + return NULL; + + s++; + len--; + + ret = __bencode_item_alloc(buf, 0); + if (!ret) + return NULL; + bencode_dictionary_init(ret); + + while (len > 0) { + item = bencode_decode(buf, s, len); + if (!item) + return NULL; + s += item->str_len; + len -= item->str_len; + if (item->type == BENCODE_END_MARKER) + break; + if (item->type != BENCODE_STRING) + return NULL; + __bencode_container_add(ret, item); + + if (len <= 0) + return NULL; + item = bencode_decode(buf, s, len); + if (!item) + return NULL; + s += item->str_len; + len -= item->str_len; + if (item->type == BENCODE_END_MARKER) + return NULL; + __bencode_container_add(ret, item); + } + + return ret; +} + +static bencode_item_t *bencode_decode_list(bencode_buffer_t *buf, const char *s, int len) { + bencode_item_t *ret, *item; + + if (*s != 'l') + return NULL; + + s++; + len--; + + ret = __bencode_item_alloc(buf, 0); + if (!ret) + return NULL; + bencode_list_init(ret); + + while (len >= 0) { + item = bencode_decode(buf, s, len); + if (!item) + return NULL; + s += item->str_len; + len -= item->str_len; + if (item->type == BENCODE_END_MARKER) + break; + __bencode_container_add(ret, item); + } + + return ret; +} + +static bencode_item_t *bencode_decode_integer(bencode_buffer_t *buf, const char *s, int len) { + long long int i; + const char *orig = s; + char *end; + bencode_item_t *ret; + + if (*s != 'i') + return NULL; + + s++; + len--; + + if (len <= 0) + return NULL; + + if (*s == '0') { + i = 0; + s++; + len--; + goto done; + } + + i = strtoll(s, &end, 10); + if (end == s) + return NULL; + len -= (end - s); + s += (end - s); + +done: + if (len <= 0) + return NULL; + if (*s != 'e') + return NULL; + s++; + len--; + + ret = __bencode_item_alloc(buf, 0); + if (!ret) + return NULL; + ret->type = BENCODE_INTEGER; + ret->iov[0].iov_base = (void *) orig; + ret->iov[0].iov_len = s - orig; + ret->iov[1].iov_base = NULL; + ret->iov[1].iov_len = 0; + ret->iov_cnt = 1; + ret->str_len = s - orig; + ret->value = i; + + return ret; +} + +static bencode_item_t *bencode_decode_string(bencode_buffer_t *buf, const char *s, int len) { + unsigned long int sl; + char *end; + const char *orig = s; + bencode_item_t *ret; + + if (*s == '0') { + sl = 0; + s++; + len--; + goto colon; + } + + sl = strtoul(s, &end, 10); + if (end == s) + return NULL; + len -= (end - s); + s += (end - s); + +colon: + if (len <= 0) + return NULL; + if (*s != ':') + return NULL; + + len--; + s++; + + if (len < sl) + return NULL; + + ret = __bencode_item_alloc(buf, 0); + if (!ret) + return NULL; + ret->type = BENCODE_STRING; + ret->iov[0].iov_base = (void *) orig; + ret->iov[0].iov_len = s - orig; + ret->iov[1].iov_base = (void *) s; + ret->iov[1].iov_len = sl; + ret->iov_cnt = 2; + ret->str_len = s - orig + sl; + + return ret; +} + +bencode_item_t *bencode_decode(bencode_buffer_t *buf, const char *s, int len) { + assert(s != NULL); + assert(len > 0); + + switch (*s) { + case 'd': + return bencode_decode_dictionary(buf, s, len); + case 'l': + return bencode_decode_list(buf, s, len); + case 'i': + return bencode_decode_integer(buf, s, len); + case 'e': + return &__bencode_end_marker; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return bencode_decode_string(buf, s, len); + default: + return NULL; + } +} + +/* XXX inefficient, use a proper hash instead */ +bencode_item_t *bencode_dictionary_get_len(bencode_item_t *dict, const char *keystr, int keylen) { + bencode_item_t *key, *val; + + if (!dict) + return NULL; + if (dict->type != BENCODE_DICTIONARY) + return NULL; + + for (val = dict->child; val; val = key->sibling) { + key = val->sibling; + assert(key != NULL); + assert(key->type == BENCODE_STRING); + + if (keylen != key->iov[1].iov_len) + continue; + if (memcmp(keystr, key->iov[1].iov_base, keylen)) + continue; + + return val; + } + + return NULL; +} diff --git a/daemon/bencode.h b/daemon/bencode.h new file mode 100644 index 0000000..f36eb3d --- /dev/null +++ b/daemon/bencode.h @@ -0,0 +1,284 @@ +#ifndef _BENCODE_H_ +#define _BENCODE_H_ + +#include +#include + +#ifndef BENCODE_MALLOC +#define BENCODE_MALLOC malloc +#define BENCODE_FREE free +#endif + +struct bencode_buffer; +enum bencode_type; +struct bencode_item; +struct __bencode_buffer_piece; + +typedef enum bencode_type bencode_type_t; +typedef struct bencode_buffer bencode_buffer_t; +typedef struct bencode_item bencode_item_t; + +enum bencode_type { + BENCODE_INVALID = 0, + BENCODE_STRING, /* byte string */ + BENCODE_INTEGER, /* long long int */ + BENCODE_LIST, /* flat list of other objects */ + BENCODE_DICTIONARY, /* dictionary of key/values pairs. keys are always strings */ + BENCODE_END_MARKER, /* used internally only */ +}; + +struct bencode_item { + bencode_type_t type; + struct iovec iov[2]; /* when decoding, iov[1] contains the contents of a string object */ + unsigned int iov_cnt; + unsigned int str_len; /* length of the whole ENCODED object. NOT the length of a byte string */ + long long int value; /* when decoding an integer, contains the value */ + bencode_item_t *parent, *child, *sibling; + bencode_buffer_t *buffer; + char __buf[0]; +}; + +struct bencode_buffer { + struct __bencode_buffer_piece *pieces; +}; + +/* Initializes a bencode_buffer_t object. This object is used to group together all memory allocations + * made when encoding or decoding. Its memory usage is always growing, until it is freed, at which point + * all objects created through it become invalid. The actual object must be allocated separately, for + * example by being put on the stack. + * Returns 0 on success or -1 on failure (if no memory could be allocated). */ +int bencode_buffer_init(bencode_buffer_t *buf); + +/* Destroys a previously initialized bencode_buffer_t object. All memory used by the object is freed + * and all objects created through it become invalid. */ +void bencode_buffer_free(bencode_buffer_t *buf); + +/* Creates a new empty dictionary object. Memory will be allocated from the bencode_buffer_t object. + * Returns NULL if no memory could be allocated. */ +bencode_item_t *bencode_dictionary(bencode_buffer_t *buf); + +/* Creates a new empty list object. Memory will be allocated from the bencode_buffer_t object. + * Returns NULL if no memory could be allocated. */ +bencode_item_t *bencode_list(bencode_buffer_t *buf); + +/* Adds a new key/value pair to a dictionary. Memory will be allocated from the same bencode_buffer_t + * object as the dictionary was allocated from. Returns NULL if no memory could be allocated, otherwise + * returns "val". + * The function does not check whether the key being added is already present in the dictionary. + * Also, the function does not reorder keys into lexicographical order; keys will be encoded in + * the same order as they've been added. The key must a null-terminated string. + * The value to be added must not have been previously linked into any other dictionary or list. */ +static inline bencode_item_t *bencode_dictionary_add(bencode_item_t *dict, const char *key, bencode_item_t *val); + +/* Identical to bencode_dictionary_add() but doesn't require the key string to be null-terminated */ +bencode_item_t *bencode_dictionary_add_len(bencode_item_t *dict, const char *key, int keylen, bencode_item_t *val); + +/* Convenience function to add a string value to a dictionary */ +static inline bencode_item_t *bencode_dictionary_add_string(bencode_item_t *dict, const char *key, const char *val); + +/* Convenience function to add an integer value to a dictionary */ +static inline bencode_item_t *bencode_dictionary_add_integer(bencode_item_t *dict, const char *key, long long int val); + +/* Adds a new item to a list. Returns "item". + * The item to be added must not have been previously linked into any other dictionary or list. */ +bencode_item_t *bencode_list_add(bencode_item_t *list, bencode_item_t *item); + +/* Convenience function to add a string item to a list */ +static inline bencode_item_t *bencode_list_add_string(bencode_item_t *list, const char *s); + +/* Creates a new byte-string object. The given string does not have to be null-terminated, instead + * the length of the string is specified by the "len" parameter. Returns NULL if no memory could + * be allocated. + * Strings are not copied or duplicated, so the string pointed to by "s" must remain valid until + * the complete document is finally encoded or sent out. */ +bencode_item_t *bencode_string_len(bencode_buffer_t *buf, const char *s, int len); + +/* Creates a new byte-string object. The given string must be null-terminated. Otherwise identical + * to bencode_string_len(). */ +static inline bencode_item_t *bencode_string(bencode_buffer_t *buf, const char *s); + +/* Creates a new integer object. Returns NULL if no memory could be allocated. */ +bencode_item_t *bencode_integer(bencode_buffer_t *buf, long long int i); + +/* Collapses and encodes the complete document structure under the "root" element (which normally + * is either a dictionary or a list) into an array of "iovec" structures. This array can then be + * passed to functions ala writev() or sendmsg() to output the encoded document as a whole. Memory + * is allocated from the same bencode_buffer_t object as the "root" object was allocated from. + * The "head" and "tail" parameters specify additional "iovec" structures that should be included + * in the allocated array before or after (respectively) the iovec structures used by the encoded + * document. Both parameters can be zero if no additional elements in the array are required. + * Returns a pointer to the allocated array or NULL if no memory could be allocated. The number of + * array elements is returned in "cnt" which must be a valid pointer to an int. This number does + * not include any additional elements allocated through the "head" or "tail" parameters. + * + * Therefore, the contents of the returned array are: + * [0 .. (head - 1)] = unused and uninitialized iovec structures + * [(head) .. (head + cnt - 1)] = the encoded document + * [(head + cnt) .. (head + cnt + tail - 1)] = unused and uninitialized iovec structures + * + * The returned array will be freed when the corresponding bencode_buffer_t object is destroyed. */ +struct iovec *bencode_iovec(bencode_item_t *root, int *cnt, unsigned int head, unsigned int tail); + +/* Similar to bencode_iovec(), but instead returns the encoded document as a null-terminated string. + * Memory for the string is allocated from the same bencode_buffer_t object as the "root" object + * was allocated from. If "len" is a non-NULL pointer, the length of the genrated string is returned + * in *len. This is important if the encoded document contains binary data, in which case null + * termination cannot be trusted. The returned string is freed when the corresponding + * bencode_buffer_t object is destroyed. */ +char *bencode_collapse(bencode_item_t *root, int *len); + +/* Identical to bencode_collapse(), but the memory for the returned string is not allocated from + * a bencode_buffer_t object, but instead using the function defined as BENCODE_MALLOC (normally + * malloc() or pkg_malloc()), similar to strdup(). Using this function, the bencode_buffer_t + * object can be destroyed, but the returned string remains valid and usable. */ +char *bencode_collapse_dup(bencode_item_t *root, int *len); + +/* Decodes an encoded document from a string into a tree of bencode_item_t objects. The string does + * not need to be null-terminated, instead the length of the string is given through the "len" + * parameter. Memory is allocated from the bencode_buffer_t object. Returns NULL if no memory could + * be allocated or if the document could not be successfully decoded. + * + * The returned element is the "root" of the document tree and normally is either a list object or + * a dictionary object, but can also be a single string or integer object with no other objects + * underneath or besides it (no childred and no siblings). The type of the object can be determined + * by its ->type property. + * + * The number of bytes that could successfully be decoded into an object tree can be accessed through + * the root element's ->str_len property. Normally, this number should be equal to the "len" parameter + * passed, in which case the full string could be decoded. If ->str_len is less than "len", then there + * was additional stray byte data after the end of the encoded document. + * + * The document tree can be traversed through the ->child and ->sibling pointers in each object. The + * ->child pointer will be NULL for string and integer objects, as they don't contain other objects. + * For lists and dictionaries, ->child will be a pointer to the LAST contained object. This last + * contained object's ->sibling pointer will point to the last-but-one contained object of the list + * or the dictionary, and so on. The FIRST contained element of a list of dictionary will have a + * NULL ->sibling pointer. + * + * Lists are built in reverse and so traversing a list by following the ->sibling pointers will + * traverse all the elements in reverse as well. This includes the ordering of key/value pairs in + * dictionary: The first element in the list (where ->child points to) will be the VALUE of the LAST + * key/value pair. The next element (following one ->sibling) will be the KEY of the LAST key/value + * pair (guaranteed to be a string and guaranteed to be present). Following another ->sibling will + * point to the VALUE of the last-but-one key/value pair, and so on. + * + * The decoding function for dictionary object does not check whether keys are unique within the + * dictionary. It also does not care about lexicographical order of the keys. + * + * Decoded string objects will contain the raw decoded byte string in ->iov[1] (including the correct + * length). Strings are NOT null-terminated. Decoded integer objects will contain the decoded value + * in ->value. + * + * All memory is freed when the bencode_buffer_t object is destroyed. + */ +bencode_item_t *bencode_decode(bencode_buffer_t *buf, const char *s, int len); + +/* Identical to bencode_decode(), but returns successfully only if the type of the decoded object match + * "expect". */ +static inline bencode_item_t *bencode_decode_expect(bencode_buffer_t *buf, const char *s, int len, bencode_type_t expect); + +/* Searches the given dictionary object for the given key and returns the respective value. Returns + * NULL if the given object isn't a dictionary or if the key doesn't exist. The key must be a + * null-terminated string. */ +static inline bencode_item_t *bencode_dictionary_get(bencode_item_t *dict, const char *key); + +/* Identical to bencode_dictionary_get() but doesn't require the key to be null-terminated. */ +bencode_item_t *bencode_dictionary_get_len(bencode_item_t *dict, const char *key, int key_len); + +/* Identical to bencode_dictionary_get() but returns the value only if its type is a string, and + * returns it as a pointer to the string itself. Returns NULL if the value is of some other type. The + * returned string is NOT null-terminated. Length of the string is returned in *len, which must be a + * valid pointer. The returned string will be valid until dict's bencode_buffer_t object is destroyed. */ +static inline const char *bencode_dictionary_get_string(bencode_item_t *dict, const char *key, int *len); + +/* Identical to bencode_dictionary_get() but returns the string in a newly allocated buffer (using the + * BENCODE_MALLOC function), which remains valid even after bencode_buffer_t is destroyed. */ +static inline char *bencode_dictionary_get_string_dup(bencode_item_t *dict, const char *key, int *len); + +/* Identical to bencode_dictionary_get_string() but expects an integer object. The parameter "defval" + * specified which value should be returned if the key is not found or if the value is not an integer. */ +static inline long long int bencode_dictionary_get_integer(bencode_item_t *dict, const char *key, long long int defval); + +/* Identical to bencode_dictionary_get(), but returns the object only if its type matches "expect". */ +static inline bencode_item_t *bencode_dictionary_get_expect(bencode_item_t *dict, const char *key, bencode_type_t expect); + + +/**************************/ + +static inline bencode_item_t *bencode_string(bencode_buffer_t *buf, const char *s) { + return bencode_string_len(buf, s, strlen(s)); +} + +static inline bencode_item_t *bencode_dictionary_add(bencode_item_t *dict, const char *key, bencode_item_t *val) { + if (!key) + return NULL; + return bencode_dictionary_add_len(dict, key, strlen(key), val); +} + +static inline bencode_item_t *bencode_dictionary_add_string(bencode_item_t *dict, const char *key, const char *val) { + if (!val) + return NULL; + return bencode_dictionary_add(dict, key, bencode_string(dict->buffer, val)); +} + +static inline bencode_item_t *bencode_dictionary_add_integer(bencode_item_t *dict, const char *key, long long int val) { + return bencode_dictionary_add(dict, key, bencode_integer(dict->buffer, val)); +} + +static inline bencode_item_t *bencode_list_add_string(bencode_item_t *list, const char *s) { + return bencode_list_add(list, bencode_string(list->buffer, s)); +} + +static inline bencode_item_t *bencode_dictionary_get(bencode_item_t *dict, const char *key) { + if (!key) + return NULL; + return bencode_dictionary_get_len(dict, key, strlen(key)); +} + +static inline const char *bencode_dictionary_get_string(bencode_item_t *dict, const char *key, int *len) { + bencode_item_t *val; + val = bencode_dictionary_get(dict, key); + if (!val || val->type != BENCODE_STRING) + return NULL; + *len = val->iov[1].iov_len; + return val->iov[1].iov_base; +} + +static inline char *bencode_dictionary_get_string_dup(bencode_item_t *dict, const char *key, int *len) { + const char *s; + char *ret; + s = bencode_dictionary_get_string(dict, key, len); + if (!s) + return NULL; + ret = BENCODE_MALLOC(*len); + if (!ret) + return NULL; + memcpy(ret, s, *len); + return ret; +} + +static inline long long int bencode_dictionary_get_integer(bencode_item_t *dict, const char *key, long long int defval) { + bencode_item_t *val; + val = bencode_dictionary_get(dict, key); + if (!val || val->type != BENCODE_INTEGER) + return defval; + return val->value; +} + +static inline bencode_item_t *bencode_decode_expect(bencode_buffer_t *buf, const char *s, int len, bencode_type_t expect) { + bencode_item_t *ret; + ret = bencode_decode(buf, s, len); + if (!ret || ret->type != expect) + return NULL; + return ret; +} + +static inline bencode_item_t *bencode_dictionary_get_expect(bencode_item_t *dict, const char *key, bencode_type_t expect) { + bencode_item_t *ret; + ret = bencode_dictionary_get(dict, key); + if (!ret || ret->type != expect) + return NULL; + return ret; +} + +#endif