summaryrefslogtreecommitdiff
path: root/bittorrent/bittorrent/libs/mbtbe/src/decode.c
diff options
context:
space:
mode:
Diffstat (limited to 'bittorrent/bittorrent/libs/mbtbe/src/decode.c')
-rw-r--r--bittorrent/bittorrent/libs/mbtbe/src/decode.c220
1 files changed, 220 insertions, 0 deletions
diff --git a/bittorrent/bittorrent/libs/mbtbe/src/decode.c b/bittorrent/bittorrent/libs/mbtbe/src/decode.c
new file mode 100644
index 0000000..875f0db
--- /dev/null
+++ b/bittorrent/bittorrent/libs/mbtbe/src/decode.c
@@ -0,0 +1,220 @@
+#include <mbt/be/bencode.h>
+#include <mbt/utils/utils.h>
+#include <mbt/utils/view.h>
+
+// libc
+#include <ctype.h>
+#include <stdlib.h>
+
+#define MBT_INT64_MAX_ULL 9223372036854775807LLU
+
+static inline bool mbt_be_consume(struct mbt_cview *buf, char expected);
+static inline bool mbt_be_parse_u64(struct mbt_cview *buf, uint64_t *val,
+ char end);
+static inline bool mbt_be_parse_i64(struct mbt_cview *buf, int64_t *val);
+static inline bool mbt_be_parse_str(struct mbt_cview *buf, struct mbt_str *str);
+static inline struct mbt_be_node *mbt_be_decode_rec(struct mbt_cview *buf);
+static inline struct mbt_be_node *mbt_be_decode_num(struct mbt_cview *buf);
+static inline struct mbt_be_node *mbt_be_decode_str(struct mbt_cview *buf);
+static inline struct mbt_be_node *mbt_be_decode_list(struct mbt_cview *buf);
+static inline struct mbt_be_node *mbt_be_decode_dict(struct mbt_cview *buf);
+
+struct mbt_be_node *mbt_be_decode(struct mbt_cview *buf)
+{
+ struct mbt_cview copy = *buf;
+ struct mbt_be_node *res = mbt_be_decode_rec(&copy);
+ if (!res)
+ return NULL;
+ *buf = copy;
+ return res;
+}
+
+static inline bool mbt_be_consume(struct mbt_cview *buf, char expected)
+{
+ if (!buf->size || *buf->data != expected)
+ return false;
+ *buf = MBT_CVIEW_SUB(*buf, 1);
+ return true;
+}
+
+static inline bool mbt_be_parse_u64(struct mbt_cview *buf, uint64_t *val,
+ char end)
+{
+ uint64_t v = 0;
+ if (!buf->size || *buf->data == end
+ || (*buf->data == '0' && (buf->size == 1 || buf->data[1] != end)))
+ return false;
+ for (; !mbt_be_consume(buf, end); *buf = MBT_CVIEW_SUB(*buf, 1))
+ {
+ if (!buf->size || !isdigit(*buf->data))
+ return false;
+ if (__builtin_mul_overflow(v, 10, &v))
+ return false;
+ unsigned char c = *buf->data - '0';
+ if (__builtin_add_overflow(v, c, &v))
+ return false;
+ }
+ *val = v;
+ return true;
+}
+
+static inline bool mbt_be_parse_i64(struct mbt_cview *buf, int64_t *val)
+{
+ bool neg = mbt_be_consume(buf, '-');
+ uint64_t v;
+ if (!mbt_be_parse_u64(buf, &v, 'e'))
+ return false;
+ if ((v == 0 && neg) || v > MBT_INT64_MAX_ULL + (neg ? 1ull : 0ull))
+ return false;
+ if (v == MBT_INT64_MAX_ULL + 1ull)
+ *val = INT64_MIN;
+ else if (neg)
+ *val = -v;
+ else
+ *val = v;
+ return true;
+}
+
+static inline bool mbt_be_parse_str(struct mbt_cview *buf, struct mbt_str *str)
+{
+ uint64_t len;
+ if (!mbt_be_parse_u64(buf, &len, ':'))
+ return false;
+
+ if (!mbt_str_pushcv(str, MBT_CVIEW(buf->data, len)))
+ return false;
+
+ *buf = MBT_CVIEW_SUB(*buf, len);
+ return true;
+}
+
+static inline struct mbt_be_node *mbt_be_decode_rec(struct mbt_cview *buf)
+{
+ if (!buf->size)
+ return NULL;
+ switch (*buf->data)
+ {
+ case 'i':
+ *buf = MBT_CVIEW_SUB(*buf, 1);
+ return mbt_be_decode_num(buf);
+ case 'd':
+ *buf = MBT_CVIEW_SUB(*buf, 1);
+ return mbt_be_decode_dict(buf);
+ case 'l':
+ *buf = MBT_CVIEW_SUB(*buf, 1);
+ return mbt_be_decode_list(buf);
+ default:
+ return mbt_be_decode_str(buf);
+ }
+}
+
+static inline struct mbt_be_node *mbt_be_decode_num(struct mbt_cview *buf)
+{
+ int64_t val;
+ if (!mbt_be_parse_i64(buf, &val))
+ return NULL;
+
+ return mbt_be_num_init(val);
+}
+
+static inline struct mbt_be_node *mbt_be_decode_str(struct mbt_cview *buf)
+{
+ struct mbt_be_node *bn = mbt_be_str_init(MBT_CVIEW(NULL, 0));
+ if (!bn)
+ return NULL;
+
+ if (!mbt_be_parse_str(buf, &bn->v.str))
+ {
+ mbt_be_free(bn);
+ return NULL;
+ }
+
+ return bn;
+}
+
+static inline struct mbt_be_node *mbt_be_decode_list(struct mbt_cview *buf)
+{
+ struct mbt_be_node *bn = mbt_be_list_init(NULL);
+ if (!bn)
+ return NULL;
+ size_t nb_elements = 0;
+ bn->v.list = calloc(1, sizeof(struct mbt_be_node *));
+ if (!bn->v.list)
+ {
+ free(bn);
+ return NULL;
+ }
+
+ for (; !mbt_be_consume(buf, 'e'); ++nb_elements)
+ {
+ struct mbt_be_node **ptr = realloc(
+ bn->v.list, (nb_elements + 2) * sizeof(struct mbt_be_node *));
+ if (!bn->v.list)
+ {
+ mbt_be_free(bn);
+ return NULL;
+ }
+ bn->v.list = ptr;
+ bn->v.list[nb_elements + 1] = NULL;
+
+ bn->v.list[nb_elements] = mbt_be_decode_rec(buf);
+ if (!bn->v.list[nb_elements])
+ {
+ mbt_be_free(bn);
+ return NULL;
+ }
+ }
+
+ bn->v.list[nb_elements] = NULL;
+ return bn;
+}
+
+static inline struct mbt_be_node *mbt_be_decode_dict(struct mbt_cview *buf)
+{
+ struct mbt_be_node *bn = mbt_be_dict_init(NULL);
+ if (!bn)
+ return NULL;
+
+ size_t nb_elements = 0;
+ bn->v.dict = calloc(1, sizeof(struct mbt_be_pair *));
+ if (!bn->v.dict)
+ {
+ free(bn);
+ return NULL;
+ }
+
+ for (; !mbt_be_consume(buf, 'e'); ++nb_elements)
+ {
+ struct mbt_be_pair **ptr = realloc(
+ bn->v.dict, (nb_elements + 2) * sizeof(struct mbt_be_pair *));
+ if (!ptr)
+ {
+ mbt_be_free(bn);
+ return NULL;
+ }
+ bn->v.dict = ptr;
+ bn->v.dict[nb_elements + 1] = NULL;
+
+ bn->v.dict[nb_elements] = calloc(1, sizeof(**ptr));
+ if (!bn->v.dict[nb_elements])
+ {
+ mbt_be_free(bn);
+ return NULL;
+ }
+
+ if (!mbt_be_parse_str(buf, &bn->v.dict[nb_elements]->key))
+ {
+ mbt_be_free(bn);
+ return NULL;
+ }
+
+ bn->v.dict[nb_elements]->val = mbt_be_decode_rec(buf);
+ if (!bn->v.dict[nb_elements]->val)
+ {
+ mbt_be_free(bn);
+ return NULL;
+ }
+ }
+
+ return bn;
+}