mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-08-14 17:28:07 +00:00
fix info stream layout
This commit is contained in:
@@ -1,234 +1,25 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#include "third_party/martins_bitscan/bitscan.h"
|
||||
|
||||
internal U32Array
|
||||
bit_array_init32(Arena *arena, U64 word_count)
|
||||
bit_array_init32(Arena *arena, U64 bit_count)
|
||||
{
|
||||
U32Array result;
|
||||
result.count = CeilIntegerDiv(word_count, 32);
|
||||
result.v = push_array(arena, U32, word_count);
|
||||
return result;
|
||||
U64 count = CeilIntegerDiv(bit_count, 32);
|
||||
return (U32Array){ .count = count, .v = push_array(arena, U32, count) };
|
||||
}
|
||||
|
||||
internal U64
|
||||
bit_array_scan_left_to_right32(U32Array bit_array, U64 lo, U64 hi, B32 state)
|
||||
{
|
||||
Assert(lo < bit_array.count*32);
|
||||
Assert(hi <= bit_array.count*32);
|
||||
Assert(lo <= hi);
|
||||
Assert(state == 0 || state == 1);
|
||||
|
||||
U64 word_lo = lo / 32;
|
||||
U64 word_hi = CeilIntegerDiv(hi, 32) - 1;
|
||||
|
||||
U64 word_idx = word_lo;
|
||||
U64 bit_idx = 0;
|
||||
|
||||
U64 scan_count = hi - lo;
|
||||
if (scan_count < 32) {
|
||||
U64 bit_lo = lo % 32;
|
||||
U64 bit_hi = hi % 32;
|
||||
U64 word = bit_array.v[word_idx];
|
||||
word ^= state - 1;
|
||||
word &= (1U << bit_hi) - (1U << bit_lo);
|
||||
if (word) {
|
||||
bit_idx = ctz32(word);
|
||||
goto exit;
|
||||
}
|
||||
} else {
|
||||
U32 first_word = bit_array.v[word_idx];
|
||||
first_word ^= state - 1;
|
||||
first_word &= ~0u << (lo % 32);
|
||||
if (first_word) {
|
||||
bit_idx = ctz32(first_word);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (word_idx += 1; word_idx < word_hi; word_idx += 1) {
|
||||
U32 word = bit_array.v[word_idx];
|
||||
word ^= state - 1;
|
||||
if (word != 0) {
|
||||
bit_idx = ctz32(word);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
U64 bit_hi = hi - (word_idx * 32);
|
||||
U32 last_word = bit_array.v[word_idx];
|
||||
last_word ^= state - 1;
|
||||
last_word &= (1 << bit_hi) - 1;
|
||||
if (last_word) {
|
||||
bit_idx = ctz32(last_word);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
word_idx = 0;
|
||||
bit_idx = max_U32;
|
||||
|
||||
exit:;
|
||||
|
||||
U64 result = word_idx * 32 + bit_idx;
|
||||
return result;
|
||||
return bitscan_lsb_index32(bit_array.v, lo, hi, state);
|
||||
}
|
||||
|
||||
internal U64
|
||||
bit_array_scan_right_to_left32(U32Array bit_array, U64 lo, U64 hi, B32 state)
|
||||
{
|
||||
Assert(lo <= hi);
|
||||
Assert(state == 0 || state == 1);
|
||||
|
||||
S64 word_lo = lo / 32;
|
||||
S64 word_hi = CeilIntegerDiv(hi, 32) - 1;
|
||||
|
||||
S64 word_idx = word_hi;
|
||||
S64 bit_idx = -1;
|
||||
|
||||
U64 scan_count = hi - lo;
|
||||
if (scan_count < 32) {
|
||||
S64 bit_lo = lo % 32;
|
||||
S64 bit_hi = bit_lo + scan_count;
|
||||
U32 word = bit_array.v[word_idx];
|
||||
for (bit_idx = bit_hi; bit_idx >= bit_lo; bit_idx -= 1) {
|
||||
U32 bit = ExtractBit(word, bit_idx);
|
||||
if (bit == state) {
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
U32 last_word = bit_array.v[word_idx];
|
||||
S64 bit_hi = hi % 32;
|
||||
for (bit_idx = bit_hi; bit_idx >= 0; bit_idx -= 1) {
|
||||
U32 bit = ExtractBit(last_word, bit_idx);
|
||||
if (bit == state) {
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
for (word_idx -= 1; word_idx > word_lo; word_idx -= 1) {
|
||||
U32 word = bit_array.v[word_idx];
|
||||
for (bit_idx = 32 - 1; bit_idx >= 0; bit_idx -= 1) {
|
||||
U32 bit = ExtractBit(word, bit_idx);
|
||||
if (bit == state) {
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
U32 first_word = bit_array.v[word_idx];
|
||||
S64 bit_lo = lo % 32;
|
||||
for (bit_idx = 32 - 1; bit_idx >= bit_lo; bit_idx -= 1) {
|
||||
U32 bit = ExtractBit(first_word, bit_idx);
|
||||
if (bit == state) {
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
word_idx = 0;
|
||||
bit_idx = max_U32;
|
||||
|
||||
exit:;
|
||||
|
||||
S64 result_s64 = word_idx * 32 + bit_idx;
|
||||
U64 result_u64 = (U64)result_s64;
|
||||
return result_u64;
|
||||
}
|
||||
|
||||
internal Rng1U64
|
||||
bit_array_scan_left_to_right32_contiguous(U32Array bit_array, U64 lo, U64 hi, B32 state, U64 in_row_count)
|
||||
{
|
||||
Rng1U64 result = rng_1u64(max_U64, max_U64);
|
||||
|
||||
U64 curr_count = 0, rover = lo;
|
||||
while (curr_count < in_row_count) {
|
||||
rover = bit_array_scan_left_to_right32(bit_array, rover, hi, state);
|
||||
|
||||
// no more bits in range
|
||||
if (rover >= hi) {
|
||||
break;
|
||||
}
|
||||
|
||||
// set first match
|
||||
if (result.v[0] == max_U64) {
|
||||
result = rng_1u64(rover, rover);
|
||||
continue;
|
||||
}
|
||||
|
||||
// reset on non-contiguous range
|
||||
B32 is_bit_index_not_adjoined = (result.v[0] + 1 < rover);
|
||||
if (is_bit_index_not_adjoined) {
|
||||
curr_count = 0;
|
||||
result = rng_1u64(max_U64, max_U64);
|
||||
continue;
|
||||
}
|
||||
|
||||
// advance
|
||||
result.v[1] = rover;
|
||||
curr_count -= 1;
|
||||
}
|
||||
|
||||
// did we allocate enough bits?
|
||||
if (curr_count != in_row_count) {
|
||||
result = rng_1u64(max_U64, max_U64);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal Rng1U64
|
||||
bit_array_scan_right_to_left32_contiguous(U32Array bit_array, U64 lo, U64 hi, B32 state, U64 in_row_count)
|
||||
{
|
||||
Rng1U64 result = rng_1u64(max_U64, max_U64);
|
||||
|
||||
U64 curr_count = 0, rover = lo;
|
||||
while (curr_count < in_row_count) {
|
||||
rover = bit_array_scan_right_to_left32(bit_array, lo, rover, state);
|
||||
|
||||
// no more bits in range
|
||||
if (rover >= hi) {
|
||||
break;
|
||||
}
|
||||
|
||||
// set first match
|
||||
if (result.v[0] == max_U64) {
|
||||
result = rng_1u64(rover, rover);
|
||||
continue;
|
||||
}
|
||||
|
||||
// reset on non-contiguous range
|
||||
B32 is_bit_index_not_adjoined = (result.v[0] + 1 < rover);
|
||||
if (is_bit_index_not_adjoined) {
|
||||
curr_count = 0;
|
||||
result = rng_1u64(max_U64, max_U64);
|
||||
continue;
|
||||
}
|
||||
|
||||
// advance
|
||||
result.v[0] = rover;
|
||||
curr_count -= 1;
|
||||
}
|
||||
|
||||
// did we allocate enough bits?
|
||||
if (curr_count != in_row_count) {
|
||||
result = rng_1u64(max_U64, max_U64);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal U64
|
||||
bit_array_find_next_unset_bit32(U32Array bit_array)
|
||||
{
|
||||
U64 result = bit_array_scan_left_to_right32(bit_array, 0, bit_array.count*32, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal U64
|
||||
bit_array_find_next_set_bit32(U32Array bit_array)
|
||||
{
|
||||
U64 result = bit_array_scan_left_to_right32(bit_array, 0, bit_array.count*32, 1);
|
||||
return result;
|
||||
return bitscan_msb_index32(bit_array.v, lo, hi, state);
|
||||
}
|
||||
|
||||
internal void
|
||||
@@ -244,14 +35,6 @@ bit_array_set_bit32(U32Array bit_array, U64 idx, B32 state)
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
bit_array_set_bit_range32(U32Array bit_array, Rng1U64 range, B32 state)
|
||||
{
|
||||
for (U64 idx = range.min ; idx < range.max; idx += 1) {
|
||||
bit_array_set_bit32(bit_array, idx, state);
|
||||
}
|
||||
}
|
||||
|
||||
internal U32
|
||||
bit_array_get_bit32(U32Array bit_array, U64 idx)
|
||||
{
|
||||
|
||||
@@ -3,16 +3,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
internal U32Array bit_array_init32 (Arena *arena, U64 word_count);
|
||||
internal U64 bit_array_scan_left_to_right32 (U32Array bit_array, U64 lo, U64 hi, B32 state);
|
||||
internal U64 bit_array_scan_right_to_left32 (U32Array bit_array, U64 lo, U64 hi, B32 state);
|
||||
internal Rng1U64 bit_array_scan_left_to_right32_contiguous(U32Array bit_array, U64 lo, U64 hi, B32 state, U64 in_row_count);
|
||||
internal Rng1U64 bit_array_scan_right_to_left32_contiguous(U32Array bit_array, U64 lo, U64 hi, B32 state, U64 in_row_count);
|
||||
internal B32 byte_scan_right_to_left (U8 *start, U8 *opl, U8 byte, U64 *offset_out);
|
||||
internal U64 bit_array_find_next_unset_bit32 (U32Array bit_array);
|
||||
internal U64 bit_array_find_next_set_bit32 (U32Array bit_array);
|
||||
internal void bit_array_set_bit32 (U32Array bit_array, U64 idx, B32 state);
|
||||
internal void bit_array_set_bit_range32 (U32Array bit_array, Rng1U64 range, B32 state);
|
||||
internal U32 bit_array_get_bit32 (U32Array bit_array, U64 idx);
|
||||
internal U32Array bit_array_init32 (Arena *arena, U64 word_count);
|
||||
internal U64 bit_array_scan_left_to_right32(U32Array bit_array, U64 lo, U64 hi, B32 state);
|
||||
internal U64 bit_array_scan_right_to_left32(U32Array bit_array, U64 lo, U64 hi, B32 state);
|
||||
internal void bit_array_set_bit32 (U32Array bit_array, U64 idx, B32 state);
|
||||
internal U32 bit_array_get_bit32 (U32Array bit_array, U64 idx);
|
||||
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ pdb_hash_table_alloc(PDB_HashTable *ht, U32 max)
|
||||
ht->deleted_bits = bit_array_init32(ht->arena, max);
|
||||
ht->max = max;
|
||||
ht->count = 0;
|
||||
bit_array_set_bit_range32(ht->deleted_bits, rng_1u64(0, max), 1);
|
||||
ProfEnd();
|
||||
}
|
||||
|
||||
@@ -148,51 +147,48 @@ pdb_hash_table_from_data(PDB_HashTable *ht,
|
||||
return error;
|
||||
}
|
||||
|
||||
internal int
|
||||
pdb_hash_table_bucket_is_before(void *raw_a, void *raw_b)
|
||||
{
|
||||
PDB_HashTableBucket *a = *(PDB_HashTableBucket **)raw_a, *b = *(PDB_HashTableBucket **)raw_b;
|
||||
return a->insert_idx < b->insert_idx;
|
||||
}
|
||||
|
||||
internal String8
|
||||
pdb_data_from_hash_table(Arena *arena,
|
||||
PDB_HashTable *ht,
|
||||
B32 has_local_data,
|
||||
PDB_HashTablePackFunc *pack_func,
|
||||
void *pack_ud)
|
||||
pdb_data_from_hash_table(Arena *arena, PDB_HashTable *ht, PDB_HashTablePackFunc *pack_func, void *pack_ud)
|
||||
{
|
||||
ProfBeginFunction();
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
|
||||
String8Array key_arr = {0};
|
||||
String8Array value_arr = {0};
|
||||
pdb_hash_table_get_present_keys_and_values(scratch.arena, ht, &key_arr, &value_arr);
|
||||
String8List kv_srl = {0}; str8_serial_begin(scratch.arena, &kv_srl);
|
||||
PDB_HashTableBucket **buckets = pdb_hash_table_get_present_buckets(scratch.arena, ht);
|
||||
for EachIndex(i, ht->count) { pack_func(scratch.arena, &kv_srl, buckets[i], buckets[i]->key, buckets[i]->value, pack_ud); }
|
||||
|
||||
String8List local_data_srl = {0};
|
||||
String8List key_value_srl = {0};
|
||||
str8_serial_begin(scratch.arena, &local_data_srl);
|
||||
str8_serial_begin(scratch.arena, &key_value_srl);
|
||||
// compute count of present words that are needed
|
||||
U64 present_word_count = 0;
|
||||
U64 present_msb = bit_array_scan_right_to_left32(ht->present_bits, 0, ht->present_bits.count*32, 1);
|
||||
if (present_msb < ht->present_bits.count*32) { present_word_count = present_msb / 32 + 1; }
|
||||
|
||||
for (U64 i = 0; i < ht->count; i += 1) {
|
||||
String8 key = key_arr.v[i];
|
||||
String8 value = value_arr.v[i];
|
||||
pack_func(scratch.arena, &local_data_srl, &key_value_srl, key, value, pack_ud);
|
||||
}
|
||||
// compute count of deleted words that are needed
|
||||
U64 deleted_word_count = 0;
|
||||
U64 deleted_msb = bit_array_scan_right_to_left32(ht->deleted_bits, 0, ht->deleted_bits.count*32, 1);
|
||||
if (deleted_msb < ht->deleted_bits.count*32) { deleted_word_count = deleted_msb / 32 + 1; }
|
||||
|
||||
// serialize hash table
|
||||
String8List srl = {0};
|
||||
str8_serial_begin(scratch.arena, &srl);
|
||||
if (has_local_data) {
|
||||
U32 local_data_size32 = safe_cast_u32(local_data_srl.total_size);
|
||||
str8_serial_push_u32(scratch.arena, &srl, local_data_size32);
|
||||
str8_list_concat_in_place(&srl, &local_data_srl);
|
||||
}
|
||||
str8_serial_push_u32(scratch.arena, &srl, ht->count);
|
||||
str8_serial_push_u32(scratch.arena, &srl, ht->max);
|
||||
str8_serial_push_u32(scratch.arena, &srl, ht->present_bits.count);
|
||||
str8_serial_push_array(scratch.arena, &srl, &ht->present_bits.v[0], ht->present_bits.count);
|
||||
str8_serial_push_u32(scratch.arena, &srl, ht->deleted_bits.count);
|
||||
str8_serial_push_array(scratch.arena, &srl, &ht->deleted_bits.v[0], ht->deleted_bits.count);
|
||||
str8_list_concat_in_place(&srl, &key_value_srl);
|
||||
String8 data = str8_serial_end(arena, &srl);
|
||||
// write hash table
|
||||
String8List ht_srl = {0}; str8_serial_begin(scratch.arena, &ht_srl);
|
||||
str8_serial_push_u32 (scratch.arena, &ht_srl, ht->count);
|
||||
str8_serial_push_u32 (scratch.arena, &ht_srl, ht->max);
|
||||
str8_serial_push_u32 (scratch.arena, &ht_srl, present_word_count);
|
||||
str8_serial_push_array(scratch.arena, &ht_srl, &ht->present_bits.v[0], present_word_count);
|
||||
str8_serial_push_u32 (scratch.arena, &ht_srl, deleted_word_count);
|
||||
str8_serial_push_array(scratch.arena, &ht_srl, &ht->deleted_bits.v[0], deleted_word_count);
|
||||
str8_list_concat_in_place(&ht_srl, &kv_srl);
|
||||
|
||||
String8 result = str8_serial_end(arena, &ht_srl);
|
||||
|
||||
scratch_end(scratch);
|
||||
ProfEnd();
|
||||
return data;
|
||||
return result;
|
||||
}
|
||||
|
||||
internal void
|
||||
@@ -230,8 +226,9 @@ pdb_hash_table_try_set(PDB_HashTable *ht, String8 key, String8 value)
|
||||
B32 is_present = pdb_hash_table_is_present(ht, ibucket);
|
||||
if ( ! is_present) {
|
||||
PDB_HashTableBucket *bucket = &ht->bucket_arr[ibucket];
|
||||
bucket->key = push_str8_copy(ht->arena, key);
|
||||
bucket->value = push_str8_copy(ht->arena, value);
|
||||
bucket->key = push_str8_copy(ht->arena, key);
|
||||
bucket->value = push_str8_copy(ht->arena, value);
|
||||
bucket->insert_idx = ht->count;
|
||||
|
||||
bit_array_set_bit32(ht->present_bits, ibucket, 1);
|
||||
bit_array_set_bit32(ht->deleted_bits, ibucket, 0);
|
||||
@@ -254,7 +251,7 @@ pdb_hash_table_set(PDB_HashTable *ht, String8 key, String8 value)
|
||||
// should resize?
|
||||
U64 load_factor = pdb_hash_table_compute_load_factor(ht->max);
|
||||
if (ht->count + 1 >= load_factor) {
|
||||
pdb_hash_table_grow(ht, ht->max * 2);
|
||||
pdb_hash_table_grow(ht, load_factor * 2);
|
||||
}
|
||||
|
||||
// set new item
|
||||
@@ -328,6 +325,21 @@ pdb_hash_table_is_deleted(PDB_HashTable *ht, U32 k)
|
||||
return bit_array_is_bit_set(ht->deleted_bits, k);
|
||||
}
|
||||
|
||||
internal PDB_HashTableBucket **
|
||||
pdb_hash_table_get_present_buckets(Arena *arena, PDB_HashTable *ht)
|
||||
{
|
||||
U64 result_count = 0;
|
||||
PDB_HashTableBucket **result = push_array(arena, PDB_HashTableBucket *, ht->count);
|
||||
for EachIndex(bucket_idx, ht->max) {
|
||||
if (bit_array_is_bit_set(ht->present_bits, bucket_idx)) {
|
||||
PDB_HashTableBucket *bucket = &ht->bucket_arr[bucket_idx];
|
||||
Assert(result_count < ht->count);
|
||||
result[result_count++] = bucket;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal void
|
||||
pdb_hash_table_get_present_keys_and_values(Arena *arena, PDB_HashTable *ht, String8Array *keys_out, String8Array *values_out)
|
||||
{
|
||||
@@ -422,12 +434,7 @@ PDB_HASH_TABLE_PACK_FUNC(pdb_named_stream_ht_pack)
|
||||
{
|
||||
Assert(!ud);
|
||||
Assert(value.size == sizeof(U32));
|
||||
|
||||
U64 key_data_offset = local_data_srl->total_size;
|
||||
str8_serial_push_cstr(arena, local_data_srl, key);
|
||||
|
||||
U32 key_data_offset32 = safe_cast_u32(key_data_offset);
|
||||
str8_serial_push_u32(arena, key_value_srl, key_data_offset32);
|
||||
str8_serial_push_u32(arena, key_value_srl, bucket->key_offset);
|
||||
str8_serial_push_string(arena, key_value_srl, value);
|
||||
}
|
||||
|
||||
@@ -436,7 +443,7 @@ PDB_HASH_TABLE_PACK_FUNC(pdb_hash_adj_ht_pack)
|
||||
{
|
||||
Assert(value.size == sizeof(CV_TypeIndex));
|
||||
|
||||
PDB_StringTable *strtab = (PDB_StringTable*)ud;
|
||||
PDB_StringTable *strtab = ud;
|
||||
|
||||
PDB_StringIndex string_idx = PDB_INVALID_STRING_INDEX;
|
||||
B32 is_found = pdb_strtab_search(strtab, key, &string_idx);
|
||||
@@ -453,7 +460,7 @@ PDB_HASH_TABLE_PACK_FUNC(pdb_src_header_block_ht_pack)
|
||||
{
|
||||
Assert(value.size == sizeof(PDB_SrcHeaderBlockEntry));
|
||||
|
||||
PDB_StringTable *strtab = (PDB_StringTable*)ud;
|
||||
PDB_StringTable *strtab = ud;
|
||||
|
||||
PDB_StringIndex path_idx = 0;
|
||||
B32 is_found = pdb_strtab_search(strtab, key, &path_idx);
|
||||
@@ -488,22 +495,44 @@ pdb_named_stream_ht_from_data(PDB_HashTable *ht, String8 data, U64 *read_bytes_o
|
||||
internal String8
|
||||
pdb_data_from_hash_adj_hash_table(Arena *arena, PDB_HashTable *ht, PDB_StringTable *strtab)
|
||||
{
|
||||
String8 data = pdb_data_from_hash_table(arena, ht, 0, pdb_hash_adj_ht_pack, strtab);
|
||||
return data;
|
||||
return pdb_data_from_hash_table(arena, ht, pdb_hash_adj_ht_pack, strtab);
|
||||
}
|
||||
|
||||
internal String8
|
||||
pdb_data_from_src_header_block_ht(Arena *arena, PDB_HashTable *ht, PDB_StringTable *strtab)
|
||||
{
|
||||
String8 data = pdb_data_from_hash_table(arena, ht, 0, pdb_src_header_block_ht_pack, strtab);
|
||||
return data;
|
||||
return pdb_data_from_hash_table(arena, ht, pdb_src_header_block_ht_pack, strtab);
|
||||
}
|
||||
|
||||
internal String8
|
||||
pdb_data_from_named_stream_ht(Arena *arena, PDB_HashTable *ht)
|
||||
{
|
||||
String8 data = pdb_data_from_hash_table(arena, ht, 1, pdb_named_stream_ht_pack, 0);
|
||||
return data;
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
|
||||
// serialize names (layout must be in insert order)
|
||||
String8List key_data_srl = {0}; str8_serial_begin(scratch.arena, &key_data_srl);
|
||||
{
|
||||
PDB_HashTableBucket **buckets = pdb_hash_table_get_present_buckets(scratch.arena, ht);
|
||||
radsort(buckets, ht->count, pdb_hash_table_bucket_is_before);
|
||||
for EachIndex(i, ht->count) {
|
||||
buckets[i]->key_offset = key_data_srl.total_size;
|
||||
str8_serial_push_cstr(scratch.arena, &key_data_srl, buckets[i]->key);
|
||||
}
|
||||
}
|
||||
|
||||
// serialize hash table
|
||||
String8 ht_data = pdb_data_from_hash_table(arena, ht, pdb_named_stream_ht_pack, 0);
|
||||
|
||||
// put names and hash table together
|
||||
String8List srl = {0}; str8_serial_begin(scratch.arena, &srl);
|
||||
str8_serial_push_u32(scratch.arena, &srl, safe_cast_u32(key_data_srl.total_size));
|
||||
str8_serial_push_data_list(scratch.arena, &srl, key_data_srl.first);
|
||||
str8_serial_push_string(scratch.arena, &srl, ht_data);
|
||||
|
||||
String8 result = str8_serial_end(arena, &srl);
|
||||
|
||||
scratch_end(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
@@ -830,126 +859,6 @@ pdb_type_server_alloc(U64 bucket_cap)
|
||||
return ts;
|
||||
}
|
||||
|
||||
internal PDB_TypeServer *
|
||||
pdb_type_server_open_v80(MSF_Context *msf, MSF_StreamNumber sn, PDB_StringTable *strtab)
|
||||
{
|
||||
ProfBeginFunction();
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
|
||||
PDB_TypeServer *ts = NULL;
|
||||
|
||||
PDB_TpiHeader header = {0};
|
||||
msf_stream_seek(msf, sn, 0);
|
||||
MSF_UInt read_header_size = msf_stream_read_struct(msf, sn, &header);
|
||||
|
||||
// have we read enough bytes?
|
||||
if (read_header_size != sizeof(PDB_TpiHeader)) {
|
||||
goto exit;
|
||||
}
|
||||
// is lowest non-simple type index valid?
|
||||
if (header.ti_lo < CV_MinComplexTypeIndex) {
|
||||
goto exit;
|
||||
}
|
||||
// is high non-simple type index valid?
|
||||
if (header.ti_lo > header.ti_hi) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// validate hash bucket count
|
||||
if (header.hash_bucket_count == 0) {
|
||||
goto exit;
|
||||
}
|
||||
if (header.hash_bucket_count > PDB_TYPE_SERVER_HASH_BUCKET_COUNT_MAX) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// are there enough bytes in the stream to read hash values?
|
||||
U64 hash_stream_size = msf_stream_get_size(msf, header.hash_sn);
|
||||
if (header.hash_vals.off + header.hash_vals.size > hash_stream_size) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ts = pdb_type_server_alloc(header.hash_bucket_count);
|
||||
|
||||
// read & parse code view types
|
||||
String8 types_data = msf_stream_read_block(ts->arena, msf, sn, header.leaf_data_size);
|
||||
CV_DebugT debug_t = cv_debug_t_from_data(scratch.arena, types_data, PDB_LEAF_ALIGN);
|
||||
|
||||
// read hash data
|
||||
U8 *hash_buffer = push_array(scratch.arena, U8, header.hash_vals.size);
|
||||
msf_stream_seek(msf, header.hash_sn, header.hash_vals.off);
|
||||
MSF_UInt hash_buffer_size = msf_stream_read(msf, header.hash_sn, hash_buffer, header.hash_vals.size);
|
||||
Assert(hash_buffer_size == header.hash_vals.size);
|
||||
|
||||
// rebuild type buckets
|
||||
for (U64 cursor = 0, leaf_idx = 0;
|
||||
cursor + header.hash_key_size <= hash_buffer_size;
|
||||
cursor += header.hash_key_size, leaf_idx += 1) {
|
||||
String8 raw_leaf = cv_debug_t_get_raw_leaf(&debug_t, leaf_idx);
|
||||
|
||||
str8_list_push(ts->arena, &ts->leaf_list, raw_leaf);
|
||||
|
||||
// read out bucket hash
|
||||
U64 hash = 0;
|
||||
MemoryCopy(&hash, hash_buffer + cursor, header.hash_key_size);
|
||||
|
||||
// push bucket
|
||||
PDB_TypeBucket *bucket = push_array(ts->arena, PDB_TypeBucket, 1);
|
||||
bucket->raw_leaf = raw_leaf;
|
||||
bucket->type_index = header.ti_lo + leaf_idx;
|
||||
SLLStackPush(ts->buckets[hash], bucket);
|
||||
}
|
||||
|
||||
// adjust type buckets
|
||||
msf_stream_seek(msf, header.hash_sn, header.hash_adj.off);
|
||||
String8 adjust_data = msf_stream_read_block(scratch.arena, msf, header.hash_sn, header.hash_adj.size);
|
||||
|
||||
// open adjust hash table
|
||||
PDB_HashTableParseError hash_adj_parse_error = pdb_hash_adj_hash_table_from_data(&ts->hash_adj, adjust_data, strtab, 0);
|
||||
if (hash_adj_parse_error == PDB_HashTableParseError_OUT_OF_BYTES) {
|
||||
pdb_hash_table_alloc(&ts->hash_adj, 16);
|
||||
} else {
|
||||
Assert(hash_adj_parse_error == PDB_HashTableParseError_OK);
|
||||
}
|
||||
|
||||
// grab keys and values
|
||||
String8Array key_arr = {0};
|
||||
String8Array value_arr = {0};
|
||||
pdb_hash_table_get_present_keys_and_values(scratch.arena, &ts->hash_adj, &key_arr, &value_arr);
|
||||
|
||||
// adjust type buckets
|
||||
for (U64 i = 0; i < ts->hash_adj.count; i += 1) {
|
||||
String8 type_name = key_arr.v[i];
|
||||
CV_TypeIndex type_index = *(CV_TypeIndex*)value_arr.v[i].str;
|
||||
|
||||
// name -> hash
|
||||
U64 hash = pdb_hash_v1(type_name);
|
||||
hash %= ts->bucket_cap;
|
||||
|
||||
// search for type bucket
|
||||
PDB_TypeBucket *curr, *prev;
|
||||
for (curr = ts->buckets[hash], prev = 0; curr != 0; prev = curr, curr = curr->next) {
|
||||
if (curr->type_index == type_index) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// move type to the head
|
||||
if (prev && curr) {
|
||||
prev->next = curr->next;
|
||||
curr->next = ts->buckets[hash];
|
||||
ts->buckets[hash] = curr;
|
||||
}
|
||||
|
||||
Assert(curr);
|
||||
}
|
||||
|
||||
exit:
|
||||
scratch_end(scratch);
|
||||
ProfEnd();
|
||||
return ts;
|
||||
}
|
||||
|
||||
internal
|
||||
THREAD_POOL_TASK_FUNC(pdb_write_type_to_bucket_map_32_task)
|
||||
{
|
||||
@@ -1509,7 +1418,7 @@ pdb_info_alloc(U32 age, COFF_TimeStamp time_stamp, Guid guid)
|
||||
info->age = age;
|
||||
info->guid = guid;
|
||||
pdb_strtab_alloc(&info->strtab, 0x3fff);
|
||||
pdb_hash_table_alloc(&info->named_stream_ht, 4);
|
||||
pdb_hash_table_alloc(&info->named_stream_ht, 1);
|
||||
pdb_hash_table_alloc(&info->src_header_block_ht, 8);
|
||||
ProfEnd();
|
||||
return info;
|
||||
@@ -1615,9 +1524,13 @@ pdb_info_build(PDB_InfoContext *info, MSF_Context *msf, MSF_StreamNumber sn)
|
||||
Temp scratch = scratch_begin(0,0);
|
||||
|
||||
// finalize named streams
|
||||
pdb_info_build_src_header_block(info, msf);
|
||||
if (info->src_header_block_ht.count) {
|
||||
pdb_info_build_src_header_block(info, msf);
|
||||
}
|
||||
pdb_info_build_link_info(info, msf);
|
||||
pdb_info_build_names(info, msf);
|
||||
if (info->strtab.bucket_count > 1) {
|
||||
pdb_info_build_names(info, msf);
|
||||
}
|
||||
|
||||
// serialize named streams hash table
|
||||
String8 named_stream_ht_data = pdb_data_from_named_stream_ht(scratch.arena, &info->named_stream_ht);
|
||||
@@ -1634,6 +1547,7 @@ pdb_info_build(PDB_InfoContext *info, MSF_Context *msf, MSF_StreamNumber sn)
|
||||
str8_serial_begin(scratch.arena, &info_srl);
|
||||
str8_serial_push_struct(scratch.arena, &info_srl, &header);
|
||||
str8_serial_push_string(scratch.arena, &info_srl, named_stream_ht_data);
|
||||
str8_serial_push_u32(scratch.arena, &info_srl, 0);
|
||||
if (info->flags & PDB_FeatureFlag_HAS_ID_STREAM) {
|
||||
str8_serial_push_u32(scratch.arena, &info_srl, PDB_FeatureSig_VC140);
|
||||
}
|
||||
@@ -1666,12 +1580,9 @@ internal MSF_StreamNumber
|
||||
pdb_push_named_stream(PDB_HashTable *named_stream_ht, MSF_Context *msf, String8 name)
|
||||
{
|
||||
ProfBeginFunction();
|
||||
Temp scratch = scratch_begin(0,0);
|
||||
MSF_StreamNumber sn = msf_stream_alloc(msf);
|
||||
String8 name_cstr = push_cstr(scratch.arena, name);
|
||||
U32 sn32 = (U32)sn;
|
||||
pdb_hash_table_set(named_stream_ht, name_cstr, str8_struct(&sn32));
|
||||
scratch_end(scratch);
|
||||
pdb_hash_table_set(named_stream_ht, name, str8_struct(&sn32));
|
||||
ProfEnd();
|
||||
return sn;
|
||||
}
|
||||
@@ -2665,8 +2576,6 @@ dbi_build_module_info(Arena *arena, PDB_DbiContext *dbi, MSF_Context *msf)
|
||||
// TODO: generate EC info
|
||||
header->src_file = 0;
|
||||
header->pdb_file = 0;
|
||||
|
||||
Assert(header->sn != MSF_INVALID_STREAM_NUMBER);
|
||||
|
||||
// push module info
|
||||
str8_serial_push_struct(arena, &module_info_list, header);
|
||||
@@ -2941,7 +2850,7 @@ dbi_build_dbg_header(Arena *arena, PDB_DbiContext *dbi, MSF_Context *msf)
|
||||
}
|
||||
|
||||
internal void
|
||||
dbi_build(TP_Context *tp, PDB_DbiContext *dbi, MSF_Context *msf, MSF_StreamNumber dbi_sn, CV_StringHashTable string_ht)
|
||||
dbi_build(TP_Context *tp, PDB_DbiContext *dbi, MSF_Context *msf, MSF_StreamNumber dbi_sn, CV_StringHashTable string_ht, B32 is_stripped)
|
||||
{
|
||||
ProfBeginFunction();
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
@@ -2976,6 +2885,10 @@ dbi_build(TP_Context *tp, PDB_DbiContext *dbi, MSF_Context *msf, MSF_StreamNumbe
|
||||
header.flags = 0;
|
||||
header.machine = dbi->machine;
|
||||
header.reserved = 0;
|
||||
|
||||
if (is_stripped) {
|
||||
header.flags |= PDB_DbiHeaderFlag_Stripped;
|
||||
}
|
||||
|
||||
ProfBegin("MSF Write");
|
||||
|
||||
@@ -3229,7 +3142,7 @@ pdb_get_guid(PDB_Context *pdb)
|
||||
}
|
||||
|
||||
internal void
|
||||
pdb_build(TP_Context *tp, TP_Arena *pool_temp, PDB_Context *pdb, CV_StringHashTable string_ht, B32 build_gsi)
|
||||
pdb_build(TP_Context *tp, TP_Arena *pool_temp, PDB_Context *pdb, CV_StringHashTable string_ht, B32 build_gsi, B32 is_stripped)
|
||||
{
|
||||
ProfBeginFunction();
|
||||
|
||||
@@ -3244,7 +3157,7 @@ pdb_build(TP_Context *tp, TP_Arena *pool_temp, PDB_Context *pdb, CV_StringHashTa
|
||||
pdb_type_server_build(tp, ipi, strtab, pdb->msf, PDB_FixedStream_Ipi);
|
||||
}
|
||||
|
||||
dbi_build(tp, pdb->dbi, pdb->msf, PDB_FixedStream_Dbi, string_ht);
|
||||
dbi_build(tp, pdb->dbi, pdb->msf, PDB_FixedStream_Dbi, string_ht, is_stripped);
|
||||
pdb_info_build(pdb->info, pdb->msf, PDB_FixedStream_Info);
|
||||
|
||||
if (build_gsi) {
|
||||
|
||||
@@ -11,16 +11,12 @@
|
||||
////////////////////////////////
|
||||
// Hash table
|
||||
|
||||
#define PDB_HASH_TABLE_PACK_FUNC(name) void name(Arena *arena, String8List *local_data_srl, String8List *key_value_srl, String8 key, String8 value, void *ud)
|
||||
typedef PDB_HASH_TABLE_PACK_FUNC(PDB_HashTablePackFunc);
|
||||
|
||||
#define PDB_HASH_TABLE_UNPACK_FUNC(name) B32 name(void *ud, String8 local_data, String8 key_value_data, U64 *key_value_cursor, String8 *key_out, String8 *value_out)
|
||||
typedef PDB_HASH_TABLE_UNPACK_FUNC(PDB_HashTableUnpackFunc);
|
||||
|
||||
typedef struct PDB_HashTableBucket
|
||||
{
|
||||
String8 key;
|
||||
String8 value;
|
||||
U32 key_offset;
|
||||
U32 insert_idx;
|
||||
} PDB_HashTableBucket;
|
||||
|
||||
typedef struct PDB_HashTable
|
||||
@@ -33,6 +29,12 @@ typedef struct PDB_HashTable
|
||||
U32 count;
|
||||
} PDB_HashTable;
|
||||
|
||||
#define PDB_HASH_TABLE_PACK_FUNC(name) void name(Arena *arena, String8List *key_value_srl, PDB_HashTableBucket *bucket, String8 key, String8 value, void *ud)
|
||||
typedef PDB_HASH_TABLE_PACK_FUNC(PDB_HashTablePackFunc);
|
||||
|
||||
#define PDB_HASH_TABLE_UNPACK_FUNC(name) B32 name(void *ud, String8 local_data, String8 key_value_data, U64 *key_value_cursor, String8 *key_out, String8 *value_out)
|
||||
typedef PDB_HASH_TABLE_UNPACK_FUNC(PDB_HashTableUnpackFunc);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
PDB_HashTableParseError_OK,
|
||||
@@ -331,7 +333,7 @@ typedef struct
|
||||
|
||||
internal PDB_Context * pdb_alloc(U64 page_size, COFF_MachineType machine, COFF_TimeStamp time_stamp, U32 age, Guid guid);
|
||||
internal void pdb_release(PDB_Context **pdb_ptr);
|
||||
internal void pdb_build(TP_Context *tp, TP_Arena *pool_temp, PDB_Context *pdb, CV_StringHashTable string_ht, B32 build_gsi);
|
||||
internal void pdb_build(TP_Context *tp, TP_Arena *pool_temp, PDB_Context *pdb, CV_StringHashTable string_ht, B32 build_gsi, B32 is_stripped);
|
||||
internal void pdb_set_machine(PDB_Context *pdb, COFF_MachineType machine);
|
||||
internal void pdb_set_guid(PDB_Context *pdb, Guid guid);
|
||||
internal void pdb_set_time_stamp(PDB_Context *pdb, COFF_TimeStamp time_stamp);
|
||||
@@ -382,7 +384,7 @@ internal CV_SymbolNode * psi_push(PDB_PsiContext *psi, CV_Pub32Flags flags, U32
|
||||
// DBI
|
||||
|
||||
internal PDB_DbiContext * dbi_alloc(COFF_MachineType machine, U32 age);
|
||||
internal void dbi_build(TP_Context *tp, PDB_DbiContext *dbi, MSF_Context *msf, MSF_StreamNumber dbi_sn, CV_StringHashTable string_ht);
|
||||
internal void dbi_build(TP_Context *tp, PDB_DbiContext *dbi, MSF_Context *msf, MSF_StreamNumber dbi_sn, CV_StringHashTable string_ht, B32 is_stripped);
|
||||
internal void dbi_release(PDB_DbiContext **dbi_ptr);
|
||||
internal PDB_DbiModule * dbi_push_module(PDB_DbiContext *dbi, String8 obj_path, String8 lib_path);
|
||||
internal String8 dbi_module_read_symbol_data(Arena *arena, MSF_Context *msf, PDB_DbiModule *mod);
|
||||
@@ -403,7 +405,7 @@ internal void dbi_build_section_header_stream(PDB_DbiContex
|
||||
internal void pdb_hash_table_alloc(PDB_HashTable *ht, U32 max);
|
||||
internal void pdb_hash_table_release(PDB_HashTable *ht);
|
||||
internal PDB_HashTableParseError pdb_hash_table_from_data(PDB_HashTable *ht, String8 data, B32 has_local_data, PDB_HashTableUnpackFunc *unpack_func, void *unpack_ud, U64 *read_bytes_out);
|
||||
internal String8 pdb_data_from_hash_table(Arena *arena, PDB_HashTable *ht, B32 has_local_data, PDB_HashTablePackFunc *pack_func, void *pack_ud);
|
||||
internal String8 pdb_data_from_hash_table(Arena *arena, PDB_HashTable *ht, PDB_HashTablePackFunc *pack_func, void *pack_ud);
|
||||
internal void pdb_hash_table_set(PDB_HashTable *ht, String8 key, String8 value);
|
||||
internal B32 pdb_hash_table_get(PDB_HashTable *ht, String8 key, String8 *value_out);
|
||||
internal void pdb_hash_table_delete(PDB_HashTable *ht, String8 key);
|
||||
@@ -412,6 +414,7 @@ internal B32 pdb_hash_table_is_present(PDB_HashTable *ht, U3
|
||||
internal B32 pdb_hash_table_is_deleted(PDB_HashTable *ht, U32 k);
|
||||
internal U32 pdb_hash_table_hash(String8 key);
|
||||
internal void pdb_hash_table_grow(PDB_HashTable *ht, U64 new_capacity);
|
||||
internal PDB_HashTableBucket ** pdb_hash_table_get_present_buckets(Arena *arena, PDB_HashTable *ht);
|
||||
internal void pdb_hash_table_get_present_keys_and_values(Arena *arena, PDB_HashTable *ht, String8Array *keys_out, String8Array *values_out);
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
+664
@@ -0,0 +1,664 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// returns index of bit that is equal to "bit" value in [lo,hi) interval starting from lo position (lsb->msb)
|
||||
// returns "hi" value if none found, or interval is empty
|
||||
// "bit" value must be 0 or 1
|
||||
// "bits" array should be large enough to allow indexing it with [hi/32] or [hi/64] index
|
||||
|
||||
static inline size_t bitscan_lsb_index32(const uint32_t* bits, size_t lo, size_t hi, int bit);
|
||||
static inline size_t bitscan_lsb_index64(const uint64_t* bits, size_t lo, size_t hi, int bit);
|
||||
|
||||
// returns index of bit that is equal to "bit" value in [lo,hi) interval starting from hi position (msb->lsb)
|
||||
// returns "hi" value if none found, or interval is empty
|
||||
// "bit" value must be 0 or 1
|
||||
// "bits" array should be large enough to allow indexing it with [hi/32] or [hi/64] index
|
||||
|
||||
static inline size_t bitscan_msb_index32(const uint32_t* bits, size_t lo, size_t hi, int bit);
|
||||
static inline size_t bitscan_msb_index64(const uint64_t* bits, size_t lo, size_t hi, int bit);
|
||||
|
||||
//
|
||||
// implementation
|
||||
//
|
||||
|
||||
// to run tests:
|
||||
// cl.exe -O2 -fsanitize=address -DBITSCAN_TEST=1 -TC bitscan.h && bitscan.exe
|
||||
// clang.exe -O2 -fsanitize=address,undefined -DBITSCAN_TEST=1 -x c bitscan.h && a.exe
|
||||
|
||||
#if !(defined(__GNUC__) || defined(__clang__))
|
||||
# include <intrin.h> // _BitScanForward/Reverse
|
||||
#endif
|
||||
|
||||
#if defined(_M_AMD64) || defined(__x86_64__)
|
||||
# include <emmintrin.h> // SSE2
|
||||
#endif
|
||||
|
||||
// count of zero bits, either starting from lsb (trailing), or from msb (leading)
|
||||
// input value must be non-zero
|
||||
// bit index is from 0 (lsb) to 31/63 (msb)
|
||||
|
||||
static inline size_t bitscan__ctz32(uint32_t x)
|
||||
{
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
return __builtin_ctz(x);
|
||||
#else
|
||||
unsigned long index;
|
||||
_BitScanForward(&index, x);
|
||||
return index;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline size_t bitscan__ctz64(uint64_t x)
|
||||
{
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
return __builtin_ctzll(x);
|
||||
#else
|
||||
unsigned long index;
|
||||
_BitScanForward64(&index, x);
|
||||
return index;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline size_t bitscan__clz32(uint32_t x)
|
||||
{
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
return __builtin_clz(x);
|
||||
#else
|
||||
unsigned long index;
|
||||
_BitScanReverse(&index, x);
|
||||
return 31 - index;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline size_t bitscan__clz64(uint64_t x)
|
||||
{
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
return __builtin_clzll(x);
|
||||
#else
|
||||
unsigned long index;
|
||||
_BitScanReverse64(&index, x);
|
||||
return 63 - index;
|
||||
#endif
|
||||
}
|
||||
|
||||
// returns index of first bit "1" in x when scanning from msb downwards
|
||||
// bit index is from 0 (lsb) to 31/63 (msb)
|
||||
|
||||
static inline size_t bitscan__lindex32(uint32_t x)
|
||||
{
|
||||
return 31 - bitscan__clz32(x);
|
||||
}
|
||||
static inline size_t bitscan__lindex64(uint64_t x)
|
||||
{
|
||||
return 63 - bitscan__clz64(x);
|
||||
}
|
||||
|
||||
size_t bitscan_lsb_index32(const uint32_t* bits, size_t lo, size_t hi, int bit)
|
||||
{
|
||||
#if 0
|
||||
|
||||
// reference implementation
|
||||
for (size_t i = lo; i<hi; i++)
|
||||
{
|
||||
uint32_t word = bits[i/32];
|
||||
if (((word >> (i%32)) & 1) == bit)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
if (lo >= hi)
|
||||
{
|
||||
return hi;
|
||||
}
|
||||
|
||||
// to be able to use "ctz" for "find first zero bit"
|
||||
// do xor with this mask, it will flip all bit values
|
||||
// thus changing operation to "find first set bit" - which allows to use "ctz"
|
||||
const uint32_t mask = bit ? 0U : ~0U;
|
||||
|
||||
size_t count = hi - lo;
|
||||
size_t offset = lo / 32;
|
||||
size_t first = (unsigned)(-(int)lo) % 32; // 0 if lo%32 == 0, otherwise (32 - lo%32) % 32
|
||||
|
||||
if (first) // first < 32, how many max bits to use in top of first word
|
||||
{
|
||||
uint32_t word = bits[offset] ^ mask;
|
||||
|
||||
// first = lo%32 = 18
|
||||
// count = 10
|
||||
// in cases count > first, clamp it to first
|
||||
//
|
||||
// 3 2 1 0
|
||||
// 10987654321098765432109876543210 bit index
|
||||
// ....xxxxxxxxxx..................
|
||||
// ^ ^ ^
|
||||
// | | |
|
||||
// | | +------------------ lo%32 = 18
|
||||
// | | |
|
||||
// | +- count -+ count = 10 bits to process
|
||||
// | |
|
||||
// +--- first ---+ first = 32 - lo%32 = 14
|
||||
|
||||
size_t n = (count < first ? count : first);
|
||||
word &= (~0U >> (32 - n)) << (lo % 32);
|
||||
|
||||
if (word)
|
||||
{
|
||||
return offset * 32 + bitscan__ctz32(word);
|
||||
}
|
||||
offset += 1;
|
||||
count -= n;
|
||||
}
|
||||
|
||||
#if defined(_M_AMD64) || defined(__x86_64__)
|
||||
while (count >= 128)
|
||||
{
|
||||
__m128i words = _mm_loadu_si128((const __m128i*)&bits[offset]);
|
||||
__m128i cmp = _mm_cmpeq_epi32(words, _mm_set1_epi32(mask));
|
||||
|
||||
// if all 16-bytes of "words" are same as mask, then m will be 0
|
||||
uint16_t m = 1 + (uint16_t)_mm_movemask_epi8(cmp);
|
||||
if (m)
|
||||
{
|
||||
// find word index to use [0,4)
|
||||
size_t n = bitscan__ctz32(m) / 4;
|
||||
|
||||
uint32_t word = bits[offset + n] ^ mask;
|
||||
return offset * 32 + n * 32 + bitscan__ctz32(word);
|
||||
}
|
||||
|
||||
offset += 4;
|
||||
count -= 128;
|
||||
}
|
||||
#endif
|
||||
|
||||
while (count >= 32)
|
||||
{
|
||||
uint32_t word = bits[offset] ^ mask;
|
||||
if (word)
|
||||
{
|
||||
return offset * 32 + bitscan__ctz32(word);
|
||||
}
|
||||
offset += 1;
|
||||
count -= 32;
|
||||
}
|
||||
|
||||
if (count) // now count < 32, how many bits to process in bottom of last word
|
||||
{
|
||||
uint32_t word = bits[offset] ^ mask;
|
||||
|
||||
// use first count bits, rest of bits are masked out to 1
|
||||
word |= ~0U << count;
|
||||
|
||||
if (word)
|
||||
{
|
||||
return offset * 32 + bitscan__ctz32(word);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return hi;
|
||||
}
|
||||
|
||||
size_t bitscan_lsb_index64(const uint64_t* bits, size_t lo, size_t hi, int bit)
|
||||
{
|
||||
#if 0
|
||||
|
||||
// reference implementation
|
||||
for (size_t i = lo; i < hi; i++)
|
||||
{
|
||||
uint64_t word = bits[i / 64];
|
||||
if (((word >> (i % 64)) & 1) == bit)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
if (lo >= hi)
|
||||
{
|
||||
return hi;
|
||||
}
|
||||
|
||||
const uint64_t mask = bit ? 0ULL : ~0ULL;
|
||||
|
||||
size_t count = hi - lo;
|
||||
size_t offset = lo / 64;
|
||||
size_t first = (unsigned)(-(int)lo) % 64;
|
||||
|
||||
if (first)
|
||||
{
|
||||
uint64_t word = bits[offset] ^ mask;
|
||||
|
||||
size_t n = (count < first ? count : first);
|
||||
word &= (~0ULL >> (64 - n)) << (lo % 64);
|
||||
|
||||
if (word)
|
||||
{
|
||||
return offset * 64 + bitscan__ctz64(word);
|
||||
}
|
||||
offset += 1;
|
||||
count -= n;
|
||||
}
|
||||
|
||||
#if defined(_M_AMD64) || defined(__x86_64__)
|
||||
while (count >= 128)
|
||||
{
|
||||
__m128i words = _mm_loadu_si128((const __m128i*)&bits[offset]);
|
||||
__m128i cmp = _mm_cmpeq_epi32(words, _mm_set1_epi32((uint32_t)mask));
|
||||
uint16_t m = 1 + (uint16_t)_mm_movemask_epi8(cmp);
|
||||
if (m)
|
||||
{
|
||||
size_t n = bitscan__ctz32(m) / 8;
|
||||
|
||||
uint64_t word = bits[offset + n] ^ mask;
|
||||
return offset * 64 + n * 64 + bitscan__ctz64(word);
|
||||
}
|
||||
|
||||
offset += 2;
|
||||
count -= 128;
|
||||
}
|
||||
#endif
|
||||
|
||||
while (count >= 64)
|
||||
{
|
||||
uint64_t word = bits[offset] ^ mask;
|
||||
if (word)
|
||||
{
|
||||
return offset * 64 + bitscan__ctz64(word);
|
||||
}
|
||||
offset += 1;
|
||||
count -= 64;
|
||||
}
|
||||
|
||||
if (count)
|
||||
{
|
||||
uint64_t word = bits[offset] ^ mask;
|
||||
word |= ~0ULL << count;
|
||||
|
||||
if (word)
|
||||
{
|
||||
return offset * 64 + bitscan__ctz64(word);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return hi;
|
||||
}
|
||||
|
||||
size_t bitscan_msb_index32(const uint32_t* bits, size_t lo, size_t hi, int bit)
|
||||
{
|
||||
#if 0
|
||||
|
||||
// reference implementation
|
||||
for (size_t i = hi; i-- > lo; )
|
||||
{
|
||||
uint32_t word = bits[i/32];
|
||||
if (((word >> (i%32)) & 1) == bit)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
if (lo >= hi)
|
||||
{
|
||||
return hi;
|
||||
}
|
||||
|
||||
// to be able to use "clz" for "find last zero bit"
|
||||
// do xor with this mask, it will flip all bit values
|
||||
// thus changing operation to "find last set bit" - which allows to use "clz"
|
||||
const uint32_t mask = bit ? 0U : ~0U;
|
||||
|
||||
size_t count = hi - lo;
|
||||
size_t offset = (hi - 1) / 32;
|
||||
size_t first = hi % 32;
|
||||
|
||||
if (first) // first < 32, how many max bits to use in bottom of first word
|
||||
{
|
||||
uint32_t word = bits[offset] ^ mask;
|
||||
|
||||
// first = hi%32 = 14
|
||||
// count = 10
|
||||
// in cases count > first, clamp it to first
|
||||
//
|
||||
// 3 2 1 0
|
||||
// 10987654321098765432109876543210 bit index
|
||||
// ..................xxxxxxxxxx....
|
||||
// ^ ^
|
||||
// | |
|
||||
// +- count -+ count = 10 bits to process
|
||||
// |
|
||||
// +-------------- first = 14
|
||||
|
||||
size_t n = (count < first ? count : first);
|
||||
word &= (1U << first) - (1U << (first - n));
|
||||
|
||||
if (word)
|
||||
{
|
||||
return offset * 32 + bitscan__lindex32(word);
|
||||
}
|
||||
offset -= 1;
|
||||
count -= n;
|
||||
}
|
||||
|
||||
#if defined(_M_AMD64) || defined(__x86_64__)
|
||||
while (count >= 128)
|
||||
{
|
||||
__m128i words = _mm_loadu_si128((const __m128i*)&bits[offset - 3]);
|
||||
__m128i cmp = _mm_cmpeq_epi32(words, _mm_set1_epi32(mask));
|
||||
|
||||
// if all 16-bytes of "words" are same as mask, then m will be 0xffff
|
||||
uint16_t m = (uint16_t)_mm_movemask_epi8(cmp);
|
||||
|
||||
if ((uint16_t)(m + 1))
|
||||
{
|
||||
// find word index to use [0,4)
|
||||
size_t n = bitscan__lindex32((uint16_t)~m) / 4;
|
||||
|
||||
uint32_t word = bits[offset + n - 3] ^ mask;
|
||||
return offset * 32 + (n - 3) * 32 + bitscan__lindex32(word);
|
||||
}
|
||||
offset -= 4;
|
||||
count -= 128;
|
||||
}
|
||||
#endif
|
||||
|
||||
while (count >= 32)
|
||||
{
|
||||
uint32_t word = bits[offset] ^ mask;
|
||||
if (word)
|
||||
{
|
||||
return offset * 32 + bitscan__lindex32(word);
|
||||
}
|
||||
offset -= 1;
|
||||
count -= 32;
|
||||
}
|
||||
|
||||
if (count) // now count < 32, how many bits to process in top of last word
|
||||
{
|
||||
uint32_t word = bits[offset] ^ mask;
|
||||
|
||||
// use last count bits, rest of bits are masked out to 0
|
||||
word &= ~0U << (32 - count);
|
||||
|
||||
if (word)
|
||||
{
|
||||
return offset * 32 + bitscan__lindex32(word);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return hi;
|
||||
}
|
||||
|
||||
size_t bitscan_msb_index64(const uint64_t* bits, size_t lo, size_t hi, int bit)
|
||||
{
|
||||
#if 0
|
||||
|
||||
// reference implementation
|
||||
for (size_t i = hi; i-- > lo; )
|
||||
{
|
||||
uint64_t word = bits[i/64];
|
||||
if (((word >> (i%64)) & 1) == bit)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
if (lo >= hi)
|
||||
{
|
||||
return hi;
|
||||
}
|
||||
|
||||
const uint64_t mask = bit ? 0ULL : ~0ULL;
|
||||
|
||||
size_t count = hi - lo;
|
||||
size_t offset = (hi - 1) / 64;
|
||||
size_t first = hi % 64;
|
||||
|
||||
if (first)
|
||||
{
|
||||
uint64_t word = bits[offset] ^ mask;
|
||||
|
||||
size_t n = (count < first ? count : first);
|
||||
word &= (1ULL << first) - (1ULL << (first - n));
|
||||
|
||||
if (word)
|
||||
{
|
||||
return offset * 64 + bitscan__lindex64(word);
|
||||
}
|
||||
offset -= 1;
|
||||
count -= n;
|
||||
}
|
||||
|
||||
#if defined(_M_AMD64) || defined(__x86_64__)
|
||||
while (count >= 128)
|
||||
{
|
||||
__m128i words = _mm_loadu_si128((const __m128i*)&bits[offset - 1]);
|
||||
__m128i cmp = _mm_cmpeq_epi32(words, _mm_set1_epi32(mask));
|
||||
uint16_t m = (uint16_t)_mm_movemask_epi8(cmp);
|
||||
if ((uint16_t)(m + 1))
|
||||
{
|
||||
size_t n = bitscan__lindex32((uint16_t)~m) / 8;
|
||||
|
||||
uint64_t word = bits[offset + n - 1] ^ mask;
|
||||
return offset * 64 + (n - 1) * 64 + bitscan__lindex64(word);
|
||||
}
|
||||
offset -= 2;
|
||||
count -= 128;
|
||||
}
|
||||
#endif
|
||||
|
||||
while (count >= 64)
|
||||
{
|
||||
uint64_t word = bits[offset] ^ mask;
|
||||
if (word)
|
||||
{
|
||||
return offset * 64 + bitscan__lindex64(word);
|
||||
}
|
||||
offset -= 1;
|
||||
count -= 64;
|
||||
}
|
||||
|
||||
if (count)
|
||||
{
|
||||
uint64_t word = bits[offset] ^ mask;
|
||||
word &= ~0ULL << (64 - count);
|
||||
|
||||
if (word)
|
||||
{
|
||||
return offset * 64 + bitscan__lindex64(word);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return hi;
|
||||
}
|
||||
|
||||
#if defined(BITSCAN_TEST)
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static uint64_t random64()
|
||||
{
|
||||
static uint64_t x = 0, w = 0;
|
||||
x = x*x + (w += 0xb5ad4eceda1ce2a9);
|
||||
return x = (x>>32) | (x<<32);
|
||||
}
|
||||
|
||||
#define BIT32_SET(v,i) v[(i)/32] |= 1U << ((i)%32)
|
||||
#define BIT32_CLEAR(v,i) v[(i)/32] &= ~(1U << ((i)%32))
|
||||
|
||||
#define BIT64_SET(v,i) v[(i)/64] |= 1ULL << ((i)%64)
|
||||
#define BIT64_CLEAR(v,i) v[(i)/64] &= ~(1ULL << ((i)%64))
|
||||
|
||||
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
||||
#define MAX(a,b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
int main()
|
||||
{
|
||||
enum { kBitCount = 512 };
|
||||
enum { kRngCount = 32 };
|
||||
|
||||
static const size_t offsets[] =
|
||||
{
|
||||
0, 1, 10, 31, 32, 33, 50, 63, 64, 65, 130, kBitCount-65, kBitCount-64, kBitCount-63, kBitCount-33, kBitCount-32, kBitCount-31, kBitCount-1, kBitCount,
|
||||
};
|
||||
size_t offset_count = sizeof(offsets) / sizeof(offsets[0]);
|
||||
|
||||
for (size_t n=0; n<=kBitCount; n++)
|
||||
{
|
||||
printf("."); fflush(stdout);
|
||||
|
||||
// bitscan_lsb_index32
|
||||
|
||||
for (size_t r=0; r<kRngCount; r++)
|
||||
{
|
||||
uint32_t v[kBitCount/32];
|
||||
for (size_t i=0; i<kBitCount/32; i++) v[i] = (uint32_t)random64();
|
||||
|
||||
for (size_t olo=0; olo<offset_count; olo++)
|
||||
for (size_t ohi=0; ohi<offset_count; ohi++)
|
||||
{
|
||||
size_t lo = offsets[olo];
|
||||
size_t hi = offsets[ohi];
|
||||
if (lo >= n) continue;
|
||||
|
||||
size_t r;
|
||||
size_t expected = lo >= hi || hi < n ? hi : n;
|
||||
|
||||
// first n bits are 0, then bit 1 when possible
|
||||
for (size_t i=0; i<n; i++) BIT32_CLEAR(v, i);
|
||||
if (n < kBitCount) BIT32_SET(v, n);
|
||||
|
||||
r = bitscan_lsb_index32(v, lo, hi, 1);
|
||||
assert(r == expected);
|
||||
|
||||
// first n bits are 1, then bit 0 when possible
|
||||
for (size_t i=0; i<n; i++) BIT32_SET(v, i);
|
||||
if (n < kBitCount) BIT32_CLEAR(v, n);
|
||||
|
||||
r = bitscan_lsb_index32(v, lo, hi, 0);
|
||||
assert(r == expected);
|
||||
}
|
||||
}
|
||||
|
||||
// bitscan_msb_index32
|
||||
|
||||
for (size_t r=0; r<kRngCount; r++)
|
||||
{
|
||||
uint32_t v[kBitCount/32];
|
||||
for (size_t i=0; i<kBitCount/32; i++) v[i] = (uint32_t)random64();
|
||||
|
||||
for (size_t olo=0; olo<offset_count; olo++)
|
||||
for (size_t ohi=0; ohi<offset_count; ohi++)
|
||||
{
|
||||
size_t lo = offsets[olo];
|
||||
size_t hi = offsets[ohi];
|
||||
if (hi < kBitCount - n) continue;
|
||||
|
||||
size_t r;
|
||||
size_t expected = lo >= hi || lo >= kBitCount-n ? hi : MIN(hi-1, kBitCount-1-n);
|
||||
|
||||
// last n bits are 0, then bit 1 when possible
|
||||
for (size_t i=kBitCount-n; i<kBitCount; i++) BIT32_CLEAR(v, i);
|
||||
if (n < kBitCount) BIT32_SET(v, kBitCount-n-1);
|
||||
|
||||
r = bitscan_msb_index32(v, lo, hi, 1);
|
||||
assert(r == expected);
|
||||
|
||||
// last n bits are 1, then bit 0 when possible
|
||||
for (size_t i=kBitCount-n; i<kBitCount; i++) BIT32_SET(v, i);
|
||||
if (n < kBitCount) BIT32_CLEAR(v, kBitCount-n-1);
|
||||
|
||||
r = bitscan_msb_index32(v, lo, hi, 0);
|
||||
assert(r == expected);
|
||||
}
|
||||
}
|
||||
|
||||
// bitscan_lsb_index64
|
||||
|
||||
for (size_t r = 0; r < kRngCount; r++)
|
||||
{
|
||||
uint64_t v[kBitCount / 64];
|
||||
for (size_t i = 0; i < kBitCount / 64; i++) v[i] = random64();
|
||||
|
||||
for (size_t olo = 0; olo < offset_count; olo++)
|
||||
for (size_t ohi = 0; ohi < offset_count; ohi++)
|
||||
{
|
||||
size_t lo = offsets[olo];
|
||||
size_t hi = offsets[ohi];
|
||||
if (lo >= n) continue;
|
||||
|
||||
size_t r;
|
||||
size_t expected = lo >= hi || hi < n ? hi : n;
|
||||
|
||||
// first n bits are 0, then bit 1 when possible
|
||||
for (size_t i = 0; i < n; i++) BIT64_CLEAR(v, i);
|
||||
if (n < kBitCount) BIT64_SET(v, n);
|
||||
|
||||
r = bitscan_lsb_index64(v, lo, hi, 1);
|
||||
assert(r == expected);
|
||||
|
||||
// first n bits are 1, then bit 0 when possible
|
||||
for (size_t i = 0; i < n; i++) BIT64_SET(v, i);
|
||||
if (n < kBitCount) BIT64_CLEAR(v, n);
|
||||
|
||||
r = bitscan_lsb_index64(v, lo, hi, 0);
|
||||
assert(r == expected);
|
||||
}
|
||||
}
|
||||
|
||||
// bitscan_msb_index64
|
||||
|
||||
for (size_t r = 0; r < kRngCount; r++)
|
||||
{
|
||||
uint64_t v[kBitCount / 64];
|
||||
for (size_t i = 0; i < kBitCount / 64; i++) v[i] = random64();
|
||||
|
||||
for (size_t olo = 0; olo < offset_count; olo++)
|
||||
for (size_t ohi = 0; ohi < offset_count; ohi++)
|
||||
{
|
||||
size_t lo = offsets[olo];
|
||||
size_t hi = offsets[ohi];
|
||||
if (hi < kBitCount - n) continue;
|
||||
|
||||
size_t r;
|
||||
size_t expected = lo >= hi || lo >= kBitCount-n ? hi : MIN(hi-1, kBitCount-1-n);
|
||||
|
||||
// last n bits are 0, then bit 1 when possible
|
||||
for (size_t i = kBitCount - n; i < kBitCount; i++) BIT64_CLEAR(v, i);
|
||||
if (n < kBitCount) BIT64_SET(v, kBitCount-n-1);
|
||||
|
||||
r = bitscan_msb_index64(v, lo, hi, 1);
|
||||
assert(r == expected);
|
||||
|
||||
// last n bits are 1, then bit 0 when possible
|
||||
for (size_t i = kBitCount - n; i < kBitCount; i++) BIT64_SET(v, i);
|
||||
if (n < kBitCount) BIT64_CLEAR(v, kBitCount-n-1);
|
||||
|
||||
r = bitscan_msb_index64(v, lo, hi, 0);
|
||||
assert(r == expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
printf(" OK!\n");
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -69,4 +69,29 @@ T_BeginTest(str8_list_substr)
|
||||
}
|
||||
}
|
||||
|
||||
T_BeginTest(bit_array)
|
||||
{
|
||||
for (U64 start=0; start<32*3; start++) {
|
||||
for (U64 end=start; end<32*3; end++) {
|
||||
U32 v[3] = { 0 };
|
||||
for (U64 i=start; i<end; i++) {
|
||||
v[i/32] |= 1 << (i%32);
|
||||
}
|
||||
for (U64 lo=0; lo<32*3; lo++) {
|
||||
for (U64 hi=0; hi<32*3; hi++) {
|
||||
U64 expected_idx = Min(hi, end) - 1;
|
||||
B32 expected_r = hi <= start || lo >= end || lo >= hi || start >= end ? 0 : 1;
|
||||
U64 idx = bit_array_scan_right_to_left32((U32Array){.v=v, .count=ArrayCount(v)}, lo, hi, 1);
|
||||
B32 r = idx < hi;
|
||||
T_Ok(r == expected_r);
|
||||
if (r) {
|
||||
T_Ok(idx == expected_idx);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef T_Group
|
||||
|
||||
@@ -76,6 +76,7 @@
|
||||
#include "ui/ui_inc.h"
|
||||
#include "dbg_engine/dbg_engine_inc.h"
|
||||
#include "raddbg/raddbg_inc.h"
|
||||
#include "linker/base_ext/base_crc32.h"
|
||||
#include "linker/base_ext/base_core.h"
|
||||
#include "linker/base_ext/base_arena.h"
|
||||
#include "linker/base_ext/base_arrays.h"
|
||||
@@ -83,6 +84,9 @@
|
||||
#include "linker/thread_pool/thread_pool.h"
|
||||
#include "linker/codeview_ext/codeview.h"
|
||||
#include "linker/pdb_ext/msf_builder.h"
|
||||
#include "linker/pdb_ext/pdb.h"
|
||||
#include "linker/pdb_ext/pdb_helpers.h"
|
||||
#include "linker/pdb_ext/pdb_builder.h"
|
||||
#include "linker/lnk_cmd_line.h"
|
||||
#include "linker/lnk_cmd_line.c"
|
||||
#include "linker/lnk_log.h"
|
||||
@@ -148,9 +152,13 @@
|
||||
#include "linker/base_ext/base_arena.c"
|
||||
#include "linker/base_ext/base_arrays.c"
|
||||
#include "linker/base_ext/base_bit_array.c"
|
||||
#include "linker/base_ext/base_crc32.c"
|
||||
#include "linker/thread_pool/thread_pool.c"
|
||||
#include "linker/codeview_ext/codeview.c"
|
||||
#include "linker/pdb_ext/msf_builder.c"
|
||||
#include "linker/pdb_ext/pdb.c"
|
||||
#include "linker/pdb_ext/pdb_helpers.c"
|
||||
#include "linker/pdb_ext/pdb_builder.c"
|
||||
|
||||
#include "torture.c"
|
||||
#include "torture_base.c"
|
||||
|
||||
Reference in New Issue
Block a user