From 4add147a098ccdf326ba7e508dee26d5dffa7c26 Mon Sep 17 00:00:00 2001 From: Nikita Smith Date: Sat, 19 Sep 2026 21:13:14 -0700 Subject: [PATCH] radlink: refactor MSF writer to allow allocation of contiguous stream backing * remove MSF reader API * remove PDB reader API --- src/linker/lnk_debug_info.c | 80 +- src/linker/pdb_ext/msf_builder.c | 2156 ++++++------------------------ src/linker/pdb_ext/msf_builder.h | 205 +-- src/linker/pdb_ext/pdb_builder.c | 425 +----- src/linker/pdb_ext/pdb_builder.h | 15 +- src/linker/pdb_ext/pdb_helpers.c | 20 - src/linker/pdb_ext/pdb_helpers.h | 2 - src/linker/tests/linker_tests.c | 150 ++- 8 files changed, 653 insertions(+), 2400 deletions(-) diff --git a/src/linker/lnk_debug_info.c b/src/linker/lnk_debug_info.c index 154d902d..870ca736 100644 --- a/src/linker/lnk_debug_info.c +++ b/src/linker/lnk_debug_info.c @@ -2697,17 +2697,18 @@ THREAD_POOL_TASK_FUNC(lnk_write_pdb_modules) if (mod->sn == MSF_INVALID_STREAM_NUMBER) { continue; } CV_DebugS debug_s = task->cv->debug_s_arr[obj_idx]; - String8List mod_data = msf_data_from_sn(temp.arena, task->pdb->msf, mod->sn); + String8 mod_data = msf_stream_data(task->pdb->msf, mod->sn); - if (mod_data.node_count) { - String8Node buf = *mod_data.first; + if (mod_data.size) { + String8Node buf = { .string = mod_data }; U64 pos = 0; lnk_write_debug_s_to_pdb_module(mod, debug_s, &buf, &pos); - // sub range symbol data pages and patch symbol tree offsets + // Patch the contiguous symbol buffer before handing it to output. if (mod->sym_data_size) { Rng1U64 sym_data_range = r1u64(sizeof(CV_Signature), mod->sym_data_size); - String8List mod_symbols = str8_list_substr(temp.arena, mod_data, sym_data_range); + String8List mod_symbols = {0}; + str8_list_push(temp.arena, &mod_symbols, str8_substr(mod_data, sym_data_range)); Assert(mod_symbols.total_size == dim_1u64(sym_data_range)); cv_patch_symbol_tree_offsets(mod_symbols, sizeof(CV_Signature), PDB_SYMBOL_ALIGN); } @@ -2754,50 +2755,38 @@ THREAD_POOL_TASK_FUNC(lnk_write_pdb_modules) if (mod->sn == MSF_INVALID_STREAM_NUMBER) { continue; } - String8List mod_data = msf_data_from_sn(temp.arena, task->pdb->msf, mod->sn); + String8 mod_data = msf_stream_data(task->pdb->msf, mod->sn); Rng1U64 c13_data_range = r1u64(mod->sym_data_size + mod->c11_data_size, mod->sym_data_size + mod->c11_data_size + mod->c13_data_size); - String8List c13_data = str8_list_substr(temp.arena, mod_data, c13_data_range); + String8 c13_data = str8_substr(mod_data, c13_data_range); CV_DebugS debug_s = task->cv->debug_s_arr[obj_idx]; String8 string_table = cv_string_table_from_debug_s(debug_s); // checksum is always at the head of C13 data - String8List file_chksms_raw = {0}; + String8 file_chksms_raw = {0}; { - String8Node buf = *c13_data.first; - U64 buf_pos = 0; - CV_C13SubSectionHeader header = {0}; - str8_buffer_read(&buf, &buf_pos, sizeof(header), &header); + str8_deserial_read_struct(c13_data, 0, &header); if (header.kind == CV_C13SubSectionKind_FileChksms) { Rng1U64 file_chksms_range = r1u64(sizeof(CV_C13SubSectionHeader), sizeof(CV_C13SubSectionHeader) + header.size); - file_chksms_raw = str8_list_substr(temp.arena, c13_data, file_chksms_range); + file_chksms_raw = str8_substr(c13_data, file_chksms_range); } } // fixup file name offsets in checksum headers - if (file_chksms_raw.total_size) { - String8Node buf = *file_chksms_raw.first; - U64 buf_pos = 0; + if (file_chksms_raw.size) { U64 cursor = 0; for (;;) { CV_C13Checksum header = {0}; - if (str8_buffer_peek(&buf, &buf_pos, sizeof(header), &header) != sizeof(header)) { break; } + if (str8_deserial_read_struct(file_chksms_raw, cursor, &header) != sizeof(header)) { break; } String8 name = str8_cstring_capped(string_table.str + header.name_off, string_table.str + string_table.size); CV_StringBucket *bucket = cv_string_hash_table_lookup(task->string_ht, name); U64 name_off = task->string_table_base_offset + bucket->u.offset; - // update name offset - { - String8Node buf_copy = buf; - U64 buf_pos_copy = buf_pos; - str8_buffer_skip(&buf_copy, &buf_pos_copy, OffsetOf(CV_C13Checksum, name_off)); - str8_buffer_write_u32(&buf_copy, &buf_pos_copy, safe_cast_u32(name_off)); - } - - str8_buffer_skip(&buf, &buf_pos, AlignPow2(sizeof(header) + header.len, CV_FileCheckSumsAlign)); + memory_write32(file_chksms_raw.str + cursor + OffsetOf(CV_C13Checksum, name_off), safe_cast_u32(name_off)); + cursor += AlignPow2(sizeof(header) + header.len, CV_FileCheckSumsAlign); } } @@ -2889,29 +2878,6 @@ typedef struct U64 sealed_stream_cap; } LNK_PdbOutput; -typedef struct LNK_MsfPageCursor -{ - MSF_PageDataNode *node; - U64 node_idx; - U64 pages_per_node; -} LNK_MsfPageCursor; - -internal U8 * -lnk_msf_data_from_pn(LNK_MsfPageCursor *cursor, MSF_Context *msf, MSF_PageNumber pn) -{ - U64 node_idx = pn / cursor->pages_per_node; - if (node_idx < cursor->node_idx) { - cursor->node = msf->page_data_list.first; - cursor->node_idx = 0; - } - while (cursor->node_idx < node_idx) { - cursor->node = cursor->node->next; - cursor->node_idx += 1; - } - Assert(cursor->node != 0); - return cursor->node->data + (pn % cursor->pages_per_node) * msf->page_size; -} - internal void lnk_pdb_output_enqueue_stream(LNK_PdbOutput *output, MSF_Context *msf, MSF_StreamNumber sn) { @@ -2923,16 +2889,14 @@ lnk_pdb_output_enqueue_stream(LNK_PdbOutput *output, MSF_Context *msf, MSF_Strea Assert(stream != 0); if (stream->page_list.count == 0) { return; } - LNK_MsfPageCursor cursor = { - .node = msf->page_data_list.first, - .pages_per_node = msf_get_data_node_size(msf->page_size) / msf->page_size, - }; + U64 stream_offset = 0; MSF_PageNumber run_first_pn = 0; MSF_PageNumber run_last_pn = 0; U8 *run_data = 0; U64 run_size = 0; for EachNode(page, MSF_PageNode, stream->page_list.first) { - U8 *page_data = lnk_msf_data_from_pn(&cursor, msf, page->pn); + U8 *page_data = stream->data + stream_offset; + stream_offset += msf->page_size; if (run_data != 0 && page->pn == run_last_pn + 1 && page_data == run_data + run_size) { run_last_pn = page->pn; run_size += msf->page_size; @@ -2973,15 +2937,11 @@ lnk_pdb_output_enqueue_remaining(LNK_PdbOutput *output, MSF_Context *msf) } } - LNK_MsfPageCursor cursor = { - .node = msf->page_data_list.first, - .pages_per_node = msf_get_data_node_size(msf->page_size) / msf->page_size, - }; U64 run_first_pn = 0; U8 *run_data = 0; U64 run_size = 0; for EachIndex(pn, page_count) { - U8 *page_data = lnk_msf_data_from_pn(&cursor, msf, pn); + U8 *page_data = msf_data_from_pn(msf, pn).str; U64 page_size = Min(msf->page_size, save_size - pn * msf->page_size); if (!is_written[pn]) { if (run_data != 0 && page_data == run_data + run_size) { @@ -3183,7 +3143,7 @@ lnk_build_pdb(TP_Context *tp, TP_Arena *tp_arena, String8 image_data, LNK_Config pdb_build_dbi_info(tp, task.pdb, task.string_ht, 0, cv->is_stripped, &build_hooks); MSF_Error msf_err = msf_build(task.pdb->msf); - if (msf_err != MSF_Error_OK) { + if (msf_err != MSF_Error_Ok) { lnk_error(LNK_Error_UnableToSerializeMsf, "unable to serialize MSF: %s", msf_error_to_string(msf_err)); } diff --git a/src/linker/pdb_ext/msf_builder.c b/src/linker/pdb_ext/msf_builder.c index b996bdf2..37b3bbd7 100644 --- a/src/linker/pdb_ext/msf_builder.c +++ b/src/linker/pdb_ext/msf_builder.c @@ -1,140 +1,80 @@ // Copyright (c) Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) -internal U64 -msf_get_data_node_size(MSF_UInt page_size) +internal MSF_UInt +msf_count_pages(MSF_UInt page_size, U64 data_size) { - U64 interval = msf_get_fpm_interval_correct(page_size); - U64 bytes_per_interval = interval * (U64)page_size; - return bytes_per_interval; + return safe_cast_u32(CeilIntegerDiv(data_size, page_size)); } -internal void -msf_page_data_list_push(Arena *arena, MSF_PageDataList *list, MSF_UInt page_size, MSF_UInt count) +internal MSF_PageBlock * +msf_block_from_index(MSF_Context *msf, U64 idx) { - U64 data_size = msf_get_data_node_size(page_size); - for (MSF_UInt i = 0; i < count; i += 1) { - // TODO: clearing memory to zero here is expensive, - // with 4KiB pages we have to zero-out 128 MiB - // memory block - // - // we can make API for stream allocation to let user - // choose between zeroed and dirty allocations - // - U8 *data = push_array_aligned(arena, U8, data_size, page_size); - - // init node - MSF_PageDataNode *node = push_array_no_zero(arena, MSF_PageDataNode, 1); - node->prev = 0; - node->next = 0; - node->data = data; - - // push node to list - DLLPushBack(list->first, list->last, node); - list->count += 1; - } + MSF_PageBlock *block = msf->first_block; + for (U64 i = 0; i < idx && block != 0; i += 1) { block = block->next; } + Assert(block != 0); + return block; } -internal MSF_PageDataList -msf_page_data_list_pop(MSF_PageDataList *list, MSF_UInt count) +internal U8 ** +msf_page_data_slot(MSF_Context *msf, MSF_PageNumber pn) { - MSF_PageDataList result = {0}; - - MSF_UInt to_remove = Min(count, list->count); - for (MSF_UInt i = 0; i < to_remove; i += 1) { - MSF_PageDataNode *node = list->last; - DLLRemove(list->first, list->last, node); - - node->prev = 0; - node->next = 0; - - DLLPushBack(result.first, result.last, node); - result.count += 1; - } - list->count -= to_remove; - - return result; -} - -internal void -msf_page_data_list_concat_in_place(MSF_PageDataList *list, MSF_PageDataList *to_concat) -{ - DLLConcatInPlace(list, to_concat); -} - -internal void -msf_set_page_data_list(Arena *arena, MSF_PageDataList *list, MSF_UInt page_size, String8 data) -{ - ProfBeginFunction(); - - U64 node_size = msf_get_data_node_size(page_size); - U64 node_count = CeilIntegerDiv(data.size, node_size); - - U64 node_idx; - for (node_idx = 0; node_idx < node_count - 1; node_idx += 1) { - MSF_PageDataNode *node = push_array(arena, MSF_PageDataNode, 1); - node->data = data.str + node_idx * node_size; - SLLQueuePush(list->first, list->last, node); - list->count += 1; - } - - ProfBegin("Last Page Handle"); - B32 is_last_node_size_aligned = (data.size & (node_size - 1)) == 0; - U8 *last_node_data = 0; - if (is_last_node_size_aligned) { - last_node_data = data.str + node_idx * node_size; - } else { - U64 last_node_size = data.size % node_size; - last_node_data = push_array_no_zero(arena, U8, node_size); - MemoryCopy(last_node_data, data.str + node_idx * node_size, last_node_size); - } - ProfEnd(); - - MSF_PageDataNode *last_node = push_array(arena, MSF_PageDataNode, 1); - last_node->data = last_node_data; - SLLQueuePush(list->first, list->last, last_node); - list->count += 1; - - ProfEnd(); + U64 interval = (U64)msf->page_size * MSF_BITS_PER_CHAR; + MSF_PageBlock *block = msf_block_from_index(msf, pn / interval); + return &block->data[pn % interval]; } internal String8 -msf_data_from_pn(MSF_PageDataList list, MSF_UInt page_size, MSF_PageNumber pn) +msf_data_from_pn(MSF_Context *msf, MSF_PageNumber pn) { - U64 node_size = msf_get_data_node_size(page_size); - U64 page_offset = (U64)pn * (U64)page_size; - U64 data_node_idx = page_offset / node_size; - MSF_PageDataNode *node = list.first; - for (U64 i = 0; i < data_node_idx; i += 1) { - node = node->next; + U8 *data = *msf_page_data_slot(msf, pn); + return str8(data != 0 ? data : msf->zero_page, msf->page_size); +} + +internal B32 +msf_grow_page_table(MSF_Context *msf) +{ + U64 interval = (U64)msf->page_size * MSF_BITS_PER_CHAR; + if (((U64)msf->block_count + 1) * interval > MSF_PN_MAX) { return 0; } + + MSF_PageBlock *block = push_array(msf->arena, MSF_PageBlock, 1); + block->free_bits = push_array_no_zero(msf->arena, U32, msf->page_size / sizeof(U32)); + block->fpm_data = push_array_no_zero(msf->arena, U8, msf->page_size); + block->data = push_array(msf->arena, U8 *, interval); + MemorySet(block->free_bits, 0xff, msf->page_size); + SLLQueuePush(msf->first_block, msf->last_block, block); + U64 block_idx = msf->block_count++; + + // MS readers reserve slots 1 and 2 every page_size pages, even though one + // FPM page describes page_size*8 pages; keep those slots out of streams + U32Array bits = { .v = block->free_bits, .count = msf->page_size / sizeof(U32) }; + for (U64 i = 0; i < interval; i += msf->page_size) { + bit_array_set_bit32(bits, i + MSF_FPM0, MSF_PAGE_STATE_ALLOC); + bit_array_set_bit32(bits, i + MSF_FPM1, MSF_PAGE_STATE_ALLOC); + block->data[i + MSF_FPM0] = msf->empty_fpm; + block->data[i + MSF_FPM1] = msf->empty_fpm; } - U64 node_offset = page_offset % node_size; - U8 *ptr = node->data + node_offset; - String8 data = str8(ptr, page_size); - return data; + + // the bitmap pages are spaced page_size pages apart. Both copies describe + // the same fresh file; this builder does not edit existing files + *msf_page_data_slot(msf, safe_cast_u32(block_idx * msf->page_size + MSF_FPM0)) = block->fpm_data; + *msf_page_data_slot(msf, safe_cast_u32(block_idx * msf->page_size + MSF_FPM1)) = block->fpm_data; + + return 1; } -//////////////////////////////// - -internal MSF_StreamNode * -msf_stream_list_push(Arena *arena, MSF_StreamList *list) +internal MSF_PageNode * +msf_page_list_pop_last(MSF_PageList *list) { - MSF_StreamNode *n = push_array(arena, MSF_StreamNode, 1); - DLLPushBack(list->first, list->last, n); - list->count += 1; - return n; + MSF_PageNode *node = list->last; + if (node) { + DLLRemove(list->first, list->last, node); + list->count -= 1; + node->next = node->prev = 0; + } + return node; } -internal void -msf_stream_list_remove(MSF_StreamList *list, MSF_StreamNode *node) -{ - Assert(list->count > 0); - DLLRemove(list->first, list->last, node); - list->count -= 1; -} - -//////////////////////////////// - internal void msf_page_list_push_node(MSF_PageList *list, MSF_PageNode *node) { @@ -142,670 +82,143 @@ msf_page_list_push_node(MSF_PageList *list, MSF_PageNode *node) list->count += 1; } -internal MSF_PageNode * -msf_page_list_push(Arena *arena, MSF_PageList *list) +internal MSF_PageList +msf_alloc_pages(MSF_Context *msf, MSF_UInt count) { - MSF_PageNode *node = push_array(arena, MSF_PageNode, 1); - msf_page_list_push_node(list, node); - return node; -} - -internal MSF_PageNode * -msf_page_list_pop_last(MSF_PageList *list) -{ - MSF_PageNode *node = NULL; - if (list->count) { - node = list->last; - DLLRemove(list->first, list->last, node); - list->count -= 1; - } - return node; -} - -internal void -msf_page_list_concat_in_place(MSF_PageList *list, MSF_PageList *to_concat) -{ - DLLConcatInPlace(list, to_concat); -} - -internal MSF_PageNumber * -msf_page_list_to_arr(Arena *arena, MSF_PageList list) -{ - MSF_PageNumber *arr = push_array(arena, MSF_PageNumber, list.count); - MSF_UInt i = 0; - for (MSF_PageNode *node = list.first; node != 0; node = node->next, i += 1) { - arr[i] = node->pn; - } - return arr; -} - -internal MSF_PageNode * -msf_page_from_index(MSF_PageList page_list, MSF_UInt index) -{ - MSF_PageNode *page; - - B32 scan_from_last_node = index > page_list.count/2; - if (scan_from_last_node) { - page = page_list.last; - if (page_list.count > 0) { - for (MSF_UInt i = page_list.count - 1; i > index; i -= 1) { - page = page->prev; - if (!page) { - return 0; - } - } + MSF_PageList result = {0}; + U64 interval = (U64)msf->page_size * MSF_BITS_PER_CHAR; + while (result.count < count) { + U64 block_idx = msf->fpm_rover / interval; + if (block_idx == msf->block_count && !msf_grow_page_table(msf)) { + msf_free_pages(msf, &result); + break; } - } else { - page = page_list.first; - for (MSF_UInt i = 0; i < index; i += 1) { - page = page->next; - if (!page) { - return 0; - } - } - } - return page; -} -internal void -msf_page_list_push_extant_page_arr(Arena *arena, MSF_PageList *list, - MSF_PageDataList page_data_list, MSF_UInt page_size, - MSF_PageNumber *pn_arr, MSF_UInt pn_count) -{ - U64 node_size = msf_get_data_node_size(page_size); - U64 data_max = page_data_list.count * node_size; - for (MSF_PageNumber *pn_ptr = pn_arr, *pn_opl = pn_ptr + pn_count; pn_ptr < pn_opl; pn_ptr += 1) { - // is page number valid? - Assert(*pn_ptr * page_size + page_size <= data_max); + MSF_PageBlock *block = msf_block_from_index(msf, block_idx); + U32Array bits = { .v = block->free_bits, .count = msf->page_size / sizeof(U32) }; + U64 bit = bit_array_scan_left_to_right32(bits, msf->fpm_rover % interval, interval, MSF_PAGE_STATE_FREE); + if (bit >= interval) { + msf->fpm_rover = safe_cast_u32((block_idx + 1) * interval); + continue; + } + + MSF_PageNumber pn = safe_cast_u32(block_idx * interval + bit); + bit_array_set_bit32(bits, bit, MSF_PAGE_STATE_ALLOC); - // init page node - MSF_PageNode *page_node = msf_page_list_push(arena, list); - page_node->pn = *pn_ptr; + MSF_PageNode *node = msf_page_list_pop_last(&msf->page_pool); + if (node == 0) { node = push_array(msf->arena, MSF_PageNode, 1); } + + node->pn = pn; + msf_page_list_push_node(&result, node); + msf->fpm_rover = pn + 1; } + return result; } internal void -msf_page_list_push_extant_page(Arena *arena, MSF_PageList *list, MSF_PageDataList page_data_list, MSF_UInt page_size, MSF_PageNumber pn) +msf_free_pages(MSF_Context *msf, MSF_PageList *pages) { - msf_page_list_push_extant_page_arr(arena, list, page_data_list, page_size, &pn, 1); -} - -#if LNK_PARANOID -internal void -msf_check_fpm_bits_for_page_list(MSF_PageDataList page_data_list, MSF_UInt page_size, MSF_PageNumber active_fpm, MSF_PageList page_list, MSF_UInt test_state) -{ - for (MSF_PageNode *page_node = page_list.first; page_node != 0; page_node = page_node->next) { - MSF_UInt state = msf_get_fpm_page_bit_state(page_data_list, page_size, active_fpm, page_node->pn); - if (state != test_state) { - //Assert(!"state bit doesn't match"); - } + U64 interval = (U64)msf->page_size * MSF_BITS_PER_CHAR; + for (MSF_PageNode *node; (node = msf_page_list_pop_last(pages)) != 0; ) { + MSF_PageBlock *block = msf_block_from_index(msf, node->pn / interval); + U64 bit = node->pn % interval; + U32Array bits = { .v = block->free_bits, .count = msf->page_size / sizeof(U32) }; + Assert(bit_array_get_bit32(bits, bit) == MSF_PAGE_STATE_ALLOC); + bit_array_set_bit32(bits, bit, MSF_PAGE_STATE_FREE); + block->data[bit] = 0; + msf->fpm_rover = Min(msf->fpm_rover, node->pn); + msf_page_list_push_node(&msf->page_pool, node); } } -#endif - -//////////////////////////////// - -internal MSF_UInt -msf_count_pages(MSF_UInt page_size, U64 data_size) -{ - MSF_UInt page_count = CeilIntegerDiv(data_size, page_size); - return page_count; -} - -internal MSF_PageNumber -msf_get_page_count_cap(MSF_PageDataList page_data_list, MSF_UInt page_size) -{ - U64 node_size = msf_get_data_node_size(page_size); - U64 file_size = page_data_list.count * node_size; - U64 count = CeilIntegerDiv(file_size, (U64)page_size); - return safe_cast_u32(count); -} //////////////////////////////// -internal U32Array -msf_fpm_data_from_pn(MSF_PageDataList page_data_list, MSF_UInt page_size, MSF_PageNumber fpm_pn) -{ - String8 raw_fpm = msf_data_from_pn(page_data_list, page_size, fpm_pn); - U32Array fpm_data; - fpm_data.count = raw_fpm.size / sizeof(fpm_data.v[0]); - fpm_data.v = (U32*)raw_fpm.str; - return fpm_data; -} - -internal MSF_UInt -msf_get_fpm_interval_correct(MSF_UInt page_size) -{ - return page_size * MSF_BITS_PER_CHAR; -} - -internal MSF_UInt -msf_get_fpm_interval_wrong(MSF_UInt page_size) -{ - return page_size; -} - -internal MSF_UInt -msf_get_fpm_idx_from_pn(MSF_UInt page_size, MSF_PageNumber pn) -{ - MSF_UInt fpm_interval_correct = msf_get_fpm_interval_correct(page_size); - MSF_UInt fpm_idx = pn / fpm_interval_correct; - return fpm_idx; -} - -internal MSF_UInt -msf_get_fpm_page_count(MSF_PageDataList page_data_list, MSF_UInt page_size, MSF_UInt fpm_interval) -{ - U64 node_size = msf_get_data_node_size(page_size); - U64 file_size = (U64)page_data_list.count * node_size; - U64 file_page_count = CeilIntegerDiv(file_size, page_size); - U64 fpm_page_count = CeilIntegerDiv(file_page_count, (U64)fpm_interval); - return safe_cast_u32(fpm_page_count); -} - -internal MSF_PageNumberArray -msf_get_fpm_page_arr(Arena *arena, MSF_PageDataList page_data_list, MSF_UInt page_size, MSF_UInt active_fpm) -{ - Assert(active_fpm == MSF_FPM0 || active_fpm == MSF_FPM1); - MSF_UInt fpm_interval_correct = msf_get_fpm_interval_correct(page_size); - MSF_UInt fpm_interval_wrong = msf_get_fpm_interval_wrong(page_size); - MSF_UInt page_count = msf_get_page_count_cap(page_data_list, page_size); - MSF_PageNumberArray arr; - arr.count = CeilIntegerDiv(page_count, fpm_interval_correct); - arr.v = push_array(arena, MSF_PageNumber, arr.count); - for (MSF_UInt interval_idx = 0; interval_idx < arr.count; interval_idx += 1) { - arr.v[interval_idx] = active_fpm + interval_idx * fpm_interval_wrong; - } - return arr; -} - -internal MSF_PageNumber -msf_get_fpm_from_page_number(MSF_UInt page_size, MSF_PageNumber active_fpm, MSF_PageNumber pn) -{ - Assert(active_fpm == 1 || active_fpm == 2); - MSF_UInt fpm_interval_correct = msf_get_fpm_interval_correct(page_size); - MSF_UInt fpm_interval_wrong = msf_get_fpm_interval_wrong(page_size); - MSF_PageNumber fpm_pn = active_fpm; - fpm_pn += (pn / fpm_interval_correct) * fpm_interval_wrong; - return fpm_pn; -} - -internal MSF_UInt -msf_get_fpm_page_bit_state(MSF_PageDataList page_data_list, MSF_UInt page_size, MSF_PageNumber active_fpm, MSF_PageNumber pn) -{ - // fetch FPM - MSF_PageNumber fpm_pn = msf_get_fpm_from_page_number(page_size, active_fpm, pn); - U32Array fpm_data = msf_fpm_data_from_pn(page_data_list, page_size, fpm_pn); - - // get page bit - MSF_UInt fpm_interval_correct = msf_get_fpm_interval_correct(page_size); - MSF_UInt page_bit_idx = pn % fpm_interval_correct; - MSF_UInt state = bit_array_get_bit32(fpm_data, page_bit_idx); - - return state; -} - -internal void -msf_set_fpm_bit_(MSF_PageDataList page_data_list, MSF_UInt page_size, MSF_PageNumber active_fpm, MSF_PageNumber pn, B32 state) -{ - // fetch FPM - MSF_PageNumber fpm_pn = msf_get_fpm_from_page_number(page_size, active_fpm, pn); - U32Array fpm_data = msf_fpm_data_from_pn(page_data_list, page_size, fpm_pn); - - // set page bit - MSF_UInt fpm_interval_correct = msf_get_fpm_interval_correct(page_size); - MSF_UInt page_bit_idx = pn % fpm_interval_correct; - bit_array_set_bit32(fpm_data, page_bit_idx, state); -} - -internal void -msf_set_fpm_bit(MSF_PageDataList page_data_list, MSF_UInt page_size, MSF_PageNumber active_fpm, MSF_PageNumber pn, B32 state) -{ - msf_set_fpm_bit_(page_data_list, page_size, active_fpm, pn, state); -} - internal B32 -msf_grow(MSF_Context *msf, MSF_PageNumber new_page_count) +msf_stream_reserve_capacity(MSF_Context *msf, MSF_Stream *stream, U64 size) { - MSF_UInt fpm_interval_correct = msf_get_fpm_interval_correct(msf->page_size); - MSF_UInt fpm_interval_wrong = msf_get_fpm_interval_wrong(msf->page_size); - - // check alloc limit - U64 new_page_count64 = AlignPow2((U64)new_page_count, (U64)fpm_interval_correct); - B32 is_overflowed = new_page_count64 > MSF_PN_MAX; - if (is_overflowed) { - return 0; - } - - // check can only grow MSF here - new_page_count = safe_cast_u32(new_page_count64); - if (new_page_count < msf->page_count) { - return 0; + if (size >= MSF_DELETED_STREAM_STAMP) { return 0; } + + U64 capacity = AlignPow2(size, (U64)msf->page_size); + MSF_UInt page_count = msf_count_pages(msf->page_size, size); + B32 remap = 0; + + if (page_count > stream->page_list.count) { + MSF_UInt count = page_count - stream->page_list.count; + MSF_PageList pages = msf_alloc_pages(msf, count); + if (pages.count != count) { return 0; } + DLLConcatInPlace(&stream->page_list, &pages); + remap = 1; } - // compute number of FPM pages to allocate - // - // we allocate 8 times more FPMs because in MS impl they use wrong interval: - // https://github.com/microsoft/microsoft-pdb/blob/master/PDB/msf/msf.cpp#L509 - // - MSF_PageNumber prev_fpm_page_cap_wrong = msf_get_fpm_page_count(msf->page_data_list, msf->page_size, fpm_interval_wrong); - MSF_PageNumber curr_fpm_page_cap_wrong = CeilIntegerDiv(new_page_count, fpm_interval_wrong); - MSF_PageNumber alloc_count_wrong = curr_fpm_page_cap_wrong - prev_fpm_page_cap_wrong; - MSF_PageNumber next_pn_wrong = prev_fpm_page_cap_wrong * fpm_interval_wrong; - MSF_PageNumber end_pn_wrong = next_pn_wrong + alloc_count_wrong * fpm_interval_wrong; - - // compute correct number of FPM pages to grow - MSF_PageNumber prev_fpm_page_cap_correct = msf_get_fpm_page_count(msf->page_data_list, msf->page_size, fpm_interval_correct); - MSF_PageNumber curr_fpm_page_cap_correct = CeilIntegerDiv(new_page_count, fpm_interval_correct); - MSF_PageNumber alloc_count_correct = curr_fpm_page_cap_correct - prev_fpm_page_cap_correct; - MSF_PageNumber next_pn_correct = prev_fpm_page_cap_correct * fpm_interval_correct; - MSF_PageNumber end_pn_correct = next_pn_correct + alloc_count_correct * fpm_interval_correct; - - MSF_PageNumber to_alloc = alloc_count_correct; - - // are there unused data nodes? - if (msf->page_data_pool.count) { - MSF_PageNumber pool_alloc_count = Min(msf->page_data_pool.count, alloc_count_correct); - MSF_PageDataList page_data_list = msf_page_data_list_pop(&msf->page_data_pool, pool_alloc_count); - msf_page_data_list_concat_in_place(&msf->page_data_list, &page_data_list); - to_alloc -= pool_alloc_count; + if (capacity > stream->capacity) { + // sized callers allocate once. Incremental writers must reserve before + // retaining views; arena growth leaves previous views obsolete + U8 *data = push_array(msf->arena, U8, capacity); + if (stream->size != 0) { MemoryCopy(data, stream->data, stream->size); } + stream->data = data; + stream->capacity = capacity; + remap = 1; } - - // push enough data nodes to encompass allocated FPMs - msf_page_data_list_push(msf->arena, &msf->page_data_list, msf->page_size, to_alloc); - - // set FPM bits to free - for (MSF_PageNumber pn = next_pn_wrong; pn < end_pn_wrong; pn += fpm_interval_wrong) { - MSF_PageNumber fpm0_pn = pn + MSF_FPM0; - MSF_PageNumber fpm1_pn = pn + MSF_FPM1; - String8 fpm0_data = msf_data_from_pn(msf->page_data_list, msf->page_size, fpm0_pn); - String8 fpm1_data = msf_data_from_pn(msf->page_data_list, msf->page_size, fpm1_pn); - MemorySet(fpm0_data.str, 0xFF, msf->page_size); - MemorySet(fpm1_data.str, 0xFF, msf->page_size); + + if (remap) { + U64 offset = 0; + for EachNode(page, MSF_PageNode, stream->page_list.first) { + *msf_page_data_slot(msf, page->pn) = stream->data + offset; + offset += msf->page_size; + } } - - // set correct FPM bits - for (MSF_PageNumber pn = next_pn_correct; pn < end_pn_correct; pn += fpm_interval_correct) { - MSF_PageNumber fpm0_pn = pn + MSF_FPM0; - MSF_PageNumber fpm1_pn = pn + MSF_FPM1; - msf_set_fpm_bit(msf->page_data_list, msf->page_size, MSF_FPM0, fpm0_pn, MSF_PAGE_STATE_ALLOC); - msf_set_fpm_bit(msf->page_data_list, msf->page_size, MSF_FPM0, fpm1_pn, MSF_PAGE_STATE_ALLOC); - msf_set_fpm_bit(msf->page_data_list, msf->page_size, MSF_FPM1, fpm0_pn, MSF_PAGE_STATE_ALLOC); - msf_set_fpm_bit(msf->page_data_list, msf->page_size, MSF_FPM1, fpm1_pn, MSF_PAGE_STATE_ALLOC); - } - - // update context - msf->page_count += alloc_count_wrong * 2; return 1; } -#if 0 internal B32 -msf_shrink(MSF_Context *msf, MSF_PageNumber new_page_count) +msf_stream_resize_ex(MSF_Context *msf, MSF_Stream *stream, MSF_UInt size) { - MSF_UInt fpm_interval_wrong = msf_get_fpm_interval_wrong(msf->page_size); - MSF_UInt fpm_interval_correct = msf_get_fpm_interval_correct(msf->page_size); - - U64 new_page_count64 = AlignPow2((U64)new_page_count, (U64)fpm_interval_correct); - new_page_count = safe_cast_u32(new_page_count64); - Assert(new_page_count < msf->page_count); - - // compute number of FPM pages to deallocate - MSF_PageNumber prev_fpm_page_count_wrong = msf_get_fpm_page_count(msf->page_data_list, msf->page_size, fpm_interval_wrong); - MSF_PageNumber curr_fpm_page_count_wrong = CeilIntegerDiv(new_page_count, fpm_interval_wrong); - MSF_PageNumber dealloc_count_wrong = prev_fpm_page_count_wrong - curr_fpm_page_count_wrong; - - // compute next FPM page number - MSF_PageNumber next_pn = prev_fpm_page_count_wrong * fpm_interval_wrong; - MSF_PageNumber end_pn = next_pn - dealloc_count_wrong * fpm_interval_wrong; - - // pop data nodes - MSF_PageNumber prev_fpm_page_count_correct = msf_get_fpm_page_count(msf->page_data_list, msf->page_size, fpm_interval_correct); - MSF_PageNumber curr_fpm_page_count_correct = CeilIntegerDiv(new_page_count, fpm_interval_correct); - MSF_PageNumber dealloc_count_correct = prev_fpm_page_count_correct - curr_fpm_page_count_correct; - MSF_PageDataList free_page_data_list = msf_page_data_list_pop(&msf->page_data_list, dealloc_count_correct); - msf_page_data_list_concat_in_place(&msf->page_data_pool, &free_page_data_list); - - for (MSF_PageNumber pn = next_pn; pn > end_pn; pn -= fpm_interval_wrong) { - MSF_PageNumber fpm0_pn = pn + MSF_FPM0; - MSF_PageNumber fpm1_pn = pn + MSF_FPM1; - - // free FPM pages - msf_set_fpm_bit(msf->page_data_list, msf->page_size, 1, fpm0_pn, MSF_PAGE_STATE_FREE); - msf_set_fpm_bit(msf->page_data_list, msf->page_size, 1, fpm1_pn, MSF_PAGE_STATE_FREE); - msf_set_fpm_bit(msf->page_data_list, msf->page_size, 2, fpm0_pn, MSF_PAGE_STATE_FREE); - msf_set_fpm_bit(msf->page_data_list, msf->page_size, 2, fpm1_pn, MSF_PAGE_STATE_FREE); - } - - // update context - msf->page_count -= dealloc_count_wrong * 2; - - return true; -} -#endif + if (!msf_stream_reserve_capacity(msf, stream, size)) { return 0; } -internal MSF_PageNumber * -msf_alloc_pn_arr(Arena *arena, MSF_Context *msf, MSF_UInt alloc_count) -{ - // make sure FPM has enough space for new page numbers - // - // grow FPM at correct intervals here because we pre-alloc unused FPM pages ahead of time - MSF_UInt curr_page_cap = msf_get_page_count_cap(msf->page_data_list, msf->page_size); - for (;;) { - U64 new_page_count = (U64)msf->page_count + alloc_count; - if (new_page_count <= curr_page_cap) { - break; - } - if (new_page_count > MSF_PN_MAX || !msf_grow(msf, (MSF_PageNumber)new_page_count)) { - return 0; - } - curr_page_cap = msf_get_page_count_cap(msf->page_data_list, msf->page_size); + MSF_UInt page_count = msf_count_pages(msf->page_size, size); + MSF_PageList removed = {0}; + + while (stream->page_list.count > page_count) { + msf_page_list_push_node(&removed, msf_page_list_pop_last(&stream->page_list)); } - Temp scratch = scratch_begin(&arena, 1); + msf_free_pages(msf, &removed); - // reserve memory for page numbers - MSF_PageNumber *pn_arr = push_array(arena, MSF_PageNumber, alloc_count); - - MSF_UInt fpm_interval_correct = msf_get_fpm_interval_correct(msf->page_size); - MSF_UInt fpm_interval_wrong = msf_get_fpm_interval_wrong(msf->page_size); - - // get first FPM page - MSF_PageNumberArray fpm_pn_arr = msf_get_fpm_page_arr(scratch.arena, msf->page_data_list, msf->page_size, msf->active_fpm); - - for (MSF_UInt alloc_idx = 0; alloc_idx < alloc_count; ) { - // get FPM bits - MSF_UInt fpm_idx = msf->fpm_rover / fpm_interval_correct; - Assert(fpm_idx < fpm_pn_arr.count); - MSF_PageNumber fpm_pn = fpm_pn_arr.v[fpm_idx]; - U32Array fpm_data = msf_fpm_data_from_pn(msf->page_data_list, msf->page_size, fpm_pn); - - // scan FPM for free bit - MSF_UInt fpm_rover_page_relative = msf->fpm_rover % fpm_interval_correct; - U32 bit_idx = bit_array_scan_left_to_right32(fpm_data, fpm_rover_page_relative, fpm_interval_correct, MSF_PAGE_STATE_FREE); - - B32 is_full = (bit_idx >= fpm_interval_correct); - if (is_full) { - msf->fpm_rover = (fpm_idx + 1) * fpm_interval_correct; - continue; - } - - // compute page number - MSF_PageNumber pn = bit_idx + (fpm_idx * fpm_interval_correct); - - // make sure unused FPMs aren't allocated for regular streams, - // we used to mark with free bits unused FPMs but in VS2022 - // update they started to check for these bits and VS began - // to error out with "PDB format is not supported" message - B32 is_pn_valid = (pn % fpm_interval_wrong) != MSF_FPM0 && - (pn % fpm_interval_wrong) != MSF_FPM1; - if (is_pn_valid) { - // update FPM - bit_array_set_bit32(fpm_data, bit_idx, MSF_PAGE_STATE_ALLOC); - - // store page number - pn_arr[alloc_idx++] = pn; - } - - // advance FPM rover - msf->fpm_rover = pn + 1; + if (size < stream->size) { MemoryZero(stream->data + size, stream->size - size); } + + stream->size = size; + stream->pos = Min(stream->pos, size); + + return 1; +} + +internal MSF_StreamNode * +msf_find_stream_node(MSF_Context *msf, MSF_StreamNumber sn) +{ + for EachNode(node, MSF_StreamNode, msf->sectab.first) { + if (node->data.sn == sn) { return node; } } - - // update context - msf->page_count += alloc_count; - - scratch_end(scratch); - return pn_arr; + return 0; } -internal void -msf_free_pn_arr(MSF_Context *msf, MSF_PageNumber *pn_arr, MSF_UInt pn_count) +internal MSF_Stream * +msf_find_stream(MSF_Context *msf, MSF_StreamNumber sn) { - // set FPM bits - for (MSF_UInt i = 0; i < pn_count; i += 1) { - MSF_PageNumber pn = pn_arr[i]; - msf_set_fpm_bit(msf->page_data_list, msf->page_size, msf->active_fpm, pn, MSF_PAGE_STATE_FREE); - - // update FPM cursor - msf->fpm_rover = Min(msf->fpm_rover, pn); - } - - // update context - Assert(msf->page_count >= pn_count); - msf->page_count -= pn_count; -} - -internal MSF_PageList -msf_alloc_pages(MSF_Context *msf, MSF_UInt alloc_count) -{ - Temp scratch = scratch_begin(0, 0); - - MSF_PageList alloc_list = {0}; - MSF_PageNumber *pn_arr = msf_alloc_pn_arr(scratch.arena, msf, alloc_count); - if (pn_arr) { - for (MSF_UInt page_idx = 0; page_idx < alloc_count; page_idx += 1) { - // get page node - MSF_PageNode *page_node = 0; - if (msf->page_pool.count) { - page_node = msf_page_list_pop_last(&msf->page_pool); - msf_page_list_push_node(&alloc_list, page_node); - } else { - page_node = msf_page_list_push(msf->arena, &alloc_list); - } - - // copy page number - page_node->pn = pn_arr[page_idx]; - } - } - - scratch_end(scratch); - return alloc_list; -} - -internal void -msf_free_pages(MSF_Context *msf, MSF_PageList *page_list) -{ - Temp scratch = scratch_begin(0, 0); - - // free page numbers - MSF_PageNumber *pn_arr = msf_page_list_to_arr(scratch.arena, *page_list); - msf_free_pn_arr(msf, pn_arr, page_list->count); - - // push free nodes - msf_page_list_concat_in_place(&msf->page_pool, page_list); - - scratch_end(scratch); -} - -internal MSF_PageNumber -msf_find_max_pn_(MSF_PageDataList page_data_list, MSF_UInt page_size, MSF_PageNumberArray fpm_pn_arr) -{ - MSF_PageNumber max_pn = 0; - - MSF_UInt fpm_interval_correct = msf_get_fpm_interval_correct(page_size); - MSF_UInt fpm_interval_wrong = msf_get_fpm_interval_wrong(page_size); - MSF_UInt fpm_page_count = fpm_interval_correct / fpm_interval_wrong; - for (MSF_Int fpm_pn_idx = (MSF_Int)fpm_pn_arr.count - 1; fpm_pn_idx >= 0; fpm_pn_idx -= 1) { - MSF_PageNumber fpm_pn = fpm_pn_arr.v[fpm_pn_idx]; - U32Array fpm_data = msf_fpm_data_from_pn(page_data_list, page_size, fpm_pn); - - U64 bit_idx = max_U64; - U64 hi = fpm_data.count*32; - while (hi > 0) { - U64 msb_idx = bit_array_scan_right_to_left32(fpm_data, 0, hi, MSF_PAGE_STATE_ALLOC); - - // FPM is empty - if (msb_idx >= hi) { break; } - - // Skip reserved FPM pages, but not slot 0: after the file header's - // interval, that slot can contain stream data. - U64 k = msb_idx % fpm_interval_wrong; - if (k == MSF_FPM0 || k == MSF_FPM1) { - hi = msb_idx; - continue; - } - - bit_idx = msb_idx; - break; - } - - // stop if there is a page - if (bit_idx != max_U64) { - max_pn = Max(bit_idx + fpm_pn_idx * fpm_interval_correct, MSF_FPM1); - break; - } - } - - return max_pn; -} - -internal MSF_PageNumber -msf_find_max_pn(MSF_PageDataList page_data_list, MSF_UInt page_size) -{ - Temp scratch = scratch_begin(0, 0); - MSF_PageNumberArray fpm0_pn_arr = msf_get_fpm_page_arr(scratch.arena, page_data_list, page_size, MSF_FPM0); - MSF_PageNumberArray fpm1_pn_arr = msf_get_fpm_page_arr(scratch.arena, page_data_list, page_size, MSF_FPM1); - MSF_PageNumber fpm0_max = msf_find_max_pn_(page_data_list, page_size, fpm0_pn_arr); - MSF_PageNumber fpm1_max = msf_find_max_pn_(page_data_list, page_size, fpm1_pn_arr); - MSF_PageNumber max_pn = Max(fpm0_max, fpm1_max); - Assert(max_pn > 1); - scratch_end(scratch); - return max_pn; -} - -//////////////////////////////// - -internal B32 -msf_write__(MSF_PageDataList page_data_list, MSF_UInt page_size, MSF_PageNode **page_ptr, MSF_UInt *pos_ptr, void *buffer, MSF_UInt buffer_size) -{ - MSF_PageNode *start_page = *page_ptr; - MSF_UInt start_pos = *pos_ptr; - - MSF_UInt buffer_pos = 0; - while (*page_ptr) { - MSF_UInt page_offset = *pos_ptr % page_size; - - // compute copy size - MSF_UInt buffer_bytes_left = buffer_size - buffer_pos; - MSF_UInt page_bytes_left = page_size - page_offset; - MSF_UInt copy_size = Min(buffer_bytes_left, page_bytes_left); - - // fetch page bytes - MSF_PageNumber page_number = (*page_ptr)->pn; - String8 page_bytes = msf_data_from_pn(page_data_list, page_size, page_number); - - // copy bytes to buffer - U8 *buffer_copy_ptr = (U8*)buffer + buffer_pos; - U8 *page_bytes_ptr = page_bytes.str + page_offset; - MemoryCopy(page_bytes_ptr, buffer_copy_ptr, copy_size); - - // advance - buffer_pos += copy_size; - *pos_ptr += copy_size; - - // have we used all bytes in this page? - if (page_bytes_left <= copy_size) { - *page_ptr = (*page_ptr)->next; - } - - // have we copied all bytes? - if (buffer_bytes_left <= copy_size) { - break; - } - } - - B32 is_write_ok = (buffer_pos == buffer_size); - - // not enough bytes to perform write - restore positions - if (!is_write_ok) { - *page_ptr = start_page; - *pos_ptr = start_pos; - } - - return is_write_ok; -} - -internal MSF_UInt -msf_read__(MSF_PageDataList page_data_list, MSF_UInt page_size, MSF_PageNode **page_ptr, MSF_UInt *pos_ptr, void *buffer, MSF_UInt buffer_size) -{ - MSF_UInt buffer_pos = 0; - while (*page_ptr) { - MSF_UInt page_offset = *pos_ptr % page_size; - - // compute copy size - MSF_UInt buffer_bytes_left = buffer_size - buffer_pos; - MSF_UInt page_bytes_left = page_size - page_offset; - MSF_UInt copy_size = Min(buffer_bytes_left, page_bytes_left); - - // fetch page bytes - MSF_PageNumber page_number = (*page_ptr)->pn; - String8 page_bytes = msf_data_from_pn(page_data_list, page_size, page_number); - - // copy bytes to buffer - U8 *buffer_ptr = (U8*)buffer + buffer_pos; - U8 *page_bytes_ptr = page_bytes.str + page_offset; - MemoryCopy(buffer_ptr, page_bytes_ptr, copy_size); - - // advance - buffer_pos += copy_size; - *pos_ptr += copy_size; - - // no more bytes left in this page - if (page_bytes_left <= copy_size) { - *page_ptr = (*page_ptr)->next; - } - - // have we copied all bytes? - if (buffer_bytes_left <= copy_size) { - break; - } - } - - MSF_UInt bytes_read = buffer_pos; - //Assert(bytes_read == buffer_size); - - return bytes_read; -} - -internal B32 -msf_write(MSF_PageDataList page_data_list, MSF_UInt page_size, MSF_PageList page_list, MSF_UInt offset, void *buffer, MSF_UInt buffer_size) -{ - MSF_UInt page_idx = offset / page_size; - MSF_PageNode *page = msf_page_from_index(page_list, page_idx); - B32 is_write_ok = msf_write__(page_data_list, page_size, &page, &offset, buffer, buffer_size); - return is_write_ok; -} - -internal MSF_UInt -msf_read(MSF_PageDataList page_data_list, MSF_UInt page_size, MSF_PageList page_list, MSF_UInt offset, void *buffer, MSF_UInt buffer_size) -{ - MSF_UInt page_idx = offset / page_size; - MSF_PageNode *page = msf_page_from_index(page_list, page_idx); - MSF_UInt bytes_read = msf_read__(page_data_list, page_size, &page, &offset, buffer, buffer_size); - return bytes_read; -} - -//////////////////////////////// - -internal MSF_StreamNode * -msf_stream_alloc_(Arena *arena, MSF_StreamList *list) -{ - Assert(list->count < MSF_STREAM_NUMBER_MAX); - MSF_UInt sn = list->count; - MSF_StreamNode *stream_node = msf_stream_list_push(arena, list); - MSF_Stream *stream = &stream_node->data; - stream->sn = safe_cast_u16(sn); - return stream_node; + MSF_StreamNode *node = msf_find_stream_node(msf, sn); + return node != 0 && node->data.size != MSF_DELETED_STREAM_STAMP ? &node->data : 0; } internal MSF_StreamNumber msf_stream_alloc_ex(MSF_Context *msf, MSF_UInt size) { - MSF_StreamNode *node = msf_stream_alloc_(msf->arena, &msf->sectab); - MSF_Stream *stream = &node->data; - msf_stream_resize_ex(msf, stream, size); - return stream->sn; + Assert(msf->sectab.count < MSF_STREAM_NUMBER_MAX); + MSF_StreamNode *node = push_array(msf->arena, MSF_StreamNode, 1); + node->data.sn = safe_cast_u16(msf->sectab.count); + if (!msf_stream_resize_ex(msf, &node->data, size)) { return MSF_INVALID_STREAM_NUMBER; } + DLLPushBack(msf->sectab.first, msf->sectab.last, node); + msf->sectab.count += 1; + return node->data.sn; } internal MSF_StreamNumber @@ -815,132 +228,51 @@ msf_stream_alloc(MSF_Context *msf) } internal B32 -msf_stream_resize_ex(MSF_Context *msf, MSF_Stream *stream, MSF_UInt size) +msf_stream_free(MSF_Context *msf, MSF_StreamNumber sn) { - MSF_UInt new_page_count = msf_count_pages(msf->page_size, size); - MSF_UInt cur_page_count = stream->page_list.count; - - if (new_page_count > cur_page_count) { - MSF_UInt alloc_count = new_page_count - cur_page_count; - MSF_PageList page_list = msf_alloc_pages(msf, alloc_count); - msf_page_list_concat_in_place(&stream->page_list, &page_list); - } else { - MSF_PageList free_page_list = {0}; - for (MSF_UInt i = cur_page_count; i > new_page_count; i -= 1) { - MSF_PageNode *page_node = msf_page_list_pop_last(&stream->page_list); - msf_page_list_push_node(&free_page_list, page_node); - } - msf_free_pages(msf, &free_page_list); - } - - // update stream - stream->size = size; - stream->pos = Min(stream->pos, stream->size); - stream->pos_page = 0; - + MSF_Stream *stream = msf_find_stream(msf, sn); + if (stream == 0) { return 0; } + msf_free_pages(msf, &stream->page_list); + stream->size = MSF_DELETED_STREAM_STAMP; + stream->pos = 0; return 1; } internal B32 -msf_stream_resize(MSF_Context *msf, MSF_StreamNumber sn, MSF_UInt new_size) +msf_stream_resize(MSF_Context *msf, MSF_StreamNumber sn, MSF_UInt size) { MSF_Stream *stream = msf_find_stream(msf, sn); - B32 is_resized = 0; - if (stream) { - is_resized = msf_stream_resize_ex(msf, stream, new_size); - } - return is_resized; -} - -internal B32 -msf_stream_free(MSF_Context *msf, MSF_StreamNumber sn) -{ - B32 is_free_ok = 0; - MSF_StreamNode *stream_node = msf_find_stream_node(msf, sn); - if (stream_node) { - msf_stream_list_remove(&msf->sectab, stream_node); - msf_stream_resize_ex(msf, &stream_node->data, 0); - stream_node->data.size = MSF_DELETED_STREAM_STAMP; - is_free_ok = 1; - } - return is_free_ok; -} - -internal void -msf_stream_set_size(MSF_Context *msf, MSF_StreamNumber sn, MSF_UInt size) -{ - MSF_Stream *stream = msf_find_stream(msf, sn); - if (stream) { - stream->size = Min(size, stream->page_list.count * msf->page_size); - } else { - Assert(!"invalid stream number"); - } + return stream != 0 && msf_stream_resize_ex(msf, stream, size); } internal MSF_UInt msf_stream_get_size(MSF_Context *msf, MSF_StreamNumber sn) { - MSF_UInt size = MSF_UINT_MAX; MSF_Stream *stream = msf_find_stream(msf, sn); - if (stream) { - size = stream->size; - } - return size; -} - -internal MSF_UInt -msf_stream_get_cap__(MSF_Context *msf, MSF_Stream *stream) -{ - return stream->page_list.count * msf->page_size; -} - -internal MSF_UInt -msf_stream_get_cap(MSF_Context *msf, MSF_StreamNumber sn) -{ - MSF_Stream *stream = msf_find_stream(msf, sn); - MSF_UInt cap = 0; - if (stream) { - cap = msf_stream_get_cap__(msf, stream); - } - return cap; -} - -internal MSF_UInt -msf_stream_get_pos__(MSF_Context *msf, MSF_Stream *stream) -{ - return stream->pos; + return stream != 0 ? stream->size : MSF_UINT_MAX; } internal MSF_UInt msf_stream_get_pos(MSF_Context *msf, MSF_StreamNumber sn) { MSF_Stream *stream = msf_find_stream(msf, sn); - MSF_UInt pos = MSF_UINT_MAX; - if (stream) { - pos = msf_stream_get_pos__(msf, stream); - } - return pos; + return stream != 0 ? stream->pos : MSF_UINT_MAX; } internal B32 -msf_stream_seek__(MSF_Context *msf, MSF_Stream *stream, MSF_UInt new_pos) -{ (void)msf; - stream->pos = Min(new_pos, stream->size); - stream->pos_page = 0; - return 1; +msf_stream_reserve(MSF_Context *msf, MSF_StreamNumber sn, MSF_UInt size) +{ + MSF_Stream *stream = msf_find_stream(msf, sn); + return stream != 0 && msf_stream_reserve_capacity(msf, stream, (U64)stream->pos + size); } -internal B32 -msf_stream_seek(MSF_Context *msf, MSF_StreamNumber sn, MSF_UInt new_pos) +internal B32 +msf_stream_seek(MSF_Context *msf, MSF_StreamNumber sn, MSF_UInt pos) { - B32 is_seek_ok = 0; MSF_Stream *stream = msf_find_stream(msf, sn); - if (stream) { - is_seek_ok = msf_stream_seek__(msf, stream, new_pos); - } else { - Assert(!"failed to stream seek"); - } - return is_seek_ok; + if (stream == 0) { return 0; } + stream->pos = Min(pos, stream->size); + return 1; } internal B32 @@ -949,517 +281,142 @@ msf_stream_seek_start(MSF_Context *msf, MSF_StreamNumber sn) return msf_stream_seek(msf, sn, 0); } -internal B32 -msf_stream_seek_end(MSF_Context *msf, MSF_StreamNumber sn) +internal String8 +msf_stream_data(MSF_Context *msf, MSF_StreamNumber sn) { - MSF_UInt end = msf_stream_get_size(msf, sn); - return msf_stream_seek(msf, sn, end); + MSF_Stream *stream = msf_find_stream(msf, sn); + return stream != 0 ? str8(stream->data, stream->size) : str8_zero(); } - -internal B32 -msf_stream_write__(MSF_Context *msf, MSF_Stream *stream, void *buffer, MSF_UInt buffer_size) +internal B32 +msf_stream_write(MSF_Context *msf, MSF_StreamNumber sn, void *buffer, MSF_UInt size) { - B32 is_write_ok = 0; + MSF_Stream *stream = msf_find_stream(msf, sn); + if (stream == 0) { return 0; } - // are we writing over limit? - Assert((U64)stream->pos + (U64)buffer_size <= (U64)MSF_UINT_MAX); - - // make sure we have enough space to write buffer - MSF_UInt stream_cap = msf_stream_get_cap__(msf, stream); - MSF_UInt stream_pos_opl = stream->pos + buffer_size; - B32 grow_stream = stream_pos_opl > stream_cap; - if (grow_stream) { - B32 is_resize_ok = msf_stream_resize_ex(msf, stream, stream_pos_opl); - if (!is_resize_ok) { - goto exit; - } + U64 end = (U64)stream->pos + size; + if (end >= MSF_DELETED_STREAM_STAMP) { return 0; } + + if (end > stream->capacity || msf_count_pages(msf->page_size, end) > stream->page_list.count) { + if (!msf_stream_reserve_capacity(msf, stream, end)) { return 0; } } - - if (buffer) { - // lookup page for current stream position - if (!stream->pos_page) { - MSF_UInt page_idx = stream->pos / msf->page_size; - stream->pos_page = msf_page_from_index(stream->page_list, page_idx); - } - - // make write - is_write_ok = msf_write__(msf->page_data_list, msf->page_size, &stream->pos_page, &stream->pos, buffer, buffer_size); - } else { - stream->pos += buffer_size; - stream->pos_page = 0; - is_write_ok = 1; + + if (size != 0) { + if (buffer != 0) { MemoryCopy(stream->data + stream->pos, buffer, size); } + else { MemoryZero(stream->data + stream->pos, size); } } - - // update stream size + + stream->pos = safe_cast_u32(end); stream->size = Max(stream->size, stream->pos); -exit:; - Assert(is_write_ok); - return is_write_ok; -} - -internal MSF_UInt -msf_stream_reserve__(MSF_Context *msf, MSF_Stream *stream, MSF_UInt res) -{ - ProfBeginV("MSF Reserve %m", res); - - B32 is_ok = 1; - - MSF_UInt cap = msf_stream_get_cap__(msf, stream); - MSF_UInt pos = msf_stream_get_pos__(msf, stream); - MSF_UInt cur = cap - pos; - - if (cur < res) { - is_ok = msf_stream_write__(msf, stream, 0, res); - AssertAlways(is_ok); - - is_ok = msf_stream_seek__(msf, stream, pos); - AssertAlways(is_ok); - } - - ProfEnd(); - return is_ok; -} - -internal B32 -msf_stream_reserve(MSF_Context *msf, MSF_StreamNumber sn, MSF_UInt res) -{ - MSF_Stream *stream = msf_find_stream(msf, sn); - B32 is_res_ok = 0; - if (stream) { - is_res_ok = msf_stream_reserve__(msf, stream, res); - } - return is_res_ok; -} - -internal B32 -msf_stream_write(MSF_Context *msf, MSF_StreamNumber sn, void *buffer, MSF_UInt buffer_size) -{ - B32 is_write_ok = 0; - MSF_Stream *stream = msf_find_stream(msf, sn); - if (stream) { - is_write_ok = msf_stream_write__(msf, stream, buffer, buffer_size); - } - return is_write_ok; + return 1; } internal B32 msf_stream_write_string(MSF_Context *msf, MSF_StreamNumber sn, String8 string) { - return msf_stream_write(msf, sn, string.str, string.size); + return msf_stream_write(msf, sn, string.str, safe_cast_u32(string.size)); } internal B32 msf_stream_write_list(MSF_Context *msf, MSF_StreamNumber sn, String8List list) { - B32 is_write_ok = 0; - MSF_Stream *stream = msf_find_stream(msf, sn); - if (stream) { - for (String8Node *node = list.first; node != 0; node = node->next) { - is_write_ok = msf_stream_write__(msf, stream, node->string.str, node->string.size); - if (!is_write_ok) { - break; - } - } - } - return is_write_ok; -} + if (!msf_stream_reserve(msf, sn, safe_cast_u32(list.total_size))) { return 0; } -internal B32 -msf_stream_write_uint(MSF_Context *msf, MSF_StreamNumber sn, MSF_UInt value) -{ - return msf_stream_write_struct(msf, sn, &value); + for EachNode(node, String8Node, list.first) { + if (!msf_stream_write_string(msf, sn, node->string)) { return 0; } + } + + return 1; } internal B32 msf_stream_write_cstr(MSF_Context *msf, MSF_StreamNumber sn, String8 string) { - B32 is_string_written = msf_stream_write_string(msf, sn, string); - B32 is_null_written = msf_stream_write(msf, sn, 0, 1); - return is_string_written && is_null_written; + return msf_stream_write_string(msf, sn, string) && msf_stream_write_u8(msf, sn, 0); } -internal B32 -msf_stream_write_u8(MSF_Context *msf, MSF_StreamNumber sn, U8 value) -{ - return msf_stream_write(msf, sn, &value, sizeof(value)); -} +#define MSF_WRITE_SCALAR(T, suffix) \ +internal B32 msf_stream_write_##suffix(MSF_Context *msf, MSF_StreamNumber sn, T value) \ +{ return msf_stream_write(msf, sn, &value, sizeof(value)); } +MSF_WRITE_SCALAR(U8, u8) +MSF_WRITE_SCALAR(U16, u16) +MSF_WRITE_SCALAR(U32, u32) +MSF_WRITE_SCALAR(U64, u64) +MSF_WRITE_SCALAR(S8, s8) +MSF_WRITE_SCALAR(S16, s16) +MSF_WRITE_SCALAR(S32, s32) +MSF_WRITE_SCALAR(S64, s64) +#undef MSF_WRITE_SCALAR -internal B32 -msf_stream_write_u16(MSF_Context *msf, MSF_StreamNumber sn, U16 value) +internal void +msf_stream_align(MSF_Context *msf, MSF_StreamNumber sn, MSF_UInt align) { - return msf_stream_write(msf, sn, &value, sizeof(value)); -} - -internal B32 -msf_stream_write_u32(MSF_Context *msf, MSF_StreamNumber sn, U32 value) -{ - return msf_stream_write(msf, sn, &value, sizeof(value)); -} - -internal B32 -msf_stream_write_u64(MSF_Context *msf, MSF_StreamNumber sn, U64 value) -{ - return msf_stream_write(msf, sn, &value, sizeof(value)); -} - -internal B32 -msf_stream_write_s8(MSF_Context *msf, MSF_StreamNumber sn, S8 value) -{ - return msf_stream_write(msf, sn, &value, sizeof(value)); -} - -internal B32 -msf_stream_write_s16(MSF_Context *msf, MSF_StreamNumber sn, S16 value) -{ - return msf_stream_write(msf, sn, &value, sizeof(value)); -} - -internal B32 -msf_stream_write_s32(MSF_Context *msf, MSF_StreamNumber sn, S32 value) -{ - return msf_stream_write(msf, sn, &value, sizeof(value)); -} - -internal B32 -msf_stream_write_s64(MSF_Context *msf, MSF_StreamNumber sn, S64 value) -{ - return msf_stream_write(msf, sn, &value, sizeof(value)); + MSF_UInt pos = msf_stream_get_pos(msf, sn); + msf_stream_write(msf, sn, 0, AlignPadPow2(pos, align)); } internal THREAD_POOL_TASK_FUNC(msf_write_task) { - ProfBeginFunction(); MSF_WriteTask *task = raw_task; - - Rng1U64 range = task->range_arr[task_id]; - String8 data = str8_substr(task->data, range); - MSF_UInt data_pos = range.min + task->stream_pos; - - MSF_UInt page_idx = data_pos / task->page_size; - MSF_PageNode *page = msf_page_from_index(task->page_list, page_idx); - - if (!msf_write__(task->page_data_list, task->page_size, &page, &data_pos, data.str, data.size)) { - InvalidPath; - } - ProfEnd(); + Rng1U64 range = task->ranges[task_id]; + if (dim_1u64(range) != 0) { MemoryCopy(task->dst + range.min, task->src + range.min, dim_1u64(range)); } } internal B32 -msf_stream_write_parallel(TP_Context *tp, MSF_Context *msf, MSF_StreamNumber sn, void *buffer, MSF_UInt buffer_size) +msf_stream_write_parallel(TP_Context *tp, MSF_Context *msf, MSF_StreamNumber sn, void *buffer, MSF_UInt size) { - ProfBeginV("MSF Write Parallel %m", buffer_size); + if (buffer == 0 || size < KB(64)) { return msf_stream_write(msf, sn, buffer, size); } + + if (!msf_stream_reserve(msf, sn, size)) { return 0; } + + Temp scratch = scratch_begin(0, 0); MSF_Stream *stream = msf_find_stream(msf, sn); + MSF_WriteTask task = { + .dst = stream->data + stream->pos, + .src = buffer, + .ranges = tp_divide_work(scratch.arena, size, tp->worker_count), + }; - B32 is_write_ok = msf_stream_reserve__(msf, stream, buffer_size); + tp_for_parallel(tp, 0, tp->worker_count, msf_write_task, &task); - if (is_write_ok) { - U64 expected_pos = stream->pos + buffer_size; + stream->pos += size; + stream->size = Max(stream->size, stream->pos); - MSF_UInt pre_size = Min(AlignPadPow2(stream->pos, msf->page_size), buffer_size); - MSF_UInt mid_size = AlignDownPow2(buffer_size - pre_size, msf->page_size); - MSF_UInt end_size = buffer_size - (pre_size + mid_size); - - U8 *pre_ptr = (U8*)buffer; - U8 *mid_ptr = (U8*)buffer + pre_size; - U8 *end_ptr = (U8*)buffer + pre_size + mid_size; - - ProfBeginV("Write Buffer Pre %M", pre_size); - B32 is_pre_written = msf_stream_write__(msf, stream, pre_ptr, pre_size); - AssertAlways(is_pre_written); - ProfEnd(); - - // write buffer mid - if (mid_size > 0) { - Temp scratch = scratch_begin(0,0); - - Assert(stream->pos % msf->page_size == 0); - Assert(mid_size % msf->page_size == 0); - - MSF_WriteTask task; - task.page_size = msf->page_size; - task.page_data_list = msf->page_data_list; - task.page_list = stream->page_list; - task.stream_pos = stream->pos; - task.data = str8(mid_ptr, mid_size); - task.range_arr = tp_divide_work(scratch.arena, mid_size, tp->worker_count); - tp_for_parallel(tp, 0, tp->worker_count, msf_write_task, &task); - - // we rely on low-level msf_write__ to copy bytes which doesn't advance stream pos - U64 after_mid = stream->pos + mid_size; - B32 is_seek_ok = msf_stream_seek__(msf, stream, after_mid); - AssertAlways(is_seek_ok); - - scratch_end(scratch); - } - - ProfBeginV("Write Buffer End %M", end_size); - B32 is_end_ok = msf_stream_write__(msf, stream, end_ptr, end_size); - AssertAlways(is_end_ok); - ProfEnd(); - - // did we write bytes correctly? - AssertAlways(stream->pos == expected_pos); - } - - ProfEnd(); - return is_write_ok; -} - -internal B32 -msf_stream_write_string_parallel(TP_Context *tp, MSF_Context *msf, MSF_StreamNumber sn, String8 string) -{ - return msf_stream_write_parallel(tp, msf, sn, string.str, string.size); -} - -internal String8List -msf_data_from_sn(Arena *arena, MSF_Context *msf, MSF_StreamNumber sn) -{ - String8List result = {0}; - - MSF_Stream *stream = msf_find_stream(msf, sn); - String8 acc = {0}; - - MSF_PageNode *n; - for (n = stream->page_list.first; n != stream->page_list.last; n = n->next) { - String8 page = msf_data_from_pn(msf->page_data_list, msf->page_size, n->pn); - - if (acc.str + acc.size != page.str) { - if (acc.size) { - str8_list_push(arena, &result, acc); - } - acc = page; - } else { - acc.size += page.size; - } - } - - if (acc.size) { - str8_list_push(arena, &result, acc); - MemoryZeroStruct(&acc); - } - - if (n) { - String8 page = msf_data_from_pn(msf->page_data_list, msf->page_size, n->pn); - if (acc.str + acc.size != page.str) { - acc = page; - } else { - acc.size += page.size; - } - - acc.size = Min(stream->size - result.total_size, acc.size); - - if (acc.size) { - str8_list_push(arena, &result, acc); - } - } - - Assert(result.total_size == stream->size); - return result; + scratch_end(scratch); + return 1; } //////////////////////////////// -internal MSF_UInt -msf_stream_read__(MSF_Context *msf, MSF_Stream *stream, void *buffer, MSF_UInt buffer_size) -{ - // are we reading over limit? - Assert((U64)stream->pos + (U64)buffer_size <= (U64)MSF_UINT_MAX); - - // lookup page for current stream position - if (!stream->pos_page) { - MSF_UInt pos_page_idx = stream->pos / msf->page_size; - stream->pos_page = msf_page_from_index(stream->page_list, pos_page_idx); - } - - MSF_UInt bytes_read = msf_read__(msf->page_data_list, msf->page_size, &stream->pos_page, &stream->pos, buffer, buffer_size); - return bytes_read; -} - -internal MSF_UInt -msf_stream_read(MSF_Context *msf, MSF_StreamNumber sn, void *buffer, MSF_UInt buffer_size) -{ - MSF_Stream *stream = msf_find_stream(msf, sn); - if (stream) { - return msf_stream_read__(msf, stream, buffer, buffer_size); - } - return 0; -} - -internal S8 -msf_stream_read_s8(MSF_Context *msf, MSF_StreamNumber sn) -{ - S8 result = 0; - msf_stream_read_struct(msf, sn, &result); - return result; -} - -internal S16 -msf_stream_read_s16(MSF_Context *msf, MSF_StreamNumber sn) -{ - S16 result = 0; - msf_stream_read_struct(msf, sn, &result); - return result; -} - -internal S32 -msf_stream_read_s32(MSF_Context *msf, MSF_StreamNumber sn) -{ - S32 result = 0; - msf_stream_read_struct(msf, sn, &result); - return result; -} - -internal S64 -msf_stream_read_s64(MSF_Context *msf, MSF_StreamNumber sn) -{ - S64 result = 0; - msf_stream_read_struct(msf, sn, &result); - return result; -} - -internal U8 -msf_stream_read_u8(MSF_Context *msf, MSF_StreamNumber sn) -{ - U8 result = 0; - msf_stream_read_struct(msf, sn, &result); - return result; -} - -internal U16 -msf_stream_read_u16(MSF_Context *msf, MSF_StreamNumber sn) -{ - U16 result = 0; - msf_stream_read_struct(msf, sn, &result); - return result; -} - -internal U32 -msf_stream_read_u32(MSF_Context *msf, MSF_StreamNumber sn) -{ - U32 result = 0; - msf_stream_read_struct(msf, sn, &result); - return result; -} - -internal U64 -msf_stream_read_u64(MSF_Context *msf, MSF_StreamNumber sn) -{ - U64 result = 0; - msf_stream_read_struct(msf, sn, &result); - return result; -} - -internal String8 -msf_stream_read_block(Arena *arena, MSF_Context *msf, MSF_StreamNumber sn, U64 block_size) -{ - U8 *block_buffer = push_array(arena, U8, block_size); - MSF_UInt block_read = msf_stream_read(msf, sn, block_buffer, block_size); - Assert((U64)block_read == block_size); - String8 block = str8(block_buffer, block_size); - return block; -} - -internal String8 -msf_stream_read_string(Arena *arena, MSF_Context *msf, MSF_StreamNumber sn) -{ - MSF_UInt start_pos = msf_stream_get_pos(msf, sn); - U64 size = 0; - for (;; size += 1) { - U8 cp = msf_stream_read_u8(msf, sn); - if (cp == 0) { - break; - } - } - - msf_stream_seek(msf, sn, start_pos); - String8 string = msf_stream_read_block(arena, msf, sn, size); - msf_stream_seek(msf, sn, start_pos + size + 1); // skip null - - return string; -} - -internal void -msf_stream_align(MSF_Context *msf, MSF_StreamNumber sn, MSF_UInt align) -{ - local_persist U8 zeroes[64]; - MSF_UInt pos = msf_stream_get_pos(msf, sn); - MSF_UInt pad = AlignPadPow2(pos, align); - if (pad > 0) { - if (pad < sizeof(zeroes)) { - msf_stream_write(msf, sn, zeroes, pad); - } else { - NotImplemented; - } - } -} - -//////////////////////////////// - -internal MSF_Context * -msf_alloc__(Arena *arena, MSF_UInt page_size, MSF_PageNumber active_fpm) -{ - ProfBeginFunction(); - Assert(active_fpm == MSF_FPM0 || active_fpm == MSF_FPM1); - Assert(IsPow2(page_size)); - - MSF_Context *msf = push_array(arena, MSF_Context, 1); - msf->arena = arena; - msf->page_size = page_size; - msf->active_fpm = active_fpm; - - ProfEnd(); - return msf; -} - internal MSF_Context * msf_alloc_(Arena *arena, MSF_UInt page_size, MSF_UInt active_fpm) { - MSF_Context *msf = msf_alloc__(arena, page_size, active_fpm); - - // reserve first page for header - msf->header_page_list = msf_alloc_pages(msf, 1); - Assert(msf->header_page_list.count > 0); - Assert(msf->header_page_list.first->pn == 0); - - // reserve root page close to start of the file so we don't have to seek too far (not required) - msf->root_page_list = msf_alloc_pages(msf, 1); - Assert(msf->root_page_list.count == 1); - Assert(msf->root_page_list.first->pn == 3); - + Assert(IsPow2(page_size) && MSF_MIN_PAGE_SIZE <= page_size && page_size <= MSF_MAX_PAGE_SIZE); + Assert(active_fpm == MSF_FPM0 || active_fpm == MSF_FPM1); + + MSF_Context *msf = push_array(arena, MSF_Context, 1); + msf->arena = arena; + msf->page_size = page_size; + msf->active_fpm = active_fpm; + msf->zero_page = push_array(arena, U8, page_size); + msf->empty_fpm = push_array_no_zero(arena, U8, page_size); + MemorySet(msf->empty_fpm, 0xff, page_size); + + AssertAlways(msf_stream_resize_ex(msf, &msf->header, page_size)); + AssertAlways(msf_stream_resize_ex(msf, &msf->root, page_size)); + Assert(msf->header.page_list.first->pn == 0); + Assert(msf->root.page_list.first->pn == 3); + return msf; } internal MSF_Context * msf_alloc(MSF_UInt page_size, MSF_UInt active_fpm) { - return msf_alloc_(arena_alloc( .name = "MSF"), page_size, active_fpm); -} - -internal MSF_StreamNode * -msf_find_stream_node(MSF_Context *msf, MSF_StreamNumber sn) -{ - MSF_StreamNode *node; - for (node = msf->sectab.first; node != 0; node = node->next) { - if (node->data.sn == sn) { - break; - } - } - return node; -} - -internal MSF_Stream * -msf_find_stream(MSF_Context *msf, MSF_StreamNumber sn) -{ - MSF_StreamNode *node = msf_find_stream_node(msf, sn); - MSF_Stream *data = 0; - if (node) { - data = &node->data; - } - return data; + return msf_alloc_(arena_alloc(.name = "MSF"), page_size, active_fpm); } internal void @@ -1468,537 +425,170 @@ msf_release(MSF_Context *msf) arena_release(msf->arena); } -internal String8List -msf_build_stream_table_data(Arena *arena, MSF_StreamList *sectab, MSF_UInt page_size, MSF_UInt page_count) +internal U64 +msf_get_save_size(MSF_Context *msf) { - ProfBeginFunction(); - - MSF_UInt *stream_count_ptr = push_array(arena, MSF_UInt, 1); - *stream_count_ptr = sectab->count; + U64 interval = (U64)msf->page_size * MSF_BITS_PER_CHAR; + U64 max_pn = MSF_FPM1; + U64 base = 0; - MSF_UInt *stream_size_arr = push_array(arena, MSF_UInt, sectab->count); - MSF_UInt stream_page_count = 0; - - MSF_PageNumber *stream_pages_arr = push_array(arena, MSF_PageNumber, page_count); - - for (MSF_StreamNode *stream_node = sectab->first; stream_node != 0; stream_node = stream_node->next) { - MSF_Stream *stream = &stream_node->data; - - // is page list correct? - MSF_UInt expected_stream_page_count = msf_count_pages(page_size, stream->size); - if (expected_stream_page_count > stream->page_list.count) { - Assert(!"invalid page list "); - } - - // store stream sizes - stream_size_arr[stream->sn] = stream->size; - - // store stream pages - for (MSF_PageNode *page_node = stream->page_list.first; page_node != 0; page_node = page_node->next) { - // first three pages are reserved for header, FPM0, and FPM1 - Assert(page_node->pn > 2); - - // it's not necessarily a bug to use interval FPM pages, - // but for sake of correctness make sure there is no stream - // aside from FPM that uses these pages - // - // also, actual FPM pages should be asserted on: pn % (msf->page_size * MSF_BITS_PER_CHAR) - Assert((page_node->pn % page_size) != 1); - Assert((page_node->pn % page_size) != 2); - - // is there a stream with too many page nodes? - Assert(stream_page_count < page_count); - - // is this page number allocated? - //Assert(msf_get_fpm_page_bit_state(msf, page_node->pn) == MSF_PAGE_STATE_ALLOC); - - stream_pages_arr[stream_page_count] = page_node->pn; - stream_page_count += 1; + for EachNode(block, MSF_PageBlock, msf->first_block) { + U32Array bits = { .v = block->free_bits, .count = msf->page_size / sizeof(U32) }; + U64 hi = interval; + while (hi != 0) { + U64 bit = bit_array_scan_right_to_left32(bits, 0, hi, MSF_PAGE_STATE_ALLOC); + if (bit >= hi) { break; } + U64 slot = bit % msf->page_size; + if (slot != MSF_FPM0 && slot != MSF_FPM1) { + max_pn = Max(max_pn, base + bit); + break; + } + hi = bit; } + base += interval; } - - // on disk stream table: - // MSF_UInt stream_count; - // MSF_UInt stream_size[stream_count]; - // MSF_PageNumber pages[stream_count][*]; - String8List st_data_list = {0}; - str8_list_push(arena, &st_data_list, str8((U8*)stream_count_ptr, sizeof(*stream_count_ptr))); - str8_list_push(arena, &st_data_list, str8((U8*)stream_size_arr, sizeof(*stream_size_arr) * (*stream_count_ptr))); - str8_list_push(arena, &st_data_list, str8((U8*)stream_pages_arr, sizeof(*stream_pages_arr) * stream_page_count)); - - ProfEnd(); - return st_data_list; + + return (max_pn + 1) * msf->page_size; } internal MSF_Error -msf_build_stream_table(MSF_Context *msf, MSF_UInt *stream_table_size_out) -{ - ProfBeginFunction(); - Temp scratch = scratch_begin(0, 0); - - MSF_Error error = MSF_Error_OK; - - String8List st_data_list = msf_build_stream_table_data(scratch.arena, &msf->sectab, msf->page_size, msf->page_count); - - MSF_UInt st_page_count = msf_count_pages(msf->page_size, st_data_list.total_size); - msf_free_pages(msf, &msf->st_page_list); // TODO: page reuse - msf->st_page_list = msf_alloc_pages(msf, st_page_count); - - MSF_UInt cursor = 0; - for (String8Node *node = st_data_list.first; node != 0; node = node->next) { - B32 is_data_written = msf_write(msf->page_data_list, msf->page_size, msf->st_page_list, cursor, node->string.str, node->string.size); - if (!is_data_written) { - error = MSF_BuildError_UNABLE_TO_WRITE_STREAM_TABLE; - goto exit; - } - cursor += node->string.size; - } - - *stream_table_size_out = st_data_list.total_size; - - exit:; - scratch_end(scratch); - ProfEnd(); - return error; -} - -internal MSF_Error -msf_build_root_directory(MSF_Context *msf) -{ - ProfBeginFunction(); - Temp scratch = scratch_begin(0, 0); - - MSF_Error error = MSF_Error_OK; - - // MS impl doesn't handle root directory with page count above 1. - MSF_UInt max_page_count_in_root_directory = msf->page_size / sizeof(MSF_PageNumber); - if (msf->st_page_list.count > max_page_count_in_root_directory) { - error = MSF_Error_STREAM_TABLE_HAS_TOO_MANY_PAGES; - goto exit; - } - - // collect stream table page numbers - MSF_PageNumber *pn_arr = push_array(scratch.arena, MSF_PageNumber, msf->st_page_list.count); - MSF_UInt pn_count = 0; - for (MSF_PageNode *page = msf->st_page_list.first; page != 0; page = page->next) { - pn_arr[pn_count++] = page->pn; - } - - MSF_UInt root_page_count = msf_count_pages(msf->page_size, pn_count * sizeof(pn_arr[0])); - Assert(root_page_count == 1); - - msf_free_pages(msf, &msf->root_page_list); // TODO: page reuse - msf->root_page_list = msf_alloc_pages(msf, root_page_count); - B32 is_root_written = msf_write(msf->page_data_list, msf->page_size, msf->root_page_list, 0, pn_arr, sizeof(pn_arr[0]) * pn_count); - if (!is_root_written) { - error = MSF_BuildError_UNABLE_TO_WRITE_ROOT_DIRECTORY; - goto exit; - } - -exit:; - scratch_end(scratch); - ProfEnd(); - return error; -} - -internal MSF_Error -msf_build_header(MSF_Context *msf, MSF_UInt stream_table_size) -{ - ProfBeginFunction(); - MSF_Error error = MSF_Error_OK; - - MSF_Header70 header; - MemoryCopy(&header.magic[0], &msf_msf70_magic[0], sizeof(msf_msf70_magic)); - header.page_size = msf->page_size; - header.active_fpm = msf->active_fpm; - // The allocator also counts reserved FPM pages beyond the serialized extent. - // The stream table and root are finalized; use the same extent as the writer. - header.page_count = (MSF_UInt)(msf_get_save_size(msf) / msf->page_size); - header.stream_table_size = stream_table_size; - header.unknown = 0; - header.root_pn = msf->root_page_list.first->pn; - - B32 is_header_written = msf_write(msf->page_data_list, msf->page_size, msf->header_page_list, 0, &header, sizeof(header)); - if (!is_header_written) { - error = MSF_BuildError_UNABLE_TO_WRITE_HEADER; - goto exit; - } - - exit:; - ProfEnd(); - return error; -} - -internal MSF_Error msf_build(MSF_Context *msf) { ProfBeginFunction(); - - MSF_Error err; - do { - MSF_UInt stream_table_size; - err = msf_build_stream_table(msf, &stream_table_size); - if (err != MSF_Error_OK) { - break; + + U64 table_size = sizeof(MSF_UInt) * (1 + (U64)msf->sectab.count); + for EachNode(node, MSF_StreamNode, msf->sectab.first) { + if (node->data.size != MSF_DELETED_STREAM_STAMP) { + table_size += sizeof(MSF_PageNumber) * (U64)msf_count_pages(msf->page_size, node->data.size); } - - err = msf_build_root_directory(msf); - if (err != MSF_Error_OK) { - break; + } + + U64 table_pages = CeilIntegerDiv(table_size, msf->page_size); + if (table_pages > msf->page_size / sizeof(MSF_PageNumber)) { + ProfEnd(); + return MSF_Error_StreamTableHasTooManyPages; + } + + if (!msf_stream_resize_ex(msf, &msf->stream_table, safe_cast_u32(table_size))) { + ProfEnd(); + return MSF_Error_FailedToWriteStreamTable; + } + + U32 *sizes = (U32 *)msf->stream_table.data; + *sizes++ = msf->sectab.count; + + MSF_PageNumber *pages = (MSF_PageNumber *)(sizes + msf->sectab.count); + for EachNode(node, MSF_StreamNode, msf->sectab.first) { + MSF_Stream *stream = &node->data; + sizes[stream->sn] = stream->size; + if (stream->size == MSF_DELETED_STREAM_STAMP) { continue; } + MSF_UInt count = msf_count_pages(msf->page_size, stream->size); + Assert(count <= stream->page_list.count); + MSF_PageNode *page = stream->page_list.first; + for EachIndex(i, count) { + *pages++ = page->pn; + page = page->next; } - - err = msf_build_header(msf, stream_table_size); - if (err != MSF_Error_OK) { - break; + } + + // keep the root's allocation-time page number, including on repeated builds + MemoryZero(msf->root.data, msf->root.capacity); + MSF_PageNumber *root = (MSF_PageNumber *)msf->root.data; + for EachNode(page, MSF_PageNode, msf->stream_table.page_list.first) { *root++ = page->pn; } + + MSF_Header70 header = {0}; + MemoryCopy(&header.magic[0], &msf_msf70_magic[0], sizeof(header.magic)); + header.page_size = msf->page_size; + header.active_fpm = msf->active_fpm; + header.page_count = safe_cast_u32(msf_get_save_size(msf) / msf->page_size); + header.stream_table_size = safe_cast_u32(table_size); + header.root_pn = msf->root.page_list.first->pn; + MemoryCopy(msf->header.data, &header, sizeof(header)); + + // allocation reserves future FPM slots too, but on disk all pages beyond + // EOF must be free; MS readers validate those trailing bits + U64 interval = (U64)msf->page_size * MSF_BITS_PER_CHAR; + U64 base = 0; + for EachNode(block, MSF_PageBlock, msf->first_block) { + MemoryCopy(block->fpm_data, block->free_bits, msf->page_size); + U64 used_bits = header.page_count > base ? Min((U64)header.page_count - base, interval) : 0; + U64 byte = used_bits / 8; + if (used_bits % 8) { + block->fpm_data[byte++] |= (U8)(0xff << (used_bits % 8)); } - } while (0); + MemorySet(block->fpm_data + byte, 0xff, msf->page_size - byte); + base += interval; + } ProfEnd(); - return err; + return MSF_Error_Ok; } +//////////////////////////////// + internal String8List msf_get_page_data_nodes(Arena *arena, MSF_Context *msf) { - ProfBeginFunction(); + String8List result = {0}; - String8List list; MemoryZeroStruct(&list); + U64 page_count = msf_get_save_size(msf) / msf->page_size; + U64 interval = (U64)msf->page_size * MSF_BITS_PER_CHAR; + U64 pn = 0; - U64 total_size = msf_get_save_size(msf); - U64 bytes_left = total_size; - U64 node_size = msf_get_data_node_size(msf->page_size); - - for (MSF_PageDataNode *data_node = msf->page_data_list.first; data_node != 0; data_node = data_node->next) { - // compute byte count for the node - U64 to_copy = Min(bytes_left, node_size); - bytes_left -= to_copy; - - String8 data = str8(data_node->data, to_copy); - str8_list_push(arena, &list, data); + for EachNode(block, MSF_PageBlock, msf->first_block) { + for (U64 i = 0; i < interval && pn < page_count; i += 1, pn += 1) { + U8 *data = block->data[i] != 0 ? block->data[i] : msf->zero_page; + if (result.last != 0 && result.last->string.str + result.last->string.size == data) { + result.last->string.size += msf->page_size; + result.total_size += msf->page_size; + } else { + str8_list_push(arena, &result, str8(data, msf->page_size)); + } + } } - ProfEnd(); - return list; -} - -internal U64 -msf_get_save_size(MSF_Context *msf) -{ - MSF_PageNumber max_pn = msf_find_max_pn(msf->page_data_list, msf->page_size); - U64 page_count = max_pn + 1; - U64 size = page_count * msf->page_size; - return size; + return result; } internal B32 msf_save(MSF_Context *msf, void *buffer, U64 buffer_size) { - ProfBeginFunction(); + if (buffer_size != msf_get_save_size(msf)) { return 0; } - U64 node_size = msf_get_data_node_size(msf->page_size); - U64 cursor = 0; - - for (MSF_PageDataNode *node = msf->page_data_list.first; node != 0; node = node->next) { - // compute byte count for the copy - U64 bytes_in_buffer = buffer_size - cursor; - U64 to_copy = Min(bytes_in_buffer, node_size); - - // copy MSF bytes to output buffer - U8 *dst = (U8 *)buffer + cursor; - U8 *src = node->data; - MemoryCopy(dst, src, to_copy); - - // advance cursor - cursor += to_copy; - - // is output buffer full? - if (to_copy == 0) { - break; - } + Temp scratch = scratch_begin(0, 0); + String8List spans = msf_get_page_data_nodes(scratch.arena, msf); + U64 pos = 0; + for EachNode(node, String8Node, spans.first) { + MemoryCopy((U8 *)buffer + pos, node->string.str, node->string.size); + pos += node->string.size; } - B32 is_save_ok = (cursor == buffer_size); - Assert(is_save_ok); - - ProfEnd(); - return is_save_ok; + scratch_end(scratch); + return pos == buffer_size; } internal MSF_Error msf_save_arena(Arena *arena, MSF_Context *msf, String8 *data_out) { - ProfBeginFunction(); - MSF_Error err = msf_build(msf); - if (err == MSF_Error_OK) { - U64 buffer_size = msf_get_save_size(msf); - U8 *buffer = push_array(arena, U8, buffer_size); - B32 is_saved = msf_save(msf, buffer, buffer_size); - if (is_saved) { - *data_out = str8(buffer, buffer_size); - } else { - arena_pop(arena, buffer_size); - } + MSF_Error error = msf_build(msf); + if (error == MSF_Error_Ok) { + U64 size = msf_get_save_size(msf); + U8 *data = push_array_no_zero(arena, U8, size); + if (!msf_save(msf, data, size)) { return MSF_Error_FailedToWriteHeader; } + *data_out = str8(data, size); } - ProfEnd(); - return err; + return error; } internal char * msf_error_to_string(MSF_Error code) { - char *str = ""; switch (code) { - case MSF_Error_OK: break; - - case MSF_Error_STREAM_TABLE_HAS_TOO_MANY_PAGES: str = "stream table exceeds page limit"; break; - - case MSF_OpenError_NOT_ENOUGH_BYTES_TO_READ_HEADER: str = "input does not have enough bytes to read header"; break; - case MSF_OpenError_INVALID_MAGIC: str = "magic value does not match"; break; - case MSF_OpenError_PAGE_SIZE_IS_NOT_POW2: str = "page size is not power of two"; break; - case MSF_OpenError_INVALID_PAGE_SIZE: str = "invalid page size"; break; - case MSF_OpenError_NOT_ENOUGH_PAGES_TO_INIT: str = "not enough pages to initialize MSF"; break; - case MSF_OpenError_INVALID_ROOT_STREAM_PAGE_NUMBER: str = "invalid root stream page number"; break; - case MSF_OpenError_UNABLE_TO_READ_STREAM_TABLE_PAGE_NUMBERS: str = "unable to read stream table's page numbers"; break; - case MSF_OpenError_STREAM_COUNT_OVERFLOW: str = "stream count is overflown"; break; - case MSF_OpenError_UNABLE_TO_READ_STREAM_SIZES: str = "unable to read streams sizes"; break; - case MSF_OpenError_INVALID_STREAM_TABLE: str = "invalid stream table"; break; - case MSF_OpenError_INVALID_ACTIVE_FPM: str = "invalid active FPM"; break; - case MSF_OpenError_PAGE_COUNT_DOESNT_MATCH_DATA_SIZE: str = "page count from MSF header does not match data page count"; break; - - case MSF_BuildError_UNABLE_TO_WRITE_STREAM_TABLE: str = "unable to write stream table"; break; - case MSF_BuildError_UNABLE_TO_WRITE_STREAM_TABLE_PAGE_NUMBER_DIRECTORY: str = "unable to write stream table page number directory"; break; - case MSF_BuildError_UNABLE_TO_WRITE_ROOT_DIRECTORY: str = "unable to write root directory"; break; - case MSF_BuildError_UNABLE_TO_WRITE_HEADER: str = "unable to write header"; break; + case MSF_Error_Ok: return ""; + case MSF_Error_StreamTableHasTooManyPages: return "stream table exceeds page limit"; + case MSF_Error_FailedToWriteStreamTable: return "failed to write stream table"; + case MSF_Error_FailedToWriteHeader: return "failed to write header"; } - - return str; + return "unknown MSF error"; } -//////////////////////////////// - -/* - Multi-Stream-Format is a database type of format for storing debug info - but in principle can store anything you want. MSF divides file - into fixed-sized pages (default page size is 4KiB) and puts them - together into streams. A stream is made up from a non-contigous - number of pages and supports following operations: alloc, free, open, write, read. - Current MSF 7.0 allows creating up to 64K of streams, where each stream can potentially - contain 2GiB of data (assuming default page size). - - Free Page Map assigns a bit to each page to indicate page alloc state. 0 = allocated and 1 = free. - FPM is alloced at fixed intervals of 'page_size * MSF_BITS_PER_CHAR'. At the begining of interval - two pages are reserved for status bits. The 'active_fpm' field in the MSF header tells which FPM page - is in use. On commit time MSF alternates between two pages, this way they support atomic read and write. - - FPM Bug: - Let's say you have a MSF file with page size 0x1000 bytes, you can represent 0x1000 * 8 = 0x8000 pages - or 0x8000 * 0x1000 = 128MiB. And when file exceeds this size a new FPM group should be allocated - at page numbers 0x8001 and 0x8002. However, in MS impl there is a bug where they don't multiply - interval by 8 and each FPM group is allocated at intervals of page size or 0x1000, so each FPM group is placed - at page numbers 0x1001, 0x1002, 0x2001, 0x2002, and so on. This way MSF files end up allocating 8 times more pages. - - Also, MS impl marks unused pages as allocated thus leaving them empty but LLVM repurposes them - for regular allocations and things work out fine because of the fact that MS computes correct - number of FPM pages when they save and load and the trailing pages aren't being touched: - https://github.com/microsoft/microsoft-pdb/blob/master/PDB/msf/msf.cpp#L2512 - - Root directory is a single paged stored as a page number in 'MSF_Header70.root_pn'. - The directory contains an array of page numbers needed to read the stream table. This is a late - addition introduced in version 7.0 that lets us have bigger stream tables. However, there is a limit - if stream table exceeds root directory, MSF becomes invalid. MS impl isn't - clear what should happen in this case, so we tried to contiguously allocate root pages - but VS and LLVM error out. In practice you can double page size to work around the limit. - -TODO: explain stream table - - */ - -#if 0 - -internal void -msf_bytedump_stream(char *file_name, MSF_Context *msf, MSF_StreamNumber sn, U64 start, U64 byte_count) -{ - Temp scratch = scratch_begin(0, 0); - U64 pos = msf_stream_get_pos(msf, sn); - msf_stream_seek(msf, sn, start); - U64 buffer_size = byte_count; - U8 *buffer = push_array(scratch.arena, U8, buffer_size); - MSF_UInt read_size = msf_stream_read(msf, sn, buffer, buffer_size); - os_write_file(str8_cstring(file_name), str8(buffer, read_size)); - msf_stream_seek(msf, sn, pos); - scratch_end(scratch); -} - -internal void -msf_hexdump_stream(FILE *file, MSF_Context *msf, MSF_StreamNumber sn, U64 start, U64 byte_count, U64 stride) -{ - Temp scratch = scratch_begin(0, 0); - U8 *row_buffer = push_array(scratch.arena, U8, stride); - U64 stream_size = msf_stream_get_size(msf, sn); - U64 cursor = start; - U64 end = Min(start + byte_count, stream_size); - while (cursor < stream_size) { - MSF_UInt read_size = msf_stream_read(msf, sn, row_buffer, stride); - - // print offset - fprintf(file, "%04llX", cursor); - - // print bytes - fprintf(file, " "); - for (U64 i = 0; i < read_size; i += 1) { - if (i > 0) { - fprintf(file, " "); - } - fprintf(file, "%02X", row_buffer[i]); - } - - // print ascii - fprintf(file, " "); - for (U64 i = 0; i < read_size; i += 1) { - U8 print_char = row_buffer[i]; - if (0x20 > print_char || print_char > 0x7E) { - print_char = '.'; - } - fprintf(file, "%c", print_char); - } - - // row is done - fprintf(file, "\n"); - - cursor += stride; - } - - scratch_end(scratch); -} - -internal void -msf_hexdump_stream_to_file(char *name, MSF_Context *msf, MSF_StreamNumber sn, U64 start, U64 byte_count, U64 stride) -{ - FILE *f = fopen(name, "w"); - msf_hexdump_stream(f, msf, sn, start, byte_count, stride); - fclose(f); -} - -#endif - -#if 0 -internal void -test_msf_open_save(void) -{ - Temp scratch = scratch_begin(0, 0); - - U32 item0 = 123; - U32 item1 = 321; - - MSF_StreamNumber stream; - String8 data; - { - MSF_Context *msf = msf_alloc(MSF_DEFAULT_PAGE_SIZE, MSF_DEFAULT_FPM); - stream = msf_stream_alloc(msf); - msf_stream_write_u32(msf, stream, item0); - msf_stream_write_u32(msf, stream, item1); - data = msf_save_arena(scratch.arena, msf); - msf_release(&msf); - } - - String8 data1; - { - MSF_Context *msf = 0; - MSF_Error err = msf_open(data, &msf); - Assert(err == MSF_Error_OK); - U32 read0 = msf_stream_read_u32(msf, stream); - Assert(read0 == item0); - U32 read1 = msf_stream_read_u32(msf, stream); - Assert(read1 == item1); - data1 = msf_save_arena(scratch.arena, msf); - msf_release(&msf); - } - - { - MSF_Context *msf = 0; - MSF_Error err = msf_open(data, &msf); - Assert(err == MSF_Error_OK); - U32 read0 = msf_stream_read_u32(msf, stream); - Assert(read0 == item0); - U32 read1 = msf_stream_read_u32(msf, stream); - Assert(read1 == item1); - msf_release(&msf); - } - - scratch_end(scratch); -} - -internal void -test_size_limit(void) -{ - Temp scratch = scratch_begin(0, 0); - - MSF_Context *msf = msf_alloc(8192, MSF_DEFAULT_FPM); - Assert(msf); - - U64 c = (1 * 1024 * 1024 * 1024) / msf->page_size; - U64 stream_count = 8; - - U64 data_size = msf->page_size; - U8 *data = push_array(scratch.arena, U8, data_size); - - for (U64 stream_idx = 0; stream_idx < stream_count; stream_idx += 1) { - MSF_StreamNumber stream = msf_stream_alloc(msf); - Assert(stream != MSF_INVALID_STREAM_NUMBER); - - MemorySet(&data[0], 1 + stream_idx, data_size); - - msf_stream_resize(msf, stream, c * msf->page_size); - - for (U64 i = 0; i < c; i += 1) { - B32 is_written = msf_stream_write(msf, stream, data, data_size); - Assert(is_written); - } - } - - //msf_grow(msf, MSF_PN_MAX); - - msf_stream_free(msf, 7); - msf_stream_free(msf, 6); - msf_stream_free(msf, 5); - - stream_count -= 3; - - String8 msf_data = msf_save_arena(scratch.arena, msf); - Assert(msf_data.size > 0); - msf_release(&msf); - - //os_write_file(str8_lit("test.msf"), msf_data); - - MSF_Error err = msf_open(msf_data, &msf); - Assert(err == MSF_Error_OK); - -#if 1 - U8 *buffer = push_array(scratch.arena, U8, data_size); - for (U64 stream_idx = 0; stream_idx < stream_count; stream_idx += 1) { - MSF_StreamNumber sn = (MSF_StreamNumber)stream_idx; - - MemorySet(&data[0], 1 + stream_idx, data_size); - - for (U64 i = 0; i < c; i += 1) { - MSF_UInt read_size = msf_stream_read(msf, sn, buffer, data_size); - Assert(read_size == data_size); - - int cmp = MemoryCompare(buffer, data, data_size); - Assert(cmp == 0); - } - } -#endif - - msf_release(&msf); - scratch_end(scratch); -} - -internal void -test_msf(void) -{ - test_size_limit(); - test_msf_open_save(); -} -#endif - diff --git a/src/linker/pdb_ext/msf_builder.h b/src/linker/pdb_ext/msf_builder.h index c07b582a..0cbaf29d 100644 --- a/src/linker/pdb_ext/msf_builder.h +++ b/src/linker/pdb_ext/msf_builder.h @@ -5,25 +5,18 @@ #define MSF_PAGE_STATE_FREE 1 #define MSF_PAGE_STATE_ALLOC 0 - #define MSF_FPM0 1 #define MSF_FPM1 2 - #define MSF_DEFAULT_PAGE_SIZE 4096 #define MSF_DEFAULT_FPM MSF_FPM0 -typedef struct MSF_PageNumberArray +typedef struct MSF_PageNode MSF_PageNode; +struct MSF_PageNode { - U64 count; - MSF_PageNumber *v; -} MSF_PageNumberArray; - -typedef struct MSF_PageNode -{ - struct MSF_PageNode *next; - struct MSF_PageNode *prev; - MSF_PageNumber pn; -} MSF_PageNode; + MSF_PageNode *next; + MSF_PageNode *prev; + MSF_PageNumber pn; +}; typedef struct MSF_PageList { @@ -34,19 +27,21 @@ typedef struct MSF_PageList typedef struct MSF_Stream { - MSF_StreamNumber sn; - MSF_UInt size; - MSF_UInt pos; - MSF_PageNode *pos_page; - MSF_PageList page_list; + MSF_StreamNumber sn; + MSF_UInt size; + MSF_UInt pos; + U64 capacity; + U8 *data; + MSF_PageList page_list; } MSF_Stream; -typedef struct MSF_StreamNode +typedef struct MSF_StreamNode MSF_StreamNode; +struct MSF_StreamNode { - struct MSF_StreamNode *next; - struct MSF_StreamNode *prev; - MSF_Stream data; -} MSF_StreamNode; + MSF_StreamNode *next; + MSF_StreamNode *prev; + MSF_Stream data; +}; typedef struct MSF_StreamList { @@ -55,125 +50,84 @@ typedef struct MSF_StreamList MSF_StreamNode *last; } MSF_StreamList; -typedef struct MSF_PageDataNode +typedef struct MSF_PageBlock MSF_PageBlock; +struct MSF_PageBlock { - struct MSF_PageDataNode *next; - struct MSF_PageDataNode *prev; - U8 *data; -} MSF_PageDataNode; - -typedef struct MSF_PageDataList -{ - MSF_PageDataNode *first; - MSF_PageDataNode *last; - MSF_UInt count; -} MSF_PageDataList; + MSF_PageBlock *next; + U32 *free_bits; + U8 *fpm_data; + U8 **data; +}; typedef struct MSF_Context { - Arena *arena; - MSF_UInt page_size; - MSF_UInt active_fpm; - MSF_UInt fpm_rover; - MSF_PageNumber page_count; - MSF_PageDataList page_data_list; - MSF_PageDataList page_data_pool; - MSF_PageList header_page_list; - MSF_PageList root_page_list; - MSF_PageList st_page_list; - MSF_PageList page_pool; - MSF_StreamList sectab; + Arena *arena; + MSF_UInt page_size; + MSF_UInt active_fpm; + MSF_PageNumber fpm_rover; + MSF_UInt block_count; + MSF_PageBlock *first_block; + MSF_PageBlock *last_block; + U8 *zero_page; + U8 *empty_fpm; + MSF_PageList page_pool; + MSF_Stream header; + MSF_Stream root; + MSF_Stream stream_table; + MSF_StreamList sectab; } MSF_Context; typedef enum MSF_Error { - MSF_Error_OK, - - // if you get this error this means stream table was divided into too many - // pages, and to fix this you need to bump up the page size - MSF_Error_STREAM_TABLE_HAS_TOO_MANY_PAGES, - - MSF_OpenError_NOT_ENOUGH_BYTES_TO_READ_HEADER, - MSF_OpenError_INVALID_MAGIC, - MSF_OpenError_PAGE_SIZE_IS_NOT_POW2, - MSF_OpenError_INVALID_PAGE_SIZE, - MSF_OpenError_NOT_ENOUGH_PAGES_TO_INIT, - MSF_OpenError_INVALID_ROOT_STREAM_PAGE_NUMBER, - MSF_OpenError_UNABLE_TO_READ_STREAM_TABLE_PAGE_NUMBERS, - MSF_OpenError_STREAM_COUNT_OVERFLOW, - MSF_OpenError_UNABLE_TO_READ_STREAM_SIZES, - MSF_OpenError_INVALID_STREAM_TABLE, - MSF_OpenError_INVALID_ACTIVE_FPM, - MSF_OpenError_PAGE_COUNT_DOESNT_MATCH_DATA_SIZE, - - MSF_BuildError_UNABLE_TO_WRITE_STREAM_TABLE, - MSF_BuildError_UNABLE_TO_WRITE_STREAM_TABLE_PAGE_NUMBER_DIRECTORY, - MSF_BuildError_UNABLE_TO_WRITE_ROOT_DIRECTORY, - MSF_BuildError_UNABLE_TO_WRITE_HEADER, + MSF_Error_Ok, + MSF_Error_StreamTableHasTooManyPages, + MSF_Error_FailedToWriteStreamTable, + MSF_Error_FailedToWriteHeader, } MSF_Error; -//////////////////////////////// - -typedef struct +typedef struct MSF_WriteTask { - MSF_UInt page_size; - MSF_PageDataList page_data_list; - MSF_PageList page_list; - MSF_UInt stream_pos; - String8 data; - Rng1U64 *range_arr; + U8 *dst; + U8 *src; + Rng1U64 *ranges; } MSF_WriteTask; -//////////////////////////////// +internal MSF_Context *msf_alloc(MSF_UInt page_size, MSF_UInt active_fpm); +internal void msf_release(MSF_Context *msf); +internal MSF_Error msf_build(MSF_Context *msf); +internal U64 msf_get_save_size(MSF_Context *msf); -internal MSF_Context * msf_alloc(MSF_UInt page_size, MSF_UInt active_fpm); -internal void msf_release(MSF_Context *msf_ptr); -internal MSF_Error msf_build(MSF_Context *msf); -internal U64 msf_get_save_size(MSF_Context *msf); -internal String8List msf_get_page_data_nodes(Arena *arena, MSF_Context *msf); -internal B32 msf_save(MSF_Context *msf, void *buffer, U64 buffer_size); -internal MSF_Error msf_save_arena(Arena *arena, MSF_Context *msf, String8 *data_out); -internal MSF_StreamNode * msf_find_stream_node(MSF_Context *msf, MSF_StreamNumber sn); -internal MSF_Stream * msf_find_stream(MSF_Context *msf, MSF_StreamNumber sn); -internal B32 msf_grow(MSF_Context *msf, MSF_PageNumber page_count); -internal MSF_PageNumber * msf_alloc_pn_arr(Arena *arena, MSF_Context *msf, MSF_UInt alloc_count); -internal void msf_free_pn_arr(MSF_Context *msf, MSF_PageNumber *pn_arr, MSF_UInt pn_count); -internal MSF_PageList msf_alloc_pages(MSF_Context *msf, MSF_UInt alloc_count); -internal void msf_free_pages(MSF_Context *msf, MSF_PageList *page_list); +// File-order spans borrow stream/metadata storage and remain valid until it +// is modified or released. No second copy of the stream payloads is made. +internal String8List msf_get_page_data_nodes(Arena *arena, MSF_Context *msf); +internal String8 msf_data_from_pn(MSF_Context *msf, MSF_PageNumber pn); +internal B32 msf_save(MSF_Context *msf, void *buffer, U64 buffer_size); +internal MSF_Error msf_save_arena(Arena *arena, MSF_Context *msf, String8 *data_out); +internal MSF_StreamNode *msf_find_stream_node(MSF_Context *msf, MSF_StreamNumber sn); +internal MSF_Stream *msf_find_stream(MSF_Context *msf, MSF_StreamNumber sn); +internal MSF_PageList msf_alloc_pages(MSF_Context *msf, MSF_UInt count); +internal void msf_free_pages(MSF_Context *msf, MSF_PageList *pages); internal MSF_StreamNumber msf_stream_alloc_ex(MSF_Context *msf, MSF_UInt size); internal MSF_StreamNumber msf_stream_alloc(MSF_Context *msf); internal B32 msf_stream_free(MSF_Context *msf, MSF_StreamNumber sn); -internal B32 msf_stream_resize(MSF_Context *msf, MSF_StreamNumber sn, MSF_UInt new_size); +internal B32 msf_stream_resize(MSF_Context *msf, MSF_StreamNumber sn, MSF_UInt size); internal B32 msf_stream_resize_ex(MSF_Context *msf, MSF_Stream *stream, MSF_UInt size); -internal void msf_stream_set_size(MSF_Context *msf, MSF_StreamNumber sn, MSF_UInt size); internal MSF_UInt msf_stream_get_size(MSF_Context *msf, MSF_StreamNumber sn); -internal MSF_UInt msf_stream_get_cap(MSF_Context *msf, MSF_StreamNumber); internal MSF_UInt msf_stream_get_pos(MSF_Context *msf, MSF_StreamNumber sn); -internal void msf_stream_align(MSF_Context *msf, MSF_StreamNumber sn, MSF_UInt align); internal B32 msf_stream_reserve(MSF_Context *msf, MSF_StreamNumber sn, MSF_UInt size); -internal B32 msf_stream_seek(MSF_Context *msf, MSF_StreamNumber sn, MSF_UInt new_pos); +internal B32 msf_stream_seek(MSF_Context *msf, MSF_StreamNumber sn, MSF_UInt pos); internal B32 msf_stream_seek_start(MSF_Context *msf, MSF_StreamNumber sn); -internal B32 msf_stream_seek_end(MSF_Context *msf, MSF_StreamNumber sn); +internal void msf_stream_align(MSF_Context *msf, MSF_StreamNumber sn, MSF_UInt align); -internal MSF_UInt msf_stream_read(MSF_Context *msf, MSF_StreamNumber sn, void *dst, MSF_UInt dst_len); -internal String8 msf_stream_read_block(Arena *arena, MSF_Context *msf, MSF_StreamNumber sn, U64 block_size); -internal String8 msf_stream_read_string(Arena *arena, MSF_Context *msf, MSF_StreamNumber sn); -internal S8 msf_stream_read_s8(MSF_Context *msf, MSF_StreamNumber sn); -internal S16 msf_stream_read_s16(MSF_Context *msf, MSF_StreamNumber sn); -internal S32 msf_stream_read_s32(MSF_Context *msf, MSF_StreamNumber sn); -internal S64 msf_stream_read_s64(MSF_Context *msf, MSF_StreamNumber sn); -internal U8 msf_stream_read_u8(MSF_Context *msf, MSF_StreamNumber sn); -internal U16 msf_stream_read_u16(MSF_Context *msf, MSF_StreamNumber sn); -internal U32 msf_stream_read_u32(MSF_Context *msf, MSF_StreamNumber sn); -internal U64 msf_stream_read_u64(MSF_Context *msf, MSF_StreamNumber sn); -#define msf_stream_read_array(msf, sn, ptr, count) msf_stream_read(msf, sn, ptr, sizeof(*ptr) * (count)) -#define msf_stream_read_struct(msf, sn, ptr) msf_stream_read_array(msf, sn, ptr, 1) +internal String8 msf_stream_data(MSF_Context *msf, MSF_StreamNumber sn); +// Writable, contiguous construction storage. Reserve/resize before taking a +// view: growth beyond capacity invalidates views, but preserves page numbers. +// Finalized streams must not be changed while asynchronous output borrows them. -internal B32 msf_stream_write(MSF_Context *msf, MSF_StreamNumber sn, void *buffer, MSF_UInt buffer_size); +internal B32 msf_stream_write(MSF_Context *msf, MSF_StreamNumber sn, void *buffer, MSF_UInt size); internal B32 msf_stream_write_string(MSF_Context *msf, MSF_StreamNumber sn, String8 string); internal B32 msf_stream_write_list(MSF_Context *msf, MSF_StreamNumber sn, String8List list); -internal B32 msf_stream_write_uint(MSF_Context *msf, MSF_StreamNumber sn, MSF_UInt value); internal B32 msf_stream_write_cstr(MSF_Context *msf, MSF_StreamNumber sn, String8 string); internal B32 msf_stream_write_u8(MSF_Context *msf, MSF_StreamNumber sn, U8 value); internal B32 msf_stream_write_u16(MSF_Context *msf, MSF_StreamNumber sn, U16 value); @@ -183,21 +137,10 @@ internal B32 msf_stream_write_s8(MSF_Context *msf, MSF_StreamNumber sn, S8 value internal B32 msf_stream_write_s16(MSF_Context *msf, MSF_StreamNumber sn, S16 value); internal B32 msf_stream_write_s32(MSF_Context *msf, MSF_StreamNumber sn, S32 value); internal B32 msf_stream_write_s64(MSF_Context *msf, MSF_StreamNumber sn, S64 value); -internal B32 msf_stream_write_parallel(TP_Context *tp, MSF_Context *msf, MSF_StreamNumber sn, void *buffer, MSF_UInt buffer_size); -#define msf_stream_write_array(m, s, v, c) msf_stream_write(m, s, (void*)(v), sizeof(*(v)) * (c)) -#define msf_stream_write_struct(m, s, v ) msf_stream_write_array(m, s, v, 1) +internal B32 msf_stream_write_parallel(TP_Context *tp, MSF_Context *msf, MSF_StreamNumber sn, void *buffer, MSF_UInt size); +#define msf_stream_write_array(m, s, v, c) msf_stream_write(m, s, (void *)(v), sizeof(*(v)) * (c)) +#define msf_stream_write_struct(m, s, v) msf_stream_write_array(m, s, v, 1) -internal String8List msf_data_from_sn(Arena *arena, MSF_Context *msf, MSF_StreamNumber sn); - -internal MSF_UInt msf_count_pages(MSF_UInt page_size, U64 data_size); -internal MSF_PageNumber msf_get_page_count_cap(MSF_PageDataList page_data_list, MSF_UInt page_size); -internal MSF_UInt msf_get_fpm_interval_correct(MSF_UInt page_size); -internal MSF_UInt msf_get_fpm_interval_wrong(MSF_UInt page_size); -internal MSF_UInt msf_get_fpm_idx_from_pn(MSF_UInt page_size, MSF_PageNumber pn); -internal MSF_UInt msf_get_fpm_page_bit_state(MSF_PageDataList page_data_list, MSF_UInt page_size, MSF_PageNumber active_fpm, MSF_PageNumber pn); -internal void msf_set_fpm_bit(MSF_PageDataList page_data_list, MSF_UInt page_size, MSF_PageNumber active_fpm, MSF_PageNumber pn, B32 state); -internal B32 msf_write(MSF_PageDataList page_data_list, MSF_UInt page_size, MSF_PageList page_list, MSF_UInt offset, void *buffer, MSF_UInt buffer_size); -internal MSF_UInt msf_read(MSF_PageDataList page_data_list, MSF_UInt page_size, MSF_PageList page_list, MSF_UInt offset, void *buffer, MSF_UInt buffer_size); - -internal char * msf_error_to_string(MSF_Error code); +internal MSF_UInt msf_count_pages(MSF_UInt page_size, U64 data_size); +internal char *msf_error_to_string(MSF_Error code); diff --git a/src/linker/pdb_ext/pdb_builder.c b/src/linker/pdb_ext/pdb_builder.c index 1c6a523e..f72c203c 100644 --- a/src/linker/pdb_ext/pdb_builder.c +++ b/src/linker/pdb_ext/pdb_builder.c @@ -594,6 +594,7 @@ pdb_strtab_build(PDB_StringTable *strtab, MSF_Context *msf, MSF_StreamNumber sn) MSF_UInt reserve_size = sizeof(header) + sizeof(strtab->size) + strtab->size + + sizeof(strtab->bucket_max) + sizeof(bucket_offset_arr[0]) * strtab->bucket_max + sizeof(strtab->bucket_count); msf_stream_reserve(msf, sn, reserve_size); @@ -890,11 +891,32 @@ pdb_type_hash_stream_build(TP_Context *tp, ProfBeginFunction(); Temp scratch = scratch_begin(0,0); + String8 hash_adj_data = {0}; + if (ts->hash_adj.count) { + hash_adj_data = pdb_data_from_hash_adj_hash_table(scratch.arena, &ts->hash_adj, strtab); + } + + PDB_OffsetSize hash_vals = { .off = 0, .size = sizeof(U32) * ts->leaf_list.node_count }; + PDB_OffsetSize hint_offs = { .off = hash_vals.size, .size = sizeof(hint_arr[0]) * hint_count }; + PDB_OffsetSize hash_adj = {0}; + + if (ts->hash_adj.count) { + hash_adj.off = hint_offs.off + hint_offs.size; + hash_adj.size = hash_adj_data.size; + } + + U64 stream_size = hash_vals.size + hint_offs.size + hash_adj_data.size; + if (ts->hash_sn == MSF_INVALID_STREAM_NUMBER) { ts->hash_sn = msf_stream_alloc(msf); } + + AssertAlways(msf_stream_resize(msf, ts->hash_sn, safe_cast_u32(stream_size))); + String8 stream_data = msf_stream_data(msf, ts->hash_sn); + // write (type index -> bucket index) map // // zero-out entire map so non-UDTs type indices, that are NOT in the hash table, // map to zero offset - U32 *type_to_bucket_map = push_array(scratch.arena, U32, ts->leaf_list.node_count); + U32 *type_to_bucket_map = (U32 *)stream_data.str; + if (hash_vals.size != 0) { MemoryZero(type_to_bucket_map, hash_vals.size); } { ProfBegin("Bucket Map"); PDB_WriteTypeToBucketMap type_to_bucket_task; @@ -904,33 +926,11 @@ pdb_type_hash_stream_build(TP_Context *tp, ProfEnd(); } - ProfBegin("MSF Write"); - // write data to stream - if (ts->hash_sn == MSF_INVALID_STREAM_NUMBER) { - ts->hash_sn = msf_stream_alloc(msf); - } - msf_stream_seek_start(msf, ts->hash_sn); - - PDB_OffsetSize hash_vals; - hash_vals.off = msf_stream_get_pos(msf, ts->hash_sn); - hash_vals.size = sizeof(type_to_bucket_map[0]) * ts->leaf_list.node_count; - msf_stream_write(msf, ts->hash_sn, &type_to_bucket_map[0], hash_vals.size); - - PDB_OffsetSize hint_offs; - hint_offs.off = msf_stream_get_pos(msf, ts->hash_sn); - hint_offs.size = sizeof(hint_arr[0]) * hint_count; + msf_stream_seek(msf, ts->hash_sn, hint_offs.off); msf_stream_write(msf, ts->hash_sn, &hint_arr[0], hint_offs.size); - - PDB_OffsetSize hash_adj = {0}; - if (ts->hash_adj.count) { - // write bucket adjust info - String8 hash_adj_data = pdb_data_from_hash_adj_hash_table(scratch.arena, &ts->hash_adj, strtab); - hash_adj.off = msf_stream_get_pos(msf, ts->hash_sn); - hash_adj.size = hash_adj_data.size; - msf_stream_write_string(msf, ts->hash_sn, hash_adj_data); - } + msf_stream_write_string(msf, ts->hash_sn, hash_adj_data); ProfEnd(); @@ -990,7 +990,7 @@ pdb_type_server_build(TP_Context *tp, PDB_TypeServer *ts, PDB_StringTable *strta U64 lf_buf_size = 0; U64 lf_node_idx = 0; U64 lf_arr_idx = 0; - for (String8Node *lf = ts->leaf_list.first; lf != 0; lf = lf->next) { + for EachNode(lf, String8Node, ts->leaf_list.first) { if (lf_node_idx == lf_range_arr[lf_arr_idx].min) { // :thread_pool_dummy_range lf_cursor_arr[lf_arr_idx] = lf_buf_size; lf_arr[lf_arr_idx] = lf; @@ -1012,7 +1012,8 @@ pdb_type_server_build(TP_Context *tp, PDB_TypeServer *ts, PDB_StringTable *strta write_types_task.lf_arr = lf_arr; write_types_task.lf_range_arr = lf_range_arr; write_types_task.lf_cursor_arr = lf_cursor_arr; - write_types_task.lf_buf = push_array_no_zero(scratch.arena, U8, lf_buf_size); + AssertAlways(msf_stream_resize(msf, sn, safe_cast_u32(sizeof(PDB_TpiHeader) + lf_buf_size))); + write_types_task.lf_buf = msf_stream_data(msf, sn).str + sizeof(PDB_TpiHeader); write_types_task.lf_buf_size = lf_buf_size; tp_for_parallel(tp, 0, tp->worker_count, pdb_write_types_task, &write_types_task); @@ -1040,7 +1041,7 @@ pdb_type_server_build(TP_Context *tp, PDB_TypeServer *ts, PDB_StringTable *strta ProfBegin("MSF Commit"); msf_stream_seek_start(msf, sn); msf_stream_write_struct(msf, sn, &header); - msf_stream_write_parallel(tp, msf, sn, write_types_task.lf_buf, lf_buf_size); + msf_stream_seek(msf, sn, sizeof(header) + lf_buf_size); ProfEnd(); scratch_end(scratch); @@ -1278,135 +1279,6 @@ pdb_type_server_push_parallel(TP_Context *tp, PDB_TypeServer *type_server, U64 l ProfEnd(); } -#if 0 -internal CV_LeafNode * -pdb_type_server_leaf_from_string(PDB_TypeServer *ts, String8 string) -{ - ProfBeginFunction(); - U32 hash = pdb_hash_v1(string); - U32 bucket_idx = hash % ts->bucket_count; - PDB_TypeBucket *head_bucket = ts->bucket_table[bucket_idx]; - CV_LeafNode *result = 0; - for (PDB_TypeBucket *i = head_bucket; i != 0; i = i->next) { - CV_LeafNode *leaf = i->leaf_node; - String8 leaf_name = cv_get_leaf_name(leaf->data.kind, leaf->data.data); - if (str8_match(leaf_name, string, 0)) { - result = leaf; - break; - } - } - ProfEnd(); - return result; -} -#endif - -//////////////////////////////// - -#if 0 -internal PDB_TypeIndexMap * -pdb_load_types_from_leaf_list(PDB_TypeServer **type_server_arr, CV_LeafList leaf_list) -{ - ProfBeginFunction(); - Temp scratch = scratch_begin(0, 0); - - // 1. redistribute leaves in parallel - CV_LeafList leaf_list_arr[CV_TypeIndexSource_COUNT] = {0}; - for (CV_LeafNode *curr = leaf_list.first, *next = 0; curr != 0; curr = next) { - next = curr->next; - curr->next = 0; - CV_TypeIndexSource ti_source = cv_type_index_source_from_leaf_kind(curr->data.kind); - CV_LeafList *list = &leaf_list_arr[ti_source]; - SLLQueuePush(list->first, list->last, curr); - list->count += 1; - } - - // 2. reserve type leafs on main thread - PDB_TypeLeaf *leaf_arr_arr[CV_TypeIndexSource_COUNT]; - for (U64 source_idx = 0; source_idx < ArrayCount(leaf_list_arr); source_idx += 1) { - PDB_TypeServer *type_server = type_server_arr[source_idx]; - CV_LeafList input_leaf_list = leaf_list_arr[source_idx]; - PDB_TypeLeaf *leaf_arr = pdb_type_server_reserve(type_server, input_leaf_list.count); - leaf_arr_arr[source_idx] = leaf_arr; - } - - // 3. populate type index map in parallel - PDB_TypeIndexMap *ti_map = pdb_type_index_map_alloc(); - for (U64 source_idx = 0; source_idx < ArrayCount(leaf_list_arr); source_idx += 1) { - CV_LeafList input_leaf_list = leaf_list_arr[source_idx]; - PDB_TypeLeaf *leaf_arr = leaf_arr_arr[source_idx]; - for (U64 leaf_idx = 0; leaf_idx < input_leaf_list.count; leaf_idx += 1) { - CV_TypeIndex external_ti = ti_map->min_itype[source_idx] + leaf_idx; - CV_TypeIndex internal_ti = leaf_arr[leaf_idx].type_index; - pdb_type_index_map_add(ti_map, (CV_TypeIndexSource)source_idx, external_ti, internal_ti); - } - } - - // 4. patch type indices in parallel - for (U64 source_idx = 0; source_idx < ArrayCount(leaf_list_arr); source_idx += 1) { - CV_LeafList list = leaf_list_arr[source_idx]; - for (CV_LeafNode *node = list.first; node != 0; node = node->next) { - Temp temp = temp_begin(scratch.arena); - - // get offsets for type indices in data blob - CV_Leaf *leaf = &node->data; - CV_TypeIndexInfoList ti_info_list = cv_get_leaf_type_index_offsets(temp.arena, leaf->kind, leaf->data); - - for (CV_TypeIndexInfo *ti_info = ti_info_list.first; ti_info != 0; ti_info = ti_info->next) { - Assert(ti_info->offset + sizeof(CV_TypeIndex) <= leaf->data.size); - CV_TypeIndex *ti_ptr = (CV_TypeIndex *)(leaf->data.str + ti_info->offset); - CV_TypeIndex external_ti = *ti_ptr; - - B32 is_complex_type = external_ti >= ti_map->min_itype[ti_info->source]; - if (is_complex_type) { - // search external type index - CV_TypeIndex internal_tpi_idx = pdb_type_index_map_search(ti_map, CV_TypeIndexSource_TPI, external_ti); - CV_TypeIndex internal_ipi_idx = pdb_type_index_map_search(ti_map, CV_TypeIndexSource_IPI, external_ti); - - // error checks - if (internal_tpi_idx == 0 && internal_ipi_idx == 0) { - lnk_invalid_path("unable to find match for external type index 0x%X", external_ti); - continue; - } - if (internal_tpi_idx != 0 && internal_ipi_idx != 0) { - lnk_invalid_path("both TPI and IPI matched for external type index 0x%X", external_ti); - continue; - } - - // rewrite index - CV_TypeIndex internal_ti = internal_tpi_idx ? internal_tpi_idx : internal_ipi_idx; - *ti_ptr = internal_ti; - } - } - - temp_end(temp); - } - } - - // 5. push types to hash table on main thread - for (U64 source_idx = 0; source_idx < ArrayCount(leaf_list_arr); source_idx += 1) { - PDB_TypeServer *type_server = type_server_arr[source_idx]; - CV_LeafList list = leaf_list_arr[source_idx]; - PDB_TypeLeaf *leaf_arr = leaf_arr_arr[source_idx]; - U64 leaf_idx = 0; - for (CV_LeafNode *node = list.first; node != 0; node = node->next, leaf_idx += 1) { - CV_Leaf *external_leaf = &node->data; - - // move patched type data - PDB_TypeLeaf *internal_leaf = leaf_arr + leaf_idx; - internal_leaf->kind = external_leaf->kind; - internal_leaf->data = push_str8_copy(type_server->arena, external_leaf->data); - - // push leaf to type server - pdb_type_server_push_(type_server, internal_leaf); - } - } - - scratch_end(scratch); - ProfEnd(); - return ti_map; -} -#endif - //////////////////////////////// internal PDB_InfoContext * @@ -1491,6 +1363,7 @@ pdb_info_build_src_header_block(PDB_InfoContext *info, MSF_Context *msf) MemoryZeroStruct(&src_header.pad); // write to stream + msf_stream_reserve(msf, src_header_block_sn, safe_cast_u32(src_header_stream_size)); B32 is_header_written = msf_stream_write_struct(msf, src_header_block_sn, &src_header); B32 is_hash_table_written = msf_stream_write_string(msf, src_header_block_sn, hash_table_data); AssertAlways(is_header_written); @@ -1710,8 +1583,7 @@ internal void gsi_write_build_result(TP_Context *tp, PDB_GsiBuildResult build, MSF_Context *msf, - MSF_StreamNumber gsi_sn, - MSF_StreamNumber symbols_sn) + MSF_StreamNumber gsi_sn) { ProfBeginFunction(); @@ -1724,10 +1596,6 @@ gsi_write_build_result(TP_Context *tp, msf_stream_reserve(msf, gsi_sn, gsi_size); ProfEnd(); - ProfBeginV("Reserve %M for symbols", build.symbol_data.size); - msf_stream_reserve(msf, symbols_sn, build.symbol_data.size); - ProfEnd(); - ProfBegin("Write GSI header"); msf_stream_write_struct(msf, gsi_sn, &build.header); ProfEnd(); @@ -1744,10 +1612,6 @@ gsi_write_build_result(TP_Context *tp, msf_stream_write(msf, gsi_sn, &build.compressed_bucket_arr[0], compressed_bucket_arr_size); ProfEnd(); - ProfBegin("Write symbols [%M]", build.symbol_data.size); - msf_stream_write_string_parallel(tp, msf, symbols_sn, build.symbol_data); - ProfEnd(); - ProfEnd(); } @@ -2242,7 +2106,7 @@ THREAD_POOL_TASK_FUNC(gsi_serialize_symbols_task) } internal PDB_GsiBuildResult -gsi_build_ex(TP_Context *tp, Arena *arena, PDB_GsiContext *gsi, U64 symbol_data_base, B32 is_pub32, U64 msf_page_size) +gsi_build_ex(TP_Context *tp, Arena *arena, PDB_GsiContext *gsi, MSF_Context *msf, MSF_StreamNumber symbols_sn, B32 is_pub32) { ProfBeginFunction(); Temp scratch = scratch_begin(&arena,1); @@ -2250,6 +2114,7 @@ gsi_build_ex(TP_Context *tp, Arena *arena, PDB_GsiContext *gsi, U64 symbol_data_ ProfBegin("Serialize & Sort Symbols"); PDB_GsiSerializeSymbolsTask serial_task = {0}; + U64 symbol_data_base = msf_stream_get_pos(msf, symbols_sn); serial_task.symbol_data_base = symbol_data_base; serial_task.symbol_align = gsi->symbol_align; serial_task.bucket_arr = gsi->bucket_arr; @@ -2260,7 +2125,11 @@ gsi_build_ex(TP_Context *tp, Arena *arena, PDB_GsiContext *gsi, U64 symbol_data_ // prepare serial buffer U64 buffer_size = sum_array_u64(gsi->bucket_count, serial_task.bucket_size_arr); - serial_task.buffer = push_array_no_zero(arena, U8, buffer_size); + U64 symbol_data_end = symbol_data_base + buffer_size; + if (symbol_data_end > msf_stream_get_size(msf, symbols_sn)) { + AssertAlways(msf_stream_resize(msf, symbols_sn, safe_cast_u32(symbol_data_end))); + } + serial_task.buffer = msf_stream_data(msf, symbols_sn).str + symbol_data_base; serial_task.bucket_off_arr = push_array_copy_u64(scratch.arena, serial_task.bucket_size_arr, gsi->bucket_count); u64_array_counts_to_offsets(gsi->bucket_count, serial_task.bucket_off_arr); @@ -2275,6 +2144,7 @@ gsi_build_ex(TP_Context *tp, Arena *arena, PDB_GsiContext *gsi, U64 symbol_data_ // fill out sort records & serialize symbols TP_TaskFunc *serial_func = is_pub32 ? gsi_serialize_pub32 : gsi_serialize_symbols_task; tp_for_parallel(tp, 0, gsi->bucket_count, serial_func, &serial_task); + msf_stream_seek(msf, symbols_sn, safe_cast_u32(symbol_data_end)); ProfEnd(); @@ -2325,7 +2195,6 @@ gsi_build_ex(TP_Context *tp, Arena *arena, PDB_GsiContext *gsi, U64 symbol_data_ result.compressed_bucket_count = compressed_offset_count; result.compressed_bucket_arr = compressed_offset_arr; result.total_hash_size = sizeof(header) + header.hash_record_arr_size + header.bucket_data_size; - result.symbol_data = str8(serial_task.buffer, buffer_size); scratch_end(scratch); ProfEnd(); @@ -2338,9 +2207,8 @@ gsi_build(TP_Context *tp, PDB_GsiContext *gsi, MSF_Context *msf, MSF_StreamNumbe ProfBeginFunction(); Temp scratch = scratch_begin(0,0); - U64 symbol_data_base = msf_stream_get_pos(msf, symbols_sn); - PDB_GsiBuildResult build = gsi_build_ex(tp, scratch.arena, gsi, symbol_data_base, /* is_pub32: */ 0, msf->page_size); - gsi_write_build_result(tp, build, msf, sn, symbols_sn); + PDB_GsiBuildResult build = gsi_build_ex(tp, scratch.arena, gsi, msf, symbols_sn, /* is_pub32: */ 0); + gsi_write_build_result(tp, build, msf, sn); scratch_end(scratch); ProfEnd(); @@ -2471,8 +2339,7 @@ psi_build(TP_Context *tp, PDB_PsiContext *psi, MSF_Context *msf, MSF_StreamNumbe ProfBeginFunction(); Temp scratch = scratch_begin(0,0); - U64 symbol_data_base = msf_stream_get_pos(msf, symbols_sn); - PDB_GsiBuildResult gsi_build = gsi_build_ex(tp, scratch.arena, psi->gsi, symbol_data_base, /* is_pub32: */ 1, msf->page_size); + PDB_GsiBuildResult gsi_build = gsi_build_ex(tp, scratch.arena, psi->gsi, msf, symbols_sn, /* is_pub32: */ 1); ProfBegin("Address Map"); @@ -2493,8 +2360,9 @@ psi_build(TP_Context *tp, PDB_PsiContext *psi, MSF_Context *msf, MSF_StreamNumbe header.sec_count = 0; ProfBegin("MSF Write"); + msf_stream_reserve(msf, sn, safe_cast_u32(sizeof(header) + gsi_build.total_hash_size + addr_map_size)); msf_stream_write_struct(msf, sn, &header); - gsi_write_build_result(tp, gsi_build, msf, sn, symbols_sn); + gsi_write_build_result(tp, gsi_build, msf, sn); msf_stream_write_array(msf, sn, &addr_map[0], addr_map_count); ProfEnd(); @@ -2554,149 +2422,6 @@ dbi_alloc(COFF_MachineType machine, U32 age) return dbi; } -internal String8List * -dbi_open_file_info(Arena *arena, MSF_Context *msf, MSF_StreamNumber sn, PDB_DbiHeader *dbi_header) -{ - ProfBeginFunction(); - Temp scratch = scratch_begin(&arena, 1); - - MSF_UInt file_info_pos = sizeof(PDB_DbiHeader) + - dbi_header->module_info_size + - dbi_header->sec_con_size + - dbi_header->sec_map_size; - msf_stream_seek(msf, sn, file_info_pos); - - U16 mod_count = msf_stream_read_u16(msf, sn); - U16 total_file_count16 = msf_stream_read_u16(msf, sn); - - CV_ModIndex *imod_array = push_array(scratch.arena, CV_ModIndex, mod_count); - msf_stream_read_array(msf, sn, &imod_array[0], mod_count); - - U16 *mod_file_count = push_array(scratch.arena, U16, mod_count); - msf_stream_read_array(msf, sn, &mod_file_count[0], mod_count); - - U64 total_file_count = 0; - for (U16 imod = 0; imod < mod_count; imod += 1) { - total_file_count += mod_file_count[imod]; - } - - U32 *file_name_offset_array = push_array(scratch.arena, U32, total_file_count); - msf_stream_read_array(msf, sn, &file_name_offset_array[0], total_file_count); - - U64 file_name_buffer_offset = sizeof(mod_count) + - sizeof(total_file_count16) + - sizeof(imod_array[0]) * mod_count + - sizeof(mod_file_count[0]) * mod_count + - sizeof(file_name_offset_array[0]) * total_file_count; - Assert(dbi_header->file_info_size >= file_name_buffer_offset); - U64 file_name_buffer_size = dbi_header->file_info_size - file_name_buffer_offset; - char *file_name_buffer = push_array(arena, char, file_name_buffer_size + 1); - msf_stream_read_array(msf, sn, &file_name_buffer[0], file_name_buffer_size); - - String8List *file_info = push_array(arena, String8List, mod_count + 1); - - U32 *file_name_offset_ptr = &file_name_offset_array[0]; - for (U64 mod_idx = 0; mod_idx < mod_count; ++mod_idx) { - String8List *file_list = &file_info[mod_idx]; - U16 file_count = mod_file_count[mod_idx]; - for (U16 ifile = 0; ifile < file_count; ifile += 1, file_name_offset_ptr += 1) { - Assert(*file_name_offset_ptr <= file_name_buffer_size); - String8 file_path = str8_cstring(file_name_buffer + *file_name_offset_ptr); - str8_list_push(arena, file_list, file_path); - } - } - - scratch_end(scratch); - ProfEnd(); - return file_info; -} - -internal PDB_DbiModuleList -dbi_open_module_info(Arena *arena, MSF_Context *msf, MSF_StreamNumber sn, PDB_DbiHeader *dbi_header, String8List *file_info) -{ - ProfBeginFunction(); - - PDB_DbiModuleList list = {0}; - - MSF_UInt module_info_pos = sizeof(PDB_DbiHeader); - msf_stream_seek(msf, sn, module_info_pos); - - MSF_UInt module_info_opl = module_info_pos + dbi_header->module_info_size; - while (msf_stream_get_pos(msf, sn) < module_info_opl) { - PDB_DbiCompUnitHeader header = {0}; - msf_stream_read_struct(msf, sn, &header); - String8 obj_path = msf_stream_read_string(arena, msf, sn); - String8 lib_path = msf_stream_read_string(arena, msf, sn); - msf_stream_align(msf, sn, PDB_MODULE_ALIGN); - - String8List source_file_list = {0}; - if (header.contribution.base.mod != CV_ModIndex_Invalid) { - source_file_list = file_info[header.contribution.base.mod]; - } - - PDB_DbiModule *mod = push_array(arena, PDB_DbiModule, 1); - mod->next = 0; - mod->sn = header.sn; - mod->imod = header.contribution.base.mod; - mod->sym_data_size = header.symbols_size; - mod->c11_data_size = header.c11_lines_size; - mod->c13_data_size = header.c13_lines_size; - mod->source_file_list = source_file_list; - mod->obj_path = obj_path; - mod->lib_path = lib_path; - mod->first_sc = header.contribution; - - SLLQueuePush(list.first, list.last, mod); - list.count += 1; - } - - ProfEnd(); - return list; -} - -internal PDB_DbiSCArray -dbi_open_sec_contrib(Arena *arena, MSF_Context *msf, MSF_StreamNumber sn, PDB_DbiHeader *dbi_header) -{ - ProfBeginFunction(); - - PDB_DbiSCArray sec_contribs = {0}; - - if (dbi_header->sec_con_size > sizeof(PDB_DbiSC)) { - Temp scratch = scratch_begin(&arena, 1); - - // seek to start of section contrib info - MSF_UInt sec_con_pos = sizeof(PDB_DbiHeader) + dbi_header->module_info_size; - msf_stream_seek(msf, sn, sec_con_pos); - - // read header - PDB_DbiSCVersion version = 0; - msf_stream_read_struct(msf, sn, &version); - - // parse contrib items - switch (version) { - case PDB_DbiSCVersion_1: { - U64 contrib_count = dbi_header->sec_con_size / sizeof(PDB_DbiSC); - sec_contribs.v = push_array_no_zero(arena, PDB_DbiSC, contrib_count); - sec_contribs.count = contrib_count; - sec_contribs.cap = contrib_count; - MSF_UInt sec_con_read = msf_stream_read_array(msf, sn, sec_contribs.v, contrib_count); - Assert(sec_con_read == sizeof(sec_contribs.v[0]) * contrib_count); - } break; - case PDB_DbiSCVersion_2: { - NotImplemented; - } break; - default: Assert(!"unknown section contrib version"); break; - } - - // have we exhausted sec-con bytes? - Assert(sec_con_pos + dbi_header->sec_con_size == msf_stream_get_pos(msf, sn)); - scratch_end(scratch); - } - - ProfEnd(); - return sec_contribs; -} - internal void dbi_build_section_header_stream(PDB_DbiContext *dbi, MSF_Context *msf, MSF_StreamNumber sn) { @@ -3357,47 +3082,6 @@ dbi_module_push_section_contrib(PDB_DbiContext *dbi, ProfEnd(); } -internal String8 -dbi_module_read_symbol_data(Arena *arena, MSF_Context *msf, PDB_DbiModule *mod) -{ - String8 symbol_data = str8(0,0); - if (mod->sn != MSF_INVALID_STREAM_NUMBER) { - B32 is_seek_ok = msf_stream_seek(msf, mod->sn, 0); - if (is_seek_ok) { - symbol_data = msf_stream_read_block(arena, msf, mod->sn, mod->sym_data_size); - } - } - return symbol_data; -} - -internal String8 -dbi_module_read_c11_data(Arena *arena, MSF_Context *msf, PDB_DbiModule *mod) -{ - String8 c11_data = str8(0,0); - if (mod->sn != MSF_INVALID_STREAM_NUMBER) { - MSF_UInt c11_data_pos = mod->sym_data_size; - B32 is_seek_ok = msf_stream_seek(msf, mod->sn, c11_data_pos); - if (is_seek_ok) { - c11_data = msf_stream_read_block(arena, msf, mod->sn, mod->c13_data_size); - } - } - return c11_data; -} - -internal String8 -dbi_module_read_c13_data(Arena *arena, MSF_Context *msf, PDB_DbiModule *mod) -{ - String8 c13_data = str8(0,0); - if (mod->sn != MSF_INVALID_STREAM_NUMBER) { - MSF_UInt c13_data_pos = mod->sym_data_size + mod->c11_data_size; - B32 is_seek_ok = msf_stream_seek(msf, mod->sn, c13_data_pos); - if (is_seek_ok) { - c13_data = msf_stream_read_block(arena, msf, mod->sn, mod->c13_data_size); - } - } - return c13_data; -} - internal void dbi_push_section(PDB_DbiContext *dbi, COFF_SectionHeader *hdr) { @@ -3523,6 +3207,27 @@ pdb_build_gsi_psi(TP_Context *tp, PDB_Context *pdb) { PDB_DbiContext *dbi = pdb->dbi; + // Publics and globals share one symbol stream. Size both before either + // serializer borrows the backing so the second append does not copy it. + if (pdb->psi->gsi->symbol_count || pdb->gsi->symbol_count) { + Temp scratch = scratch_begin(0, 0); + PDB_GsiContext *contexts[] = { pdb->psi->gsi, pdb->gsi }; + U64 size = 0; + for EachElement(i, contexts) { + PDB_GsiSerializeSymbolsTask task = { + .symbol_align = contexts[i]->symbol_align, + .bucket_arr = contexts[i]->bucket_arr, + .bucket_size_arr = push_array(scratch.arena, U64, contexts[i]->bucket_count), + }; + tp_for_parallel(tp, 0, contexts[i]->bucket_count, gsi_size_buckets_task, &task); + size += sum_array_u64(contexts[i]->bucket_count, task.bucket_size_arr); + } + if (dbi->symbols_sn == MSF_INVALID_STREAM_NUMBER) { dbi->symbols_sn = msf_stream_alloc(pdb->msf); } + AssertAlways(msf_stream_resize(pdb->msf, dbi->symbols_sn, safe_cast_u32(size))); + msf_stream_seek_start(pdb->msf, dbi->symbols_sn); + scratch_end(scratch); + } + if (pdb->psi->gsi->symbol_count) { if (dbi->publics_sn == MSF_INVALID_STREAM_NUMBER) { dbi->publics_sn = msf_stream_alloc(pdb->msf); } if (dbi->symbols_sn == MSF_INVALID_STREAM_NUMBER) { dbi->symbols_sn = msf_stream_alloc(pdb->msf); } diff --git a/src/linker/pdb_ext/pdb_builder.h b/src/linker/pdb_ext/pdb_builder.h index 016e4aeb..3de6e583 100644 --- a/src/linker/pdb_ext/pdb_builder.h +++ b/src/linker/pdb_ext/pdb_builder.h @@ -204,7 +204,6 @@ typedef struct PDB_GsiBuildResult U64 compressed_bucket_count; U32 *compressed_bucket_arr; U64 total_hash_size; - String8 symbol_data; } PDB_GsiBuildResult; typedef struct PDB_GsiSerializeSymbolsTask @@ -364,8 +363,8 @@ internal PDB_SrcError pdb_add_src(PDB_InfoContext *info, MSF_Context *msf, internal PDB_GsiContext * gsi_alloc(void); internal void gsi_build(TP_Context *tp, PDB_GsiContext *gsi, MSF_Context *msf, MSF_StreamNumber gsi_sn, MSF_StreamNumber symbols_sn); internal void gsi_release(PDB_GsiContext *gsi); -internal void gsi_write_build_result(TP_Context *tp, PDB_GsiBuildResult build, MSF_Context *msf, MSF_StreamNumber sn, MSF_StreamNumber symbols_sn); -internal PDB_GsiBuildResult gsi_build_ex(TP_Context *tp, Arena *arena, PDB_GsiContext *gsi, U64 symbol_data_base, B32 export_symbol_ptr_arr, U64 msf_page_size); +internal void gsi_write_build_result(TP_Context *tp, PDB_GsiBuildResult build, MSF_Context *msf, MSF_StreamNumber sn); +internal PDB_GsiBuildResult gsi_build_ex(TP_Context *tp, Arena *arena, PDB_GsiContext *gsi, MSF_Context *msf, MSF_StreamNumber symbols_sn, B32 is_pub32); internal U32 gsi_hash(PDB_GsiContext *gsi, String8 input); internal CV_SymbolNode * gsi_push(PDB_GsiContext *gsi, CV_Symbol *symbol); internal void gsi_push_many_arr(TP_Context *tp, PDB_GsiContext *gsi, U64 count, CV_SymbolNode **symbol_arr); @@ -391,16 +390,7 @@ 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, B32 is_stripped); internal void dbi_release(PDB_DbiContext *dbi); 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_c11_data(Arena *arena, MSF_Context *msf, PDB_DbiModule *mod); -internal String8 dbi_module_read_c13_data(Arena *arena, MSF_Context *msf, PDB_DbiModule *mod); internal void dbi_module_push_section_contrib(PDB_DbiContext *dbi, PDB_DbiModule *mod, ISectOff isect_off, U32 size, U32 data_crc, U32 reloc_crc, COFF_SectionFlags flags); -internal String8List * dbi_open_file_info(Arena *arena, MSF_Context *msf, MSF_StreamNumber sn, PDB_DbiHeader *dbi_header); -internal PDB_DbiModuleList dbi_open_module_info(Arena *arena, MSF_Context *msf, MSF_StreamNumber sn, PDB_DbiHeader *dbi_header, String8List *file_info); -internal PDB_DbiSCArray dbi_open_sec_contrib(Arena *arena, MSF_Context *msf, MSF_StreamNumber sn, PDB_DbiHeader *dbi_header); -internal PDB_StringTable dbi_open_ec_names(Arena *arena, MSF_Context *msf, MSF_StreamNumber sn, PDB_DbiHeader *dbi_header); -internal void dbi_open_dbg_streams(MSF_StreamNumber *dbg_streams, MSF_Context *msf, MSF_StreamNumber sn, PDB_DbiHeader *dbi_header); -internal PDB_DbiSectionList dbi_open_section_headers(Arena *arena, MSF_Context *msf, MSF_StreamNumber sn); internal void dbi_build_section_header_stream(PDB_DbiContext *dbi, MSF_Context *msf, MSF_StreamNumber sn); //////////////////////////////// @@ -450,7 +440,6 @@ internal U32 pdb_strtab_hash(PDB_StringTable *strtab, Strin // Type Server internal PDB_TypeServer * pdb_type_server_alloc(U64 bucket_count); -internal PDB_TypeServer * pdb_type_server_open_v80(MSF_Context *msf, MSF_StreamNumber sn, PDB_StringTable *strtab); internal void pdb_type_server_build(TP_Context *tp, PDB_TypeServer *ts, PDB_StringTable *strtab, MSF_Context *msf, MSF_StreamNumber sn); internal void pdb_type_server_release(PDB_TypeServer **serv_ptr); internal void pdb_type_server_push(PDB_TypeServer *ts, String8 raw_leaf); diff --git a/src/linker/pdb_ext/pdb_helpers.c b/src/linker/pdb_ext/pdb_helpers.c index 8bdef67e..061d96c5 100644 --- a/src/linker/pdb_ext/pdb_helpers.c +++ b/src/linker/pdb_ext/pdb_helpers.c @@ -25,25 +25,6 @@ pdb_read_bit_vector_string(String8 data, U64 offset, U32Array *bits_out) return read_size; } -internal U64 -pdb_read_bit_vector_msf(Arena *arena, MSF_Context *msf, MSF_StreamNumber sn, U32Array *bits_out) -{ - // peek word count - MSF_UInt pos = msf_stream_get_pos(msf, sn); - U32 word_count = msf_stream_read_u32(msf, sn); - msf_stream_seek(msf, sn, pos); - - // read out header + packed words - U64 buffer_size = sizeof(word_count) + word_count * sizeof(U32); - U8 *buffer = push_array(arena, U8, buffer_size); - MSF_UInt read_size = msf_stream_read(msf, sn, buffer, buffer_size); - Assert(read_size == buffer_size); - - // parse words - U64 parse_size = pdb_read_bit_vector_string(str8(buffer, buffer_size), 0, bits_out); - return parse_size; -} - internal B32 pdb_write_bit_vector(MSF_Context *msf, MSF_StreamNumber sn, B32 *flag_array, U64 flag_count) { @@ -86,4 +67,3 @@ pdb_get_bit_vector_size(U32 bucket_count) return result; } - diff --git a/src/linker/pdb_ext/pdb_helpers.h b/src/linker/pdb_ext/pdb_helpers.h index 007eb64f..72363bc8 100644 --- a/src/linker/pdb_ext/pdb_helpers.h +++ b/src/linker/pdb_ext/pdb_helpers.h @@ -7,8 +7,6 @@ internal U32 pdb_hash_v1(String8 data); internal U32 pdb_hash_udt(CV_UDTInfo udt_info, String8 data); internal U64 pdb_read_bit_vector_string(String8 data, U64 offset, U32Array *bits_out); -internal U64 pdb_read_bit_vector_msf(Arena *arena, MSF_Context *msf, MSF_StreamNumber sn, U32Array *bits_out); internal B32 pdb_write_bit_vector(MSF_Context *msf, MSF_StreamNumber sn, B32 *flag_array, U64 flag_count); internal U64 pdb_get_bit_vector_size(U32 bucket_count); - diff --git a/src/linker/tests/linker_tests.c b/src/linker/tests/linker_tests.c index 8622aec9..63fde26f 100644 --- a/src/linker/tests/linker_tests.c +++ b/src/linker/tests/linker_tests.c @@ -7779,7 +7779,7 @@ TEST(cyclic_type) T_Ok(is_cycle_detected); } -TEST(get_msf_stream_pages) +TEST(msf_contiguous_stream) { MSF_Context *msf = msf_alloc(MSF_DEFAULT_PAGE_SIZE, MSF_DEFAULT_FPM); @@ -7792,44 +7792,107 @@ TEST(get_msf_stream_pages) MemorySet(test, 0xca, stream_size/2); MemorySet(test + stream_size/2, 0xbe, stream_size/2); - String8List stream_data = msf_data_from_sn(arena, msf, sn); - T_Ok(stream_data.total_size == stream_size); - T_Ok(stream_data.node_count == 12); + String8 stream_data = msf_stream_data(msf, sn); + T_Ok(stream_data.size == stream_size); + MemoryCopy(stream_data.str, test, stream_size); - String8Array a = str8_array_from_list(arena, &stream_data); - T_Ok(a.v[0].size == 0xffd000); - T_Ok(a.v[1].size == 0xffe000); - T_Ok(a.v[2].size == 0xffe000); - T_Ok(a.v[3].size == 0xffe000); - T_Ok(a.v[4].size == 0xffe000); - T_Ok(a.v[5].size == 0xffe000); - T_Ok(a.v[6].size == 0xffe000); - T_Ok(a.v[7].size == 0xffd000); - T_Ok(a.v[8].size == 0x1000); - T_Ok(a.v[9].size == 0xffe000); - T_Ok(a.v[10].size == 0x613000); - T_Ok(a.v[11].size == 1); - - String8Node buf = *stream_data.first; - U64 buf_pos = 0; - str8_buffer_write(&buf, &buf_pos, str8(test, stream_size)); - - String8 cmp = msf_stream_read_block(arena, msf, sn, stream_size); + // Physical pages alias the one contiguous construction allocation, even + // across reserved FPM slots and bitmap intervals. + MSF_Stream *stream = msf_find_stream(msf, sn); + U64 offset = 0; + for EachNode(page, MSF_PageNode, stream->page_list.first) { + T_Ok(msf_data_from_pn(msf, page->pn).str == stream_data.str + offset); + offset += msf->page_size; + } + MSF_PageNumber first_pn = stream->page_list.first->pn; + MSF_PageNumber last_pn = stream->page_list.last->pn; + U64 before_build = arena_pos(msf->arena); + String8 saved = {0}; + T_Ok(msf_save_arena(arena, msf, &saved) == MSF_Error_Ok); + // Building adds metadata, not another 150 MiB payload allocation. + T_Ok(arena_pos(msf->arena) - before_build < MB(1)); + T_Ok(stream->data == stream_data.str); + T_Ok(stream->page_list.first->pn == first_pn); + T_Ok(stream->page_list.last->pn == last_pn); + MSF_Parsed *parsed = msf_parsed_from_data(arena, saved); + T_Ok(parsed != 0); + String8 cmp = msf_data_from_stream(parsed, sn); T_Ok(cmp.size == stream_size); T_Ok(MemoryCompare(cmp.str, test, stream_size) == 0); } { MSF_StreamNumber sn = msf_stream_alloc_ex(msf, 1); - String8List stream_data = msf_data_from_sn(arena, msf, sn); - T_Ok(stream_data.node_count == 1); - T_Ok(stream_data.total_size == 1); - T_Ok(stream_data.first->string.size == 1); + String8 stream_data = msf_stream_data(msf, sn); + T_Ok(stream_data.size == 1); } msf_release(msf); } +TEST(msf_stream_growth_and_page_references) +{ + MSF_Context *msf = msf_alloc(512, MSF_FPM1); + U64 P = msf->page_size; + MSF_StreamNumber a = msf_stream_alloc_ex(msf, P + 3); + MSF_StreamNumber b = msf_stream_alloc_ex(msf, P); + MSF_StreamNumber empty = msf_stream_alloc(msf); + MSF_Stream *stream = msf_find_stream(msf, a); + MSF_PageNumber first_pn = stream->page_list.first->pn; + MSF_PageNumber second_pn = stream->page_list.last->pn; + MSF_PageNumber b_pn = msf_find_stream(msf, b)->page_list.first->pn; + MemorySet(stream->data, 0x5a, stream->size); + + T_Ok(msf_stream_resize(msf, a, 3 * P + 7)); + T_Ok(stream->page_list.first->pn == first_pn); + T_Ok(stream->page_list.first->next->pn == second_pn); + T_Ok(stream->page_list.first->next->next->pn != second_pn + 1); + String8 data = msf_stream_data(msf, a); + for EachIndex(i, P + 3) { T_Ok(data.str[i] == 0x5a); } + for (U64 i = P + 3; i < data.size; i += 1) { T_Ok(data.str[i] == 0); } + + // Store a page reference before build and patch a field crossing the + // boundary between two nonadjacent physical output pages. + memory_write32(msf_stream_data(msf, b).str, first_pn); + U8 patch[] = {1, 2, 3, 4, 5, 6, 7, 8}; + MemoryCopy(data.str + 2 * P - 3, patch, sizeof(patch)); + String8 expected = push_str8_copy(arena, data); + + // Reservation assigns identities but does not extend the logical stream. + T_Ok(msf_stream_reserve(msf, empty, 2 * P)); + T_Ok(msf_stream_get_size(msf, empty) == 0); + String8 saved = {0}; + T_Ok(msf_save_arena(arena, msf, &saved) == MSF_Error_Ok); + MSF_RawStreamTable *table = msf_raw_stream_table_from_data(arena, saved); + T_Ok(table->streams[a].u.page_indices_u32[0] == first_pn); + T_Ok(table->streams[a].u.page_indices_u32[1] == second_pn); + T_Ok(table->streams[empty].page_count == 0); + T_Ok(str8_match(msf_data_from_stream_number(arena, saved, table, a), expected, 0)); + String8 b_data = msf_data_from_stream_number(arena, saved, table, b); + T_Ok(memory_read32(b_data.str) == first_pn); + + // Removing a stream leaves a directory hole; later stream numbers remain + // stable, and its physical page can be reused without stale payload bytes. + T_Ok(msf_stream_free(msf, b)); + MSF_StreamNumber c = msf_stream_alloc_ex(msf, 1); + T_Ok(c > empty); + T_Ok(msf_find_stream(msf, c)->page_list.first->pn == b_pn); + T_Ok(msf_stream_data(msf, c).str[0] == 0); + T_Ok(msf_stream_write_u8(msf, c, 0x71)); + T_Ok(msf_stream_resize(msf, a, P + 1)); + T_Ok(msf_save_arena(arena, msf, &saved) == MSF_Error_Ok); + table = msf_raw_stream_table_from_data(arena, saved); + T_Ok(table->stream_count == c + 1); + T_Ok(table->streams[b].size == 0); + T_Ok(table->streams[empty].size == 0); + T_Ok(str8_match(msf_data_from_stream_number(arena, saved, table, a), str8_prefix(expected, P + 1), 0)); + T_Ok(msf_data_from_stream_number(arena, saved, table, c).str[0] == 0x71); + // The final partial page is padded, not populated with truncated bytes. + U8 *tail = saved.str + (U64)second_pn * P; + for (U64 i = 1; i < P; i += 1) { T_Ok(tail[i] == 0); } + msf_release(msf); +} + TEST(msf_header_matches_saved_extent) { MSF_UInt page_sizes[] = {512, KB(4)}; @@ -7891,9 +7954,9 @@ TEST(msf_header_matches_saved_extent) // Free the low pages so metadata can reuse them while the payload remains at // high page numbers. Unused pages below EOF exercise sparse MSF page layouts. msf_free_pages(msf, &gap); - T_Ok(msf_build(msf) == MSF_Error_OK); - T_Ok(msf->root_page_list.last->pn < last_pn); - T_Ok(msf->st_page_list.last->pn < last_pn); + T_Ok(msf_build(msf) == MSF_Error_Ok); + T_Ok(msf->root.page_list.last->pn < last_pn); + T_Ok(msf->stream_table.page_list.last->pn < last_pn); // validate that last page was assigned to the payload U64 save_size = msf_get_save_size(msf); @@ -7907,6 +7970,31 @@ TEST(msf_header_matches_saved_extent) MSF_Header70 *header = (MSF_Header70 *)saved.str; T_Ok((U64)header->page_count * header->page_size == saved.size); + // Validate the serialized allocation bitmap, including free holes + // and the reserved slots at every physical FPM interval. + U8 *used = push_array(arena, U8, header->page_count); + used[0] = 1; + for (U64 pn = 0; pn < header->page_count; pn += 1) { + if (pn % P == MSF_FPM0 || pn % P == MSF_FPM1) { used[pn] = 1; } + } + for EachNode(page, MSF_PageNode, msf->root.page_list.first) { used[page->pn] = 1; } + for EachNode(page, MSF_PageNode, msf->stream_table.page_list.first) { used[page->pn] = 1; } + for EachNode(page, MSF_PageNode, msf_find_stream(msf, sn)->page_list.first) { used[page->pn] = 1; } + for (U64 pn = 0; pn < header->page_count; pn += 1) { + U64 fpm_pn = (pn / (P * 8)) * P + header->active_fpm; + U64 bit = pn % (P * 8); + U8 free_bit = (saved.str[fpm_pn * P + bit / 8] >> (bit % 8)) & 1; + T_Ok(free_bit == !used[pn]); + } + // CDB/DIA reject an otherwise readable PDB if the final bitmap + // marks future reserved FPM slots beyond EOF as allocated. + U64 last_fpm_pn = ((header->page_count - 1) / (P * 8)) * P + header->active_fpm; + U64 end_bit = ((U64)header->page_count - 1) % (P * 8) + 1; + for (U64 bit = end_bit; bit < P * 8; bit += 1) { + U8 free_bit = (saved.str[last_fpm_pn * P + bit / 8] >> (bit % 8)) & 1; + T_Ok(free_bit == 1); + } + // match internal page data list to the serialized file bytes String8List pages = msf_get_page_data_nodes(arena, msf); T_Ok(str8_match(saved, str8_list_join(arena, &pages, 0), 0)); @@ -7952,7 +8040,7 @@ data_from_pdb(Arena *arena, PDB_Context *pdb) TP_Arena *tp_arena = tp_arena_alloc(tp); pdb_build(tp, tp_arena, pdb, (CV_StringHashTable){0}, 1, 0, 0); - AssertAlways(msf_build(pdb->msf) == MSF_Error_OK); + AssertAlways(msf_build(pdb->msf) == MSF_Error_Ok); String8List raw_msf_list = msf_get_page_data_nodes(arena, pdb->msf); AssertAlways(t_write_file_list(str8_lit("test.pdb"), raw_msf_list));