mirror of
https://github.com/JakeHillion/drgn.git
synced 2024-12-22 17:23:06 +00:00
c0d8709b45
Signed-off-by: Omar Sandoval <osandov@osandov.com>
175 lines
4.7 KiB
C
175 lines
4.7 KiB
C
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
#include <assert.h>
|
|
#include <endian.h>
|
|
|
|
#include "serialize.h"
|
|
|
|
static inline uint8_t copy_bits_step(const uint8_t *s, unsigned int src_bit_offset,
|
|
unsigned int bit_size,
|
|
unsigned int dst_bit_offset, bool lsb0)
|
|
{
|
|
uint8_t result;
|
|
if (lsb0) {
|
|
result = s[0] >> src_bit_offset;
|
|
if (bit_size > 8 - src_bit_offset)
|
|
result |= s[1] << (8 - src_bit_offset);
|
|
result <<= dst_bit_offset;
|
|
} else {
|
|
result = s[0] << src_bit_offset;
|
|
if (bit_size > 8 - src_bit_offset)
|
|
result |= s[1] >> (8 - src_bit_offset);
|
|
result >>= dst_bit_offset;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void copy_bits(void *dst, unsigned int dst_bit_offset, const void *src,
|
|
unsigned int src_bit_offset, uint64_t bit_size, bool lsb0)
|
|
{
|
|
assert(dst_bit_offset < 8);
|
|
assert(src_bit_offset < 8);
|
|
|
|
if (bit_size == 0)
|
|
return;
|
|
|
|
uint8_t *d = dst;
|
|
const uint8_t *s = src;
|
|
uint64_t dst_last_bit = dst_bit_offset + bit_size - 1;
|
|
uint8_t dst_first_mask = copy_bits_first_mask(dst_bit_offset, lsb0);
|
|
uint8_t dst_last_mask = copy_bits_last_mask(dst_last_bit, lsb0);
|
|
|
|
if (dst_bit_offset == src_bit_offset) {
|
|
/*
|
|
* In the common case that the source and destination have the
|
|
* same offset, we can use memcpy(), preserving bits at the
|
|
* start and/or end if necessary.
|
|
*/
|
|
uint8_t first_byte = d[0];
|
|
uint8_t last_byte = d[dst_last_bit / 8];
|
|
memcpy(d, s, dst_last_bit / 8 + 1);
|
|
if (dst_bit_offset != 0) {
|
|
d[0] = ((first_byte & ~dst_first_mask)
|
|
| (d[0] & dst_first_mask));
|
|
}
|
|
if (dst_last_bit % 8 != 7) {
|
|
d[dst_last_bit / 8] = ((last_byte & ~dst_last_mask)
|
|
| (d[dst_last_bit / 8] & dst_last_mask));
|
|
}
|
|
} else if (bit_size <= 8 - dst_bit_offset) {
|
|
/* Destination is only one byte. */
|
|
uint8_t dst_mask = dst_first_mask & dst_last_mask;
|
|
d[0] = ((d[0] & ~dst_mask)
|
|
| (copy_bits_step(&s[0], src_bit_offset, bit_size,
|
|
dst_bit_offset, lsb0) & dst_mask));
|
|
} else {
|
|
/* Destination is two or more bytes. */
|
|
d[0] = ((d[0] & ~dst_first_mask)
|
|
| (copy_bits_step(&s[0], src_bit_offset,
|
|
8 - dst_bit_offset, dst_bit_offset,
|
|
lsb0) & dst_first_mask));
|
|
src_bit_offset += 8 - dst_bit_offset;
|
|
size_t si = src_bit_offset / 8;
|
|
src_bit_offset %= 8;
|
|
size_t di = 1;
|
|
while (di < dst_last_bit / 8) {
|
|
d[di] = copy_bits_step(&s[si], src_bit_offset, 8, 0,
|
|
lsb0);
|
|
di++;
|
|
si++;
|
|
}
|
|
d[di] = ((d[di] & ~dst_last_mask)
|
|
| (copy_bits_step(&s[si], src_bit_offset,
|
|
dst_last_bit % 8 + 1, 0, lsb0)
|
|
& dst_last_mask));
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|