sync bencode from mediaproxy

pull/1/head
Richard Fuchs 13 years ago
parent b16af9144f
commit acdabb805d

@ -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;
}

@ -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

Loading…
Cancel
Save