You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
265 lines
7.2 KiB
265 lines
7.2 KiB
#include <cassert>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <functional>
|
|
#include <string>
|
|
|
|
#include "msgpack.h"
|
|
|
|
namespace msgpack {
|
|
|
|
[[noreturn]] void internal_error() {
|
|
printf("internal error\n");
|
|
exit(1);
|
|
}
|
|
|
|
const char *type_name(type ty) {
|
|
switch (ty) {
|
|
#define X(NAME, WIDTH, PAYLOAD, LOWER, UPPER) \
|
|
case NAME: \
|
|
return #NAME;
|
|
#include "msgpack.def"
|
|
#undef X
|
|
}
|
|
internal_error();
|
|
}
|
|
|
|
unsigned bytes_used_fixed(msgpack::type ty) {
|
|
using namespace msgpack;
|
|
switch (ty) {
|
|
#define X(NAME, WIDTH, PAYLOAD, LOWER, UPPER) \
|
|
case NAME: \
|
|
return WIDTH;
|
|
#include "msgpack.def"
|
|
#undef X
|
|
}
|
|
internal_error();
|
|
}
|
|
|
|
msgpack::type parse_type(unsigned char x) {
|
|
|
|
#define X(NAME, WIDTH, PAYLOAD, LOWER, UPPER) \
|
|
if (x >= LOWER && x <= UPPER) { \
|
|
return NAME; \
|
|
} else
|
|
#include "msgpack.def"
|
|
#undef X
|
|
{ internal_error(); }
|
|
}
|
|
|
|
template <typename T, typename R> R bitcast(T x) {
|
|
static_assert(sizeof(T) == sizeof(R), "");
|
|
R tmp;
|
|
memcpy(&tmp, &x, sizeof(T));
|
|
return tmp;
|
|
}
|
|
template int64_t bitcast<uint64_t, int64_t>(uint64_t);
|
|
} // namespace msgpack
|
|
|
|
// Helper functions for reading additional payload from the header
|
|
// Depending on the type, this can be a number of bytes, elements,
|
|
// key-value pairs or an embedded integer.
|
|
// Each takes a pointer to the start of the header and returns a uint64_t
|
|
|
|
namespace {
|
|
namespace payload {
|
|
uint64_t read_zero(const unsigned char *) { return 0; }
|
|
|
|
// Read the first byte and zero/sign extend it
|
|
uint64_t read_embedded_u8(const unsigned char *start) { return start[0]; }
|
|
uint64_t read_embedded_s8(const unsigned char *start) {
|
|
int64_t res = msgpack::bitcast<uint8_t, int8_t>(start[0]);
|
|
return msgpack::bitcast<int64_t, uint64_t>(res);
|
|
}
|
|
|
|
// Read a masked part of the first byte
|
|
uint64_t read_via_mask_0x1(const unsigned char *start) { return *start & 0x1u; }
|
|
uint64_t read_via_mask_0xf(const unsigned char *start) { return *start & 0xfu; }
|
|
uint64_t read_via_mask_0x1f(const unsigned char *start) {
|
|
return *start & 0x1fu;
|
|
}
|
|
|
|
// Read 1/2/4/8 bytes immediately following the type byte and zero/sign extend
|
|
// Big endian format.
|
|
uint64_t read_size_field_u8(const unsigned char *from) {
|
|
from++;
|
|
return from[0];
|
|
}
|
|
|
|
// TODO: detect whether host is little endian or not, and whether the intrinsic
|
|
// is available. And probably use the builtin to test the diy
|
|
const bool use_bswap = false;
|
|
|
|
uint64_t read_size_field_u16(const unsigned char *from) {
|
|
from++;
|
|
if (use_bswap) {
|
|
uint16_t b;
|
|
memcpy(&b, from, 2);
|
|
return __builtin_bswap16(b);
|
|
} else {
|
|
return (from[0] << 8u) | from[1];
|
|
}
|
|
}
|
|
uint64_t read_size_field_u32(const unsigned char *from) {
|
|
from++;
|
|
if (use_bswap) {
|
|
uint32_t b;
|
|
memcpy(&b, from, 4);
|
|
return __builtin_bswap32(b);
|
|
} else {
|
|
return (from[0] << 24u) | (from[1] << 16u) | (from[2] << 8u) |
|
|
(from[3] << 0u);
|
|
}
|
|
}
|
|
uint64_t read_size_field_u64(const unsigned char *from) {
|
|
from++;
|
|
if (use_bswap) {
|
|
uint64_t b;
|
|
memcpy(&b, from, 8);
|
|
return __builtin_bswap64(b);
|
|
} else {
|
|
return ((uint64_t)from[0] << 56u) | ((uint64_t)from[1] << 48u) |
|
|
((uint64_t)from[2] << 40u) | ((uint64_t)from[3] << 32u) |
|
|
(from[4] << 24u) | (from[5] << 16u) | (from[6] << 8u) |
|
|
(from[7] << 0u);
|
|
}
|
|
}
|
|
|
|
uint64_t read_size_field_s8(const unsigned char *from) {
|
|
uint8_t u = read_size_field_u8(from);
|
|
int64_t res = msgpack::bitcast<uint8_t, int8_t>(u);
|
|
return msgpack::bitcast<int64_t, uint64_t>(res);
|
|
}
|
|
uint64_t read_size_field_s16(const unsigned char *from) {
|
|
uint16_t u = read_size_field_u16(from);
|
|
int64_t res = msgpack::bitcast<uint16_t, int16_t>(u);
|
|
return msgpack::bitcast<int64_t, uint64_t>(res);
|
|
}
|
|
uint64_t read_size_field_s32(const unsigned char *from) {
|
|
uint32_t u = read_size_field_u32(from);
|
|
int64_t res = msgpack::bitcast<uint32_t, int32_t>(u);
|
|
return msgpack::bitcast<int64_t, uint64_t>(res);
|
|
}
|
|
uint64_t read_size_field_s64(const unsigned char *from) {
|
|
uint64_t u = read_size_field_u64(from);
|
|
int64_t res = msgpack::bitcast<uint64_t, int64_t>(u);
|
|
return msgpack::bitcast<int64_t, uint64_t>(res);
|
|
}
|
|
} // namespace payload
|
|
} // namespace
|
|
|
|
namespace msgpack {
|
|
|
|
payload_info_t payload_info(msgpack::type ty) {
|
|
using namespace msgpack;
|
|
switch (ty) {
|
|
#define X(NAME, WIDTH, PAYLOAD, LOWER, UPPER) \
|
|
case NAME: \
|
|
return payload::PAYLOAD;
|
|
#include "msgpack.def"
|
|
#undef X
|
|
}
|
|
internal_error();
|
|
}
|
|
|
|
} // namespace msgpack
|
|
|
|
const unsigned char *msgpack::skip_next_message(const unsigned char *start,
|
|
const unsigned char *end) {
|
|
class f : public functors_defaults<f> {};
|
|
return handle_msgpack({start, end}, f());
|
|
}
|
|
|
|
namespace msgpack {
|
|
bool message_is_string(byte_range bytes, const char *needle) {
|
|
bool matched = false;
|
|
size_t needleN = strlen(needle);
|
|
|
|
foronly_string(bytes, [=, &matched](size_t N, const unsigned char *str) {
|
|
if (N == needleN) {
|
|
if (memcmp(needle, str, N) == 0) {
|
|
matched = true;
|
|
}
|
|
}
|
|
});
|
|
return matched;
|
|
}
|
|
|
|
void dump(byte_range bytes) {
|
|
struct inner : functors_defaults<inner> {
|
|
inner(unsigned indent) : indent(indent) {}
|
|
const unsigned by = 2;
|
|
unsigned indent = 0;
|
|
|
|
void handle_string(size_t N, const unsigned char *bytes) {
|
|
char *tmp = (char *)malloc(N + 1);
|
|
memcpy(tmp, bytes, N);
|
|
tmp[N] = '\0';
|
|
printf("\"%s\"", tmp);
|
|
free(tmp);
|
|
}
|
|
|
|
void handle_signed(int64_t x) { printf("%ld", x); }
|
|
void handle_unsigned(uint64_t x) { printf("%lu", x); }
|
|
|
|
const unsigned char *handle_array(uint64_t N, byte_range bytes) {
|
|
printf("\n%*s[\n", indent, "");
|
|
indent += by;
|
|
|
|
for (uint64_t i = 0; i < N; i++) {
|
|
indent += by;
|
|
printf("%*s", indent, "");
|
|
const unsigned char *next = handle_msgpack<inner>(bytes, {indent});
|
|
printf(",\n");
|
|
indent -= by;
|
|
bytes.start = next;
|
|
if (!next) {
|
|
break;
|
|
}
|
|
}
|
|
indent -= by;
|
|
printf("%*s]", indent, "");
|
|
|
|
return bytes.start;
|
|
}
|
|
|
|
const unsigned char *handle_map(uint64_t N, byte_range bytes) {
|
|
printf("\n%*s{\n", indent, "");
|
|
indent += by;
|
|
|
|
for (uint64_t i = 0; i < 2 * N; i += 2) {
|
|
const unsigned char *start_key = bytes.start;
|
|
printf("%*s", indent, "");
|
|
const unsigned char *end_key =
|
|
handle_msgpack<inner>({start_key, bytes.end}, {indent});
|
|
if (!end_key) {
|
|
break;
|
|
}
|
|
|
|
printf(" : ");
|
|
|
|
const unsigned char *start_value = end_key;
|
|
const unsigned char *end_value =
|
|
handle_msgpack<inner>({start_value, bytes.end}, {indent});
|
|
|
|
if (!end_value) {
|
|
break;
|
|
}
|
|
|
|
printf(",\n");
|
|
bytes.start = end_value;
|
|
}
|
|
|
|
indent -= by;
|
|
printf("%*s}", indent, "");
|
|
|
|
return bytes.start;
|
|
}
|
|
};
|
|
|
|
handle_msgpack<inner>(bytes, {0});
|
|
printf("\n");
|
|
}
|
|
|
|
} // namespace msgpack
|