drgn/libdrgn/serialize.c
Omar Sandoval 75c3679147 Rewrite drgn core in C
The current mixed Python/C implementation works well, but it has a
couple of important limitations:

- It's too slow for some common use cases, like iterating over large
  data structures.
- It can't be reused in utilities written in other languages.

This replaces the internals with a new library written in C, libdrgn. It
includes Python bindings with mostly the same public interface as
before, with some important improvements:

- Types are now represented by a single Type class rather than the messy
  polymorphism in the Python implementation.
- Qualifiers are a bitmask instead of a set of strings.
- Bit fields are not considered a separate type.
- The lvalue/rvalue terminology is replaced with reference/value.
- Structure, union, and array values are better supported.
- Function objects are supported.
- Program distinguishes between lookups of variables, constants, and
  functions.

The C rewrite is about 6x as fast as the original Python when using the
Python bindings, and about 8x when using the C API directly.

Currently, the exposed API in C is fairly conservative. In the future,
the memory reader, type index, and object index APIs will probably be
exposed for more flexibility.
2019-04-02 14:12:07 -07:00

97 lines
2.4 KiB
C

// Copyright 2018-2019 - Omar Sandoval
// SPDX-License-Identifier: GPL-3.0+
#include <assert.h>
#include <byteswap.h>
#include <string.h>
#include "internal.h"
#include "serialize.h"
void serialize_bits(void *buf, uint64_t bit_offset, uint64_t uvalue,
uint8_t bit_size, bool little_endian)
{
uint8_t *p;
size_t bits, size;
unsigned char tmp[9];
uint8_t first_mask, last_mask;
assert(bit_size > 0);
assert(bit_size <= 64);
p = (uint8_t *)buf + bit_offset / 8;
bit_offset %= 8;
bits = bit_offset + bit_size;
size = (bits + 7) / 8;
if (little_endian) {
if (size > sizeof(uvalue))
tmp[8] = uvalue >> (64 - bit_offset);
uvalue = htole64(uvalue << bit_offset);
memcpy(tmp, &uvalue, sizeof(uvalue));
/* bit_offset least significant bits. */
first_mask = (1 << bit_offset) - 1;
/* 8 - (bit_offset + bit_size) % 8 most significant bits. */
last_mask = 0xff00 >> -bits % 8;
} else {
unsigned int shift;
shift = -bits % 8;
if (size > sizeof(uvalue)) {
tmp[0] = uvalue >> (64 - shift);
uvalue = htobe64(uvalue << shift);
memcpy(&tmp[1], &uvalue, sizeof(uvalue));
} else {
uvalue = htobe64(uvalue << (64 - bits));
memcpy(&tmp[0], &uvalue, sizeof(uvalue));
}
/* bit_offset most significant bits. */
first_mask = 0xff00 >> bit_offset;
/* 8 - (bit_offset + bit_size) % 8 least significant bits. */
last_mask = (1 << shift) - 1;
}
if (size == 1) {
p[0] = (p[0] & (first_mask | last_mask)) | tmp[0];
} else {
p[0] = (p[0] & first_mask) | tmp[0];
memcpy(p + 1, tmp + 1, size - 2);
p[size - 1] = (p[size - 1] & last_mask) | tmp[size - 1];
}
}
uint64_t deserialize_bits(const void *buf, uint64_t bit_offset,
uint8_t bit_size, bool little_endian)
{
const uint8_t *p;
size_t bits, size;
uint64_t ret = 0;
assert(bit_size > 0);
assert(bit_size <= 64);
p = (const uint8_t *)buf + bit_offset / 8;
bit_offset %= 8;
bits = bit_offset + bit_size;
size = (bits + 7) / 8;
if (little_endian) {
memcpy(&ret, p, min(size, sizeof(ret)));
ret = le64toh(ret) >> bit_offset;
if (size > sizeof(ret))
ret |= (uint64_t)p[8] << (64 - bit_offset);
} else {
unsigned int shift;
if (size > sizeof(ret))
memcpy(&ret, &p[1], sizeof(ret));
else
memcpy((char *)(&ret + 1) - size, p, size);
shift = -bits % 8;
ret = be64toh(ret) >> shift;
if (size > sizeof(ret))
ret |= (uint64_t)p[0] << (64 - shift);
}
return truncate_unsigned(ret, bit_size);
}