From acdabb805db163109f4299d31224fd5667c27dc1 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Thu, 31 Jan 2013 18:08:43 +0000 Subject: [PATCH] sync bencode from mediaproxy --- modules/rtpproxy-ng/bencode.c | 243 ++++++++++++++++++++++++++-------- modules/rtpproxy-ng/bencode.h | 63 +++++++-- 2 files changed, 235 insertions(+), 71 deletions(-) diff --git a/modules/rtpproxy-ng/bencode.c b/modules/rtpproxy-ng/bencode.c index fb6f8a9a8..66a5e8cec 100644 --- a/modules/rtpproxy-ng/bencode.c +++ b/modules/rtpproxy-ng/bencode.c @@ -10,6 +10,8 @@ /* set to 0 for alloc debugging, e.g. through valgrind */ #define BENCODE_MIN_BUFFER_PIECE_LEN 512 +#define BENCODE_HASH_BUCKETS 31 /* prime numbers work best */ + struct __bencode_buffer_piece { char *tail; unsigned int left; @@ -18,8 +20,12 @@ struct __bencode_buffer_piece { }; struct __bencode_free_list { void *ptr; + free_func_t func; struct __bencode_free_list *next; }; +struct __bencode_hash { + struct bencode_item *buckets[BENCODE_HASH_BUCKETS]; +}; @@ -52,13 +58,14 @@ static void __bencode_container_init(bencode_item_t *cont) { cont->str_len = 2; } -static void bencode_dictionary_init(bencode_item_t *dict) { +static void __bencode_dictionary_init(bencode_item_t *dict) { dict->type = BENCODE_DICTIONARY; dict->iov[0].iov_base = "d"; + dict->value = 0; __bencode_container_init(dict); } -static void bencode_list_init(bencode_item_t *list) { +static void __bencode_list_init(bencode_item_t *list) { list->type = BENCODE_LIST; list->iov[0].iov_base = "l"; __bencode_container_init(list); @@ -123,7 +130,7 @@ void bencode_buffer_free(bencode_buffer_t *buf) { struct __bencode_buffer_piece *piece, *next; for (fl = buf->free_list; fl; fl = fl->next) - BENCODE_FREE(fl->ptr); + fl->func(fl->ptr); for (piece = buf->pieces; piece; piece = next) { next = piece->next; @@ -148,7 +155,7 @@ bencode_item_t *bencode_dictionary(bencode_buffer_t *buf) { ret = __bencode_item_alloc(buf, 0); if (!ret) return NULL; - bencode_dictionary_init(ret); + __bencode_dictionary_init(ret); return ret; } @@ -158,7 +165,7 @@ bencode_item_t *bencode_list(bencode_buffer_t *buf) { ret = __bencode_item_alloc(buf, 0); if (!ret) return NULL; - bencode_list_init(ret); + __bencode_list_init(ret); return ret; } @@ -182,27 +189,47 @@ static void __bencode_container_add(bencode_item_t *parent, bencode_item_t *chil } } -bencode_item_t *bencode_string_len(bencode_buffer_t *buf, const char *s, int len) { +static bencode_item_t *__bencode_string_alloc(bencode_buffer_t *buf, const void *base, + int str_len, int iov_len, int iov_cnt, bencode_type_t type) +{ bencode_item_t *ret; int len_len; - assert((len <= 99999) && (len >= 0)); - ret = __bencode_item_alloc(buf, strlen(s) + 7); + assert((str_len <= 99999) && (str_len >= 0)); + ret = __bencode_item_alloc(buf, 7); if (!ret) return NULL; - len_len = sprintf(ret->__buf, "%d:", len); + len_len = sprintf(ret->__buf, "%d:", str_len); - ret->type = BENCODE_STRING; + ret->type = type; 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; + ret->iov[1].iov_base = (void *) base; + ret->iov[1].iov_len = iov_len; + ret->iov_cnt = iov_cnt + 1; + ret->str_len = len_len + str_len; return ret; } +bencode_item_t *bencode_string_len(bencode_buffer_t *buf, const char *s, int len) { + return __bencode_string_alloc(buf, s, len, len, 1, BENCODE_STRING); +} + +bencode_item_t *bencode_string_iovec(bencode_buffer_t *buf, const struct iovec *iov, int iov_cnt, int str_len) { + int i; + + if (iov_cnt < 0) + return NULL; + if (str_len < 0) { + str_len = 0; + for (i = 0; i < iov_cnt; i++) + str_len += iov[i].iov_len; + } + + return __bencode_string_alloc(buf, iov, str_len, iov_cnt, iov_cnt, BENCODE_IOVEC); +} + bencode_item_t *bencode_integer(bencode_buffer_t *buf, long long int i) { bencode_item_t *ret; int alen, rlen; @@ -252,12 +279,20 @@ bencode_item_t *bencode_list_add(bencode_item_t *list, bencode_item_t *item) { return item; } +static int __bencode_iovec_cpy(struct iovec *out, const struct iovec *in, int num) { + out -= num; + memcpy(out, in, num * sizeof(*out)); + return num; +} + 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]; + if (item->type == BENCODE_IOVEC) + out -= __bencode_iovec_cpy(out, item->iov[1].iov_base, item->iov[1].iov_len); + else if (item->iov[1].iov_base) + out -= __bencode_iovec_cpy(out, &item->iov[1], 1); child = item->child; while (child) { @@ -266,20 +301,32 @@ static int __bencode_iovec_dump_rev(struct iovec *out, bencode_item_t *item) { } assert(item->iov[0].iov_base != NULL); - *--out = item->iov[0]; + out -= __bencode_iovec_cpy(out, &item->iov[0], 1); assert((orig - out) == item->iov_cnt); return item->iov_cnt; } +static int __bencode_str_cpy(char *out, const struct iovec *in, int num) { + char *orig = out; + + in += num; + while (--num >= 0) { + in--; + out -= in->iov_len; + memcpy(out, in->iov_base, in->iov_len); + } + return orig - out; +} + 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); - } + if (item->type == BENCODE_IOVEC) + out -= __bencode_str_cpy(out, item->iov[1].iov_base, item->iov[1].iov_len); + else if (item->iov[1].iov_base) + out -= __bencode_str_cpy(out, &item->iov[1], 1); child = item->child; while (child) { @@ -288,8 +335,7 @@ static int __bencode_str_dump_rev(char *out, bencode_item_t *item) { } 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); + out -= __bencode_str_cpy(out, &item->iov[0], 1); assert((orig - out) == item->str_len); return item->str_len; @@ -369,44 +415,96 @@ char *bencode_collapse_dup(bencode_item_t *root, int *len) { return ret; } -static bencode_item_t *bencode_decode_dictionary(bencode_buffer_t *buf, const char *s, const char *end) { - bencode_item_t *ret, *item; +static unsigned int __bencode_hash_str_len(const unsigned char *s, int len) { + unsigned long *ul; + unsigned int *ui; + unsigned short *us; + + if (len >= sizeof(*ul)) { + ul = (void *) s; + return *ul % BENCODE_HASH_BUCKETS; + } + if (len >= sizeof(*ui)) { + ui = (void *) s; + return *ui % BENCODE_HASH_BUCKETS; + } + if (len >= sizeof(*us)) { + us = (void *) s; + return *us % BENCODE_HASH_BUCKETS; + } + if (len >= sizeof(*s)) + return *s % BENCODE_HASH_BUCKETS; + + return 0; +} + +static unsigned int __bencode_hash_str(bencode_item_t *str) { + assert(str->type == BENCODE_STRING); + return __bencode_hash_str_len(str->iov[1].iov_base, str->iov[1].iov_len); +} + +static void __bencode_hash_insert(bencode_item_t *key, bencode_item_t *value, struct __bencode_hash *hash) { + unsigned int bucket, i; + + i = bucket = __bencode_hash_str(key); + + while (1) { + if (!hash->buckets[i]) { + hash->buckets[i] = value; + break; + } + i++; + if (i >= BENCODE_HASH_BUCKETS) + i = 0; + if (i == bucket) + break; + } +} + +static bencode_item_t *__bencode_decode_dictionary(bencode_buffer_t *buf, const char *s, const char *end) { + bencode_item_t *ret, *key, *value; + struct __bencode_hash *hash; if (*s != 'd') return NULL; s++; - ret = __bencode_item_alloc(buf, 0); + ret = __bencode_item_alloc(buf, sizeof(*hash)); if (!ret) return NULL; - bencode_dictionary_init(ret); + __bencode_dictionary_init(ret); + ret->value = 1; + hash = (void *) ret->__buf; + memset(hash, 0, sizeof(*hash)); while (s < end) { - item = __bencode_decode(buf, s, end); - if (!item) + key = __bencode_decode(buf, s, end); + if (!key) return NULL; - s += item->str_len; - if (item->type == BENCODE_END_MARKER) + s += key->str_len; + if (key->type == BENCODE_END_MARKER) break; - if (item->type != BENCODE_STRING) + if (key->type != BENCODE_STRING) return NULL; - __bencode_container_add(ret, item); + __bencode_container_add(ret, key); if (s >= end) return NULL; - item = __bencode_decode(buf, s, end); - if (!item) + value = __bencode_decode(buf, s, end); + if (!value) return NULL; - s += item->str_len; - if (item->type == BENCODE_END_MARKER) + s += value->str_len; + if (value->type == BENCODE_END_MARKER) return NULL; - __bencode_container_add(ret, item); + __bencode_container_add(ret, value); + + __bencode_hash_insert(key, value, hash); } return ret; } -static bencode_item_t *bencode_decode_list(bencode_buffer_t *buf, const char *s, const char *end) { +static bencode_item_t *__bencode_decode_list(bencode_buffer_t *buf, const char *s, const char *end) { bencode_item_t *ret, *item; if (*s != 'l') @@ -416,7 +514,7 @@ static bencode_item_t *bencode_decode_list(bencode_buffer_t *buf, const char *s, ret = __bencode_item_alloc(buf, 0); if (!ret) return NULL; - bencode_list_init(ret); + __bencode_list_init(ret); while (s < end) { item = __bencode_decode(buf, s, end); @@ -431,7 +529,7 @@ static bencode_item_t *bencode_decode_list(bencode_buffer_t *buf, const char *s, return ret; } -static bencode_item_t *bencode_decode_integer(bencode_buffer_t *buf, const char *s, const char *end) { +static bencode_item_t *__bencode_decode_integer(bencode_buffer_t *buf, const char *s, const char *end) { long long int i; const char *orig = s; char *convend; @@ -477,7 +575,7 @@ done: return ret; } -static bencode_item_t *bencode_decode_string(bencode_buffer_t *buf, const char *s, const char *end) { +static bencode_item_t *__bencode_decode_string(bencode_buffer_t *buf, const char *s, const char *end) { unsigned long int sl; char *convend; const char *orig = s; @@ -524,11 +622,11 @@ static bencode_item_t *__bencode_decode(bencode_buffer_t *buf, const char *s, co switch (*s) { case 'd': - return bencode_decode_dictionary(buf, s, end); + return __bencode_decode_dictionary(buf, s, end); case 'l': - return bencode_decode_list(buf, s, end); + return __bencode_decode_list(buf, s, end); case 'i': - return bencode_decode_integer(buf, s, end); + return __bencode_decode_integer(buf, s, end); case 'e': return &__bencode_end_marker; case '0': @@ -541,7 +639,7 @@ static bencode_item_t *__bencode_decode(bencode_buffer_t *buf, const char *s, co case '7': case '8': case '9': - return bencode_decode_string(buf, s, end); + return __bencode_decode_string(buf, s, end); default: return NULL; } @@ -553,32 +651,60 @@ bencode_item_t *bencode_decode(bencode_buffer_t *buf, const char *s, int len) { } -/* XXX inefficient, use a proper hash instead */ +static bencode_item_t *__bencode_dictionary_key_test(bencode_item_t *val, const char *keystr, int keylen) { + bencode_item_t *key; + + key = val->sibling; + assert(key != NULL); + assert(key->type == BENCODE_STRING); + + if (keylen != key->iov[1].iov_len) + return key; + if (memcmp(keystr, key->iov[1].iov_base, keylen)) + return key; + + return NULL; +} + bencode_item_t *bencode_dictionary_get_len(bencode_item_t *dict, const char *keystr, int keylen) { bencode_item_t *key, *val; + unsigned int bucket, i; + struct __bencode_hash *hash; 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; + /* try hash lookup first if possible */ + if (dict->value == 1) { + hash = (void *) dict->__buf; + i = bucket = __bencode_hash_str_len((const unsigned char *) keystr, keylen); + while (1) { + val = hash->buckets[i]; + if (!val) + return NULL; /* would be there, but isn't */ + key = __bencode_dictionary_key_test(val, keystr, keylen); + if (!key) + return val; + i++; + if (i >= BENCODE_HASH_BUCKETS) + i = 0; + if (i == bucket) + break; /* fall back to regular lookup */ + } + } - return val; + for (val = dict->child; val; val = key->sibling) { + key = __bencode_dictionary_key_test(val, keystr, keylen); + if (!key) + return val; } return NULL; } -void bencode_buffer_freelist_add(bencode_buffer_t *buf, void *p) { +void bencode_buffer_destroy_add(bencode_buffer_t *buf, free_func_t func, void *p) { struct __bencode_free_list *li; if (!p) @@ -587,6 +713,7 @@ void bencode_buffer_freelist_add(bencode_buffer_t *buf, void *p) { if (!li) return; li->ptr = p; + li->func = func; li->next = buf->free_list; buf->free_list = li; } diff --git a/modules/rtpproxy-ng/bencode.h b/modules/rtpproxy-ng/bencode.h index 3c6ba750a..6111849f7 100644 --- a/modules/rtpproxy-ng/bencode.h +++ b/modules/rtpproxy-ng/bencode.h @@ -30,6 +30,7 @@ struct __bencode_free_list; typedef enum bencode_type bencode_type_t; typedef struct bencode_buffer bencode_buffer_t; typedef struct bencode_item bencode_item_t; +typedef void (*free_func_t)(void *); enum bencode_type { BENCODE_INVALID = 0, @@ -37,6 +38,7 @@ enum bencode_type { BENCODE_INTEGER, /* long long int */ BENCODE_LIST, /* flat list of other objects */ BENCODE_DICTIONARY, /* dictionary of key/values pairs. keys are always strings */ + BENCODE_IOVEC, /* special case of a string, built through bencode_string_iovec() */ BENCODE_END_MARKER, /* used internally only */ }; @@ -45,7 +47,7 @@ struct bencode_item { 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 */ + long long int value; /* when decoding an integer, contains the value; otherwise used internally */ bencode_item_t *parent, *child, *sibling; bencode_buffer_t *buffer; char __buf[0]; @@ -54,7 +56,7 @@ struct bencode_item { struct bencode_buffer { struct __bencode_buffer_piece *pieces; struct __bencode_free_list *free_list; - int error:1; + int error:1; /* set to !0 if allocation failed at any point */ }; @@ -84,7 +86,11 @@ bencode_item_t *bencode_list(bencode_buffer_t *buf); /* Adds a pointer to the bencode_buffer_t object's internal free list. When the bencode_buffer_t * object is destroyed, BENCODE_FREE will be called on this pointer. */ -void bencode_buffer_freelist_add(bencode_buffer_t *buf, void *); +static inline void bencode_buffer_freelist_add(bencode_buffer_t *buf, void *); + +/* Similar to bencode_buffer_freelist_add(), but instead of freeing/destroying the object at the + * given pointer with BENCODE_FREE, the specified destroy function is called. */ +void bencode_buffer_destroy_add(bencode_buffer_t *buf, free_func_t, void *); @@ -110,6 +116,11 @@ static inline bencode_item_t *bencode_dictionary_add_string(bencode_item_t *dict /* Ditto, but for a "str" object */ static inline bencode_item_t *bencode_dictionary_add_str(bencode_item_t *dict, const char *key, const str *val); +/* Ditto, but adds a string created through an iovec array to the dictionary. See + * bencode_string_iovec(). */ +static inline bencode_item_t *bencode_dictionary_add_iovec(bencode_item_t *dict, const char *key, + const struct iovec *iov, int iov_cnt, int str_len); + /* Ditto again, but adds the str object (val) to the bencode_buffer_t's internal free list. When * the bencode_item_t object is destroyed, BENCODE_FREE will be called on this pointer. */ static inline bencode_item_t *bencode_dictionary_add_str_free(bencode_item_t *dict, const char *key, str *val); @@ -130,6 +141,12 @@ 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); + + + + +/*** STRING BUILDING & HANDLING ***/ + /* 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. @@ -137,24 +154,26 @@ static inline bencode_item_t *bencode_list_add_string(bencode_item_t *list, cons * the complete document is finally encoded or sent out. */ bencode_item_t *bencode_string_len(bencode_buffer_t *buf, const char *s, int len); - - - - -/*** STRING BUILDING & HANDLING ***/ - /* 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); -/* Convenience function to compare a string object to a regular C string. Returns 2 if object - * isn't a string object, otherwise returns according to strcmp(). */ -static inline int bencode_strcmp(bencode_item_t *a, const char *b); - /* Creates a new byte-string object from a "str" object. The string does not have to be null- * terminated. */ static inline bencode_item_t *bencode_str(bencode_buffer_t *buf, const str *s); +/* Creates a new byte-string object from an iovec array. The created object has different internal + * semantics (not a BENCODE_STRING, but a BENCODE_IOVEC) and must not be treated like other string + * objects. The array pointer and contents must still be valid and accessible when the complete + * document is encoded. The full length of the string composed of the iovec array is given in the + * "str_len" parameter, which can be negative, in which case the array is iterated to calculate the + * length. */ +bencode_item_t *bencode_string_iovec(bencode_buffer_t *buf, const struct iovec *iov, int iov_cnt, int str_len); + +/* Convenience function to compare a string object to a regular C string. Returns 2 if object + * isn't a string object, otherwise returns according to strcmp(). */ +static inline int bencode_strcmp(bencode_item_t *a, const char *b); + /* Converts the string object "in" into a str object "out". Returns "out" on success, or NULL on * error ("in" was NULL or not a string object). */ static inline str *bencode_get_str(bencode_item_t *in, str *out); @@ -245,6 +264,14 @@ char *bencode_collapse_dup(bencode_item_t *root, int *len); * 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. * + * However, to access children objects of dictionaries, the special functions following the naming + * scheme bencode_dictionary_get_* below should be used. They perform key lookup through a simple + * hash built into the dictionary object and so perform the lookup much faster. Only dictionaries + * created through a decoding process (i.e. not ones created from bencode_dictionary()) have this + * property. The hash is efficient only up to a certain number of elements (BENCODE_HASH_BUCKETS + * in bencode.c) contained in the dictionary. If the number of children object exceeds this number, + * key lookup will be slower than simply linearily traversing the list. + * * 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. * @@ -452,4 +479,14 @@ static inline str *bencode_get_str(bencode_item_t *in, str *out) { return out; } +static inline void bencode_buffer_freelist_add(bencode_buffer_t *buf, void *p) { + bencode_buffer_destroy_add(buf, BENCODE_FREE, p); +} + +static inline bencode_item_t *bencode_dictionary_add_iovec(bencode_item_t *dict, const char *key, + const struct iovec *iov, int iov_cnt, int str_len) +{ + return bencode_dictionary_add(dict, key, bencode_string_iovec(dict->buffer, iov, iov_cnt, str_len)); +} + #endif