Skip to content

Commit

Permalink
Change the DNMD APIs to act on single rows and bring other perf impro…
Browse files Browse the repository at this point in the history
…vements from the CoreCLR + DNMD experiment to DNMD. (#55)
jkoritzinsky authored Oct 17, 2024
1 parent daba646 commit c42d927
Showing 17 changed files with 1,687 additions and 1,421 deletions.
120 changes: 90 additions & 30 deletions src/dnmd/access.c
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
#include "internal.h"

bool create_access_context(mdcursor_t* cursor, col_index_t col_idx, uint32_t row_count, bool make_writable, access_cxt_t* acxt)
bool create_access_context(mdcursor_t* cursor, col_index_t col_idx, bool make_writable, access_cxt_t* acxt)
{
assert(acxt != NULL);
mdtable_t* table = CursorTable(cursor);
if (table == NULL)
return false;
@@ -16,22 +15,90 @@ bool create_access_context(mdcursor_t* cursor, col_index_t col_idx, uint32_t row

// Metadata row indexing is 1-based.
row--;
acxt->table = table;
acxt->col_details = table->column_details[idx];

// Compute the offset into the first row.
uint32_t offset = ExtractOffset(acxt->col_details);
mdtcol_t col = table->column_details[idx];

uint32_t offset_to_table_data = row * table->row_size_bytes + ExtractOffset(col);
#ifndef NDEBUG
size_t len = (col & mdtc_b2) ? 2 : 4;
assert(offset_to_table_data + len <= table->data.size);
#endif

acxt->table = table;
acxt->data = table->data.ptr + offset_to_table_data;
acxt->col_details = col;
if (make_writable)
{
acxt->writable_data = get_writable_table_data(table, make_writable);
acxt->writable_data = acxt->writable_data + (row * table->row_size_bytes) + offset;
acxt->writable_data = get_writable_table_data(table, make_writable) + offset_to_table_data;
}
else
{
acxt->writable_data = NULL;
}

return true;
}

bool read_column_data(access_cxt_t* acxt, uint32_t* data)
{
assert(acxt != NULL && acxt->data != NULL && data != NULL);

uint8_t const* table_data = acxt->data;

if ((acxt->col_details & mdtc_b4) == mdtc_b4)
{
size_t len = 4;
return read_u32(&table_data, &len, data);
}
else
{
size_t len = 2;
uint16_t value;
if (!read_u16(&table_data, &len, &value))
return false;

*data = value;
return true;
}
}

bool write_column_data(access_cxt_t* acxt, uint32_t data)
{
assert(acxt != NULL && acxt->writable_data != NULL);
uint8_t* table_data = acxt->writable_data;
if ((acxt->col_details & mdtc_b4) == mdtc_b4)
{
size_t len = 4;
return write_u32(&table_data, &len, data);
}
else
{
size_t len = 2;
return write_u16(&table_data, &len, (uint16_t)data);
}
}

bool create_bulk_access_context(mdcursor_t* cursor, col_index_t col_idx, uint32_t row_count, bulk_access_cxt_t* acxt)
{
assert(acxt != NULL);
mdtable_t* table = CursorTable(cursor);
if (table == NULL)
return false;

uint32_t row = CursorRow(cursor);
if (row == 0 || row > table->row_count)
return false;

uint8_t idx = col_to_index(col_idx, table);
assert(idx < table->column_count);

// Metadata row indexing is 1-based.
row--;
acxt->table = table;
acxt->col_details = table->column_details[idx];

// Compute the offset into the first row.
uint32_t offset = ExtractOffset(acxt->col_details);

acxt->start = acxt->data = table->data.ptr + (row * table->row_size_bytes) + offset;

// Compute the beginning of the row after the last valid row.
@@ -50,40 +117,33 @@ bool create_access_context(mdcursor_t* cursor, col_index_t col_idx, uint32_t row
return true;
}

bool read_column_data(access_cxt_t* acxt, uint32_t* data)
bool read_column_data_and_advance(bulk_access_cxt_t* acxt, uint32_t* data)
{
assert(acxt != NULL && data != NULL);
*data = 0;

if (acxt->writable_data != NULL)
acxt->writable_data += (acxt->col_details & mdtc_b2) ? 2 : 4;

return (acxt->col_details & mdtc_b2)
? read_u16(&acxt->data, &acxt->data_len, (uint16_t*)data)
: read_u32(&acxt->data, &acxt->data_len, data);
}

bool write_column_data(access_cxt_t* acxt, uint32_t data)
{
assert(acxt != NULL && acxt->writable_data != NULL);

acxt->data += (acxt->col_details & mdtc_b2) ? 2 : 4;
if ((acxt->col_details & mdtc_b4) == mdtc_b4)
{
return read_u32(&acxt->data, &acxt->data_len, data);
}
else
{
uint16_t value;
if (!read_u16(&acxt->data, &acxt->data_len, &value))
return false;

return (acxt->col_details & mdtc_b2)
? write_u16(&acxt->writable_data, &acxt->data_len, (uint16_t)data)
: write_u32(&acxt->writable_data, &acxt->data_len, data);
*data = value;
return true;
}
}

bool next_row(access_cxt_t* acxt)
bool next_row(bulk_access_cxt_t* acxt)
{
assert(acxt != NULL);
// We will only traverse correctly if we've already read the column in this row.
assert(acxt->data_len == 0);
acxt->data += acxt->next_row_stride;

if (acxt->writable_data != NULL)
acxt->writable_data += acxt->next_row_stride;

// Restore the data length of the column data.
acxt->data_len = acxt->data_len_col;
return acxt->data < acxt->end;
115 changes: 97 additions & 18 deletions src/dnmd/bytes.c
Original file line number Diff line number Diff line change
@@ -129,6 +129,9 @@ bool read_i8(uint8_t const** data, size_t* data_len, int8_t* o)
return read_le(data, data_len, o, sizeof(*o));
}

// MSVC doesn't optimize away the implementation on Little-Endian platforms,
// so manually provide an optimized implementation for MSVC.
#ifndef _MSC_VER
bool read_u16(uint8_t const** data, size_t* data_len, uint16_t* o)
{
return read_le(data, data_len, o, sizeof(*o));
@@ -159,6 +162,75 @@ bool read_i64(uint8_t const** data, size_t* data_len, int64_t* o)
return read_le(data, data_len, o, sizeof(*o));
}

#else
bool read_u16(uint8_t const** data, size_t* data_len, uint16_t* o)
{
if (*data_len < sizeof(*o))
return false;

memcpy(o, *data, sizeof(*o));
*data += sizeof(*o);
*data_len -= sizeof(*o);
return true;
}

bool read_i16(uint8_t const** data, size_t* data_len, int16_t* o)
{
if (*data_len < sizeof(*o))
return false;

memcpy(o, *data, sizeof(*o));
*data += sizeof(*o);
*data_len -= sizeof(*o);
return true;
}

bool read_u32(uint8_t const** data, size_t* data_len, uint32_t* o)
{
if (*data_len < sizeof(*o))
return false;

memcpy(o, *data, sizeof(*o));
*data += sizeof(*o);
*data_len -= sizeof(*o);
return true;
}

bool read_i32(uint8_t const** data, size_t* data_len, int32_t* o)
{
if (*data_len < sizeof(*o))
return false;

memcpy(o, *data, sizeof(*o));
*data += sizeof(*o);
*data_len -= sizeof(*o);
return true;
}

bool read_u64(uint8_t const** data, size_t* data_len, uint64_t* o)
{
if (*data_len < sizeof(*o))
return false;

memcpy(o, *data, sizeof(*o));
*data += sizeof(*o);
*data_len -= sizeof(*o);
return true;
}

bool read_i64(uint8_t const** data, size_t* data_len, int64_t* o)
{
if (*data_len < sizeof(*o))
return false;

memcpy(o, *data, sizeof(*o));
*data += sizeof(*o);
*data_len -= sizeof(*o);
return true;
}

#endif

bool write_u8(uint8_t** data, size_t* data_len, uint8_t o)
{
return write_le(data, data_len, o, sizeof(o));
@@ -208,35 +280,42 @@ bool decompress_u32(uint8_t const** data, size_t* data_len, uint32_t* o)
assert(s != NULL);

uint32_t val;
switch (*s & 0xc0)

// The valid leading bits are 00, 10, and 110.
// All others are invalid.
// PERF: Check for 00 vs 10 first as we get better codegen
// on Intel/AMD processors (shorter instruction sequences and better branch prediction).
if ((*s & 0x80) == 0x00)
{
case 0xc0:
if (*data_len < 4)
if (*data_len < 1)
return false;

*data_len -= 4;
val = ((*s++ & 0x1f) << 24);
val |= (*s++ << 16);
val |= (*s++ << 8);
val |= *s++;
break;

case 0x80:
*data_len -= 1;
val = *s++;
}
else if ((*s & 0xC0) == 0x80)
{
if (*data_len < 2)
return false;

*data_len -= 2;
val = ((*s++ & 0x3f) << 8);
val |= *s++;
break;

default:
if (*data_len < 1)
}
else if ((*s & 0xE0) == 0xC0)
{
if (*data_len < 4)
return false;

*data_len -= 1;
val = *s++;
break;
*data_len -= 4;
val = ((*s++ & 0x1f) << 24);
val |= (*s++ << 16);
val |= (*s++ << 8);
val |= *s++;
}
else
{
return false;
}

*o = val;
22 changes: 11 additions & 11 deletions src/dnmd/deltas.c
Original file line number Diff line number Diff line change
@@ -69,7 +69,7 @@ static bool initialize_token_map(mdtable_t* map, enc_token_map_t* token_map)
for (uint32_t i = 0; i < map->row_count; (void)md_cursor_next(&map_cur), ++i)
{
mdToken tk;
if (1 != md_get_column_value_as_constant(map_cur, mdtENCMap_Token, 1, &tk))
if (!md_get_column_value_as_constant(map_cur, mdtENCMap_Token, &tk))
return false;

mdtable_id_t table_id = ExtractTokenType(RemoveRecordBit(tk));
@@ -122,7 +122,7 @@ static bool resolve_token(enc_token_map_t* token_map, mdToken referenced_token,
for (uint32_t i = 0; i < token_map->map_cur_by_table[type].count; md_cursor_next(&map_record), i++)
{
mdToken mappedToken;
if (1 != md_get_column_value_as_constant(map_record, mdtENCMap_Token, 1, &mappedToken))
if (!md_get_column_value_as_constant(map_record, mdtENCMap_Token, &mappedToken))
return false;

assert((mdtable_id_t)ExtractTokenType(RemoveRecordBit(mappedToken)) == type);
@@ -165,8 +165,8 @@ static bool process_log(mdcxt_t* cxt, mdcxt_t* delta)
{
mdToken tk;
uint32_t op;
if (1 != md_get_column_value_as_constant(log_cur, mdtENCLog_Token, 1, &tk)
|| 1 != md_get_column_value_as_constant(log_cur, mdtENCLog_Op, 1, &op))
if (!md_get_column_value_as_constant(log_cur, mdtENCLog_Token, &tk)
|| !md_get_column_value_as_constant(log_cur, mdtENCLog_Op, &op))
{
return false;
}
@@ -343,11 +343,11 @@ bool merge_in_delta(mdcxt_t* cxt, mdcxt_t* delta)
mdcursor_t delta_module = create_cursor(&delta->tables[mdtid_Module], 1);

mdguid_t base_mvid;
if (1 != md_get_column_value_as_guid(base_module, mdtModule_Mvid, 1, &base_mvid))
if (!md_get_column_value_as_guid(base_module, mdtModule_Mvid, &base_mvid))
return false;

mdguid_t delta_mvid;
if (1 != md_get_column_value_as_guid(delta_module, mdtModule_Mvid, 1, &delta_mvid))
if (!md_get_column_value_as_guid(delta_module, mdtModule_Mvid, &delta_mvid))
return false;

// MVIDs must match between base and delta images.
@@ -358,8 +358,8 @@ bool merge_in_delta(mdcxt_t* cxt, mdcxt_t* delta)
// This ensures that we are applying deltas in order.
mdguid_t enc_id;
mdguid_t delta_enc_base_id;
if (1 != md_get_column_value_as_guid(base_module, mdtModule_EncId, 1, &enc_id)
|| 1 != md_get_column_value_as_guid(delta_module, mdtModule_EncBaseId, 1, &delta_enc_base_id)
if (!md_get_column_value_as_guid(base_module, mdtModule_EncId, &enc_id)
|| !md_get_column_value_as_guid(delta_module, mdtModule_EncBaseId, &delta_enc_base_id)
|| memcmp(&enc_id, &delta_enc_base_id, sizeof(mdguid_t)) != 0)
{
return false;
@@ -382,10 +382,10 @@ bool merge_in_delta(mdcxt_t* cxt, mdcxt_t* delta)
// We don't want to manipulate the heap sizes, so we'll pull the heap offset directly from the delta and use that
// in the base image.
uint32_t new_enc_base_id_offset;
if (1 != get_column_value_as_heap_offset(delta_module, mdtModule_EncId, 1, &new_enc_base_id_offset))
if (!get_column_value_as_heap_offset(delta_module, mdtModule_EncId, &new_enc_base_id_offset))
return false;
if (1 != set_column_value_as_heap_offset(base_module, mdtModule_EncId, 1, &new_enc_base_id_offset))
if (!set_column_value_as_heap_offset(base_module, mdtModule_EncId, new_enc_base_id_offset))
return false;

return true;
}
}
100 changes: 50 additions & 50 deletions src/dnmd/entry.c
Original file line number Diff line number Diff line change
@@ -258,25 +258,25 @@ static bool initialize_minimal_table_rows(mdcxt_t* cxt)
mdcursor_t module_cursor;
if (!md_append_row(cxt, mdtid_Module, &module_cursor))
return false;

// Set the Generation to 0
uint32_t generation = 0;
if (1 != md_set_column_value_as_constant(module_cursor, mdtModule_Generation, 1, &generation))
if (!md_set_column_value_as_constant(module_cursor, mdtModule_Generation, generation))
return false;

// Use the 0 index to specify the NULL guid as the guids for the image.
uint32_t guid_heap_offset = 0;
if (1 != set_column_value_as_heap_offset(module_cursor, mdtModule_Mvid, 1, &guid_heap_offset)
|| 1 != set_column_value_as_heap_offset(module_cursor, mdtModule_EncBaseId, 1, &guid_heap_offset)
|| 1 != set_column_value_as_heap_offset(module_cursor, mdtModule_EncId, 1, &guid_heap_offset))
if (!set_column_value_as_heap_offset(module_cursor, mdtModule_Mvid, guid_heap_offset)
|| !set_column_value_as_heap_offset(module_cursor, mdtModule_EncBaseId, guid_heap_offset)
|| !set_column_value_as_heap_offset(module_cursor, mdtModule_EncId, guid_heap_offset))
{
return false;
}

char const* name = "";
if (1 != md_set_column_value_as_utf8(module_cursor, mdtModule_Name, 1, &name))
if (!md_set_column_value_as_utf8(module_cursor, mdtModule_Name, name))
return false;

// Mark that we're done adding the Module row.
md_commit_row_add(module_cursor);

@@ -286,19 +286,19 @@ static bool initialize_minimal_table_rows(mdcxt_t* cxt)
return false;

uint32_t flags = 0;
if (1 != md_set_column_value_as_constant(global_type_cursor, mdtTypeDef_Flags, 1, &flags))
if (!md_set_column_value_as_constant(global_type_cursor, mdtTypeDef_Flags, flags))
return false;

char const* global_type_name = "<Module>"; // Defined in ECMA-335 II.10.8
if (1 != md_set_column_value_as_utf8(global_type_cursor, mdtTypeDef_TypeName, 1, &global_type_name))
if (!md_set_column_value_as_utf8(global_type_cursor, mdtTypeDef_TypeName, global_type_name))
return false;

char const* namespace = "";
if (1 != md_set_column_value_as_utf8(global_type_cursor, mdtTypeDef_TypeNamespace, 1, &namespace))
if (!md_set_column_value_as_utf8(global_type_cursor, mdtTypeDef_TypeNamespace, namespace))
return false;

mdToken nil_typedef = CreateTokenType(mdtid_TypeDef);
if (1 != md_set_column_value_as_token(global_type_cursor, mdtTypeDef_Extends, 1, &nil_typedef))
if (!md_set_column_value_as_token(global_type_cursor, mdtTypeDef_Extends, nil_typedef))
return false;

// Mark that we're done adding the TypeDef row.
@@ -326,7 +326,7 @@ mdhandle_t md_create_new_handle()
mdcxt_t* pcxt = allocate_full_context(&cxt);
if (pcxt == NULL)
return NULL;

if (!initialize_minimal_table_rows(pcxt))
{
free(pcxt);
@@ -451,7 +451,7 @@ static bool dump_table_rows(mdtable_t* table)
assert(table->column_count <= ARRAY_SIZE(to_get));
uint32_t raw_values[ARRAY_SIZE(to_get)];

#define IF_NOT_ONE_REPORT_RAW(exp) if (1 != (exp)) { printf("Invalid (%u) [%#x]|", j, raw_values[j]); continue; }
#define IF_NOT_REPORT_RAW(exp) if (!(exp)) { printf("Invalid (%u) [%#x]|", j, raw_values[j]); continue; }
#define IF_INVALID_BLOB_REPORT_RAW(parse_fn, handle_or_cursor, blob_type, result_buf, result_buf_len) \
{ \
result_buf = NULL; \
@@ -478,12 +478,12 @@ static bool dump_table_rows(mdtable_t* table)
{
if (table->column_details[j] & mdtc_hstring)
{
IF_NOT_ONE_REPORT_RAW(md_get_column_value_as_utf8(cursor, IDX(j), 1, &str));
IF_NOT_REPORT_RAW(md_get_column_value_as_utf8(cursor, IDX(j), &str));
printf("'%s' [%#x]|", str, raw_values[j]);
}
else if (table->column_details[j] & mdtc_hguid)
{
IF_NOT_ONE_REPORT_RAW(md_get_column_value_as_guid(cursor, IDX(j), 1, &guid));
IF_NOT_REPORT_RAW(md_get_column_value_as_guid(cursor, IDX(j), &guid));
printf("{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x} [%#x]|",
guid.data1, guid.data2, guid.data3,
guid.data4[0], guid.data4[1],
@@ -497,8 +497,8 @@ static bool dump_table_rows(mdtable_t* table)
#ifdef DNMD_PORTABLE_PDB
if (table->table_id == mdtid_Document && col == mdtDocument_Name)
{
IF_NOT_ONE_REPORT_RAW(md_get_column_value_as_blob(cursor, col, 1, &blob, &blob_len));
IF_NOT_REPORT_RAW(md_get_column_value_as_blob(cursor, col, &blob, &blob_len));

char* document_name;
size_t name_len;
IF_INVALID_BLOB_REPORT_RAW(md_parse_document_name, table->cxt, "DocumentName", document_name, name_len);
@@ -508,8 +508,8 @@ static bool dump_table_rows(mdtable_t* table)
}
else if (table->table_id == mdtid_MethodDebugInformation && col == mdtMethodDebugInformation_SequencePoints)
{
IF_NOT_ONE_REPORT_RAW(md_get_column_value_as_blob(cursor, col, 1, &blob, &blob_len));
IF_NOT_REPORT_RAW(md_get_column_value_as_blob(cursor, col, &blob, &blob_len));

if (blob_len == 0)
{
printf("Empty SequencePoints: Offset: %zu (len: %u) [%#x]|", (blob - table->cxt->blob_heap.ptr), blob_len, raw_values[j]);
@@ -556,15 +556,15 @@ static bool dump_table_rows(mdtable_t* table)
assert(!"Invalid sequence point record kind.");
}
}

printf(" } [%#x]|", raw_values[j]);

free(sequence_points);
continue;
}
else if (table->table_id == mdtid_LocalConstant && col == mdtLocalConstant_Signature)
{
IF_NOT_ONE_REPORT_RAW(md_get_column_value_as_blob(cursor, col, 1, &blob, &blob_len));
IF_NOT_REPORT_RAW(md_get_column_value_as_blob(cursor, col, &blob, &blob_len));
md_local_constant_sig_t* local_constant_sig;
size_t local_constant_sig_len;
IF_INVALID_BLOB_REPORT_RAW(md_parse_local_constant_sig, table->cxt, "LocalConstantSig", local_constant_sig, local_constant_sig_len);
@@ -591,14 +591,14 @@ static bool dump_table_rows(mdtable_t* table)
assert(!"Invalid constant kind.");
}
printf("Value Offset: %zu (len: %zu) [%#x]|", local_constant_sig->value_blob - table->cxt->blob_heap.ptr, local_constant_sig->value_len, raw_values[j]);

free(local_constant_sig);
continue;
}
else if (table->table_id == mdtid_ImportScope && col == mdtImportScope_Imports)
{
IF_NOT_ONE_REPORT_RAW(md_get_column_value_as_blob(cursor, col, 1, &blob, &blob_len));
IF_NOT_REPORT_RAW(md_get_column_value_as_blob(cursor, col, &blob, &blob_len));

if (blob_len == 0)
{
printf("Empty Imports: Offset: %zu (len: %u) [%#x]|", (blob - table->cxt->blob_heap.ptr), blob_len, raw_values[j]);
@@ -651,30 +651,30 @@ static bool dump_table_rows(mdtable_t* table)
break;
}
}

printf(" } [%#x]|", raw_values[j]);

free(imports);
continue;
}
#endif
IF_NOT_ONE_REPORT_RAW(md_get_column_value_as_blob(cursor, col, 1, &blob, &blob_len));
IF_NOT_REPORT_RAW(md_get_column_value_as_blob(cursor, col, &blob, &blob_len));
printf("Offset: %zu (len: %u) [%#x]|", (blob - table->cxt->blob_heap.ptr), blob_len, raw_values[j]);
}
else if (table->column_details[j] & mdtc_hus)
{
IF_NOT_ONE_REPORT_RAW(md_get_column_value_as_userstring(cursor, IDX(j), 1, &user_string));
IF_NOT_REPORT_RAW(md_get_column_value_as_userstring(cursor, IDX(j), &user_string));
printf("UTF-16 string (%u bytes) [%#x]|", user_string.str_bytes, raw_values[j]);
}
else if (table->column_details[j] & (mdtc_idx_table | mdtc_idx_coded))
{
IF_NOT_ONE_REPORT_RAW(md_get_column_value_as_token(cursor, IDX(j), 1, &tk));
IF_NOT_REPORT_RAW(md_get_column_value_as_token(cursor, IDX(j), &tk));
printf("0x%08x (mdToken) [%#x]|", tk, raw_values[j]);
}
else
{
assert(table->column_details[j] & mdtc_constant);
IF_NOT_ONE_REPORT_RAW(md_get_column_value_as_constant(cursor, IDX(j), 1, &constant));
IF_NOT_REPORT_RAW(md_get_column_value_as_constant(cursor, IDX(j), &constant));
printf("0x%08x [%#x]|", constant, raw_values[j]);
}
}
@@ -683,7 +683,7 @@ static bool dump_table_rows(mdtable_t* table)
return false;
}
printf("\n");
#undef IF_NOT_ONE_REPORT_RAW
#undef IF_NOT_REPORT_RAW
#undef IF_INVALID_BLOB_REPORT_RAW

return true;
@@ -803,7 +803,7 @@ static size_t get_stream_header_and_contents_size(char const* heap_name, size_t
static size_t get_table_stream_size(mdcxt_t* cxt)
{
// II.24.2.6 #~ stream
size_t const table_stream_header_size =
size_t const table_stream_header_size =
+ sizeof(uint32_t) // Reserved
+ sizeof(uint8_t) // MajorVersion
+ sizeof(uint8_t) // MinorVersion
@@ -813,9 +813,9 @@ static size_t get_table_stream_size(mdcxt_t* cxt)
+ sizeof(uint64_t) // Sorted tables
// Rows and Tables entries are both variable length and calculated below
;

size_t save_size = table_stream_header_size;

for (uint8_t i = 0; i < MDTABLE_MAX_COUNT; ++i)
{
if (cxt->tables[i].cxt != NULL && cxt->tables[i].row_count != 0)
@@ -824,15 +824,15 @@ static size_t get_table_stream_size(mdcxt_t* cxt)
save_size += cxt->tables[i].data.size; // Table data
}
}

return save_size;
}

static size_t get_image_size(mdcxt_t* cxt)
{
{
if (cxt->editor == NULL)
return cxt->raw_metadata.size;

// II.24.2.1 Metadata Root size
size_t const image_header_size =
sizeof(uint32_t) // Signature
@@ -844,7 +844,7 @@ static size_t get_image_size(mdcxt_t* cxt)
+ sizeof(uint16_t) // Flags
+ sizeof(uint16_t) // Streams (number of streams)
;

size_t save_size = image_header_size;

if (cxt->blob_heap.size != 0)
@@ -858,7 +858,7 @@ static size_t get_image_size(mdcxt_t* cxt)

if (cxt->context_flags & mdc_minimal_delta)
save_size += get_stream_header_and_contents_size("#JTD", 0);

// All names of the tables stream are the same length,
// so pick the one in the standard.
save_size += get_stream_header_and_contents_size("#~", get_table_stream_size(cxt));
@@ -918,7 +918,7 @@ bool md_write_to_buffer(mdhandle_t handle, uint8_t* buffer, size_t* len)
memcpy(buffer, cxt->raw_metadata.ptr, cxt->raw_metadata.size);
return true;
}

if (buffer == NULL || full_buffer_len < image_size)
{
*len = image_size;
@@ -934,24 +934,24 @@ bool md_write_to_buffer(mdhandle_t handle, uint8_t* buffer, size_t* len)
{
return false;
}

size_t version_str_len = strlen(cxt->version);
uint32_t version_buf_len = align_to((uint32_t)version_str_len + 1, 4);

if (!write_u32(&buffer, &remaining_buffer_len, (uint32_t)version_buf_len))
return false;

if (remaining_buffer_len < version_buf_len)
return false;

memcpy(buffer, cxt->version, version_str_len + 1);
// Pad the version string to a 4-byte boundary.
memset(buffer + version_str_len + 1, 0, version_buf_len - version_str_len - 1);
advance_output_stream(&buffer, &remaining_buffer_len, version_buf_len);

if (!write_u16(&buffer, &remaining_buffer_len, cxt->flags))
return false;

uint16_t stream_count = 0;
if (cxt->blob_heap.size != 0)
stream_count++;
@@ -983,7 +983,7 @@ bool md_write_to_buffer(mdhandle_t handle, uint8_t* buffer, size_t* len)
valid_tables |= (1ULL << i);
if (cxt->tables[i].is_sorted)
sorted_tables |= (1ULL << i);

// Indirect tables only exist in images that use the uncompresed stream.
if (table_is_indirect_table((mdtable_id_t)i))
tables_stream_name = "#-";
@@ -992,7 +992,7 @@ bool md_write_to_buffer(mdhandle_t handle, uint8_t* buffer, size_t* len)

// The tables stream is always included.
stream_count++;

if (!write_u16(&buffer, &remaining_buffer_len, stream_count))
return false;

@@ -1011,7 +1011,7 @@ bool md_write_to_buffer(mdhandle_t handle, uint8_t* buffer, size_t* len)
mddata_t offset_space;
if (!write_stream_header("#JTD", 0, &offset_space, &buffer, &remaining_buffer_len))
return false;

// Set the stream offset to the location of the stream header.
// There's no content in this stream, but the offset must be valid.
write_u32(&offset_space.ptr, &offset_space.size, (uint32_t)((uint8_t*)offset_space.ptr - buffer_start));
42 changes: 32 additions & 10 deletions src/dnmd/internal.h
Original file line number Diff line number Diff line change
@@ -20,6 +20,18 @@ typedef size_t rsize_t;

#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*a))

#ifndef NDEBUG
#define ASSERT_ASSUME(x) assert(x)
#elif defined(_MSC_VER)
#define ASSERT_ASSUME(x) __assume(x)
#elif defined(__clang__)
#define ASSERT_ASSUME(x) __builtin_assume(x)
#elif defined(__GNUC__)
#define ASSERT_ASSUME(x) do { if (!(x)) __builtin_unreachable(); } while (0)
#else
#define ASSERT_ASSUME(x) (void)(x)
#endif

// Mutable data
typedef struct mddata__
{
@@ -349,29 +361,39 @@ static col_index_t index_to_col(uint8_t idx, mdtable_id_t table_id)
// Copy data from a cursor to one row to a cursor to another row.
bool copy_cursor(mdcursor_t dest, mdcursor_t src);

// Raw table data access

// Single column access
typedef struct access_cxt__
{
mdtable_t* table;
mdtcol_t col_details;
uint8_t const* start;
uint8_t const* data;
uint8_t* writable_data;
} access_cxt_t;

bool create_access_context(mdcursor_t* cursor, col_index_t col_idx, bool make_writable, access_cxt_t* rcxt);
bool write_column_data(access_cxt_t* acxt, uint32_t data);
bool read_column_data(access_cxt_t* acxt, uint32_t* data);

// Raw bulk table access
typedef struct bulk_access_cxt__
{
mdtable_t* table;
mdtcol_t col_details;
uint8_t const* start;
uint8_t const* data;
uint8_t const* end;
size_t data_len;
uint32_t data_len_col;
uint32_t next_row_stride;
} access_cxt_t;
} bulk_access_cxt_t;

bool create_access_context(mdcursor_t* cursor, col_index_t col_idx, uint32_t row_count, bool make_writable, access_cxt_t* acxt);
bool read_column_data(access_cxt_t* acxt, uint32_t* data);
bool write_column_data(access_cxt_t* acxt, uint32_t data);
bool next_row(access_cxt_t* acxt);
bool create_bulk_access_context(mdcursor_t* cursor, col_index_t col_idx, uint32_t row_count, bulk_access_cxt_t* acxt);
bool read_column_data_and_advance(bulk_access_cxt_t* acxt, uint32_t* data);
bool next_row(bulk_access_cxt_t* acxt);

// Internal functions used to read/write columns with minimal validation.
int32_t get_column_value_as_heap_offset(mdcursor_t c, col_index_t col_idx, uint32_t out_length, uint32_t* offset);
int32_t set_column_value_as_heap_offset(mdcursor_t c, col_index_t col_idx, uint32_t in_length, uint32_t* offset);
bool get_column_value_as_heap_offset(mdcursor_t c, col_index_t col_idx, uint32_t* offset);
bool set_column_value_as_heap_offset(mdcursor_t c, col_index_t col_idx, uint32_t offset);

//
// Manipulation of bits
6 changes: 3 additions & 3 deletions src/dnmd/pdb_blobs.c
Original file line number Diff line number Diff line change
@@ -93,7 +93,7 @@ static uint32_t get_num_sequence_points(mdcursor_t method_debug_information, uin
return UINT32_MAX;

mdcursor_t document;
if (1 != md_get_column_value_as_cursor(method_debug_information, mdtMethodDebugInformation_Document, 1, &document))
if (!md_get_column_value_as_cursor(method_debug_information, mdtMethodDebugInformation_Document, &document))
return UINT32_MAX;

if (CursorNull(&document) && !decompress_u32(&blob, &blob_len, &ignored)) // header InitialDocument
@@ -175,7 +175,7 @@ md_blob_parse_result_t md_parse_sequence_points(
return mdbpr_InvalidBlob;

mdcursor_t document;
if (1 != md_get_column_value_as_cursor(method_debug_information, mdtMethodDebugInformation_Document, 1, &document))
if (!md_get_column_value_as_cursor(method_debug_information, mdtMethodDebugInformation_Document, &document))
return mdbpr_InvalidBlob;

// Create a "null" cursor to default-initialize the document field.
@@ -702,4 +702,4 @@ md_blob_parse_result_t md_parse_imports(mdhandle_t handle, uint8_t const* blob,
}
}
return mdbpr_Success;
}
}
411 changes: 216 additions & 195 deletions src/dnmd/query.c

Large diffs are not rendered by default.

428 changes: 192 additions & 236 deletions src/dnmd/write.c

Large diffs are not rendered by default.

33 changes: 18 additions & 15 deletions src/inc/dnmd.h
Original file line number Diff line number Diff line change
@@ -441,20 +441,23 @@ typedef enum
} col_index_t;

// Query row's column values
// The returned number represents the number of valid cursor(s) for indexing.
int32_t md_get_column_value_as_token(mdcursor_t c, col_index_t col_idx, uint32_t out_length, mdToken* tk);
int32_t md_get_column_value_as_cursor(mdcursor_t c, col_index_t col_idx, uint32_t out_length, mdcursor_t* cursor);
bool md_get_column_value_as_token(mdcursor_t c, col_index_t col_idx, mdToken* tk);
bool md_get_column_value_as_cursor(mdcursor_t c, col_index_t col_idx, mdcursor_t* cursor);
// Resolve the column to a cursor and a range based on the run/list pattern in tables.
// The run continues to the smaller of:
// * the last row of the target table
// * the next run in the target table, found by inspecting the column value of the next row in the current table.
// See md_find_token_of_range_element() for mapping elements in the other direction.
bool md_get_column_value_as_range(mdcursor_t c, col_index_t col_idx, mdcursor_t* cursor, uint32_t* count);
int32_t md_get_column_value_as_constant(mdcursor_t c, col_index_t col_idx, uint32_t out_length, uint32_t* constant);
int32_t md_get_column_value_as_utf8(mdcursor_t c, col_index_t col_idx, uint32_t out_length, char const** str);
int32_t md_get_column_value_as_userstring(mdcursor_t c, col_index_t col_idx, uint32_t out_length, mduserstring_t* strings);
int32_t md_get_column_value_as_blob(mdcursor_t c, col_index_t col_idx, uint32_t out_length, uint8_t const** blob, uint32_t* blob_len);
int32_t md_get_column_value_as_guid(mdcursor_t c, col_index_t col_idx, uint32_t out_length, mdguid_t* guid);
bool md_get_column_value_as_constant(mdcursor_t c, col_index_t col_idx, uint32_t* constant);
bool md_get_column_value_as_utf8(mdcursor_t c, col_index_t col_idx, char const** str);
bool md_get_column_value_as_userstring(mdcursor_t c, col_index_t col_idx, mduserstring_t* strings);
bool md_get_column_value_as_blob(mdcursor_t c, col_index_t col_idx, uint8_t const** blob, uint32_t* blob_len);
bool md_get_column_value_as_guid(mdcursor_t c, col_index_t col_idx, mdguid_t* guid);

// Read a table or coded index column from multiple rows and return the values as an array of tokens.
// The number of rows read is returned by the function. A '-1' return value indicates an error.
int32_t md_get_many_rows_column_value_as_token(mdcursor_t c, col_index_t col_idx, uint32_t out_length, mdToken* tokens);

// Return the raw column values for the row. Unlike the md_get_column_value_as_* APIs, the returned values
// are in their raw form.
@@ -497,13 +500,13 @@ bool md_resolve_indirect_cursor(mdcursor_t c, mdcursor_t* target);

// Set row's column values
// The returned number represents the number of rows updated.
int32_t md_set_column_value_as_token(mdcursor_t c, col_index_t col, uint32_t in_length, mdToken const* tk);
int32_t md_set_column_value_as_cursor(mdcursor_t c, col_index_t col, uint32_t in_length, mdcursor_t const* cursor);
int32_t md_set_column_value_as_constant(mdcursor_t c, col_index_t col_idx, uint32_t in_length, uint32_t const* constant);
int32_t md_set_column_value_as_utf8(mdcursor_t c, col_index_t col_idx, uint32_t in_length, char const* const* str);
int32_t md_set_column_value_as_blob(mdcursor_t c, col_index_t col_idx, uint32_t in_length, uint8_t const* const* blob, uint32_t const* blob_len);
int32_t md_set_column_value_as_guid(mdcursor_t c, col_index_t col_idx, uint32_t in_length, mdguid_t const* guid);
int32_t md_set_column_value_as_userstring(mdcursor_t c, col_index_t col_idx, uint32_t in_length, char16_t const* const* userstring);
bool md_set_column_value_as_token(mdcursor_t c, col_index_t col, mdToken tk);
bool md_set_column_value_as_cursor(mdcursor_t c, col_index_t col, mdcursor_t cursor);
bool md_set_column_value_as_constant(mdcursor_t c, col_index_t col_idx, uint32_t constant);
bool md_set_column_value_as_utf8(mdcursor_t c, col_index_t col_idx, char const* str);
bool md_set_column_value_as_blob(mdcursor_t c, col_index_t col_idx, uint8_t const* blob, uint32_t blob_len);
bool md_set_column_value_as_guid(mdcursor_t c, col_index_t col_idx, mdguid_t guid);
bool md_set_column_value_as_userstring(mdcursor_t c, col_index_t col_idx, char16_t const* userstring);

// Create a new row logically before the row specified by the cursor.
// If the given row is in a table that is a target of a list column, this function will return false.
2 changes: 1 addition & 1 deletion src/interfaces/dispenser.cpp
Original file line number Diff line number Diff line change
@@ -85,7 +85,7 @@ namespace
if (FAILED(hr))
return hr;

if (1 != md_set_column_value_as_guid(moduleCursor, mdtModule_Mvid, 1, &mvid))
if (!md_set_column_value_as_guid(moduleCursor, mdtModule_Mvid, mvid))
return E_OUTOFMEMORY;

dncp::com_ptr<ControllingIUnknown> obj;
7 changes: 6 additions & 1 deletion src/interfaces/hcorenum.cpp
Original file line number Diff line number Diff line change
@@ -226,8 +226,13 @@ HRESULT HCORENUMImpl::ReadOneToken(mdToken& rToken, uint32_t& count) noexcept

if (_type == HCORENUMType::Table)
{
if (!md_cursor_to_token(currData->Table.Current, &rToken))
mdcursor_t current;
if (!md_resolve_indirect_cursor(currData->Table.Current, &current))
return CLDB_E_FILE_CORRUPT;

if (!md_cursor_to_token(current, &rToken))
return S_FALSE;

(void)md_cursor_next(&currData->Table.Current);
}
else
512 changes: 257 additions & 255 deletions src/interfaces/importhelpers.cpp

Large diffs are not rendered by default.

828 changes: 414 additions & 414 deletions src/interfaces/metadataemit.cpp

Large diffs are not rendered by default.

294 changes: 147 additions & 147 deletions src/interfaces/metadataimport.cpp

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/interfaces/options.cpp
Original file line number Diff line number Diff line change
@@ -8,4 +8,4 @@
EXTERN_GUID(name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8)

// Define the IMetaDataDispenserEx option Guids here. They're declared in cor.h
MIDL_DEFINE_GUID(GUID, MetaDataSetUpdate, 0x2eee315c, 0xd7db, 0x11d2, 0x9f, 0x80, 0x0, 0xc0, 0x4f, 0x79, 0xa0, 0xa3);
MIDL_DEFINE_GUID(GUID, MetaDataSetUpdate, 0x2eee315c, 0xd7db, 0x11d2, 0x9f, 0x80, 0x0, 0xc0, 0x4f, 0x79, 0xa0, 0xa3);
67 changes: 36 additions & 31 deletions src/interfaces/signatures.cpp
Original file line number Diff line number Diff line change
@@ -35,7 +35,7 @@ namespace
struct signature_element_part_tag
{
};

struct raw_byte_tag final : signature_element_part_tag
{
};
@@ -120,7 +120,7 @@ namespace
case ELEMENT_TYPE_PINNED:
signature = WalkSignatureElement(signature, callback);
break;

case ELEMENT_TYPE_VAR:
case ELEMENT_TYPE_MVAR:
{
@@ -203,11 +203,11 @@ namespace
}
}

malloc_span<std::uint8_t> GetMethodDefSigFromMethodRefSig(span<uint8_t> methodRefSig)
void GetMethodDefSigFromMethodRefSig(span<uint8_t> methodRefSig, inline_span<uint8_t>& methodDefSig)
{
assert(methodRefSig.size() > 0);
// We don't need to do anything with the various elements of the signature,
// we just need to know how many parameters are before the sentinel.
// we just need to know how many parameters are before the sentinel.
span<uint8_t> signature = methodRefSig;
uint8_t const callingConvention = signature[0];
signature = slice(signature, 1);
@@ -217,9 +217,9 @@ malloc_span<std::uint8_t> GetMethodDefSigFromMethodRefSig(span<uint8_t> methodRe
// parameter list.
if ((callingConvention & IMAGE_CEE_CS_CALLCONV_MASK) != IMAGE_CEE_CS_CALLCONV_VARARG)
{
malloc_span<uint8_t> methodDefSig{ (uint8_t*)std::malloc(methodRefSig.size()), methodRefSig.size() };
std::memcpy(methodDefSig, methodRefSig, methodRefSig.size());
return methodDefSig;
methodDefSig.resize(methodRefSig.size());
std::copy(methodRefSig.begin(), methodRefSig.end(), methodDefSig.begin());
return;
}

uint32_t genericParameterCount = 0;
@@ -249,7 +249,7 @@ malloc_span<std::uint8_t> GetMethodDefSigFromMethodRefSig(span<uint8_t> methodRe

signature = WalkSignatureElement(signature, [](std::intmax_t, signature_element_part_tag) { });
}

// Now that we know the number of parameters, we can copy the MethodDefSig portion of the signature
// and update the parameter count.
// We need to account for the fact that the parameter count may be encoded with less bytes
@@ -259,29 +259,29 @@ malloc_span<std::uint8_t> GetMethodDefSigFromMethodRefSig(span<uint8_t> methodRe
ULONG originalParamCountCompressedSize = CorSigCompressData(originalParameterCount, buffer);
ULONG newParamCountCompressedSize = CorSigCompressData(i, buffer);
span<uint8_t> compressedNewParamCount = { buffer, newParamCountCompressedSize };

// The MethodDefSig length will be the length of the original signature up to the ELEMENT_TYPE_SENTINEL value,
// minus the difference in the compressed size of the original parameter count and the new parameter count, if any.
size_t methodDefSigBufferLength = methodRefSig.size() - signature.size() - originalParamCountCompressedSize + newParamCountCompressedSize;
malloc_span<uint8_t> methodDefSigBuffer{ (uint8_t*)std::malloc(methodDefSigBufferLength), methodDefSigBufferLength };
methodDefSig.resize(methodDefSigBufferLength);

// Copy over the signature into the new buffer.
// In case the parameter count was encoded with less bytes, we need to account for that
// and copy the signature piece by piece.
size_t offset = 0;
methodDefSigBuffer[offset++] = callingConvention;
methodDefSig[offset++] = callingConvention;
if ((callingConvention & IMAGE_CEE_CS_CALLCONV_GENERIC) == IMAGE_CEE_CS_CALLCONV_GENERIC)
{
offset += CorSigCompressData(genericParameterCount, methodDefSigBuffer + offset);
offset += CorSigCompressData(genericParameterCount, methodDefSig + offset);
}
std::memcpy(methodDefSigBuffer + offset, compressedNewParamCount, newParamCountCompressedSize);
std::memcpy(methodDefSig + offset, compressedNewParamCount, newParamCountCompressedSize);
offset += newParamCountCompressedSize;

// Now that we've re-written the parameter count, we can copy the rest of the signature directly from the MethodRefSig
assert(returnTypeAndParameters.size() >= methodDefSigBufferLength - offset);
std::memcpy(methodDefSigBuffer + offset, returnTypeAndParameters, methodDefSigBufferLength - offset);
std::memcpy(methodDefSig + offset, returnTypeAndParameters, methodDefSigBufferLength - offset);

return methodDefSigBuffer;
return;
}

// Define a function object that enables us to combine multiple lambdas into a single overload set.
@@ -298,7 +298,7 @@ namespace

// Define a perfectly-forwarding operator() that will call the function object with the given arguments.
template <typename... Args>
auto operator()(Args&&... args) const
auto operator()(Args&&... args) const
-> decltype(std::declval<T>()(std::forward<Args>(args)...))
{
return _t(std::forward<Args>(args)...);
@@ -312,7 +312,7 @@ namespace
{
using Overload<T>::operator();
using Overload<Ts...>::operator();

Overload(T&& t, Ts&&... ts)
:Overload<T>(std::forward<T>(t)),
Overload<Ts...>(std::forward<Ts>(ts)...)
@@ -335,7 +335,7 @@ HRESULT ImportSignatureIntoModule(
mdhandle_t destinationModule,
span<const uint8_t> signature,
std::function<void(mdcursor_t)> onRowAdded,
malloc_span<uint8_t>& importedSignature)
inline_span<uint8_t>& importedSignature)
{
HRESULT hr;
// We are going to copy over the signature and replace the tokens from the source module in the signature
@@ -371,7 +371,7 @@ HRESULT ImportSignatureIntoModule(
destinationModule,
onRowAdded,
&token);

// We can safely continue walking the signature even if we failed to import the token.
// We'll return the failure code when we're done.
if (FAILED(localHR))
@@ -434,14 +434,17 @@ HRESULT ImportSignatureIntoModule(
return hr;
}

uint8_t* buffer = (uint8_t*)std::malloc(importedSignatureBuffer.size());
if (buffer == nullptr)
try
{
importedSignature.resize(importedSignature.size());
}
catch (std::bad_alloc const&)
{
return E_OUTOFMEMORY;
}

std::memcpy(buffer, importedSignatureBuffer.data(), importedSignatureBuffer.size());
importedSignature = { buffer, importedSignatureBuffer.size() };
std::copy(importedSignatureBuffer.begin(), importedSignatureBuffer.end(), importedSignature.begin());

return S_OK;
}

@@ -453,7 +456,7 @@ HRESULT ImportTypeSpecBlob(
mdhandle_t destinationModule,
span<const uint8_t> typeSpecBlob,
std::function<void(mdcursor_t)> onRowAdded,
malloc_span<uint8_t>& importedTypeSpecBlob)
inline_span<uint8_t>& importedTypeSpecBlob)
{
std::vector<uint8_t> importedTypeSpecBlobBuffer;
// Our imported blob will likely be a very similar size to the original blob.
@@ -489,7 +492,7 @@ HRESULT ImportTypeSpecBlob(
destinationModule,
onRowAdded,
&token);

// We can safely continue walking the signature even if we failed to import the token.
// We'll return the failure code when we're done.
if (FAILED(localHR))
@@ -513,13 +516,15 @@ HRESULT ImportTypeSpecBlob(
return E_INVALIDARG;
}

uint8_t* buffer = (uint8_t*)std::malloc(importedTypeSpecBlobBuffer.size());
if (buffer == nullptr)
try
{
importedTypeSpecBlob.resize(importedTypeSpecBlobBuffer.size());
}
catch (std::bad_alloc const&)
{
return E_OUTOFMEMORY;
}

std::memcpy(buffer, importedTypeSpecBlobBuffer.data(), importedTypeSpecBlobBuffer.size());
importedTypeSpecBlob = { buffer, importedTypeSpecBlobBuffer.size() };
std::copy(importedTypeSpecBlobBuffer.begin(), importedTypeSpecBlobBuffer.end(), importedTypeSpecBlob.begin());
return S_OK;
}
}
119 changes: 116 additions & 3 deletions src/interfaces/signatures.hpp
Original file line number Diff line number Diff line change
@@ -6,10 +6,123 @@

#include <external/cor.h>

#include <array>
#include <cstdint>
#include <functional>
#include <cassert>

malloc_span<uint8_t> GetMethodDefSigFromMethodRefSig(span<uint8_t> methodRefSig);
/// @brief A span that that supports owning a specified number of elements in itself.
/// @tparam T The type of the elements in the span.
/// @tparam NumInlineElements The number of elements to store in the span itself.
template <typename T, size_t NumInlineElements>
struct base_inline_span : public span<T>
{
base_inline_span() : span<T>()
{
this->_ptr = _storage.data();
}

base_inline_span(size_t size) : span<T>()
{
this->_ptr = size > NumInlineElements ? base_inline_span::allocate_noninline_memory(size) : _storage.data();
this->_size = size;
}

base_inline_span(base_inline_span&& other)
{
*this = std::move(other);
}

base_inline_span& operator=(base_inline_span&& other) noexcept
{
if (this->size() > NumInlineElements)
{
base_inline_span::free_noninline_memory(this->_ptr, this->size());
this->_ptr = nullptr;
}

if (other.size() > NumInlineElements)
{
this->_ptr = other._ptr;
other._ptr = nullptr;
}
else
{
std::copy(other.begin(), other.end(), _storage.begin());
this->_ptr = _storage.data();
this->_size = other._size;
}

return *this;
}

void resize(size_t newSize)
{
if (this->size() > NumInlineElements && newSize < NumInlineElements)
{
// Transitioning from a non-inline buffer to the inline buffer.
std::copy(this->begin(), this->begin() + newSize, _storage.begin());
base_inline_span::free_noninline_memory(this->_ptr, this->size());
this->_ptr = _storage.data();
}
else if (this->size() <= NumInlineElements && newSize <= NumInlineElements)
{
// We're staying within the inline buffer, so just update the size.
this->_size = newSize;
}
else if (this->size() > NumInlineElements && newSize < this->size())
{
// Shrinking the buffer, but still keeping it as a non-inline buffer.
this->_size = newSize;
}
else
{
// Growing the buffer from the inline buffer to a non-inline buffer.
assert(this->size() <= NumInlineElements && newSize > NumInlineElements);
T* newPtr = base_inline_span::allocate_noninline_memory(this->size());
std::copy(this->begin(), this->end(), newPtr);
this->_ptr = newPtr;
this->_size = newSize;
}
}

~base_inline_span()
{
if (this->size() > NumInlineElements)
{
assert(this->_ptr != _storage.data());
base_inline_span::free_noninline_memory(this->_ptr, this->size());
this->_ptr = nullptr;
}
else
{
assert(this->_ptr == _storage.data());
}
}

private:
std::array<T, NumInlineElements> _storage;

static T* allocate_noninline_memory(size_t numElements)
{
assert(numElements > NumInlineElements);
return new T[numElements];
}

static void free_noninline_memory(T* ptr, size_t numElements)
{
UNREFERENCED_PARAMETER(numElements);
assert(numElements > NumInlineElements);
delete[] ptr;
}
};

/// @brief An span with inline storage for up to 64 bytes.
/// @tparam T The element type of the span.
template <typename T>
using inline_span = base_inline_span<T, 64 / sizeof(T)>;

void GetMethodDefSigFromMethodRefSig(span<uint8_t> methodRefSig, inline_span<uint8_t>& methodDefSig);

// Import a signature from one set of module and assembly metadata into another set of module and assembly metadata.
// The module and assembly metadata for source or destination can be the same metadata.
@@ -29,7 +142,7 @@ HRESULT ImportSignatureIntoModule(
mdhandle_t destinationModule,
span<const uint8_t> signature,
std::function<void(mdcursor_t)> onRowAdded,
malloc_span<uint8_t>& importedSignature);
inline_span<uint8_t>& importedSignature);

// Import a TypeSpecBlob (II.23.2.14) from one set of module and assembly metadata into another set of module and assembly metadata.
HRESULT ImportTypeSpecBlob(
@@ -40,6 +153,6 @@ HRESULT ImportTypeSpecBlob(
mdhandle_t destinationModule,
span<const uint8_t> typeSpecBlob,
std::function<void(mdcursor_t)> onRowAdded,
malloc_span<uint8_t>& importedTypeSpecBlob);
inline_span<uint8_t>& importedTypeSpecBlob);

#endif // _SRC_INTERFACES_SIGNATURES_HPP_

0 comments on commit c42d927

Please sign in to comment.