drgn/libdrgn/siphash.h
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

184 lines
3.7 KiB
C

// Copyright 2018 - Omar Sandoval
// SPDX-License-Identifier: GPL-3.0+
#ifndef DRGN_SIPHASH_H
#define DRGN_SIPHASH_H
#include <stdint.h>
#include <string.h>
/* SipHash-2-4 by default */
#ifndef cROUNDS
#define cROUNDS 2
#endif
#ifndef dROUNDS
#define dROUNDS 4
#endif
struct siphash {
uint64_t v0, v1, v2, v3;
uint8_t buf[8];
size_t len;
};
struct siphash128 {
struct siphash hash;
};
static inline uint64_t siphash_rotl(uint64_t x, int b)
{
return (x << b) | (x >> (64 - b));
}
static inline uint64_t siphash_u8_to_le64(const uint8_t *p)
{
return ((uint64_t)p[0] |
((uint64_t)p[1] << 8) |
((uint64_t)p[2] << 16) |
((uint64_t)p[3] << 24) |
((uint64_t)p[4] << 32) |
((uint64_t)p[5] << 40) |
((uint64_t)p[6] << 48) |
((uint64_t)p[7] << 56));
}
static inline void siphash_init(struct siphash *hash, const uint64_t key[2])
{
hash->v0 = UINT64_C(0x736f6d6570736575) ^ key[0];
hash->v1 = UINT64_C(0x646f72616e646f6d) ^ key[1];
hash->v2 = UINT64_C(0x6c7967656e657261) ^ key[0];
hash->v3 = UINT64_C(0x7465646279746573) ^ key[1];
hash->len = 0;
}
static inline void siphash128_init(struct siphash128 *hash128,
const uint64_t key[2])
{
struct siphash *hash = &hash128->hash;
siphash_init(hash, key);
hash->v1 ^= 0xee;
}
static inline void siphash_round(struct siphash *hash, int rounds) {
int i;
for (i = 0; i < rounds; i++) {
hash->v0 += hash->v1;
hash->v1 = siphash_rotl(hash->v1, 13);
hash->v1 ^= hash->v0;
hash->v0 = siphash_rotl(hash->v0, 32);
hash->v2 += hash->v3;
hash->v3 = siphash_rotl(hash->v3, 16);
hash->v3 ^= hash->v2;
hash->v0 += hash->v3;
hash->v3 = siphash_rotl(hash->v3, 21);
hash->v3 ^= hash->v0;
hash->v2 += hash->v1;
hash->v1 = siphash_rotl(hash->v1, 17);
hash->v1 ^= hash->v2;
hash->v2 = siphash_rotl(hash->v2, 32);
}
}
static inline void siphash_update(struct siphash *hash, const void *src,
size_t len)
{
const uint8_t *p = src, *end = p + len;
uint64_t m;
if (hash->len % 8) {
size_t fill = 8 - hash->len % 8;
if (fill > len)
fill = len;
memcpy(&hash->buf[hash->len % 8], p, fill);
p += fill;
hash->len += fill;
if (hash->len % 8)
return;
m = siphash_u8_to_le64(hash->buf);
hash->v3 ^= m;
siphash_round(hash, cROUNDS);
hash->v0 ^= m;
}
hash->len += end - p;
while (end - p >= 8) {
m = siphash_u8_to_le64(p);
hash->v3 ^= m;
siphash_round(hash, cROUNDS);
hash->v0 ^= m;
p += 8;
}
if (p < end)
memcpy(hash->buf, p, end - p);
}
static inline void siphash128_update(struct siphash128 *hash128,
const void *src, size_t len)
{
siphash_update(&hash128->hash, src, len);
}
static inline void siphash_final_common(struct siphash *hash)
{
uint64_t b = (uint64_t)hash->len << 56;
switch (hash->len % 8) {
case 7:
b |= (uint64_t)hash->buf[6] << 48;
case 6:
b |= (uint64_t)hash->buf[5] << 40;
case 5:
b |= (uint64_t)hash->buf[4] << 32;
case 4:
b |= (uint64_t)hash->buf[3] << 24;
case 3:
b |= (uint64_t)hash->buf[2] << 16;
case 2:
b |= (uint64_t)hash->buf[1] << 8;
case 1:
b |= (uint64_t)hash->buf[0];
case 0:
break;
}
hash->v3 ^= b;
siphash_round(hash, cROUNDS);
hash->v0 ^= b;
}
static inline uint64_t siphash_final(struct siphash *hash)
{
siphash_final_common(hash);
hash->v2 ^= 0xff;
siphash_round(hash, dROUNDS);
return hash->v0 ^ hash->v1 ^ hash->v2 ^ hash->v3;
}
static inline void siphash128_final(struct siphash128 *hash128,
uint64_t output[2])
{
struct siphash *hash = &hash128->hash;
siphash_final_common(hash);
hash->v2 ^= 0xee;
siphash_round(hash, dROUNDS);
output[0] = hash->v0 ^ hash->v1 ^ hash->v2 ^ hash->v3;
hash->v1 ^= 0xdd;
siphash_round(hash, dROUNDS);
output[1] = hash->v0 ^ hash->v1 ^ hash->v2 ^ hash->v3;
}
#endif /* DRGN_SIPHASH_H */