refactor: SwissTable implementation for ht (#5860)

* Add ht benchmarks
* Initial implementation
* Finalize native per-group lookup support
* Lookup SSE2 implementation
* Improve hashing
* Add support for custom elem_size
* Avoid double h2 hashing when reserving slot
* Make custom elem_size support conditional
* Fix issues with bitwise and default lookup implementations
* Implement deletion trick optimization
* Track growth_size instead of deleted_slots
* Refactor SDB to access ht via API instead of internals
* Modify SDB tests which rely on hashtable order
* Fix SDB build warnings
* Fix bug with finding next power of two
* foreach_kv to return a bool result
* Change SDB diff order expected by serialize_analysis unit test
* Fix bug in the bitwise lookup implementation
* Remove second call to rz_core_init() which causes memory leaks
* Update some regression tests to accept reordered output
* Adapt ht clear to new implementation
* Use fini_kv_pair and fix 1 potential leak on malloc failure
* Fix cmd/types test after merge
* Avoid second call to calsize_key and avoid iter leaks on malloc failure
* Improve hash distribution
* Extend benchmark suite
* Fix bug with string hashing
* Branchless write to mirrored ctrl bytes
* Simplify string hash and remove potential UB
* Move RZ_PREFETCH macro to rz_types.h
* Add SSE2 discovery in Meson
* Forward SDB string hash function to ht string hash
* Try to revert test_cpu_profiles() to avoid relying on a baked SDB file
* Revert SDB/CDB hash function change
* Fix SDB reference to HT hash function instead of CDB hash
* Change calloc to malloc
* Avoid storing/checking key_len and key_value if they are ut64
* Improve string hash function
* Rename default hash functions
* Improve bench code
* linter.yml: set clang-path to point to llvm-18
This commit is contained in:
Anton Angelov 2026-03-02 06:31:35 +02:00 committed by GitHub
parent 492addb3a1
commit 95f94ae258
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
36 changed files with 1617 additions and 554 deletions

View file

@ -89,7 +89,7 @@ jobs:
run: |
sudo pip install meson ninja
sudo apt update
sudo apt install libclang-14-dev
sudo apt install libclang-18-dev
- name: Build rizin
working-directory: rizin
run: |
@ -98,7 +98,7 @@ jobs:
- name: Run rz-bindgen linter
run: |
python3 rz-bindgen/src/lint.py \
--clang-path "/usr/lib/llvm-14/lib" \
--clang-path "/usr/lib/llvm-18/lib" \
--clang-args "-resource-dir=$(clang -print-resource-dir) " \
--rizin-path rizin

View file

@ -611,6 +611,18 @@ typedef enum {
}
#endif
#if defined(__GNUC__) || defined(__clang__)
#define RZ_PREFETCH(addr) __builtin_prefetch((addr), 0, 3)
#elif defined(_MSC_VER)
#include <intrin.h>
/**
* \def Prefetch data from a certain memory address to memory cache
*/
#define RZ_PREFETCH(addr) _mm_prefetch((const char *)(addr), _MM_HINT_T0)
#else
#define RZ_PREFETCH(addr) ((void)0)
#endif
static inline void rz_run_call1(void *fcn, void *arg1) {
((void (*)(void *))(fcn))(arg1);
}

View file

@ -58,6 +58,7 @@
#define HAVE___BUILTIN_CTZLL @HAVE___BUILTIN_CTZLL@
#define HAVE_POSIX_MEMALIGN @HAVE_POSIX_MEMALIGN@
#define HAVE__ALIGNED_MALLOC @HAVE__ALIGNED_MALLOC@
#define HAVE_SSE2 @HAVE_SSE2@
#define HAVE_HEADER_LINUX_ASHMEM_H @HAVE_HEADER_LINUX_ASHMEM_H@
#define HAVE_HEADER_SYS_SHM_H @HAVE_HEADER_SYS_SHM_H@

View file

@ -2,6 +2,7 @@
// SPDX-FileCopyrightText: 2016-2018 pancake <pancake@nopcode.org>
// SPDX-FileCopyrightText: 2016-2018 ret2libc <sirmy15@gmail.com>
// SPDX-FileCopyrightText: 2024 pelijah
// SPDX-FileCopyrightText: 2026 Anton Angelov <anton.angelov@protonmail.com>
// SPDX-License-Identifier: BSD-3-Clause
#include <rz_util/rz_iterator.h>
@ -17,70 +18,154 @@
#undef VALUE_TYPE
#undef KEY_TO_HASH
#undef HT_NULL_VALUE
#undef VARIABLE_KEY_LEN
#undef VARIABLE_VALUE_LEN
#if HT_TYPE == 1
// Hash table HtPP that has void* as key and void* as value
#define HtName_(name) name##PP
#define Ht_(name) ht_pp_##name
#define HT_(name) HtPP##name
#define KEY_TYPE void *
#define VALUE_TYPE void *
#define KEY_TO_HASH(x) ((ut32)(uintptr_t)(x))
#define HT_NULL_VALUE NULL
#define HtName_(name) name##PP
#define Ht_(name) ht_pp_##name
#define HT_(name) HtPP##name
#define KEY_TYPE void *
#define VALUE_TYPE void *
#define KEY_TO_HASH(key, key_size) (ht_default_hash_ut64((uintptr_t)(key)))
#define HT_NULL_VALUE NULL
#define VARIABLE_KEY_LEN
#define VARIABLE_VALUE_LEN
#elif HT_TYPE == 2
// Hash table HtPU that has void* as key and ut64 as value
#define HtName_(name) name##UP
#define Ht_(name) ht_up_##name
#define HT_(name) HtUP##name
#define KEY_TYPE ut64
#define VALUE_TYPE void *
#define KEY_TO_HASH(x) ((ut32)(x))
#define HT_NULL_VALUE 0
// Hash table HtUP that has void* as key and ut64 as value
#define HtName_(name) name##UP
#define Ht_(name) ht_up_##name
#define HT_(name) HtUP##name
#define KEY_TYPE ut64
#define VALUE_TYPE void *
#define KEY_TO_HASH(key, key_size) (ht_default_hash_ut64((ut64)(key)))
#define HT_NULL_VALUE 0
#define VARIABLE_VALUE_LEN
#elif HT_TYPE == 3
// Hash table HtUU that has ut64 as key and ut64 as value
#define HtName_(name) name##UU
#define Ht_(name) ht_uu_##name
#define HT_(name) HtUU##name
#define KEY_TYPE ut64
#define VALUE_TYPE ut64
#define KEY_TO_HASH(x) ((ut32)(x))
#define HT_NULL_VALUE 0
#define HtName_(name) name##UU
#define Ht_(name) ht_uu_##name
#define HT_(name) HtUU##name
#define KEY_TYPE ut64
#define VALUE_TYPE ut64
#define KEY_TO_HASH(key, key_size) (ht_default_hash_ut64((ut64)(key)))
#define HT_NULL_VALUE 0
#elif HT_TYPE == 4
// Hash table HtPU that has void* as key and ut64 as value
#define HtName_(name) name##PU
#define Ht_(name) ht_pu_##name
#define HT_(name) HtPU##name
#define KEY_TYPE void *
#define VALUE_TYPE ut64
#define KEY_TO_HASH(x) ((ut32)(uintptr_t)(x))
#define HT_NULL_VALUE 0
#define HtName_(name) name##PU
#define Ht_(name) ht_pu_##name
#define HT_(name) HtPU##name
#define KEY_TYPE void *
#define VALUE_TYPE ut64
#define KEY_TO_HASH(key, key_size) (ht_default_hash_ut64((uintptr_t)(key)))
#define HT_NULL_VALUE 0
#define VARIABLE_KEY_LEN
#elif HT_TYPE == 5
// Hash table HtSP that has C-string as key and void* as value
#define HtName_(name) name##SP
#define Ht_(name) ht_sp_##name
#define HT_(name) HtSP##name
#define KEY_TYPE char *
#define VALUE_TYPE void *
#define KEY_TO_HASH(x) ((ut32)(uintptr_t)(x))
#define HT_NULL_VALUE NULL
#define HtName_(name) name##SP
#define Ht_(name) ht_sp_##name
#define HT_(name) HtSP##name
#define KEY_TYPE char *
#define VALUE_TYPE void *
#define KEY_TO_HASH(key, key_size) (ht_default_hash_string(key, key_size))
#define HT_NULL_VALUE NULL
#define VARIABLE_KEY_LEN
#define VARIABLE_VALUE_LEN
#elif HT_TYPE == 6
// Hash table HtSS that has C-string as key and C-string as value
#define HtName_(name) name##SS
#define Ht_(name) ht_ss_##name
#define HT_(name) HtSS##name
#define KEY_TYPE char *
#define VALUE_TYPE char *
#define KEY_TO_HASH(x) ((ut32)(uintptr_t)(x))
#define HT_NULL_VALUE NULL
#define HtName_(name) name##SS
#define Ht_(name) ht_ss_##name
#define HT_(name) HtSS##name
#define KEY_TYPE char *
#define VALUE_TYPE char *
#define KEY_TO_HASH(key, key_size) (ht_default_hash_string(key, key_size))
#define HT_NULL_VALUE NULL
#define VARIABLE_KEY_LEN
#define VARIABLE_VALUE_LEN
#elif HT_TYPE == 7
// Hash table HtSU that has C-string as key and ut64 as value
#define HtName_(name) name##SU
#define Ht_(name) ht_su_##name
#define HT_(name) HtSU##name
#define KEY_TYPE char *
#define VALUE_TYPE ut64
#define KEY_TO_HASH(x) ((ut32)(uintptr_t)(x))
#define HT_NULL_VALUE 0
#define HtName_(name) name##SU
#define Ht_(name) ht_su_##name
#define HT_(name) HtSU##name
#define KEY_TYPE char *
#define VALUE_TYPE ut64
#define KEY_TO_HASH(key, key_size) (ht_default_hash_string(key, key_size))
#define HT_NULL_VALUE 0
#define VARIABLE_KEY_LEN
#endif
// Uncomment to enable support for custom element size (opt.elem_size != sizeof(VALUE_TYPE)). This could
// slow down lookup/insert/iteration with about 5-10%.
// #ifndef HT_ENABLE_CUSTOM_ELEM_SIZE
// #define HT_ENABLE_CUSTOM_ELEM_SIZE
// #endif
#ifndef HT_HASH_FUNCTIONS
#define HT_HASH_FUNCTIONS
/**
* \brief A Murmur3-like hash function which reduces a 64-bit key to a 32-bit non-cryptographic hash.
*
* It's simpler than MurmurHash3 which makes it run faster at the cost of slightly worse hash distribution.
* This hash function is used for HtPX and HtUX, if no custom hash function is specified.
*/
static inline ut32 ht_default_hash_ut64(ut64 key) {
key ^= key >> 33;
key *= 0xff51afd7ed558ccdULL;
key ^= key >> 33;
return (uint32_t)(key ^ (key >> 32));
}
/**
* \brief Computes 32-bit hash for a byte buffer (string).
*
* The function uses Murmur3 mixing constants and tries to process the buffer in 16, 8 or 4-byte blocks when possible.
* This hash function is used for HtSX, if no custom hash function is specified.
*/
static inline ut32 ht_default_hash_string(const char *key, ut32 len) {
const uint64_t prime1 = 0xff51afd7ed558ccdULL; /* Murmur3 consts */
const uint64_t prime2 = 0xc4ceb9fe1a85ec53ULL;
ut64 result = 0xff51afd7ed558ccdULL;
while (len >= 16) {
ut64 blocks[2];
memcpy(blocks, key, sizeof(ut64) * 2);
result += (result << 5) ^ (blocks[0] * prime1);
result += (result << 5) ^ (blocks[1] * prime1);
len -= 16;
key += 16;
}
while (len >= 8) {
ut64 block = 0;
memcpy(&block, key, sizeof(ut64));
result += (result << 5) ^ (block * prime1);
len -= 8;
key += 8;
}
while (len >= 4) {
ut32 block = 0;
memcpy(&block, key, sizeof(ut32));
result += (result << 5) ^ (block * prime1);
len -= 4;
key += 4;
}
while (len > 0) {
result += (result << 5) ^ (*key * prime1);
len -= 1;
key += 1;
}
// Finalize
result ^= result >> 33;
result *= prime2;
// Fold the result
return (ut32)(result ^ (result >> 32));
}
#endif
#ifndef HT_ENUM_DEFINED
@ -107,12 +192,18 @@ typedef enum {
#include <rz_types.h>
/* Kv represents a single key/value element in the hashtable */
/**
* \brief Kv represents a single key/value element in the hashtable
*/
typedef struct Ht_(kv) {
KEY_TYPE key;
VALUE_TYPE value;
ut32 key_len;
ut32 value_len;
#ifdef VARIABLE_KEY_LEN
ut32 key_len; ///< Size of the key. Used only for pointer or string keys.
#endif
#ifdef VARIABLE_VALUE_LEN
ut32 value_len; ///< Size of the value. Used only for pointer or string values.
#endif
} HT_(Kv);
typedef void (*HT_(FiniKv))(HT_(Kv) *kv, void *user);
@ -124,50 +215,48 @@ typedef ut32 (*HT_(CalcSizeV))(const VALUE_TYPE);
typedef ut32 (*HT_(HashFunction))(const KEY_TYPE);
typedef int (*HT_(Comparator))(const KEY_TYPE, const KEY_TYPE);
typedef bool (*HT_(ForeachCallback))(void *user, const KEY_TYPE, const VALUE_TYPE);
typedef struct Ht_(bucket_t) {
HT_(Kv) *arr;
ut32 count;
} HT_(Bucket);
typedef bool (*HT_(ForeachKvCallback))(void *user, const HT_(Kv) *kv);
/**
* Options contain all the settings of the hashtable.
*/
typedef struct Ht_(options_t) {
size_t elem_size; ///< Size of each HtKv element (useful for subclassing like SdbKv).
///< Zero value means to use default size of HtKv.
HT_(Comparator) cmp; ///< RZ_NULLABLE. Function for comparing keys.
///< Returns 0 if keys are equal.
///< Function is invoked only if == operator applied to keys returns false.
HT_(HashFunction) hashfn; ///< RZ_NULLABLE. Function for hashing items in the hash table.
///< If NULL KEY_TO_HASH macro is used.
HT_(DupKey) dupkey; ///< RZ_NULLABLE. Function for making a copy of key.
///< If NULL simple assignment operator is used.
HT_(DupValue) dupvalue; ///< RZ_NULLABLE. Function for making a copy of value.
///< If NULL simple assignment operator is used.
HT_(CalcSizeK) calcsizeK; ///< RZ_NULLABLE. Function to determine the key's size.
///< If NULL zero value is used as a size.
///< Key sizes are checked on equality during keys comparsion as a pre-check.
HT_(CalcSizeV) calcsizeV; ///< RZ_NULLABLE. Function to determine the value's size.
///< If NULL zero value is used as a size.
///< Not required for common scenarios. Could be used in subclasses.
HT_(DupKey) dupkey; ///< RZ_NULLABLE. Function for making a copy of key.
///< If NULL simple assignment operator is used.
HT_(DupValue) dupvalue; ///< RZ_NULLABLE. Function for making a copy of value.
///< If NULL simple assignment operator is used.
HT_(FiniKv) finiKV; ///< RZ_NULLABLE. Function to clean up the key-value store.
void *finiKV_user; ///< RZ_NULLABLE. User data which is passed into finiKV.
size_t elem_size; ///< Size of each HtKv element (useful for subclassing like SdbKv).
///< Zero value means to use default size of HtKv.
} HT_(Options);
/* Ht is the hashtable structure */
typedef struct Ht_(t) {
ut32 size; ///< Size of the hash table in buckets.
ut32 count; ///< Number of stored elements.
HT_(Bucket) *table; ///< Actual table.
ut32 prime_idx;
HT_(Options) opt;
ut32 capacity; ///< Capacity of the main array.
ut32 capacity_mask; ///< Set to `capacity - 1` and used for bucket/index modulo.
ut32 size; ///< Number of stored elements.
ut32 growth_left; ///< Number of empty slots.
RZ_BORROW ut8 *ctrl; ///< Control bytes (metadata) - point to the beginning of the `data` pointer.
RZ_BORROW HT_(Kv) *slots; ///< Main array (no buckets) - points to an offset after the `data` pointer.
HT_(Options) opt; ///< Methods
ut8 *data; ///< Single allocation for `ctrl` and `slots` arrays.
} HtName_(Ht);
typedef struct Ht_(iter_mut_t) {
HtName_(Ht) *ht; ///< The hash table to iterate over.
ut32 ti; ///< Table index
ut32 bi; ///< Bucket index
HT_(Kv) *kv; ///< Current Key-Value-pair.
} HT_(IterMutState);
@ -202,6 +291,7 @@ RZ_API VALUE_TYPE Ht_(find)(RZ_NONNULL HtName_(Ht) *ht, const KEY_TYPE key, RZ_N
// cb should not modify the hashtable.
// NOTE: cb can delete the current element, but it should be avoided
RZ_API void Ht_(foreach)(RZ_NONNULL HtName_(Ht) *ht, RZ_NONNULL HT_(ForeachCallback) cb, RZ_NULLABLE void *user);
RZ_API bool Ht_(foreach_kv)(RZ_NONNULL HtName_(Ht) *ht, RZ_NONNULL HT_(ForeachKvCallback) cb, RZ_NULLABLE void *user);
RZ_API ut32 Ht_(size)(const RZ_NONNULL HtName_(Ht) *ht);

File diff suppressed because it is too large Load diff

View file

@ -33,7 +33,7 @@ static void fini_kv_val(HT_(Kv) *kv, void *user) {
RZ_API RZ_OWN HtName_(Ht) *Ht_(new)(HtStrOption key_opt, RZ_NULLABLE HT_(DupValue) dup_val, RZ_NULLABLE HT_(FreeValue) free_val) {
HT_(Options) opt = {
.cmp = (HT_(Comparator))strcmp,
.hashfn = (HT_(HashFunction))sdb_hash,
.hashfn = NULL,
.dupkey = key_opt == HT_STR_DUP ? (HT_(DupKey))rz_str_dup : NULL,
.dupvalue = dup_val,
.calcsizeK = (HT_(CalcSizeK))strlen,
@ -42,5 +42,5 @@ RZ_API RZ_OWN HtName_(Ht) *Ht_(new)(HtStrOption key_opt, RZ_NULLABLE HT_(DupValu
.finiKV_user = (void *)free_val,
.elem_size = 0,
};
return internal_ht_new(ht_primes_sizes[0], 0, &opt);
return internal_ht_new(0, &opt);
}

View file

@ -32,7 +32,7 @@ static void fini_kv_val(HT_(Kv) *kv, void *user) {
RZ_API RZ_OWN HtName_(Ht) *Ht_(new)(HtStrOption key_opt, HtStrOption val_opt) {
HT_(Options) opt = {
.cmp = (HT_(Comparator))strcmp,
.hashfn = (HT_(HashFunction))sdb_hash,
.hashfn = NULL,
.dupkey = key_opt == HT_STR_DUP ? (HT_(DupKey))rz_str_dup : NULL,
.dupvalue = val_opt == HT_STR_DUP ? (HT_(DupValue))rz_str_dup : NULL,
.calcsizeK = (HT_(CalcSizeK))strlen,
@ -41,5 +41,5 @@ RZ_API RZ_OWN HtName_(Ht) *Ht_(new)(HtStrOption key_opt, HtStrOption val_opt) {
.finiKV_user = val_opt == HT_STR_CONST ? NULL : (HT_(FreeValue))free,
.elem_size = 0,
};
return internal_ht_new(ht_primes_sizes[0], 0, &opt);
return internal_ht_new(0, &opt);
}

View file

@ -20,7 +20,7 @@ static void fini_kv_key(HT_(Kv) *kv, RZ_UNUSED void *user) {
RZ_API RZ_OWN HtName_(Ht) *Ht_(new)(HtStrOption key_opt) {
HT_(Options) opt = {
.cmp = (HT_(Comparator))strcmp,
.hashfn = (HT_(HashFunction))sdb_hash,
.hashfn = NULL,
.dupkey = key_opt == HT_STR_DUP ? (HT_(DupKey))rz_str_dup : NULL,
.dupvalue = NULL,
.calcsizeK = (HT_(CalcSizeK))strlen,
@ -29,5 +29,5 @@ RZ_API RZ_OWN HtName_(Ht) *Ht_(new)(HtStrOption key_opt) {
.finiKV_user = NULL,
.elem_size = 0,
};
return internal_ht_new(ht_primes_sizes[0], 0, &opt);
return internal_ht_new(0, &opt);
}

View file

@ -36,7 +36,7 @@ static void init_options(HT_(Options) *opt, HT_(DupValue) valdup, HT_(FreeValue)
RZ_API RZ_OWN HtName_(Ht) *Ht_(new)(RZ_NULLABLE HT_(DupValue) valdup, RZ_NULLABLE HT_(FreeValue) valfree) {
HT_(Options) opt;
init_options(&opt, valdup, valfree);
return internal_ht_new(ht_primes_sizes[0], 0, &opt);
return internal_ht_new(0, &opt);
}
/**

View file

@ -12,19 +12,6 @@
#include "sdb.h"
#include "sdb_private.h"
static inline SdbKv *next_kv(HtSS *ht, SdbKv *kv) {
return (SdbKv *)((char *)kv + ht->opt.elem_size);
}
#define BUCKET_FOREACH(ht, bt, j, kv) \
for ((j) = 0, (kv) = (SdbKv *)(bt)->arr; j < (bt)->count; (j)++, (kv) = next_kv(ht, kv))
#define BUCKET_FOREACH_SAFE(ht, bt, j, count, kv) \
if ((bt)->arr) \
for ((j) = 0, (kv) = (SdbKv *)(bt)->arr, (count) = (ht)->count; \
(j) < (bt)->count; \
(j) = (count) == (ht)->count ? j + 1 : j, (kv) = (count) == (ht)->count ? next_kv(ht, kv) : kv, (count) = (ht)->count)
// TODO: use mmap instead of read.. much faster!
RZ_API RZ_OWN Sdb *sdb_new0(void) {
return sdb_new(NULL, NULL, 0);
@ -146,7 +133,7 @@ RZ_API bool sdb_isempty(Sdb *s) {
return false;
}
}
if (s->ht && s->ht->count > 0) {
if (s->ht && ht_ss_size(s->ht) > 0) {
return false;
}
}
@ -164,7 +151,7 @@ RZ_API int sdb_count(Sdb *s) {
}
}
if (s->ht) {
count += s->ht->count;
count += ht_ss_size(s->ht);
}
}
return count;
@ -241,7 +228,7 @@ RZ_API const char *sdb_const_get_len(Sdb *s, const char *key, int *vlen) {
return NULL;
}
(void)cdb_findstart(&s->db);
if (cdb_findnext(&s->db, s->ht->opt.hashfn(key), key, keylen) < 1) {
if (cdb_findnext(&s->db, sdb_hash(key), key, keylen) < 1) {
return NULL;
}
len = cdb_datalen(&s->db);
@ -752,19 +739,8 @@ RZ_API bool sdb_foreach(RZ_NONNULL Sdb *s, RZ_NONNULL SdbForeachCallback cb, RZ_
if (!result) {
return sdb_foreach_end(s, false);
}
for (ut32 i = 0; i < s->ht->size; ++i) {
HtSSBucket *bt = &s->ht->table[i];
SdbKv *kv;
ut32 j, count;
BUCKET_FOREACH_SAFE(s->ht, bt, j, count, kv) {
if (kv && sdbkv_value(kv) && *sdbkv_value(kv)) {
if (!cb(user, kv)) {
return sdb_foreach_end(s, false);
}
}
}
if (!sdb_ht_foreach_kv(s->ht, cb, user)) {
return sdb_foreach_end(s, false);
}
return sdb_foreach_end(s, true);
}
@ -778,9 +754,18 @@ static bool _insert_into_disk(void *user, const SdbKv *kv) {
return false;
}
static bool sdb_sync_foreach_cb(void *user, const SdbKv *kv) {
Sdb *s = user;
if (sdb_disk_insert(s, sdbkv_key(kv), sdbkv_value(kv))) {
sdb_remove(s, sdbkv_key(kv));
}
return true;
}
RZ_API bool sdb_sync(Sdb *s) {
bool result;
ut32 i;
if (!s || !sdb_disk_create(s)) {
return false;
@ -789,21 +774,7 @@ RZ_API bool sdb_sync(Sdb *s) {
if (!result) {
return false;
}
/* append new keyvalues */
for (i = 0; i < s->ht->size; ++i) {
HtSSBucket *bt = &s->ht->table[i];
SdbKv *kv;
ut32 j, count;
BUCKET_FOREACH_SAFE(s->ht, bt, j, count, kv) {
if (sdbkv_key(kv) && sdbkv_value(kv) && *sdbkv_value(kv)) {
if (sdb_disk_insert(s, sdbkv_key(kv), sdbkv_value(kv))) {
sdb_remove(s, sdbkv_key(kv));
}
}
}
}
sdb_ht_foreach_kv(s->ht, sdb_sync_foreach_cb, s);
sdb_disk_finish(s);
// TODO: sdb_reset memory state?
return true;
@ -842,7 +813,7 @@ RZ_API bool sdb_stats(Sdb *s, ut32 *disk, ut32 *mem) {
*disk = count;
}
if (mem) {
*mem = s->ht->count;
*mem = ht_ss_size(s->ht);
}
return disk || mem;
}

View file

@ -109,7 +109,7 @@ RZ_API void sdb_copy(Sdb *src, Sdb *dst);
RZ_API bool sdb_stats(Sdb *s, ut32 *disk, ut32 *mem);
typedef bool (*SdbForeachCallback)(void *user, const SdbKv *kv);
typedef SdbHtForeachCallback SdbForeachCallback;
RZ_API bool sdb_foreach(RZ_NONNULL Sdb *s, RZ_NONNULL SdbForeachCallback cb, RZ_NULLABLE void *user);
RZ_API RZ_OWN RzPVector /*<SdbKv *>*/ *sdb_get_items(RZ_NONNULL Sdb *s, bool sorted);
RZ_API RZ_OWN RzPVector /*<SdbKv *>*/ *sdb_get_items_filter(RZ_NONNULL Sdb *s, RZ_NONNULL SdbForeachCallback filter, RZ_NULLABLE void *user, bool sorted);

View file

@ -3,6 +3,14 @@
#include "sdbht.h"
/**
* \brief A helper struct used for forwarding iteration (foreach) callbacks between `ht_` and the `sdb_` APIs.
*/
typedef struct {
SdbHtForeachCallback cb;
void *user;
} HtSSForeachKvCallbackRedirect;
RZ_API HtSS *sdb_ht_new(void) {
HtSS *ht = ht_ss_new(HT_STR_DUP, HT_STR_DUP);
if (ht) {
@ -61,3 +69,28 @@ RZ_API void sdb_ht_free(HtSS *ht) {
RZ_API bool sdb_ht_delete(HtSS *ht, const char *key) {
return ht_ss_delete(ht, key);
}
static bool sdb_ht_foreach_kv_filter(void *user, const HtSSKv *kv) {
SdbKv *sdb_kv = (SdbKv *)kv;
if (sdbkv_key(sdb_kv) && sdbkv_value(sdb_kv) && *sdbkv_value(sdb_kv)) {
HtSSForeachKvCallbackRedirect *redirect = user;
return redirect->cb(redirect->user, (SdbKv *)kv);
}
return true;
}
/**
* \brief Iterates all elements of a `HtSS` hash table.
*
* \param ht The hash table.
* \param cb A callback to be invoked for each element.
* \param user Pointer to user data to be passed to the callback for each element.
* \return true if all elements were iterated, false if the iteration was cancelled by the user callback
*/
RZ_API bool sdb_ht_foreach_kv(RZ_NONNULL HtSS *ht, RZ_NONNULL SdbHtForeachCallback cb, RZ_NULLABLE void *user) {
HtSSForeachKvCallbackRedirect redirect = {
.cb = cb,
.user = user
};
return ht_ss_foreach_kv(ht, sdb_ht_foreach_kv_filter, &redirect);
}

View file

@ -16,6 +16,8 @@ typedef struct sdb_kv {
HtSSKv base;
} SdbKv;
typedef bool (*SdbHtForeachCallback)(void *user, const SdbKv *kv);
static inline const char *sdbkv_key(const SdbKv *kv) {
return kv->base.key;
}
@ -54,6 +56,8 @@ RZ_API bool sdb_ht_delete(HtSS *ht, const char *key);
RZ_API char *sdb_ht_find(HtSS *ht, const char *key, bool *found);
// Find the KeyValuePair corresponding to the matching key.
RZ_API SdbKv *sdb_ht_find_kvp(HtSS *ht, const char *key, bool *found);
// Iterate the hash table.
RZ_API bool sdb_ht_foreach_kv(RZ_NONNULL HtSS *ht, RZ_NONNULL SdbHtForeachCallback cb, RZ_NULLABLE void *user);
#ifdef __cplusplus
}

View file

@ -519,6 +519,13 @@ foreach it : ccs
it_userconf.set10('HAVE_@0@'.format(func.to_upper()), ok)
endforeach
# Try to compile a basic SSE2 code snippet to discover if SSE2 is supported for the target arch
sse2_code = '''#include <emmintrin.h>
int main (int argc, char *argv[]) { __m128i v = _mm_set_epi32(2, 3, 0, 1); return 0; }
'''
has_sse2 = it_cc.compiles(sse2_code, args : '-msse2', name: 'SSE2 code snippet')
it_userconf.set10('HAVE_SSE2', has_sse2)
foreach item : [
['linux/ashmem.h', '', []],
['sys/shm.h', '', []],

443
test/bench/bench_ht.c Normal file
View file

@ -0,0 +1,443 @@
// SPDX-FileCopyrightText: 2026 Anton Angelov <anton.angelov@protonmail.com>
// SPDX-License-Identifier: LGPL-3.0-only
#include "bench_utils.h"
#include <rz_util/ht_pu.h>
#include <rz_util/ht_su.h>
#include <rz_util/ht_uu.h>
/**
* \file bench_ht.c
* \brief Benchmark for hash table functions (`ht_*`)
*/
#define ITERATION_COUNT 2000000
#define SHUFFLE_MULTIPLIER 1037
static bool ht_pu_foreach_cb(RZ_UNUSED ut64 *i, RZ_UNUSED const void *key, RZ_UNUSED const ut64 value) {
return true;
}
/**
* Used for generating a pseudo-random value based on iteration number (for randomizing HtUU keys)
*/
static inline ut64 splitmix64(ut64 v) {
uint64_t z = (v + 0x9E3779B97F4A7C15ULL);
z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9ULL;
z = (z ^ (z >> 27)) * 0x94D049BB133111EBULL;
return z ^ (z >> 31);
}
/**
* A sample object to be used as a key for a HtPU hash table
*/
typedef struct {
ut32 a;
ut32 b;
} PUKey;
static inline PUKey make_pu_key(ut64 iteration) {
PUKey result;
result.a = iteration * 1000;
result.b = splitmix64(iteration);
return result;
}
/**
* Reshuffles a key in order to do the following (in a deterministic way):
* - avoid sequential key insert/lookup
* - create 87.5% chance of lookup hit vs 12.5% chance of miss
* - make 10% of the elements (hot zone) to be requested 75.5% of the time
*/
static ut64 reshuffle_key(ut64 index, ut64 max_value, ut64 unexistent_key) {
const ut64 hot_zone_denom = 10; // 10% of the hash table elements are considered in the hot zone
ut64 hot_zone_size = max_value / hot_zone_denom;
switch (index % 8) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
// 75.5% hot zone (6/8)
return (index * SHUFFLE_MULTIPLIER) % hot_zone_size * hot_zone_denom;
case 6:
// 12.5% cold zone (1/8)
return (index * SHUFFLE_MULTIPLIER) % max_value;
case 7:
// 12.5% miss rate (1/8)
return unexistent_key;
default:
rz_warn_if_reached();
return 0;
}
}
static ut32 pu_key_hash(void *p) {
PUKey *key = p;
return key->a ^ key->b;
}
static int pu_key_cmp(void *p1, void *p2) {
PUKey *key1 = p1;
PUKey *key2 = p2;
if (key1->a < key2->a) {
return -1;
}
if (key1->a > key2->a) {
return 1;
}
if (key1->b < key2->b) {
return -1;
}
if (key1->b > key2->b) {
return 1;
}
return 0;
}
static void bench_rz_ht_pu_combined(RzTable *t_out) {
ut64 temp = 0;
HtPUOptions pu_opt = { 0 };
pu_opt.hashfn = (HtPUHashFunction)pu_key_hash;
pu_opt.cmp = (HtPUComparator)pu_key_cmp;
PUKey *keys = malloc(sizeof(PUKey) * ITERATION_COUNT);
// Generate keys
for (ut64 i = 0; i < ITERATION_COUNT; i++) {
keys[i] = make_pu_key(i);
}
// Insert
{
HtPU *ht = ht_pu_new_opt(&pu_opt);
RZ_BENCH_RUN_I("[HtPU] insert", i, t_out, ITERATION_COUNT, {
ht_pu_insert(ht, &keys[i], i);
});
RZ_BENCH_RUN_I("[HtPU] delete", i, t_out, ITERATION_COUNT, {
ht_pu_delete(ht, &keys[i]);
});
ht_pu_free(ht);
}
// Lookup (small, medium, large hash tables) and iterate
{
HtPU *ht_100 = ht_pu_new_opt(&pu_opt);
HtPU *ht_1k = ht_pu_new_opt(&pu_opt);
HtPU *ht_10k = ht_pu_new_opt(&pu_opt);
HtPU *ht_100k = ht_pu_new_opt(&pu_opt);
HtPU *ht_1m = ht_pu_new_opt(&pu_opt);
for (ut64 i = 0; i < 1000000; i++) {
PUKey *key = &keys[i];
if (i < 100) {
ht_pu_insert(ht_100, key, i);
}
if (i < 1000) {
ht_pu_insert(ht_1k, key, i);
}
if (i < 10000) {
ht_pu_insert(ht_10k, key, i);
}
if (i < 100000) {
ht_pu_insert(ht_100k, key, i);
}
ht_pu_insert(ht_1m, key, i);
}
RZ_BENCH_RUN("[HtPU] iterate (100 elements)", t_out, ITERATION_COUNT, {
ht_pu_foreach(ht_100, (HtPUForeachCallback)ht_pu_foreach_cb, &temp);
});
RZ_BENCH_RUN_I("[HtPU] lookup (100 elements)", i, t_out, ITERATION_COUNT, {
PUKey temp_key = make_pu_key(reshuffle_key(i, 100, UT64_MAX));
ut64 result = ht_pu_find(ht_100, &temp_key, NULL);
});
RZ_BENCH_RUN_I("[HtPU] lookup (1k elements)", i, t_out, ITERATION_COUNT, {
PUKey temp_key = make_pu_key(reshuffle_key(i, 1000, UT64_MAX));
ut64 result = ht_pu_find(ht_1k, &temp_key, NULL);
});
RZ_BENCH_RUN_I("[HtPU] lookup (10k elements)", i, t_out, ITERATION_COUNT, {
PUKey temp_key = make_pu_key(reshuffle_key(i, 10000, UT64_MAX));
ut64 result = ht_pu_find(ht_10k, &temp_key, NULL);
});
RZ_BENCH_RUN_I("[HtPU] lookup (100k elements)", i, t_out, ITERATION_COUNT, {
PUKey temp_key = make_pu_key(reshuffle_key(i, 100000, UT64_MAX));
ut64 result = ht_pu_find(ht_100k, &temp_key, NULL);
});
RZ_BENCH_RUN_I("[HtPU] lookup (1M elements)", i, t_out, ITERATION_COUNT, {
PUKey temp_key = make_pu_key(reshuffle_key(i, 1000000, UT64_MAX));
ut64 result = ht_pu_find(ht_1m, &temp_key, NULL);
});
ht_pu_free(ht_100);
ht_pu_free(ht_1k);
ht_pu_free(ht_10k);
ht_pu_free(ht_100k);
ht_pu_free(ht_1m);
}
free(keys);
}
static char *generate_su_key(ut64 index) {
char buffer[UT8_MAX];
// Try to mimic real world string keys
switch (index % 8) {
case 0:
snprintf(buffer, UT8_MAX, "user_%" PRIx64, index); // user id
break;
case 1:
snprintf(buffer, UT8_MAX, "session_%" PRIx64 "%" PRIx64, index, index * 7919); // session token
break;
case 2: {
const char *sections[] = { "analysis", "asm", "scr", "graph", "str" };
const char *keys[] = { "flags", "prefix", "editor", "times", "server" };
snprintf(buffer, UT8_MAX, "config.%s.%s", sections[index % 5], keys[(index / 5) % 5]); // config keys
break;
}
case 3: {
const char *headers[] = { "content-type", "content-length", "authorization", "user-agent", "accept", "accept-encoding",
"cache-control", "connection", "host", "cookie", "referer", "accept-language",
"x-forwarded-for", "x-request-id", "etag" };
snprintf(buffer, UT8_MAX, "%s-%" PRIx64, headers[index % 15], index / 15); // http header
break;
}
case 4: {
const char *dirs[] = { "/home/user/documents", "/var/log", "/etc/config", "/usr/local/bin", "/tmp/cache" };
snprintf(buffer, UT8_MAX, "%s/file %" PRIx64 ".txt", dirs[index % 5], index);
break;
}
case 5: {
const char *domains[] = { "example.com", "test.org", "mail.net", "company.io" };
snprintf(buffer, UT8_MAX, "user%" PRIx64 "@%s", index, domains[index % 4]); // email
break;
}
case 6: {
snprintf(buffer, UT8_MAX, "%08" PRIx64 "-%04" PRIx64 "-%04" PRIx64 "-%04" PRIx64 "-%012" PRIx64,
index,
(index >> 16) & 0xFFFF,
(index >> 8) & 0xFFFF,
(index >> 4) & 0xFFFF,
(index * 2654435761u)); // GUID
break;
}
case 7: {
// alphanumeric code with length of 2 to 64 characters
const char chars[] = "abcdefghijklmnopqrstuvwxyz0123456789";
int len = 2 + (index % 62);
for (ut64 i = 0; i < len && i < UT8_MAX - 1; i++) {
buffer[i] = chars[(index + i * 7) % 36];
}
buffer[len] = '\0';
break;
}
default:
rz_warn_if_reached();
}
return strdup(buffer);
}
static bool ht_su_foreach_cb(RZ_UNUSED ut64 *i, RZ_UNUSED const char *key, RZ_UNUSED const ut64 value) {
return true;
}
static void bench_rz_ht_su_combined(RzTable *t_out) {
char **precomputed_keys = malloc((ITERATION_COUNT + 1) * sizeof(char *));
// Generate test keys
for (ut64 i = 0; i < ITERATION_COUNT; i++) {
precomputed_keys[i] = generate_su_key(i);
}
precomputed_keys[ITERATION_COUNT] = "non-existent";
// Insert
{
HtSU *ht = ht_su_new(HT_STR_CONST);
RZ_BENCH_RUN_I("[HtSU] insert", i, t_out, ITERATION_COUNT, {
ht_su_insert(ht, precomputed_keys[i], i);
});
RZ_BENCH_RUN_I("[HtSU] delete", i, t_out, ITERATION_COUNT, {
ht_su_delete(ht, precomputed_keys[i]);
});
ht_su_free(ht);
}
// Lookup (small, medium, large hash tables) and iterate
{
HtSU *ht_100 = ht_su_new(HT_STR_CONST);
HtSU *ht_1k = ht_su_new(HT_STR_CONST);
HtSU *ht_10k = ht_su_new(HT_STR_CONST);
HtSU *ht_100k = ht_su_new(HT_STR_CONST);
HtSU *ht_1m = ht_su_new(HT_STR_CONST);
for (ut64 i = 0; i < 1000000; i++) {
if (i < 100) {
ht_su_insert(ht_100, precomputed_keys[i], i);
}
if (i < 1000) {
ht_su_insert(ht_1k, precomputed_keys[i], i);
}
if (i < 10000) {
ht_su_insert(ht_10k, precomputed_keys[i], i);
}
if (i < 100000) {
ht_su_insert(ht_100k, precomputed_keys[i], i);
}
ht_su_insert(ht_1m, precomputed_keys[i], i);
}
RZ_BENCH_RUN("[HtSU] iterate (100 elements)", t_out, ITERATION_COUNT, {
ht_su_foreach(ht_100, (HtSUForeachCallback)ht_su_foreach_cb, NULL);
});
RZ_BENCH_RUN_I("[HtSU] lookup (100 elements)", i, t_out, ITERATION_COUNT, {
const char *key = precomputed_keys[reshuffle_key(i, 100, ITERATION_COUNT)];
ut64 result = ht_su_find(ht_100, key, NULL);
});
RZ_BENCH_RUN_I("[HtSU] lookup (1k elements)", i, t_out, ITERATION_COUNT, {
const char *key = precomputed_keys[reshuffle_key(i, 1000, ITERATION_COUNT)];
ut64 result = ht_su_find(ht_1k, key, NULL);
});
RZ_BENCH_RUN_I("[HtSU] lookup (10k elements)", i, t_out, ITERATION_COUNT, {
const char *key = precomputed_keys[reshuffle_key(i, 10000, ITERATION_COUNT)];
ut64 result = ht_su_find(ht_10k, key, NULL);
});
RZ_BENCH_RUN_I("[HtSU] lookup (100k elements)", i, t_out, ITERATION_COUNT, {
const char *key = precomputed_keys[reshuffle_key(i, 100000, ITERATION_COUNT)];
ut64 result = ht_su_find(ht_100k, key, NULL);
});
RZ_BENCH_RUN_I("[HtSU] lookup (1M elements)", i, t_out, ITERATION_COUNT, {
const char *key = precomputed_keys[reshuffle_key(i, 1000000, ITERATION_COUNT)];
ut64 result = ht_su_find(ht_1m, key, NULL);
});
ht_su_free(ht_100);
ht_su_free(ht_1k);
ht_su_free(ht_10k);
ht_su_free(ht_100k);
ht_su_free(ht_1m);
}
// Free precomputed keys
for (ut64 i = 0; i < ITERATION_COUNT; i++) {
free(precomputed_keys[i]);
}
free(precomputed_keys);
}
static bool ht_uu_foreach_cb(RZ_UNUSED ut64 *i, RZ_UNUSED const ut64 key, RZ_UNUSED const ut64 value) {
return true;
}
static void bench_rz_ht_uu_combined(RzTable *t_out) {
HtUU *ht = NULL;
// Insert
{
ht = ht_uu_new();
RZ_BENCH_RUN_I("[HtUU] insert", i, t_out, ITERATION_COUNT, {
ht_uu_insert(ht, splitmix64(i), i);
});
RZ_BENCH_RUN_I("[HtUU] delete", i, t_out, ITERATION_COUNT, {
ht_uu_delete(ht, splitmix64(i)); // reshuffle keys
});
ht_uu_free(ht);
}
// Lookup 100 elements (and iterate)
{
const ut64 size = 100;
ht = ht_uu_new();
for (ut64 i = 0; i < size; i++) {
ht_uu_insert(ht, splitmix64(i), i);
}
RZ_BENCH_RUN("[HtUU] iterate (100 elements)", t_out, ITERATION_COUNT, {
ht_uu_foreach(ht, (HtUUForeachCallback)ht_uu_foreach_cb, NULL);
});
RZ_BENCH_RUN_I("[HtUU] lookup (100 elements)", i, t_out, ITERATION_COUNT, {
ht_uu_find(ht, splitmix64(reshuffle_key(i, size, size)), NULL);
});
ht_uu_free(ht);
}
// Lookup 1k
{
const ut64 size = 1000;
ht = ht_uu_new();
for (ut64 i = 0; i < size; i++) {
ht_uu_insert(ht, splitmix64(i), i);
}
RZ_BENCH_RUN_I("[HtUU] lookup (1k elements)", i, t_out, ITERATION_COUNT, {
ht_uu_find(ht, splitmix64(reshuffle_key(i, size, size)), NULL);
});
ht_uu_free(ht);
}
// Lookup 10k
{
const ut64 size = 10000;
ht = ht_uu_new();
for (ut64 i = 0; i < size; i++) {
ht_uu_insert(ht, splitmix64(i), i);
}
RZ_BENCH_RUN_I("[HtUU] lookup (10k elements)", i, t_out, ITERATION_COUNT, {
ht_uu_find(ht, splitmix64(reshuffle_key(i, size, size)), NULL);
});
ht_uu_free(ht);
}
// Lookup 100k
{
const ut64 size = 100000;
ht = ht_uu_new();
for (ut64 i = 0; i < size; i++) {
ht_uu_insert(ht, splitmix64(i), i);
}
RZ_BENCH_RUN_I("[HtUU] lookup (100k elements)", i, t_out, ITERATION_COUNT, {
ht_uu_find(ht, splitmix64(reshuffle_key(i, size, size)), NULL);
});
ht_uu_free(ht);
}
// Lookup large
{
const ut64 size = 1000000;
ht = ht_uu_new();
for (ut64 i = 0; i < size; i++) {
ht_uu_insert(ht, splitmix64(i), i);
}
RZ_BENCH_RUN_I("[HtUU] lookup (1M elements)", i, t_out, ITERATION_COUNT, {
ht_uu_find(ht, splitmix64(reshuffle_key(i, size, size)), NULL);
});
ht_uu_free(ht);
}
}
int main() {
RzTable *t = rz_table_new();
RZ_BENCH_TABLE_INIT(t);
// Micro benchmarks
bench_rz_ht_pu_combined(t);
bench_rz_ht_su_combined(t);
bench_rz_ht_uu_combined(t);
// Print results
RZ_BENCH_TABLE_PRINT_AND_FREE(t);
return 0;
}

View file

@ -44,6 +44,18 @@ RZ_API void rz_bench_report(RZ_NONNULL RzBenchCtx *ctx, RZ_NONNULL RzTable *t);
rz_bench_report(&ctx, table); \
} while (0)
#define RZ_BENCH_RUN_I(name, i, table, iterations, code) \
do { \
RzBenchCtx ctx; \
rz_bench_init(&ctx, name, iterations); \
rz_bench_start(&ctx); \
for (ut64(i) = 0; (i) < iterations; (i)++) { \
code; \
} \
rz_bench_end(&ctx); \
rz_bench_report(&ctx, table); \
} while (0)
/**
* \brief Initializes the RzTable \p T used for storing results of microbenchmarks.
* \param T table to initialize.

View file

@ -21,6 +21,7 @@ if get_option('enable_benchmarks')
benchmarks = [
'bitvector',
'il',
'ht'
]
# Create benchmark executables

View file

@ -4240,10 +4240,10 @@ arg int64_t arg2 @ rsi
void fcn.00010270(int64_t arg1, int64_t arg2, const char **s, int64_t arg4, int64_t arg5);
var const char *s2 @ stack - 0xc8
var void *s1 @ stack - 0xc0
var unsigned long long var_b8h @ stack - 0xb8
var unsigned long var_b8h @ stack - 0xb8
var int64_t var_b0h @ stack - 0xb0
var int64_t var_a8h @ stack - 0xa8
var unsigned long long var_98h @ stack - 0x98
var unsigned long var_98h @ stack - 0x98
var int64_t var_94h @ stack - 0x94
var int64_t var_90h @ stack - 0x90
var int64_t var_88h @ stack - 0x88

View file

@ -69,7 +69,7 @@ EXPECT=<<EOF
| ; var char **arg1 @ rdi
| ; var const char *arg2 @ rsi
| ; var int64_t arg7 @ xmm0
| ; var unsigned long long arg3 @ rdx
| ; var unsigned long arg3 @ rdx
| ; var some_t *arg4 @ rcx
| ; arg int a @ stack - 0x10
| ; arg char *g @ stack - 0x18

View file

@ -556,7 +556,7 @@ aaa
aclj
EOF
EXPECT=<<EOF
[{"name":"B","bases":[{"id":"0","name":"A","offset":0}],"vtables":[{"id":"0","addr":4197640,"offset":0}],"methods":[{"name":"B","addr":4197198,"type":"CONSTRUCTOR"},{"name":"printValue","addr":4197250,"type":"VIRTUAL","vtable_offset":8},{"name":"method.A.greet","addr":4197064,"type":"VIRTUAL","vtable_offset":0}]},{"name":"std::basic_ostream_char__std::char_traits_char____std","bases":[],"vtables":[],"methods":[{"name":"operator<<<std::char_traits<char>>","addr":4196496,"type":"DEFAULT"},{"name":"endl<char, std::char_traits<char>>","addr":4196528,"type":"DEFAULT"}]},{"name":"std::ios_base::Init","bases":[],"vtables":[],"methods":[{"name":"Init","addr":4196432,"type":"DEFAULT"},{"name":"~Init","addr":4196480,"type":"DEFAULT"}]},{"name":"std","bases":[],"vtables":[],"methods":[]},{"name":"std::ostream","bases":[],"vtables":[],"methods":[{"name":"operator<<","addr":4196400,"type":"DEFAULT"},{"name":"operator<<","addr":4196512,"type":"DEFAULT"}]},{"name":"A","bases":[],"vtables":[{"id":"0","addr":4197672,"offset":0}],"methods":[{"name":"greet","addr":4197064,"type":"VIRTUAL","vtable_offset":0},{"name":"printValue","addr":4197108,"type":"VIRTUAL","vtable_offset":8},{"name":"A","addr":4197174,"type":"CONSTRUCTOR"}]},{"name":"C","bases":[{"id":"0","name":"A","offset":0}],"vtables":[{"id":"0","addr":4197608,"offset":0}],"methods":[{"name":"C","addr":4197316,"type":"CONSTRUCTOR"},{"name":"printValue","addr":4197368,"type":"VIRTUAL","vtable_offset":8},{"name":"method.A.greet","addr":4197064,"type":"VIRTUAL","vtable_offset":0}]}]
[{"name":"std::ios_base::Init","bases":[],"vtables":[],"methods":[{"name":"Init","addr":4196432,"type":"DEFAULT"},{"name":"~Init","addr":4196480,"type":"DEFAULT"}]},{"name":"std::ostream","bases":[],"vtables":[],"methods":[{"name":"operator<<","addr":4196400,"type":"DEFAULT"},{"name":"operator<<","addr":4196512,"type":"DEFAULT"}]},{"name":"std","bases":[],"vtables":[],"methods":[]},{"name":"C","bases":[{"id":"0","name":"A","offset":0}],"vtables":[{"id":"0","addr":4197608,"offset":0}],"methods":[{"name":"C","addr":4197316,"type":"CONSTRUCTOR"},{"name":"printValue","addr":4197368,"type":"VIRTUAL","vtable_offset":8},{"name":"method.A.greet","addr":4197064,"type":"VIRTUAL","vtable_offset":0}]},{"name":"std::basic_ostream_char__std::char_traits_char____std","bases":[],"vtables":[],"methods":[{"name":"operator<<<std::char_traits<char>>","addr":4196496,"type":"DEFAULT"},{"name":"endl<char, std::char_traits<char>>","addr":4196528,"type":"DEFAULT"}]},{"name":"A","bases":[],"vtables":[{"id":"0","addr":4197672,"offset":0}],"methods":[{"name":"greet","addr":4197064,"type":"VIRTUAL","vtable_offset":0},{"name":"printValue","addr":4197108,"type":"VIRTUAL","vtable_offset":8},{"name":"A","addr":4197174,"type":"CONSTRUCTOR"}]},{"name":"B","bases":[{"id":"0","name":"A","offset":0}],"vtables":[{"id":"0","addr":4197640,"offset":0}],"methods":[{"name":"B","addr":4197198,"type":"CONSTRUCTOR"},{"name":"printValue","addr":4197250,"type":"VIRTUAL","vtable_offset":8},{"name":"method.A.greet","addr":4197064,"type":"VIRTUAL","vtable_offset":0}]}]
EOF
RUN
@ -568,7 +568,7 @@ aaa
aclj
EOF
EXPECT=<<EOF
[{"name":"InAbsentia","bases":[{"id":"0","name":"Album","offset":0}],"vtables":[{"id":"0","addr":4268452,"offset":0}],"methods":[{"name":"virtual_0","addr":4198536,"type":"VIRTUAL","vtable_offset":0},{"name":"virtual_4","addr":4198579,"type":"VIRTUAL","vtable_offset":4}]},{"name":"type_info","bases":[],"vtables":[{"id":"0","addr":4268540,"offset":0}],"methods":[{"name":"virtual_0","addr":4198710,"type":"VIRTUAL","vtable_offset":0}]},{"name":"Album","bases":[],"vtables":[{"id":"0","addr":4268388,"offset":0}],"methods":[{"name":"virtual_0","addr":4198493,"type":"VIRTUAL","vtable_offset":0},{"name":"virtual_4","addr":4198567,"type":"VIRTUAL","vtable_offset":4}]}]
[{"name":"Album","bases":[],"vtables":[{"id":"0","addr":4268388,"offset":0}],"methods":[{"name":"virtual_0","addr":4198493,"type":"VIRTUAL","vtable_offset":0},{"name":"virtual_4","addr":4198567,"type":"VIRTUAL","vtable_offset":4}]},{"name":"InAbsentia","bases":[{"id":"0","name":"Album","offset":0}],"vtables":[{"id":"0","addr":4268452,"offset":0}],"methods":[{"name":"virtual_0","addr":4198536,"type":"VIRTUAL","vtable_offset":0},{"name":"virtual_4","addr":4198579,"type":"VIRTUAL","vtable_offset":4}]},{"name":"type_info","bases":[],"vtables":[{"id":"0","addr":4268540,"offset":0}],"methods":[{"name":"virtual_0","addr":4198710,"type":"VIRTUAL","vtable_offset":0}]}]
EOF
RUN

View file

@ -183,7 +183,7 @@ EXPECT=<<EOF
| | 0x00400bef mov rdx, qword [rax]
| | 0x00400bf2 mov rax, qword [var_20h.var_20h]
| | 0x00400bf6 mov rdi, rax
| | 0x00400bf9 call rdx ; Virtual Call : method.Dog.run / method.Cat.run / method.Human.run
| | 0x00400bf9 call rdx ; Virtual Call : method.Dog.run / method.Human.run / method.Cat.run
| ,==< 0x00400bfb jmp 0x400c8c
| |`-> 0x00400c00 mov ecx, dword [var_24h]
| | 0x00400c03 movsxd rax, ecx
@ -212,7 +212,7 @@ EXPECT=<<EOF
| || 0x00400c4e mov rdx, qword [rax]
| || 0x00400c51 mov rax, qword [var_20h.var_20h]
| || 0x00400c55 mov rdi, rax
| || 0x00400c58 call rdx ; Virtual Call : method.Dog.run / method.Cat.run / method.Human.run
| || 0x00400c58 call rdx ; Virtual Call : method.Dog.run / method.Human.run / method.Cat.run
| ,===< 0x00400c5a jmp 0x400c8c
| ||`-> 0x00400c5c mov edi, 0x08
| || 0x00400c61 call sym.imp.operator_new_unsigned_long
@ -226,7 +226,7 @@ EXPECT=<<EOF
| || 0x00400c80 mov rdx, qword [rax]
| || 0x00400c83 mov rax, qword [var_20h.var_20h]
| || 0x00400c87 mov rdi, rax
| || 0x00400c8a call rdx ; Virtual Call : method.Dog.run / method.Cat.run / method.Human.run
| || 0x00400c8a call rdx ; Virtual Call : method.Dog.run / method.Human.run / method.Cat.run
| || ; CODE XREFS from main @ 0x400bfb, 0x400c5a
| ``--> 0x00400c8c mov rax, qword [var_20h.var_20h]
| 0x00400c90 mov rax, qword [rax]
@ -234,7 +234,7 @@ EXPECT=<<EOF
| 0x00400c97 mov rdx, qword [rax]
| 0x00400c9a mov rax, qword [var_20h.var_20h]
| 0x00400c9e mov rdi, rax
| 0x00400ca1 call rdx ; Virtual Call : method.Dog.walk / method.Cat.walk / method.Human.walk
| 0x00400ca1 call rdx ; Virtual Call : method.Dog.walk / method.Human.walk / method.Cat.walk
| 0x00400ca3 mov rax, qword [var_20h.var_20h]
| 0x00400ca7 test rax, rax
| ,=< 0x00400caa jz 0x400cbb
@ -242,7 +242,7 @@ EXPECT=<<EOF
| | 0x00400caf add rdx, 0x08
| | 0x00400cb3 mov rdx, qword [rdx]
| | 0x00400cb6 mov rdi, rax
| | 0x00400cb9 call rdx ; Virtual Call : sym.Human::_Human_0x401090 / sym.Dog::_Dog_0x400f9a / sym.Cat::_Cat_0x400ea4
| | 0x00400cb9 call rdx ; Virtual Call : sym.Cat::_Cat_0x400ea4 / sym.Human::_Human_0x401090 / sym.Dog::_Dog_0x400f9a
| `-> 0x00400cbb mov eax, 0x00
| ,=< 0x00400cc0 jmp 0x400d13
..
@ -481,7 +481,7 @@ EXPECT=<<EOF
| | 0x100002890 ldur x0, [var_20h.var_20h]
| | 0x100002894 ldr x8, [x0]
| | 0x100002898 ldr x8, [x8, 0x10] ; [0x10:4]=-1 ; 16
| | 0x10000289c blr x8 ; Virtual Call : method.Dog.run / method.Cat.run / method.Human.run
| | 0x10000289c blr x8 ; Virtual Call : method.Dog.run / method.Human.run / method.Cat.run
| ,==< 0x1000028a0 b 0x100002980
..
| ||`-> 0x1000028c0 ldur w8, [var_24h]
@ -505,7 +505,7 @@ EXPECT=<<EOF
| ||| 0x100002900 ldur x0, [var_20h.var_20h]
| ||| 0x100002904 ldr x8, [x0]
| ||| 0x100002908 ldr x8, [x8, 0x10] ; [0x10:4]=-1 ; 16
| ||| 0x10000290c blr x8 ; Virtual Call : method.Dog.run / method.Cat.run / method.Human.run
| ||| 0x10000290c blr x8 ; Virtual Call : method.Dog.run / method.Human.run / method.Cat.run
| ,====< 0x100002910 b 0x10000297c
..
| ||||`-> 0x100002930 mov x0, 8
@ -519,7 +519,7 @@ EXPECT=<<EOF
| |||| 0x10000294c ldur x0, [var_20h.var_20h]
| |||| 0x100002950 ldr x8, [x0]
| |||| 0x100002954 ldr x8, [x8, 0x10] ; [0x10:4]=-1 ; 16
| |||| 0x100002958 blr x8 ; Virtual Call : method.Dog.run / method.Cat.run / method.Human.run
| |||| 0x100002958 blr x8 ; Virtual Call : method.Dog.run / method.Human.run / method.Cat.run
| ||||,=< 0x10000295c b 0x10000297c
..
| |||||| ; CODE XREFS from entry0 @ 0x100002910, 0x10000295c
@ -528,7 +528,7 @@ EXPECT=<<EOF
| `----`--> 0x100002980 ldur x0, [var_20h.var_20h]
| || | 0x100002984 ldr x8, [x0]
| || | 0x100002988 ldr x8, [x8, 0x18] ; [0x18:4]=-1 ; 24
| || | 0x10000298c blr x8 ; Virtual Call : method.Dog.walk / method.Cat.walk / method.Human.walk
| || | 0x10000298c blr x8 ; Virtual Call : method.Dog.walk / method.Human.walk / method.Cat.walk
| || | 0x100002990 ldur x8, [var_20h.var_20h]
| || | 0x100002994 str x8, [var_58h]
| || | 0x100002998 subs x8, x8, 0
@ -716,7 +716,7 @@ EXPECT=<<EOF
| `--> 0x100002cd4 ldr x0, [sp]
| | 0x100002cd8 ldr x8, [x0]
| | 0x100002cdc ldr x8, [x8, 8] ; [0x8:4]=-1 ; 8
| | 0x100002ce0 blr x8 ; Virtual Call : sym.Dog::_Dog_0x100002fbc / sym.non_virtual_thunk_to_Dog::_Dog_0x100003068
| | 0x100002ce0 blr x8 ; Virtual Call : sym.non_virtual_thunk_to_Dog::_Dog_0x100003068 / sym.Dog::_Dog_0x100002fbc
| ,==< 0x100002ce4 b 0x100002ce8
| || ; CODE XREF from entry0 @ 0x100002ce4
| ``-> 0x100002ce8 ldur w0, [var_14h]

View file

@ -20,8 +20,8 @@ avx method.Dog.run
EOF
EXPECT=<<EOF
Virtual xrefs to method.Dog.run
C 0x100002958 blr x8
C 0x10000289c blr x8
C 0x100002958 blr x8
C 0x10000290c blr x8
EOF
RUN
@ -37,8 +37,8 @@ EOF
EXPECT=<<EOF
from
------------
0x100002958
0x10000289c
0x100002958
0x10000290c
EOF
RUN
@ -53,12 +53,12 @@ avxt method_Employee_sayHello
EOF
EXPECT=<<EOF
Virtual xrefs to method_Employee_sayHello
C 0x100001914 call qword [reloc.objc_msgSend]
C 0x100001937 call qword [reloc.objc_msgSend]
C 0x100001914 call qword [reloc.objc_msgSend]
from
------------
0x100001914
0x100001937
0x100001914
EOF
RUN

View file

@ -58,7 +58,7 @@ axtl @ sym.mf_write
EOF
EXPECT=<<EOF
;------------------------------------------
void sym.mf_release(unsigned long long arg1, int64_t arg2, int64_t arg_28h, int64_t arg_20h, int64_t arg_30h, int64_t arg_498h, int64_t arg_38h, int64_t arg_48h, int64_t arg_40h, int64_t arg_10h);
void sym.mf_release(unsigned long arg1, int64_t arg2, int64_t arg_28h, int64_t arg_20h, int64_t arg_30h, int64_t arg_498h, int64_t arg_38h, int64_t arg_48h, int64_t arg_40h, int64_t arg_10h);
; Xref from: sym.mf_release @ 0x00219836
| : 0x00219830 mov rsi, rbx ; int64_t arg2
| : 0x00219833 mov rdi, rbp ; int64_t arg1

View file

@ -33,8 +33,8 @@ e asm.os=linux
gl
EOF
EXPECT=<<EOF
shc exec : execute cmd=/bin/sh suid=false
enc xor : xor encoder for shellcode
shc exec : execute cmd=/bin/sh suid=false
EOF
RUN

View file

@ -541,41 +541,41 @@ ftll
ftn bla
EOF
EXPECT=<<EOF
fs
env
string
network
dylib
threads
alloc
time
process
fs
stdout
["fs":[],"env":[],"string":[],"network":[],"dylib":[],"threads":[],"alloc":[],"time":[],"process":[],"stdout":[]]
string
process
alloc
network
env
threads
dylib
["time":[],"fs":[],"stdout":[],"string":[],"process":[],"alloc":[],"network":[],"env":[],"threads":[],"dylib":[]]
time:
fs:
env:
stdout:
string:
process:
alloc:
network:
dylib:
env:
threads:
alloc:
time:
process:
stdout:
bla
fs:
dylib:
process:
stdout:
network:
alloc:
env:
string:
bla
time:
fs:
stdout:
string:
process:
alloc:
network:
env:
bla:
0x00400410 foo
0x00400506 goo
threads:
dylib:
0x00400410 foo
0x00400506 goo
EOF

View file

@ -264,11 +264,11 @@ age title3 title1
agg *
EOF
EXPECT=<<EOF
agn "title3" base64:dGhpcyBpcyBteSBib2R5CndpdGggbmV3bGluZXM=
agn "title2" base64:Ym9keTI=
agn "title1" base64:Ym9keTE=
age "title3" "title1"
agn "title2" base64:Ym9keTI=
agn "title3" base64:dGhpcyBpcyBteSBib2R5CndpdGggbmV3bGluZXM=
age "title1" "title2"
age "title3" "title1"
EOF
RUN

View file

@ -64,15 +64,15 @@ Lct
Lcq
EOF
EXPECT=<<EOF
java: Suite of java commands, type `java` for more info (Made by deroad, v1.0, LGPL-3.0-only)
dex: Suite of dex commands, type `dex` for more info (Made by deroad, v1.0, LGPL-3.0-only)
[{"name":"java","description":"Suite of java commands, type `java` for more info","author":"deroad","version":"1.0","license":"LGPL-3.0-only"},{"name":"dex","description":"Suite of dex commands, type `dex` for more info","author":"deroad","version":"1.0","license":"LGPL-3.0-only"}]
java: Suite of java commands, type `java` for more info (Made by deroad, v1.0, LGPL-3.0-only)
[{"name":"dex","description":"Suite of dex commands, type `dex` for more info","author":"deroad","version":"1.0","license":"LGPL-3.0-only"},{"name":"java","description":"Suite of java commands, type `java` for more info","author":"deroad","version":"1.0","license":"LGPL-3.0-only"}]
name license author version description
------------------------------------------------------------------------------------
java LGPL-3.0-only deroad 1.0 Suite of java commands, type `java` for more info
dex LGPL-3.0-only deroad 1.0 Suite of dex commands, type `dex` for more info
java
java LGPL-3.0-only deroad 1.0 Suite of java commands, type `java` for more info
dex
java
EOF
RUN

View file

@ -1621,8 +1621,8 @@ ar bp=0x98
afvd var_70h
EOF
EXPECT=<<EOF
var unsigned long long var_80h @ stack - 0x80
var unsigned long long var_71h @ stack - 0x71
var unsigned long var_80h @ stack - 0x80
var unsigned long var_71h @ stack - 0x71
var int64_t var_70h @ stack - 0x70
var char *var_60h @ stack - 0x60
var int64_t var_58h @ stack - 0x58
@ -1635,8 +1635,8 @@ var int64_t var_40h @ stack - 0x40
arg int64_t arg_5h @ stack + 0x5
arg int argc @ rdi
arg char **argv @ rsi
var unsigned long long var_80h @ stack - 0x80
var unsigned long long var_71h @ stack - 0x71
var unsigned long var_80h @ stack - 0x80
var unsigned long var_71h @ stack - 0x71
var char [20] var_70h @ stack - 0x70
var int64_t var_58h @ stack - 0x58
var int64_t var_56h @ stack - 0x56
@ -1796,10 +1796,10 @@ s sym.funcarg
pd 1
EOF
EXPECT=<<EOF
sym.funcarg(const char *arg1, unsigned long long arg2);
sym.funcarg(const char *arg1, unsigned long arg2);
; arg const char *arg1 @ rdi
; arg unsigned long long arg2 @ rsi
; var unsigned long long var_14h @ stack - 0x14
; arg unsigned long arg2 @ rsi
; var unsigned long var_14h @ stack - 0x14
; var const char *var_10h @ stack - 0x10
0x0000068a 55 push rbp
EOF
@ -1817,10 +1817,10 @@ s sym.funcarg
pd 1
EOF
EXPECT=<<EOF
sym.funcarg(const char *arg_4h, size_t arg_8h);
sym.funcarg(const char *arg_4h, unsigned long arg_8h);
; var int32_t var_8h @ stack - 0x8
; arg const char *arg_4h @ stack + 0x4
; arg size_t arg_8h @ stack + 0x8
; arg unsigned long arg_8h @ stack + 0x8
0x0000054d 55 push ebp
EOF
RUN
@ -1887,7 +1887,7 @@ EOF
EXPECT=<<EOF
var int var_3ch @ stack - 0x3c
var int64_t var_38h @ stack - 0x38
var unsigned long long var_30h @ stack - 0x30
var unsigned long var_30h @ stack - 0x30
EOF
RUN
@ -1901,7 +1901,7 @@ afvl
EOF
EXPECT=<<EOF
var void *va_args @ stack - 0x3c
var unsigned long long var_34h @ stack - 0x34
var unsigned long var_34h @ stack - 0x34
var FILE *stream @ stack - 0x30
var const char *s @ stack - 0x28
var const char *var_20h @ stack - 0x20
@ -1940,9 +1940,9 @@ var int *wstatus @ stack - 0x14d4
var int64_t var_14cch @ stack - 0x14cc
var int64_t var_14c8h @ stack - 0x14c8
var int64_t var_14c4h @ stack - 0x14c4
var unsigned long long pid @ stack - 0x14c0
var unsigned long long var_14bch @ stack - 0x14bc
var unsigned long long var_14b8h @ stack - 0x14b8
var unsigned long pid @ stack - 0x14c0
var unsigned long var_14bch @ stack - 0x14bc
var unsigned long var_14b8h @ stack - 0x14b8
var int64_t var_14b4h @ stack - 0x14b4
var const char *var_14b0h @ stack - 0x14b0
var const char *s @ stack - 0x14a8
@ -1958,9 +1958,9 @@ var int64_t var_20h @ stack - 0x20
arg int argc @ rdi
arg char **argv @ rsi
var char *str @ stack - 0xb8
var long long signed int var_ach @ stack - 0xac
var int_fast64_t var_ach @ stack - 0xac
var int64_t var_a8h @ stack - 0xa8
var unsigned long long var_a4h @ stack - 0xa4
var unsigned long var_a4h @ stack - 0xa4
var int var_a0h @ stack - 0xa0
var int var_9ch @ stack - 0x9c
var int var_98h @ stack - 0x98
@ -1979,14 +1979,14 @@ var char *var_68h @ stack - 0x68
var char *var_60h @ stack - 0x60
var char *var_58h @ stack - 0x58
var char *var_50h @ stack - 0x50
var unsigned long long var_48h @ stack - 0x48
var unsigned long var_48h @ stack - 0x48
var const char *s1 @ stack - 0x40
var const char *s @ stack - 0x38
var const char *var_30h @ stack - 0x30
var const char *var_28h @ stack - 0x28
var int64_t var_20h @ stack - 0x20
var int64_t var_10h @ stack - 0x10
arg long long signed int arg1 @ rdi
arg int_fast64_t arg1 @ rdi
arg char *arg2 @ rsi
EOF
RUN
@ -2245,18 +2245,18 @@ tn-*
tn
EOF
EXPECT=<<EOF
ExitThread
FreeLibraryAndExitThread
RaiseException
RtlRaiseException
ExitProcess
FatalExit
ExitProcess
ExitThread
RtlRaiseException
RaiseException
---
FreeLibraryAndExitThread
ExitProcess
ExitThread
RtlRaiseException
FreeLibraryAndExitThread
RaiseException
RtlRaiseException
ExitProcess
---
EOF
RUN
@ -2323,13 +2323,13 @@ EOF
EXPECT=<<EOF
int64_t
const char *
unsigned long long
unsigned long
int
char **
=
int64_t
const char *
unsigned long long
unsigned long
const void *
size_t
mbstate_t *
@ -8283,9 +8283,9 @@ tn
tnj
EOF
EXPECT=<<EOF
fun1
fun2
["fun1","fun2"]
fun1
["fun2","fun1"]
EOF
RUN

View file

@ -590,6 +590,7 @@ le:
stack_size: 0x1000
EOF
BROKEN=1
RUN
NAME=hellolx.dll unpack iterated page

View file

@ -1419,13 +1419,13 @@ CMDS=il
EXPECT=<<EOF
library
-------------
user32.dll
kernel32.dll
pstorec.dll
shell32.dll
crypt32.dll
oleaut32.dll
advapi32.dll
shell32.dll
user32.dll
oleaut32.dll
crypt32.dll
pstorec.dll
ole32.dll
rasapi32.dll
EOF

View file

@ -285,27 +285,27 @@ ____h_ temperature LGPL3 Seva Binary temperatu
____h_ xor16 LGPL3 deroad XOR-16 checksum
____h_ xor8 LGPL3 deroad XOR-8 checksum
____h_ xxhash32 LGPL3 deroad xxHash32 non-cryptographic hash
ED____ rc4 LGPL-3 pancake RC4 symmetric-key block cipher
ED____ rc6 LGPL-3 rakholiyajenish.07 RC6 symmetric-key block cipher
_D____ ror LGPL-3 pancake Rotate Right symmetric-key block cipher
__ed__ base64 LGPL-3 rakholiyajenish.07 Base64 encoder/decoder
__ed__ base32 LGPL-3 Ahmed Ibrahim Base32 encoder/decoder
ED____ rc2 LGPL-3 lionaneesh RC2 symmetric-key block cipher
ED____ xor LGPL-3 pancake XOR symmetric-key block cipher
ED____ cps2 LGPL-3 pancake,esanfelix,pof Capcom Play System 2 (CPS-2) symmetric-key block cipher
ED____ rot LGPL-3 pancake Caesar symmetric-key cipher
__ed__ punycode LGPL-3 pancake Punycode encoder/decoder
ED____ serpent-ecb LGPL-3 NicsTr Serpent symmetric-key block cipher (ECB block mode)
ED____ sm4-ecb LGPL-3 0xSh4dy ShangMi 4 symmetric-key block cipher (ECB block mode)
__ed__ base36 LGPL-3 abcSup Base36 encoder/decoder
__ed__ base85 LGPL-3 Ahmed Ibrahim Base85 encoder/decoder
ED____ aes-cbc LGPL-3 rakholiyajenish.07 AES symmetric-key block cipher (CBC block mode)
E_____ rol LGPL-3 pancake Rotate Left symmetric-key block cipher
ED____ aes-ecb LGPL3 Nettle project,pancake AES symmetric-key block cipher (ECB block mode)
__ed__ base16 LGPL-3 Ahmed Ibrahim Base16 encoder/decoder
__ed__ base36 LGPL-3 abcSup Base36 encoder/decoder
__ed__ base64 LGPL-3 rakholiyajenish.07 Base64 encoder/decoder
ED____ des-ecb LGPL-3 deroad DES symmetric-key block cipher (ECB block mode)
ED____ blowfish LGPL3 kishorbhat Blowfish symmetric-key block cipher
ED____ sm4-ecb LGPL-3 0xSh4dy ShangMi 4 symmetric-key block cipher (ECB block mode)
ED____ xor LGPL-3 pancake XOR symmetric-key block cipher
__ed__ punycode LGPL-3 pancake Punycode encoder/decoder
__ed__ base16 LGPL-3 Ahmed Ibrahim Base16 encoder/decoder
ED____ rc4 LGPL-3 pancake RC4 symmetric-key block cipher
__ed__ base91 LGPL-3 rakholiyajenish.07 Base91 encoder/decoder
ED____ blowfish LGPL3 kishorbhat Blowfish symmetric-key block cipher
ED____ aes-cbc LGPL-3 rakholiyajenish.07 AES symmetric-key block cipher (CBC block mode)
ED____ rc6 LGPL-3 rakholiyajenish.07 RC6 symmetric-key block cipher
ED____ cps2 LGPL-3 pancake,esanfelix,pof Capcom Play System 2 (CPS-2) symmetric-key block cipher
ED____ rc2 LGPL-3 lionaneesh RC2 symmetric-key block cipher
ED____ serpent-ecb LGPL-3 NicsTr Serpent symmetric-key block cipher (ECB block mode)
ED____ aes-ecb LGPL3 Nettle project,pancake AES symmetric-key block cipher (ECB block mode)
ED____ rot LGPL-3 pancake Caesar symmetric-key cipher
__ed__ base32 LGPL-3 Ahmed Ibrahim Base32 encoder/decoder
flags legenda:
E = encryption, D = decryption

View file

@ -371,8 +371,8 @@ static bool test_rz_core_annotated_code_print(void) {
static bool test_rz_core_annotated_code_print_comment_cmds(void) {
RzAnnotatedCode *code = get_hello_world();
char *actual;
char *expected = "CCu base64:c3ltLmltcC5wdXRzKCJIZWxsbywgV29ybGQhIik= @ 0x1158\n"
"CCu base64:cmV0dXJu @ 0x115f\n";
char *expected = "CCu base64:cmV0dXJu @ 0x115f\n"
"CCu base64:c3ltLmltcC5wdXRzKCJIZWxsbywgV29ybGQhIik= @ 0x1158\n";
rz_cons_new();
rz_cons_push();
rz_core_annotated_code_print_comment_cmds(code);

View file

@ -30,6 +30,34 @@ bool test_ht_insert_lookup(void) {
mu_assert_streq(sdb_ht_find(ht, "CCCC", NULL), "vCCCC", "CCCC value wrong");
sdb_ht_free(ht);
#ifdef HT_ENABLE_CUSTOM_ELEM_SIZE
typedef struct {
HtUUKv base;
ut64 extra;
} CustomKv;
HtUUOptions opt = { 0 };
opt.elem_size = sizeof(CustomKv);
HtUU *ht_u = ht_uu_new_opt(&opt);
for (size_t i = 0; i < 100; ++i) {
CustomKv *tmp = NULL;
CustomKv kv = { 0 };
kv.base.key = 4 * i;
kv.base.value = i + 200;
kv.extra = i + 300;
ht_uu_insert_kv_ex(ht_u, (HtUUKv *)&kv, false, (HtUUKv **)&tmp);
mu_assert_notnull(tmp, "KV is set after rehashing");
mu_assert_eq(tmp->base.value, i + 200, "KV is valid after rehashing");
mu_assert_eq(tmp->extra, i + 300, "KV extra value is valid after rehashing");
}
ht_uu_free(ht_u);
#endif
mu_end;
}
@ -297,6 +325,71 @@ bool test_delete(void) {
mu_end;
}
bool test_rehash_on_delete(void) {
HtUU *ht = ht_uu_new();
ht->opt.hashfn = (HtUUHashFunction)create_collision;
for (ut32 i = 0; i < 56; i++) {
mu_assert_true(ht_uu_insert(ht, i, i * 100), "failed to insert element");
}
for (ut32 i = 0; i < 56; i++) {
mu_assert_true(ht_uu_delete(ht, i), "failed to delete element");
}
for (ut32 i = 0; i < 56; i++) {
mu_assert_true(ht_uu_insert(ht, i, i * 100), "failed to insert element");
}
for (ut32 i = 0; i < 56; i++) {
mu_assert_true(ht_uu_update_key(ht, i, i + 1000), "failed to update element key");
}
mu_assert_eq(ht_uu_size(ht), 56, "invalid table size");
ht_uu_free(ht);
mu_end;
}
bool test_ht_delete_optimized(void) {
// Test case for the "deletion trick" optimization. If the optimized implementation places
// incorrect "empty" slot, this would corrupt the hashtable and make some keys unreachable
bool found = false;
for (ut32 size = 1; size <= 32; size++) {
for (ut32 delete_key = 0; delete_key < size; delete_key++) {
HtUU *ht = ht_uu_new();
ht->opt.hashfn = (HtUUHashFunction)create_collision;
// Insert all
for (ut32 i = 0; i < size; i++) {
ht_uu_insert(ht, i, i * 100);
}
// Delete 1
ht_uu_delete(ht, delete_key);
// Confirm
for (ut32 i = 0; i < size; i++) {
HtUUKv *kv = ht_uu_find_kv(ht, i, &found);
if (i == delete_key) {
mu_assert_null(kv, "element expected to be deleted");
mu_assert_false(found, "element expected to be deleted");
} else {
mu_assert_notnull(kv, "key not found");
mu_assert_true(found, "key not found");
mu_assert_eq(kv->value, i * 100, "incorrect value");
}
}
mu_assert_eq(ht_uu_size(ht), size - 1, "invalid table size");
ht_uu_free(ht);
}
}
mu_end;
}
bool test_clear(void) {
HtSS *ht = ht_ss_new(HT_STR_DUP, HT_STR_DUP);
@ -870,6 +963,8 @@ int all_tests() {
mu_run_test(test_ht_insert_lookup);
mu_run_test(test_ht_update_lookup);
mu_run_test(test_ht_delete);
mu_run_test(test_ht_delete_optimized);
mu_run_test(test_rehash_on_delete);
mu_run_test(test_ht_insert_kvp);
mu_run_test(test_ht_insert_collision);
mu_run_test(test_ht_grow);

View file

@ -128,12 +128,12 @@ bool test_sdb_diff_ns() {
mu_assert_streq(diff,
"-NS test\n"
"-NS test/subspace\n"
"- test/subspace/here=lol\n"
"- test/subspace/some=values\n"
"- test/subspace/are=saved\n"
"- test/subspace/some=values\n"
"- test/subspace/here=lol\n"
"- test/b=test\n"
"- test/a=123\n"
"- test/c=hello\n"
"- test/b=test\n",
"- test/c=hello\n",
"ns removed diff");
free(diff);
@ -142,12 +142,12 @@ bool test_sdb_diff_ns() {
mu_assert_streq(diff,
"+NS test\n"
"+NS test/subspace\n"
"+ test/subspace/here=lol\n"
"+ test/subspace/some=values\n"
"+ test/subspace/are=saved\n"
"+ test/subspace/some=values\n"
"+ test/subspace/here=lol\n"
"+ test/b=test\n"
"+ test/a=123\n"
"+ test/c=hello\n"
"+ test/b=test\n",
"+ test/c=hello\n",
"ns added diff");
free(diff);
@ -166,9 +166,9 @@ bool test_sdb_diff_ns_sub() {
mu_assert("sub ns removed (diff)", !diff_str(a, b, &diff));
mu_assert_streq(diff,
"-NS test/subspace\n"
"- test/subspace/here=lol\n"
"- test/subspace/are=saved\n"
"- test/subspace/some=values\n"
"- test/subspace/are=saved\n",
"- test/subspace/here=lol\n",
"sub ns removed diff");
free(diff);
@ -176,9 +176,9 @@ bool test_sdb_diff_ns_sub() {
mu_assert("sub ns added (diff)", !diff_str(b, a, &diff));
mu_assert_streq(diff,
"+NS test/subspace\n"
"+ test/subspace/here=lol\n"
"+ test/subspace/are=saved\n"
"+ test/subspace/some=values\n"
"+ test/subspace/are=saved\n",
"+ test/subspace/here=lol\n",
"sub ns added diff");
free(diff);

View file

@ -275,8 +275,8 @@ static const char *text_ref_simple_unsorted =
"bbb=other stuff\n"
"\n"
"/subnamespace\n"
"key in sub=value in sub\n"
"\\/more stuff=in sub\n"
"key in sub=value in sub\n"
"\n"
"/subnamespace/subsub\n"
"some stuff=also down here\n";

View file

@ -181,7 +181,7 @@ Sdb *functions_ref_db() {
Sdb *db = sdb_new0();
sdb_set(db, "0x4d2", "{\"name\":\"effekt\",\"type\":1,\"stack\":0,\"maxstack\":0,\"ninstr\":0,\"bp_frame\":true,\"pure\":true,\"bbs\":[1337]}");
sdb_set(db, "0xbeef", "{\"name\":\"eskapist\",\"bits\":32,\"type\":16,\"stack\":0,\"maxstack\":0,\"ninstr\":0,\"bp_frame\":true,\"bbs\":[]}");
sdb_set(db, "0x539", "{\"name\":\"hirsch\",\"bits\":16,\"type\":0,\"cc\":\"fancycall\",\"stack\":42,\"maxstack\":123,\"ninstr\":13,\"bp_frame\":true,\"bp_off\":4,\"bbs\":[1337,1234],\"imports\":[\"earth\",\"rise\"],\"labels\":{\"beach\":1400,\"another\":1450,\"year\":1440}}");
sdb_set(db, "0x539", "{\"name\":\"hirsch\",\"bits\":16,\"type\":0,\"cc\":\"fancycall\",\"stack\":42,\"maxstack\":123,\"ninstr\":13,\"bp_frame\":true,\"bp_off\":4,\"bbs\":[1337,1234],\"imports\":[\"earth\",\"rise\"],\"labels\":{\"year\":1440,\"another\":1450,\"beach\":1400}}");
sdb_set(db, "0xdead", "{\"name\":\"agnosie\",\"bits\":32,\"type\":8,\"stack\":0,\"maxstack\":0,\"ninstr\":0,\"bp_frame\":true,\"bbs\":[]}");
sdb_set(db, "0xc0ffee", "{\"name\":\"lifnej\",\"bits\":32,\"type\":32,\"stack\":0,\"maxstack\":0,\"ninstr\":0,\"bp_frame\":true,\"bbs\":[]}");
sdb_set(db, "0x1092", "{\"name\":\"hiberno\",\"bits\":32,\"type\":2,\"stack\":0,\"maxstack\":0,\"ninstr\":0,\"bbs\":[]}");