fix info stream layout

This commit is contained in:
Nikita Smith
2026-04-10 17:02:04 -07:00
parent 686b64c357
commit c5dc7bee3e
7 changed files with 818 additions and 428 deletions
+7 -224
View File
@@ -1,234 +1,25 @@
// Copyright (c) Epic Games Tools // Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
#include "third_party/martins_bitscan/bitscan.h"
internal U32Array internal U32Array
bit_array_init32(Arena *arena, U64 word_count) bit_array_init32(Arena *arena, U64 bit_count)
{ {
U32Array result; U64 count = CeilIntegerDiv(bit_count, 32);
result.count = CeilIntegerDiv(word_count, 32); return (U32Array){ .count = count, .v = push_array(arena, U32, count) };
result.v = push_array(arena, U32, word_count);
return result;
} }
internal U64 internal U64
bit_array_scan_left_to_right32(U32Array bit_array, U64 lo, U64 hi, B32 state) bit_array_scan_left_to_right32(U32Array bit_array, U64 lo, U64 hi, B32 state)
{ {
Assert(lo < bit_array.count*32); return bitscan_lsb_index32(bit_array.v, lo, hi, state);
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;
} }
internal U64 internal U64
bit_array_scan_right_to_left32(U32Array bit_array, U64 lo, U64 hi, B32 state) bit_array_scan_right_to_left32(U32Array bit_array, U64 lo, U64 hi, B32 state)
{ {
Assert(lo <= hi); return bitscan_msb_index32(bit_array.v, lo, hi, state);
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;
} }
internal void 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 internal U32
bit_array_get_bit32(U32Array bit_array, U64 idx) bit_array_get_bit32(U32Array bit_array, U64 idx)
{ {
+5 -11
View File
@@ -3,16 +3,10 @@
#pragma once #pragma once
internal U32Array bit_array_init32 (Arena *arena, U64 word_count); 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_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 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 void bit_array_set_bit32 (U32Array bit_array, U64 idx, B32 state);
internal Rng1U64 bit_array_scan_right_to_left32_contiguous(U32Array bit_array, U64 lo, U64 hi, B32 state, U64 in_row_count); internal U32 bit_array_get_bit32 (U32Array bit_array, U64 idx);
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);
+97 -184
View File
@@ -21,7 +21,6 @@ pdb_hash_table_alloc(PDB_HashTable *ht, U32 max)
ht->deleted_bits = bit_array_init32(ht->arena, max); ht->deleted_bits = bit_array_init32(ht->arena, max);
ht->max = max; ht->max = max;
ht->count = 0; ht->count = 0;
bit_array_set_bit_range32(ht->deleted_bits, rng_1u64(0, max), 1);
ProfEnd(); ProfEnd();
} }
@@ -148,51 +147,48 @@ pdb_hash_table_from_data(PDB_HashTable *ht,
return error; 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 internal String8
pdb_data_from_hash_table(Arena *arena, pdb_data_from_hash_table(Arena *arena, PDB_HashTable *ht, PDB_HashTablePackFunc *pack_func, void *pack_ud)
PDB_HashTable *ht,
B32 has_local_data,
PDB_HashTablePackFunc *pack_func,
void *pack_ud)
{ {
ProfBeginFunction(); ProfBeginFunction();
Temp scratch = scratch_begin(&arena, 1); Temp scratch = scratch_begin(&arena, 1);
String8Array key_arr = {0}; String8List kv_srl = {0}; str8_serial_begin(scratch.arena, &kv_srl);
String8Array value_arr = {0}; PDB_HashTableBucket **buckets = pdb_hash_table_get_present_buckets(scratch.arena, ht);
pdb_hash_table_get_present_keys_and_values(scratch.arena, ht, &key_arr, &value_arr); 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}; // compute count of present words that are needed
String8List key_value_srl = {0}; U64 present_word_count = 0;
str8_serial_begin(scratch.arena, &local_data_srl); U64 present_msb = bit_array_scan_right_to_left32(ht->present_bits, 0, ht->present_bits.count*32, 1);
str8_serial_begin(scratch.arena, &key_value_srl); if (present_msb < ht->present_bits.count*32) { present_word_count = present_msb / 32 + 1; }
for (U64 i = 0; i < ht->count; i += 1) { // compute count of deleted words that are needed
String8 key = key_arr.v[i]; U64 deleted_word_count = 0;
String8 value = value_arr.v[i]; U64 deleted_msb = bit_array_scan_right_to_left32(ht->deleted_bits, 0, ht->deleted_bits.count*32, 1);
pack_func(scratch.arena, &local_data_srl, &key_value_srl, key, value, pack_ud); if (deleted_msb < ht->deleted_bits.count*32) { deleted_word_count = deleted_msb / 32 + 1; }
}
// serialize hash table // write hash table
String8List srl = {0}; String8List ht_srl = {0}; str8_serial_begin(scratch.arena, &ht_srl);
str8_serial_begin(scratch.arena, &srl); str8_serial_push_u32 (scratch.arena, &ht_srl, ht->count);
if (has_local_data) { str8_serial_push_u32 (scratch.arena, &ht_srl, ht->max);
U32 local_data_size32 = safe_cast_u32(local_data_srl.total_size); str8_serial_push_u32 (scratch.arena, &ht_srl, present_word_count);
str8_serial_push_u32(scratch.arena, &srl, local_data_size32); str8_serial_push_array(scratch.arena, &ht_srl, &ht->present_bits.v[0], present_word_count);
str8_list_concat_in_place(&srl, &local_data_srl); 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_serial_push_u32(scratch.arena, &srl, ht->count); str8_list_concat_in_place(&ht_srl, &kv_srl);
str8_serial_push_u32(scratch.arena, &srl, ht->max);
str8_serial_push_u32(scratch.arena, &srl, ht->present_bits.count); String8 result = str8_serial_end(arena, &ht_srl);
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);
scratch_end(scratch); scratch_end(scratch);
ProfEnd(); ProfEnd();
return data; return result;
} }
internal void 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); B32 is_present = pdb_hash_table_is_present(ht, ibucket);
if ( ! is_present) { if ( ! is_present) {
PDB_HashTableBucket *bucket = &ht->bucket_arr[ibucket]; PDB_HashTableBucket *bucket = &ht->bucket_arr[ibucket];
bucket->key = push_str8_copy(ht->arena, key); bucket->key = push_str8_copy(ht->arena, key);
bucket->value = push_str8_copy(ht->arena, value); 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->present_bits, ibucket, 1);
bit_array_set_bit32(ht->deleted_bits, ibucket, 0); 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? // should resize?
U64 load_factor = pdb_hash_table_compute_load_factor(ht->max); U64 load_factor = pdb_hash_table_compute_load_factor(ht->max);
if (ht->count + 1 >= load_factor) { 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 // 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); 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 internal void
pdb_hash_table_get_present_keys_and_values(Arena *arena, PDB_HashTable *ht, String8Array *keys_out, String8Array *values_out) 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(!ud);
Assert(value.size == sizeof(U32)); Assert(value.size == sizeof(U32));
str8_serial_push_u32(arena, key_value_srl, bucket->key_offset);
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_string(arena, key_value_srl, value); 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)); Assert(value.size == sizeof(CV_TypeIndex));
PDB_StringTable *strtab = (PDB_StringTable*)ud; PDB_StringTable *strtab = ud;
PDB_StringIndex string_idx = PDB_INVALID_STRING_INDEX; PDB_StringIndex string_idx = PDB_INVALID_STRING_INDEX;
B32 is_found = pdb_strtab_search(strtab, key, &string_idx); 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)); Assert(value.size == sizeof(PDB_SrcHeaderBlockEntry));
PDB_StringTable *strtab = (PDB_StringTable*)ud; PDB_StringTable *strtab = ud;
PDB_StringIndex path_idx = 0; PDB_StringIndex path_idx = 0;
B32 is_found = pdb_strtab_search(strtab, key, &path_idx); 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 internal String8
pdb_data_from_hash_adj_hash_table(Arena *arena, PDB_HashTable *ht, PDB_StringTable *strtab) 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 pdb_data_from_hash_table(arena, ht, pdb_hash_adj_ht_pack, strtab);
return data;
} }
internal String8 internal String8
pdb_data_from_src_header_block_ht(Arena *arena, PDB_HashTable *ht, PDB_StringTable *strtab) 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 pdb_data_from_hash_table(arena, ht, pdb_src_header_block_ht_pack, strtab);
return data;
} }
internal String8 internal String8
pdb_data_from_named_stream_ht(Arena *arena, PDB_HashTable *ht) 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); Temp scratch = scratch_begin(&arena, 1);
return data;
// 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; 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 internal
THREAD_POOL_TASK_FUNC(pdb_write_type_to_bucket_map_32_task) 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->age = age;
info->guid = guid; info->guid = guid;
pdb_strtab_alloc(&info->strtab, 0x3fff); 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); pdb_hash_table_alloc(&info->src_header_block_ht, 8);
ProfEnd(); ProfEnd();
return info; return info;
@@ -1615,9 +1524,13 @@ pdb_info_build(PDB_InfoContext *info, MSF_Context *msf, MSF_StreamNumber sn)
Temp scratch = scratch_begin(0,0); Temp scratch = scratch_begin(0,0);
// finalize named streams // 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_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 // serialize named streams hash table
String8 named_stream_ht_data = pdb_data_from_named_stream_ht(scratch.arena, &info->named_stream_ht); 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_begin(scratch.arena, &info_srl);
str8_serial_push_struct(scratch.arena, &info_srl, &header); str8_serial_push_struct(scratch.arena, &info_srl, &header);
str8_serial_push_string(scratch.arena, &info_srl, named_stream_ht_data); 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) { if (info->flags & PDB_FeatureFlag_HAS_ID_STREAM) {
str8_serial_push_u32(scratch.arena, &info_srl, PDB_FeatureSig_VC140); 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) pdb_push_named_stream(PDB_HashTable *named_stream_ht, MSF_Context *msf, String8 name)
{ {
ProfBeginFunction(); ProfBeginFunction();
Temp scratch = scratch_begin(0,0);
MSF_StreamNumber sn = msf_stream_alloc(msf); MSF_StreamNumber sn = msf_stream_alloc(msf);
String8 name_cstr = push_cstr(scratch.arena, name);
U32 sn32 = (U32)sn; U32 sn32 = (U32)sn;
pdb_hash_table_set(named_stream_ht, name_cstr, str8_struct(&sn32)); pdb_hash_table_set(named_stream_ht, name, str8_struct(&sn32));
scratch_end(scratch);
ProfEnd(); ProfEnd();
return sn; return sn;
} }
@@ -2665,8 +2576,6 @@ dbi_build_module_info(Arena *arena, PDB_DbiContext *dbi, MSF_Context *msf)
// TODO: generate EC info // TODO: generate EC info
header->src_file = 0; header->src_file = 0;
header->pdb_file = 0; header->pdb_file = 0;
Assert(header->sn != MSF_INVALID_STREAM_NUMBER);
// push module info // push module info
str8_serial_push_struct(arena, &module_info_list, header); 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 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(); ProfBeginFunction();
Temp scratch = scratch_begin(0, 0); 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.flags = 0;
header.machine = dbi->machine; header.machine = dbi->machine;
header.reserved = 0; header.reserved = 0;
if (is_stripped) {
header.flags |= PDB_DbiHeaderFlag_Stripped;
}
ProfBegin("MSF Write"); ProfBegin("MSF Write");
@@ -3229,7 +3142,7 @@ pdb_get_guid(PDB_Context *pdb)
} }
internal void 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(); 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); 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); pdb_info_build(pdb->info, pdb->msf, PDB_FixedStream_Info);
if (build_gsi) { if (build_gsi) {
+12 -9
View File
@@ -11,16 +11,12 @@
//////////////////////////////// ////////////////////////////////
// Hash table // 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 typedef struct PDB_HashTableBucket
{ {
String8 key; String8 key;
String8 value; String8 value;
U32 key_offset;
U32 insert_idx;
} PDB_HashTableBucket; } PDB_HashTableBucket;
typedef struct PDB_HashTable typedef struct PDB_HashTable
@@ -33,6 +29,12 @@ typedef struct PDB_HashTable
U32 count; U32 count;
} PDB_HashTable; } 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 typedef enum
{ {
PDB_HashTableParseError_OK, 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 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_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_machine(PDB_Context *pdb, COFF_MachineType machine);
internal void pdb_set_guid(PDB_Context *pdb, Guid guid); internal void pdb_set_guid(PDB_Context *pdb, Guid guid);
internal void pdb_set_time_stamp(PDB_Context *pdb, COFF_TimeStamp time_stamp); 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 // DBI
internal PDB_DbiContext * dbi_alloc(COFF_MachineType machine, U32 age); 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 void dbi_release(PDB_DbiContext **dbi_ptr);
internal PDB_DbiModule * dbi_push_module(PDB_DbiContext *dbi, String8 obj_path, String8 lib_path); 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); 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_alloc(PDB_HashTable *ht, U32 max);
internal void pdb_hash_table_release(PDB_HashTable *ht); 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 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 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 B32 pdb_hash_table_get(PDB_HashTable *ht, String8 key, String8 *value_out);
internal void pdb_hash_table_delete(PDB_HashTable *ht, String8 key); 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 B32 pdb_hash_table_is_deleted(PDB_HashTable *ht, U32 k);
internal U32 pdb_hash_table_hash(String8 key); internal U32 pdb_hash_table_hash(String8 key);
internal void pdb_hash_table_grow(PDB_HashTable *ht, U64 new_capacity); 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); internal void pdb_hash_table_get_present_keys_and_values(Arena *arena, PDB_HashTable *ht, String8Array *keys_out, String8Array *values_out);
//////////////////////////////// ////////////////////////////////
+664
View File
@@ -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
+25
View File
@@ -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 #undef T_Group
+8
View File
@@ -76,6 +76,7 @@
#include "ui/ui_inc.h" #include "ui/ui_inc.h"
#include "dbg_engine/dbg_engine_inc.h" #include "dbg_engine/dbg_engine_inc.h"
#include "raddbg/raddbg_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_core.h"
#include "linker/base_ext/base_arena.h" #include "linker/base_ext/base_arena.h"
#include "linker/base_ext/base_arrays.h" #include "linker/base_ext/base_arrays.h"
@@ -83,6 +84,9 @@
#include "linker/thread_pool/thread_pool.h" #include "linker/thread_pool/thread_pool.h"
#include "linker/codeview_ext/codeview.h" #include "linker/codeview_ext/codeview.h"
#include "linker/pdb_ext/msf_builder.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.h"
#include "linker/lnk_cmd_line.c" #include "linker/lnk_cmd_line.c"
#include "linker/lnk_log.h" #include "linker/lnk_log.h"
@@ -148,9 +152,13 @@
#include "linker/base_ext/base_arena.c" #include "linker/base_ext/base_arena.c"
#include "linker/base_ext/base_arrays.c" #include "linker/base_ext/base_arrays.c"
#include "linker/base_ext/base_bit_array.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/thread_pool/thread_pool.c"
#include "linker/codeview_ext/codeview.c" #include "linker/codeview_ext/codeview.c"
#include "linker/pdb_ext/msf_builder.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.c"
#include "torture_base.c" #include "torture_base.c"