parent
5c310916a2
commit
8708c99e33
@ -0,0 +1,561 @@
|
||||
#include <stdio.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
@ -0,0 +1,284 @@
|
||||
#ifndef _BENCODE_H_
|
||||
#define _BENCODE_H_
|
||||
|
||||
#include <sys/uio.h>
|
||||
#include <string.h>
|
||||
|
||||
#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
|
Loading…
Reference in new issue