From be5d10e2b0e8d1a1b307e863790eb16048cf4735 Mon Sep 17 00:00:00 2001 From: jacob Date: Thu, 13 Feb 2025 17:15:23 -0600 Subject: [PATCH] replace byteio with bitbuff --- build.c | 129 +++++--- src/app.c | 10 + src/ase.c | 193 ++++++------ src/bitbuff.c | 750 +++++++++++++++++++++++++++++++++++++++++++++ src/bitbuff.h | 112 +++++++ src/buddy.c | 2 + src/byteio.c | 334 -------------------- src/byteio.h | 96 ------ src/common.h | 6 +- src/config.h | 4 + src/host.c | 130 ++++---- src/memory.c | 12 +- src/memory.h | 8 +- src/scratch.h | 2 +- src/sim.c | 186 +++++------ src/sim.h | 25 +- src/sim_client.c | 46 ++- src/sim_client.h | 19 +- src/sim_ent.c | 45 ++- src/sim_ent.h | 8 +- src/sim_snapshot.c | 84 +++-- src/sim_snapshot.h | 10 +- src/sys_win32.c | 2 +- src/tar.c | 13 +- src/user.c | 48 ++- 25 files changed, 1379 insertions(+), 895 deletions(-) create mode 100644 src/bitbuff.c create mode 100644 src/bitbuff.h delete mode 100644 src/byteio.c delete mode 100644 src/byteio.h diff --git a/build.c b/build.c index ab504760..9d9015ec 100644 --- a/build.c +++ b/build.c @@ -60,7 +60,8 @@ typedef struct StepList StepList; typedef enum StepStatus { StepStatus_None, StepStatus_Success, - StepStatus_Failure + StepStatus_Failure, + StepStatus_Skipped } StepStatus; typedef struct Step Step; @@ -195,11 +196,15 @@ String CleanResultOutput(Arena *arena, String output) typedef struct BuildStepSimpleCommandArg BuildStepSimpleCommandArg; struct BuildStepSimpleCommandArg { String cmd; + AtomicI32 *skip_flag; + AtomicI32 *failure_flag; }; typedef struct BuildStepMsvcCompileCommandArg BuildStepMsvcCompileCommandArg; struct BuildStepMsvcCompileCommandArg { String cmd; + AtomicI32 *skip_flag; + AtomicI32 *failure_flag; D_Tag depfile_dependent; D_Tag output_depfile; D_TagList depfile_force_includes; @@ -209,28 +214,42 @@ void BuildStepSimpleCommand(void *arg_raw) { TempArena scratch = ScratchBeginNoConflict(); Step *s = arg_raw; - BuildStepSimpleCommandArg *arg = s->arg; - SH_CommandResult result = RunCommand(scratch.arena, arg->cmd); - - { - OS_Lock res_lock = OS_MutexLockE(&s->res_mutex); + AtomicI32 *skip_flag = arg->skip_flag; + AtomicI32 *failure_flag = arg->failure_flag; + if (!skip_flag || !AtomicI32Eval(skip_flag)) { + SH_CommandResult result = RunCommand(scratch.arena, arg->cmd); String result_output_cleaned = CleanResultOutput(scratch.arena, result.output); - if (result_output_cleaned.len > 0) { - OS_Lock sl_lock = OS_MutexLockE(&sl->mutex); - { - s->res_output.text = ArenaPushArrayNoZero(&sl->arena, Byte, result_output_cleaned.len); + { + OS_Lock res_lock = OS_MutexLockE(&s->res_mutex); + if (result_output_cleaned.len > 0) { + OS_Lock sl_lock = OS_MutexLockE(&sl->mutex); + { + s->res_output.text = ArenaPushArrayNoZero(&sl->arena, Byte, result_output_cleaned.len); + } + OS_MutexUnlock(&sl_lock); + s->res_output.len = result_output_cleaned.len; + MemoryCopy(s->res_output.text, result_output_cleaned.text, result_output_cleaned.len); } - OS_MutexUnlock(&sl_lock); - s->res_output.len = result_output_cleaned.len; - MemoryCopy(s->res_output.text, result_output_cleaned.text, result_output_cleaned.len); + s->res_status = result.error ? StepStatus_Failure : StepStatus_Success; + if (result.error && failure_flag) { + AtomicI32EvalExchange(failure_flag, 1); + } + OS_ConditionVariableBroadcast(&s->res_cv); + OS_MutexUnlock(&res_lock); + } + } else { + OS_Lock res_lock = OS_MutexLockE(&s->res_mutex); + s->res_status = StepStatus_Skipped; + if (failure_flag) { + AtomicI32EvalExchange(failure_flag, 1); } - s->res_status = result.error ? StepStatus_Failure : StepStatus_Success; OS_ConditionVariableBroadcast(&s->res_cv); OS_MutexUnlock(&res_lock); } + ScratchEnd(scratch); } @@ -238,28 +257,41 @@ void BuildStepMsvcCompileCommand(void *arg_raw) { TempArena scratch = ScratchBeginNoConflict(); Step *s = arg_raw; - BuildStepMsvcCompileCommandArg *arg = s->arg; - SH_CommandResult result = RunCommand(scratch.arena, arg->cmd); - if (!result.error && !D_IsNil(arg->depfile_dependent) && !D_IsNil(arg->output_depfile)) { - String depfile_data = D_DepfileTextFromMsvcOutput(scratch.arena, arg->depfile_dependent, arg->depfile_force_includes, result.output); - D_ClearWrite(arg->output_depfile, depfile_data); - } - - String result_output_cleaned = CleanResultOutput(scratch.arena, result.output); - { - OS_Lock res_lock = OS_MutexLockE(&s->res_mutex); - if (result_output_cleaned.len > 0) { - OS_Lock sl_lock = OS_MutexLockE(&sl->mutex); - { - s->res_output.text = ArenaPushArrayNoZero(&sl->arena, Byte, result_output_cleaned.len); - } - OS_MutexUnlock(&sl_lock); - s->res_output.len = result_output_cleaned.len; - MemoryCopy(s->res_output.text, result_output_cleaned.text, result_output_cleaned.len); + AtomicI32 *skip_flag = arg->skip_flag; + AtomicI32 *failure_flag = arg->failure_flag; + if (!skip_flag || !AtomicI32Eval(skip_flag)) { + SH_CommandResult result = RunCommand(scratch.arena, arg->cmd); + if (!result.error && !D_IsNil(arg->depfile_dependent) && !D_IsNil(arg->output_depfile)) { + String depfile_data = D_DepfileTextFromMsvcOutput(scratch.arena, arg->depfile_dependent, arg->depfile_force_includes, result.output); + D_ClearWrite(arg->output_depfile, depfile_data); + } + String result_output_cleaned = CleanResultOutput(scratch.arena, result.output); + { + OS_Lock res_lock = OS_MutexLockE(&s->res_mutex); + if (result_output_cleaned.len > 0) { + OS_Lock sl_lock = OS_MutexLockE(&sl->mutex); + { + s->res_output.text = ArenaPushArrayNoZero(&sl->arena, Byte, result_output_cleaned.len); + } + OS_MutexUnlock(&sl_lock); + s->res_output.len = result_output_cleaned.len; + MemoryCopy(s->res_output.text, result_output_cleaned.text, result_output_cleaned.len); + } + s->res_status = result.error ? StepStatus_Failure : StepStatus_Success; + if (result.error && failure_flag) { + AtomicI32EvalExchange(failure_flag, 1); + } + OS_ConditionVariableBroadcast(&s->res_cv); + OS_MutexUnlock(&res_lock); + } + } else { + OS_Lock res_lock = OS_MutexLockE(&s->res_mutex); + s->res_status = StepStatus_Skipped; + if (failure_flag) { + AtomicI32EvalExchange(failure_flag, 1); } - s->res_status = result.error ? StepStatus_Failure : StepStatus_Success; OS_ConditionVariableBroadcast(&s->res_cv); OS_MutexUnlock(&res_lock); } @@ -605,6 +637,7 @@ void OnBuild(StringList cli_args) * Build step: Tar archives * ========================== */ + AtomicI32 tar_success_flag = { 0 }; { AddSyncPoint(); @@ -628,6 +661,7 @@ void OnBuild(StringList cli_args) if (IsDirty(tar_file)) { BuildStepSimpleCommandArg *bs_arg = ArenaPush(&perm, BuildStepSimpleCommandArg); bs_arg->cmd = StringF(&perm, Lit("cd %F && tar cvf %F ."), FmtStr(input_dir.full_path), FmtStr(tar_file.full_path)); + bs_arg->failure_flag = &tar_success_flag; String step_name = StringF(&perm, Lit("%F -> %F"), FmtStr(D_GetName(input_dir)), FmtStr(D_GetName(tar_file))); AddStep(step_name, &BuildStepSimpleCommand, bs_arg); } @@ -638,6 +672,7 @@ void OnBuild(StringList cli_args) * Build step: Compile RC files * ========================== */ + AtomicI32 rc_success_flag = { 0 }; if (PlatformWindows) { AddSyncPoint(); @@ -671,6 +706,8 @@ void OnBuild(StringList cli_args) if (IsDirty(rc_res_file)) { BuildStepSimpleCommandArg *bs_arg = ArenaPush(&perm, BuildStepSimpleCommandArg); bs_arg->cmd = StringF(&perm, rc_compile_args_fmt, FmtStr(rc_res_file.full_path), FmtStr(rc_input_file.full_path)); + bs_arg->skip_flag = &tar_success_flag; + bs_arg->failure_flag = &rc_success_flag; String step_name = StringF(&perm, Lit("%F -> %F"), FmtStr(D_GetName(rc_input_file)), FmtStr(D_GetName(rc_res_file))); AddStep(step_name, &BuildStepSimpleCommand, bs_arg); } @@ -683,6 +720,7 @@ void OnBuild(StringList cli_args) * Build step: Compile pch files * ========================== */ + AtomicI32 pch_success_flag = { 0 }; D_TagList depfile_force_includes = { 0 }; { AddSyncPoint(); @@ -731,6 +769,8 @@ void OnBuild(StringList cli_args) bs_arg->cmd = StringF(&perm, pch_c_compile_args_fmt, FmtStr(pch_header_file.full_path), FmtStr(pch_c_src_gen_file.full_path)); bs_arg->depfile_dependent = pch_header_file; bs_arg->output_depfile = dep_file; + bs_arg->skip_flag = &rc_success_flag; + bs_arg->failure_flag = &pch_success_flag; String step_name = StringF(&perm, Lit("%F -> %F"), FmtStr(D_GetName(pch_header_file)), FmtStr(D_GetName(pch_c_file))); AddStep(step_name, &BuildStepMsvcCompileCommand, bs_arg); } @@ -744,6 +784,8 @@ void OnBuild(StringList cli_args) bs_arg->cmd = StringF(&perm, pch_cpp_compile_args_fmt, FmtStr(pch_header_file.full_path), FmtStr(pch_cpp_src_gen_file.full_path)); bs_arg->depfile_dependent = pch_header_file; bs_arg->output_depfile = dep_file; + bs_arg->skip_flag = &rc_success_flag; + bs_arg->failure_flag = &pch_success_flag; String step_name = StringF(&perm, Lit("%F -> %F"), FmtStr(D_GetName(pch_header_file)), FmtStr(D_GetName(pch_cpp_file))); AddStep(step_name, &BuildStepMsvcCompileCommand, bs_arg); } @@ -758,6 +800,8 @@ void OnBuild(StringList cli_args) if (IsDirty(pch_c_file)) { BuildStepSimpleCommandArg *bs_arg = ArenaPush(&perm, BuildStepSimpleCommandArg); bs_arg->cmd = StringF(&perm, pch_c_compile_args_fmt, FmtStr(pch_header_file.full_path), FmtStr(pch_c_file.full_path)); + bs_arg->skip_flag = &rc_success_flag; + bs_arg->failure_flag = &pch_success_flag; String step_name = StringF(&perm, Lit("%F -> %F"), FmtStr(D_GetName(pch_header_file)), FmtStr(D_GetName(pch_c_file))); AddStep(step_name, &BuildStepSimpleCommand, bs_arg); } @@ -766,6 +810,8 @@ void OnBuild(StringList cli_args) if (IsDirty(pch_cpp_file)) { BuildStepSimpleCommandArg *bs_arg = ArenaPush(&perm, BuildStepSimpleCommandArg); bs_arg->cmd = StringF(&perm, pch_cpp_compile_args_fmt, FmtStr(pch_header_file.full_path), FmtStr(pch_cpp_file.full_path)); + bs_arg->skip_flag = &rc_success_flag; + bs_arg->failure_flag = &pch_success_flag; String step_name = StringF(&perm, Lit("%F -> %F"), FmtStr(D_GetName(pch_header_file)), FmtStr(D_GetName(pch_cpp_file))); AddStep(step_name, &BuildStepSimpleCommand, bs_arg); } @@ -776,6 +822,7 @@ void OnBuild(StringList cli_args) * Build step: Compile src files * ========================== */ + AtomicI32 src_success_flag = { 0 }; { D_TagList src_input_files = { 0 }; { @@ -864,10 +911,14 @@ void OnBuild(StringList cli_args) bs_arg->depfile_dependent = obj_file; bs_arg->output_depfile = dep_file; bs_arg->depfile_force_includes = depfile_force_includes; + bs_arg->skip_flag = &pch_success_flag; + bs_arg->failure_flag = &src_success_flag; AddStep(step_name, &BuildStepMsvcCompileCommand, bs_arg); } else { BuildStepSimpleCommandArg *bs_arg = ArenaPush(&perm, BuildStepSimpleCommandArg); bs_arg->cmd = StringF(&perm, comp_cmd_fmt, FmtStr(file.full_path), FmtStr(obj_file.full_path)); + bs_arg->skip_flag = &pch_success_flag; + bs_arg->failure_flag = &src_success_flag; AddStep(step_name, &BuildStepSimpleCommand, bs_arg); } } @@ -899,6 +950,7 @@ void OnBuild(StringList cli_args) String link_args_fmt = StringFromStringLists(&perm, Lit(" "), link_args, link_warnings, compile_and_link_args); BuildStepSimpleCommandArg *bs_arg = ArenaPush(&perm, BuildStepSimpleCommandArg); bs_arg->cmd = StringF(&perm, link_args_fmt, FmtStr(link_files_str), FmtStr(executable_file.full_path)); + bs_arg->skip_flag = &src_success_flag; String step_name = Lit("Linking"); AddStep(step_name, &BuildStepSimpleCommand, bs_arg); } @@ -918,8 +970,6 @@ void OnBuild(StringList cli_args) while (s) { ++step_i; - SH_PrintF(Lit("[%F/%F] %F\n"), FmtI64(step_i), FmtI64(step_count), FmtStr(s->name)); - { OS_Lock lock = OS_MutexLockS(&s->res_mutex); while (s->res_status == StepStatus_None) { @@ -929,10 +979,11 @@ void OnBuild(StringList cli_args) } String output = s->res_output; - if (s->res_status != StepStatus_Success) { - Assert(false); + if (s->res_status == StepStatus_Success) { + SH_PrintF(Lit("[%F/%F] %F\n"), FmtI64(step_i), FmtI64(step_count), FmtStr(s->name)); + } else if (s->res_status == StepStatus_Failure) { success = false; - SH_PrintF(Lit("%F\n\n"), FmtStr(output)); + SH_PrintF(Lit("[%F/%F] %F\n\n%F\n\n"), FmtI64(step_i), FmtI64(step_count), FmtStr(s->name), FmtStr(output)); } s = s->next; @@ -943,9 +994,11 @@ void OnBuild(StringList cli_args) SH_Print(Lit("No work to do\n")); } +#if 0 if (!success) { Error(Lit("Build failed\n")); } +#endif T_ShutdownWorkers(); D_WriteStoreToHistFile(&store, hist_path); diff --git a/src/app.c b/src/app.c index 25aa6675..3121f34a 100644 --- a/src/app.c +++ b/src/app.c @@ -131,6 +131,16 @@ void app_entry_point(struct string args_str) { struct temp_arena scratch = scratch_begin_no_conflict(); +#if !RTC + /* Check that test modes aren't left on by accident in release mode */ + CT_ASSERT(BITBUFF_DEBUG == 0); + CT_ASSERT(BITBUFF_TEST == 0); +#endif + +#if BITBUFF_TEST + bitbuff_test(); +#endif + G.exit_sf = sync_flag_alloc(); G.exit_callbacks_mutex = sys_mutex_alloc(); G.exit_callbacks_arena = arena_alloc(GIGABYTE(64)); diff --git a/src/ase.c b/src/ase.c index 04d865d0..90bdf7d2 100644 --- a/src/ase.c +++ b/src/ase.c @@ -7,7 +7,7 @@ #include "ase.h" #include "arena.h" #include "scratch.h" -#include "byteio.h" +#include "bitbuff.h" #include "string.h" #include "log.h" @@ -15,12 +15,12 @@ * Bitbuf * ========================== */ -struct bitbuf { +struct huff_bb { u8 *data; u64 cur_bit; }; -INTERNAL u32 peek_bits(struct bitbuf *bb, u32 nbits) +INTERNAL u32 peek_bits(struct huff_bb *bb, u32 nbits) { ASSERT(nbits <= 32); @@ -36,14 +36,14 @@ INTERNAL u32 peek_bits(struct bitbuf *bb, u32 nbits) return val32; } -INTERNAL u32 consume_bits(struct bitbuf *bb, u32 nbits) +INTERNAL u32 consume_bits(struct huff_bb *bb, u32 nbits) { u32 val = peek_bits(bb, nbits); bb->cur_bit += nbits; return val; } -INTERNAL void skip_bits(struct bitbuf *bb, u32 nbits) +INTERNAL void skip_bits(struct huff_bb *bb, u32 nbits) { bb->cur_bit += nbits; } @@ -227,7 +227,7 @@ INTERNAL struct huffman huffman_init(struct arena *arena, u32 max_code_bits, u32 return res; } -INTERNAL u16 huffman_decode(struct huffman *huffman, struct bitbuf *bb) +INTERNAL u16 huffman_decode(struct huffman *huffman, struct huff_bb *bb) { u32 index = peek_bits(bb, huffman->max_code_bits); ASSERT(index < huffman->entries_count); @@ -240,12 +240,12 @@ INTERNAL u16 huffman_decode(struct huffman *huffman, struct bitbuf *bb) return res; } -INTERNAL void inflate(u8 *dest, u8 *encoded) +INTERNAL void inflate(u8 *dst, u8 *encoded) { struct temp_arena scratch = scratch_begin_no_conflict(); __prof; - struct bitbuf bb = { .data = encoded }; + struct huff_bb bb = { .data = encoded }; /* ZLIB header */ u32 cm = consume_bits(&bb, 4); @@ -345,7 +345,7 @@ INTERNAL void inflate(u8 *dest, u8 *encoded) while (true) { u32 lit_len = huffman_decode(&lit_len_huffman, &bb); if (lit_len <= 255) { - *dest++ = lit_len & 0xFF; + *dst++ = lit_len & 0xFF; } else if (lit_len >= 257) { u32 length_index = (lit_len - 257); struct huffman_entry length_entry = g_length_table[length_index]; @@ -362,9 +362,9 @@ INTERNAL void inflate(u8 *dest, u8 *encoded) u32 extra_bits = consume_bits(&bb, dist_entry.bits_used); distance += extra_bits; } - u8 *source = dest - distance; + u8 *source = dst - distance; while (length--) { - *dest++ = *source++; + *dst++ = *source++; } } else { break; @@ -505,12 +505,12 @@ INTERNAL u32 mul_u8(u32 a, u32 b) return ((t >> 8) + t) >> 8; } -INTERNAL u32 blend(u32 src, u32 dest, u8 opacity) +INTERNAL u32 blend(u32 src, u32 dst, u8 opacity) { - u32 dest_r = (dest & 0xff); - u32 dest_g = (dest >> 8) & 0xff; - u32 dest_b = (dest >> 16) & 0xff; - u32 dest_a = (dest >> 24) & 0xff; + u32 dst_r = (dst & 0xff); + u32 dst_g = (dst >> 8) & 0xff; + u32 dst_b = (dst >> 16) & 0xff; + u32 dst_a = (dst >> 24) & 0xff; u32 src_r = (src & 0xff); u32 src_g = (src >> 8) & 0xff; @@ -518,14 +518,14 @@ INTERNAL u32 blend(u32 src, u32 dest, u8 opacity) u32 src_a = (src >> 24) & 0xff; src_a = (u8)mul_u8(src_a, opacity); - u32 a = src_a + dest_a - mul_u8(src_a, dest_a); + u32 a = src_a + dst_a - mul_u8(src_a, dst_a); u32 r, g, b; if (a == 0) { r = g = b = 0; } else { - r = dest_r + (src_r - dest_r) * src_a / a; - g = dest_g + (src_g - dest_g) * src_a / a; - b = dest_b + (src_b - dest_b) * src_a / a; + r = dst_r + (src_r - dst_r) * src_a / a; + g = dst_g + (src_g - dst_g) * src_a / a; + b = dst_b + (src_b - dst_b) * src_a / a; } return r | (g << 8) | (b << 16) | (a << 24); @@ -558,9 +558,10 @@ struct ase_decode_image_result ase_decode_image(struct arena *arena, struct stri struct temp_arena scratch = scratch_begin(arena); struct ase_decode_image_result res = ZI; - struct byte_reader br = br_from_buffer(encoded); + struct bitbuff bb = bitbuff_from_string(encoded); + struct bitbuff_reader br = br_from_bitbuff_no_debug(&bb); struct ase_header ase_header; - br_read_to_struct(&br, &ase_header); + br_read_bytes(&br, STRING_FROM_STRUCT(&ase_header)); if (ase_header.magic != 0xA5E0) { push_error_copy_msg(arena, &res.errors, LIT("Not a valid aseprite file")); @@ -599,7 +600,7 @@ struct ase_decode_image_result ase_decode_image(struct arena *arena, struct stri u32 num_frames = 0; for (u16 i = 0; i < ase_header.frames; ++i) { struct frame_header frame_header; - br_read_to_struct(&br, &frame_header); + br_read_bytes(&br, STRING_FROM_STRUCT(&frame_header)); u32 num_chunks = frame_header.chunks_new; if (num_chunks == 0) { @@ -609,14 +610,14 @@ struct ase_decode_image_result ase_decode_image(struct arena *arena, struct stri /* Iterate chunks in frame */ for (u32 j = 0; j < num_chunks; ++j) { - u32 chunk_size = br_read_u32(&br); - enum chunk_type chunk_type = br_read_u16(&br); + u32 chunk_size = br_read_ubits(&br, 32); + enum chunk_type chunk_type = br_read_ubits(&br, 16); /* Chunk size includes size & type */ ASSERT(chunk_size >= 6); chunk_size -= 6; - u64 chunk_end_pos = br_pos(&br) + chunk_size; + u64 chunk_end_pos = br_cur_byte(&br) + chunk_size; switch (chunk_type) { case CHUNK_TYPE_LAYER: { @@ -624,14 +625,14 @@ struct ase_decode_image_result ase_decode_image(struct arena *arena, struct stri layer->next = layer_head; layer_head = layer; - layer->flags = br_read_u16(&br); - layer->type = br_read_u16(&br); - layer->child_level = br_read_u16(&br); + layer->flags = br_read_ubits(&br, 16); + layer->type = br_read_ubits(&br, 16); + layer->child_level = br_read_ubits(&br, 16); /* Ignoring layer default width & height */ - br_seek(&br, sizeof(u16) * 2); + br_seek_bytes(&br, sizeof(u16) * 2); - layer->blend_mode = br_read_u16(&br); + layer->blend_mode = br_read_ubits(&br, 16); if (layer->blend_mode != 0) { push_error_copy_msg(arena, &res.errors, @@ -639,26 +640,19 @@ struct ase_decode_image_result ase_decode_image(struct arena *arena, struct stri goto abort; } - layer->opacity = br_read_u8(&br); + layer->opacity = br_read_ubits(&br, 8); if (!(ase_header.flags & 1)) { layer->opacity = 255; } - br_seek(&br, sizeof(u8) * 3); + br_seek_bytes(&br, sizeof(u8) * 3); - u16 str_len = br_read_u16(&br); - u8 *str_bytes = br_seek(&br, str_len); - if (!str_bytes) { - str_len = 0; - } - layer->name = (struct string) { - str_len, - arena_push_array(scratch.arena, u8, str_len) - }; - MEMCPY(layer->name.text, str_bytes, str_len); + u16 str_len = br_read_ubits(&br, 16); + layer->name = (struct string) { str_len, arena_push_array(scratch.arena, u8, str_len) }; + br_read_bytes(&br, layer->name); if (layer->type == 2) { - layer->tileset_index = br_read_u32(&br); + layer->tileset_index = br_read_ubits(&br, 32); } layer->index = num_layers++; @@ -673,35 +667,36 @@ struct ase_decode_image_result ase_decode_image(struct arena *arena, struct stri } cel_tail = cel; - cel->layer_index = br_read_u16(&br); - cel->x_pos = br_read_i16(&br); - cel->y_pos = br_read_i16(&br); - cel->opacity = br_read_u8(&br); - cel->type = br_read_u16(&br); - cel->z_index = br_read_i16(&br); - br_seek(&br, sizeof(u8) * 5); + cel->layer_index = br_read_ubits(&br, 16); + cel->x_pos = br_read_ibits(&br, 16); + cel->y_pos = br_read_ibits(&br, 16); + cel->opacity = br_read_ubits(&br, 8); + cel->type = br_read_ubits(&br, 16); + cel->z_index = br_read_ibits(&br, 16); + br_seek_bytes(&br, sizeof(u8) * 5); cel->frame_index = num_frames; switch (cel->type) { case CEL_TYPE_RAW_IMAGE: { /* Unsupported */ - br_seek_to(&br, chunk_end_pos); + br_seek_to_byte(&br, chunk_end_pos); } break; case CEL_TYPE_LINKED: { - cel->frame_pos = br_read_u16(&br); + cel->frame_pos = br_read_ubits(&br, 16); /* Actual linking happens later after iteration */ } break; case CEL_TYPE_COMPRESSED_IMAGE: { - cel->width = br_read_u16(&br); - cel->height = br_read_u16(&br); + cel->width = br_read_ubits(&br, 16); + cel->height = br_read_ubits(&br, 16); cel->pixels = arena_push_array(scratch.arena, u32, cel->width * cel->height); - inflate((u8 *)cel->pixels, br.at); - - br_seek_to(&br, chunk_end_pos); + u8 *huffman_encoded = br_read_bytes_raw(&br, chunk_end_pos - br_cur_byte(&br)); + if (huffman_encoded) { + inflate((u8 *)cel->pixels, huffman_encoded); + } } break; case CEL_TYPE_COMPRESSED_TILEMAP: { @@ -713,7 +708,7 @@ struct ase_decode_image_result ase_decode_image(struct arena *arena, struct stri } break; default: { - br_seek_to(&br, chunk_end_pos); + br_seek_to_byte(&br, chunk_end_pos); } break; } } @@ -802,7 +797,7 @@ struct ase_decode_image_result ase_decode_image(struct arena *arena, struct stri } /* ASSERT all data was read */ - ASSERT(br_bytes_left(&br) == 0); + ASSERT(br_num_bytes_left(&br) == 0); abort: @@ -820,9 +815,10 @@ struct ase_decode_sheet_result ase_decode_sheet(struct arena *arena, struct stri struct ase_decode_sheet_result res = ZI; - struct byte_reader br = br_from_buffer(encoded); + struct bitbuff bb = bitbuff_from_string(encoded); + struct bitbuff_reader br = br_from_bitbuff_no_debug(&bb); struct ase_header ase_header; - br_read_to_struct(&br, &ase_header); + br_read_bytes(&br, STRING_FROM_STRUCT(&ase_header)); u64 frame_width = ase_header.width; u64 frame_height = ase_header.height; @@ -845,7 +841,7 @@ struct ase_decode_sheet_result ase_decode_sheet(struct arena *arena, struct stri /* Iterate frames */ for (u16 i = 0; i < ase_header.frames; ++i) { struct frame_header frame_header; - br_read_to_struct(&br, &frame_header); + br_read_bytes(&br, STRING_FROM_STRUCT(&frame_header)); u32 num_chunks = frame_header.chunks_new; if (num_chunks == 0) { @@ -877,39 +873,33 @@ struct ase_decode_sheet_result ase_decode_sheet(struct arena *arena, struct stri /* Iterate chunks in frame */ for (u32 j = 0; j < num_chunks; ++j) { - u32 chunk_size = br_read_u32(&br); - enum chunk_type chunk_type = br_read_u16(&br); + u32 chunk_size = br_read_ubits(&br, 32); + enum chunk_type chunk_type = br_read_ubits(&br, 16); /* Chunk size includes size & type */ ASSERT(chunk_size >= 6); chunk_size -= 6; - u64 chunk_end_pos = br_pos(&br) + chunk_size; + u64 chunk_end_pos = br_cur_byte(&br) + chunk_size; switch (chunk_type) { case CHUNK_TYPE_TAGS: { - u16 frame_span_count = br_read_u16(&br); - br_seek(&br, 8); + u16 frame_span_count = br_read_ubits(&br, 16); + br_seek_bytes(&br, 8); for (u16 k = 0; k < frame_span_count; ++k) { struct ase_span *span = arena_push_zero(arena, struct ase_span); span->next = span_head; span_head = span; - span->start = br_read_u16(&br); - span->end = br_read_u16(&br); - br_seek(&br, 13); + span->start = br_read_ubits(&br, 16); + span->end = br_read_ubits(&br, 16); + br_seek_bytes(&br, 13); + + u16 str_len = br_read_ubits(&br, 16); + span->name = (struct string) { str_len, arena_push_array(arena, u8, str_len) }; + br_read_bytes(&br, span->name); - u16 str_len = br_read_u16(&br); - u8 *str_bytes = br_seek(&br, str_len); - if (!str_bytes) { - str_len = 0; - } - span->name = (struct string) { - str_len, - arena_push_array(arena, u8, str_len) - }; - MEMCPY(span->name.text, str_bytes, str_len); ++num_spans; } @@ -920,44 +910,33 @@ struct ase_decode_sheet_result ase_decode_sheet(struct arena *arena, struct stri slice_key->next = slice_key_head; slice_key_head = slice_key; - u32 num_slices = br_read_u32(&br); + u32 num_slices = br_read_ubits(&br, 32); slice_key->num_slices = num_slices; - u32 flags = br_read_u32(&br); - br_seek(&br, 4); + u32 flags = br_read_ubits(&br, 32); + br_seek_bytes(&br, 4); - struct string name; - { - u16 str_len = br_read_u16(&br); - u8 *str_bytes = br_seek(&br, str_len); - if (!str_bytes) { - str_len = 0; - } - name = (struct string) { - str_len, - arena_push_array(arena, u8, str_len) - }; - MEMCPY(name.text, str_bytes, str_len); - } - slice_key->name = name; + u16 str_len = br_read_ubits(&br, 16); + slice_key->name = (struct string) { str_len, arena_push_array(arena, u8, str_len) }; + br_read_bytes(&br, slice_key->name); for (u32 k = 0; k < num_slices; ++k) { struct ase_slice *slice = arena_push_zero(arena, struct ase_slice); slice->next = slice_key->slice_head; slice_key->slice_head = slice; - u32 start = br_read_u32(&br); - i32 x = br_read_i32(&br); - i32 y = br_read_i32(&br); - u32 width = br_read_u32(&br); - u32 height = br_read_u32(&br); + u32 start = br_read_ubits(&br, 32); + i32 x = br_read_ibits(&br, 32); + i32 y = br_read_ibits(&br, 32); + u32 width = br_read_ubits(&br, 32); + u32 height = br_read_ubits(&br, 32); if (flags & 0x01) { /* Skip 9-patches info */ - br_seek(&br, 128); + br_seek_bytes(&br, 128); } if (flags & 0x02) { /* Skip pivot info */ - br_seek(&br, 64); + br_seek_bytes(&br, 64); } slice->start = start; @@ -974,7 +953,7 @@ struct ase_decode_sheet_result ase_decode_sheet(struct arena *arena, struct stri //case CHUNK_TYPE_USER_DATA default: { - br_seek_to(&br, chunk_end_pos); + br_seek_to_byte(&br, chunk_end_pos); } break; } } @@ -982,7 +961,7 @@ struct ase_decode_sheet_result ase_decode_sheet(struct arena *arena, struct stri } /* ASSERT all data was read */ - ASSERT(br_bytes_left(&br) == 0); + ASSERT(br_num_bytes_left(&br) == 0); res.image_size = V2(image_width, image_height); res.frame_size = V2(frame_width, frame_height); diff --git a/src/bitbuff.c b/src/bitbuff.c new file mode 100644 index 00000000..887d2b2e --- /dev/null +++ b/src/bitbuff.c @@ -0,0 +1,750 @@ +#include "bitbuff.h" +#include "memory.h" +#include "arena.h" + +/* TODO: Safety check that functions taking byte length can't overflow bit conversion (log2(num_bytes) > 61) */ + +#define WRITE_OVERFLOW_ARENA_PUSH_SIZE 4096 + +/* ========================== * + * Debug + * ========================== */ + +#if BITBUFF_DEBUG +/* Magic numbers inserted to verify read/write type & length */ +enum dbg_magic { + DBG_MAGIC_ALIGN = 0x20A4, + DBG_MAGIC_UBITS = 0xCB4A, + DBG_MAGIC_IBITS = 0xB30D, + DBG_MAGIC_UV = 0xE179, + DBG_MAGIC_IV = 0x981f, + DBG_MAGIC_F32 = 0x56F9, + DBG_MAGIC_F64 = 0x7053, + DBG_MAGIC_STRING = 0x7866, + DBG_MAGIC_BYTES = 0x8B90, +}; + +INTERNAL void bw_write_ubits_nomagic(struct bitbuff_writer *bw, u64 value, u8 num_bits); +INTERNAL void _dbg_write_magic(struct bitbuff_writer *bw, enum dbg_magic magic, u8 num_bits) +{ + if (bw->debug_enabled) { + if (bw_check_overflow_bits(bw, 24)) { + return; + } + u64 magic_ubits = (u64)magic | ((u64)num_bits << 16); + bw_write_ubits_nomagic(bw, magic_ubits, 24); + } +} + +INTERNAL u64 br_read_ubits_nomagic(struct bitbuff_reader *br, u8 num_bits); +INTERNAL void _dbg_read_magic(struct bitbuff_reader *br, enum dbg_magic expected_magic, u8 expected_num_bits) +{ + if (br->debug_enabled) { + if (br_check_overflow_bits(br, 24)) { + return; + } + u64 stored = br_read_ubits_nomagic(br, 24); + enum dbg_magic stored_magic = stored & 0xFFFF; + u8 stored_num_bits = (stored >> 16) & 0xFF; + + /* Verify stored magic match */ + ASSERT(stored_magic == expected_magic); + + /* Verify stored bit count match */ + ASSERT(stored_num_bits == expected_num_bits); + } +} + +#else +# define _dbg_write_magic(a, b, c) +# define _dbg_read_magic(a, b, c) +#endif + +/* ========================== * + * Utils + * ========================== */ + +INTERNAL u64 uint_to_twos_compliment(u64 value, u8 num_bits) +{ + u64 mask = U64_MAX; + if (num_bits < 64) { + mask = ~(U64_MAX << num_bits); + } + u64 tc = (~value & mask) + 1; + tc &= mask; + return tc; +} + +INTERNAL i64 sint_from_twos_compliment(u64 tc, u8 num_bits) +{ + u64 msb_mask = (u64)1 << (num_bits - 1); + i64 value = -(i64)(tc & msb_mask); + value += tc & ~msb_mask; + return value; +} + +/* ========================== * + * Bitbuff + * ========================== */ + +struct bitbuff bitbuff_alloc(u64 arena_reserve) +{ + struct bitbuff res = ZI; + res.arena = arena_alloc(arena_reserve); + res.is_backed_by_arena = true; + return res; +} + +void bitbuff_release(struct bitbuff *bb) +{ + /* Only arena bitbuffs need to be released */ + if (bb->is_backed_by_arena) { + arena_release(&bb->arena); + } +} + +struct bitbuff bitbuff_from_string(struct string s) +{ + struct bitbuff res = ZI; + res.fixed_buffer = s; + return res; +} + +/* ========================== * + * Writer + * ========================== */ + +struct bitbuff_writer bw_from_bitbuff(struct bitbuff *bb) +{ + struct bitbuff_writer res = ZI; + res.bb = bb; + if (bb->is_backed_by_arena) { + res.base = bb->arena.base; + } else { + res.base = bb->fixed_buffer.text; + } + res.cur_bit = 0; +#if BITBUFF_DEBUG + res.debug_enabled = true; +#endif + return res; +} + +/* Use this when writing external formats that will not verify bitbuff debug symbols / magic numbers */ +struct bitbuff_writer bw_from_bitbuff_no_debug(struct bitbuff *bb) +{ + struct bitbuff_writer res = bw_from_bitbuff(bb); +#if BITBUFF_DEBUG + res.debug_enabled = false; +#endif + return res; +} + +u64 bw_num_bits_written(struct bitbuff_writer *bw) +{ + return bw->cur_bit; +} + +u64 bw_num_bytes_written(struct bitbuff_writer *bw) +{ + return (bw->cur_bit + 7) >> 3; +} + +struct string bw_get_written(struct arena *arena, struct bitbuff_writer *bw) +{ + struct string res = ZI; + res.len = (bw->cur_bit + 7) >> 3; + res.text = arena_push_array(arena, u8, res.len); + MEMCPY(res.text, bw->base, res.len); + return res; +} + +/* Returns true if num_bits would cause the writer to overflow its fixed buffer size (if writer is not backed by a dynamic arena bitbuff) */ +b32 bw_check_overflow_bits(struct bitbuff_writer *bw, u64 num_bits) +{ + b32 res = false; + struct bitbuff *bb = bw->bb; + if (bw->overflowed) { + res = true; + } else { + u64 bytes_needed = (bw->cur_bit + num_bits + 7) >> 3; + if (bb->is_backed_by_arena) { + struct arena *arena = &bb->arena; + if (bytes_needed >= arena->pos) { + /* Grow arena */ + u64 push_size = (((bytes_needed - arena->pos) / WRITE_OVERFLOW_ARENA_PUSH_SIZE) + 1) * WRITE_OVERFLOW_ARENA_PUSH_SIZE; + arena_push_array(arena, u8, push_size); + } + } else { + u64 max_len = bb->fixed_buffer.len; + if (bytes_needed >= max_len) { + /* Writer overflowed fixed buffer */ + ASSERT(false); + res = true; + bw->cur_bit = max_len << 3; + bw->overflowed = true; + } + } + } + return res; +} + +/* Align the pos to the next byte */ +void bw_align(struct bitbuff_writer *bw) +{ + _dbg_write_magic(bw, DBG_MAGIC_ALIGN, 0); + bw->cur_bit += (8 - (bw->cur_bit & 7)) & 7; +} + +#if BITBUFF_DEBUG +INTERNAL void bw_write_ubits_nomagic(struct bitbuff_writer *bw, u64 value, u8 num_bits) +#else +void bw_write_ubits(struct bitbuff_writer *bw, u64 value, u8 num_bits) +#endif +{ + ASSERT(num_bits > 0 && (num_bits == 64 || value <= ~(U64_MAX << num_bits))); /* Bit count must be able to hold value */ + if (bw_check_overflow_bits(bw, num_bits)) { + return; + } + + u8 offset = bw->cur_bit & 7; + if (offset != 0) { + /* Write unaligned bits */ + u8 *at = bw->base + (bw->cur_bit >> 3); + u8 num_mix_bits = min_u8((8 - offset), num_bits); + u8 mix_byte = (u8)((value & ((1 << num_mix_bits) - 1)) << offset); + *at |= mix_byte; + value >>= num_mix_bits; + num_bits -= num_mix_bits; + bw->cur_bit += num_mix_bits; + } + + /* cur_bit is now aligned to byte */ + u8 *at = bw->base + (bw->cur_bit >> 3); + u8 num_bytes = (num_bits + 7) >> 3; + MEMCPY(at, &value, num_bytes); + bw->cur_bit += num_bits; +} + +#if BITBUFF_DEBUG +void bw_write_ubits(struct bitbuff_writer *bw, u64 value, u8 num_bits) +{ + _dbg_write_magic(bw, DBG_MAGIC_UBITS, num_bits); + bw_write_ubits_nomagic(bw, value, num_bits); +} +#endif + +void bw_write_ibits(struct bitbuff_writer *bw, i64 value, u8 num_bits) +{ + _dbg_write_magic(bw, DBG_MAGIC_IBITS, num_bits); + u64 ubits; + if (value >= 0) { + ubits = value; + } else { + ubits = uint_to_twos_compliment(-value, num_bits); + } + bw_write_ubits(bw, ubits, num_bits); +} + +void bw_write_bit(struct bitbuff_writer *bw, u8 value) +{ + bw_write_ubits(bw, value, 1); +} + +/* Writes a variable length unsigned integer. + * Value is written in chunks w/ 8th bit signaling continuation bit. */ +void bw_write_uv(struct bitbuff_writer *bw, u64 value) +{ + _dbg_write_magic(bw, DBG_MAGIC_UV, 0); + while (value > 0x7F) { + u8 cont_byte = 0x80 | (value & 0x7F); + bw_write_ubits(bw, cont_byte, 8); + value >>= 7; + } + bw_write_ubits(bw, value, 8); +} + +/* Writes a variable length signed integer. + * Similar to bw_write_uv, except the first byte's 7th bit is a sign bit + * indicating that the value is stored in twos compliment. */ +void bw_write_iv(struct bitbuff_writer *bw, i64 value) +{ + _dbg_write_magic(bw, DBG_MAGIC_IV, 0); + u8 sign_bit; + u64 tc; + if (value >= 0) { + sign_bit = 0; + tc = value; + } else { + sign_bit = 1; + u64 unsigned_value = -value; + u8 num_bits = 6; + unsigned_value >>= 6; + while (unsigned_value > 0) { + num_bits += 7; + unsigned_value >>= 7; + } + num_bits = min_u8(num_bits, 64); + tc = uint_to_twos_compliment(-value, num_bits); + } + + /* First byte contains not just cont bit, but sign bit as well. */ + u8 first_byte = (tc & 0x3F); + tc >>= 6; + first_byte |= (tc > 0) << 7; /* Cont bit */ + first_byte |= sign_bit << 6; /* Sign bit */ + bw_write_ubits(bw, first_byte, 8); + + if (tc > 0) { + while (tc > 0x7F) { + u8 cont_byte = 0x80 | (tc & 0x7F); + bw_write_ubits(bw, cont_byte, 8); + tc >>= 7; + } + bw_write_ubits(bw, tc, 8); + } +} + +void bw_write_f32(struct bitbuff_writer *bw, f32 value) +{ + _dbg_write_magic(bw, DBG_MAGIC_F32, 0); + bw_write_ubits(bw, *(u32 *)&value, 32); +} + +void bw_write_f64(struct bitbuff_writer *bw, f64 value) +{ + _dbg_write_magic(bw, DBG_MAGIC_F64, 0); + bw_write_ubits(bw, *(u64 *)&value, 64); +} + +void bw_write_string(struct bitbuff_writer *bw, struct string s) +{ + _dbg_write_magic(bw, DBG_MAGIC_STRING, 0); + bw_write_uv(bw, s.len); + bw_write_bytes(bw, s); +} + +void bw_write_bytes(struct bitbuff_writer *bw, struct string bytes) +{ + _dbg_write_magic(bw, DBG_MAGIC_BYTES, 0); + + /* Align start of bytes */ + bw_align(bw); + + u64 num_bits = bytes.len << 3; + if (bw_check_overflow_bits(bw, num_bits)) { + return; + } + + u8 *at = bw->base + (bw->cur_bit >> 3); + MEMCPY(at, bytes.text, bytes.len); + bw->cur_bit += num_bits; +} + +/* ========================== * + * Reader + * ========================== */ + +struct bitbuff_reader br_from_bitbuff(struct bitbuff *bb) +{ + struct bitbuff_reader res = ZI; + if (!bb->is_backed_by_arena) { + res.base = bb->fixed_buffer.text; + res.base_len = bb->fixed_buffer.len; + } else { + struct arena *arena = &bb->arena; + res.base = arena->base; + res.base_len = arena->pos; + } + res.cur_bit = 0; +#if BITBUFF_DEBUG + res.debug_enabled = true; +#endif + return res; +} + +/* Use this when reading from external formats that will not contain bitbuff debug symbols / magic numbers */ +struct bitbuff_reader br_from_bitbuff_no_debug(struct bitbuff *bb) +{ + struct bitbuff_reader res = br_from_bitbuff(bb); +#if BITBUFF_DEBUG + res.debug_enabled = false; +#endif + return res; +} + +/* Returns the number of bits read from the bitbuff */ +u64 br_cur_bit(struct bitbuff_reader *br) +{ + return br->cur_bit; +} + +/* Returns the number of *full* bytes read from the bitbuff */ +u64 br_cur_byte(struct bitbuff_reader *br) +{ + return br->cur_bit >> 3; +} + +/* Returns the number of bits left until the bitbuff overflows */ +u64 br_num_bits_left(struct bitbuff_reader *br) +{ + return (br->base_len << 3) - br->cur_bit; +} + +/* Returns the number of *full* bytes left until the bitbuff overflows */ +u64 br_num_bytes_left(struct bitbuff_reader *br) +{ + return br->base_len - (br->cur_bit >> 3); +} + +b32 br_check_overflow_bits(struct bitbuff_reader *br, u64 num_bits) +{ + b32 res = false; + if (br->overflowed) { + res = true; + } else { + u64 bits_needed = br->cur_bit + num_bits; + u64 base_len_bits = br->base_len << 3; + if (bits_needed > base_len_bits) { + /* Tried to read past bitbuff memory */ + ASSERT(false); + res = true; + br->cur_bit = base_len_bits; + br->overflowed = true; + } + } + return res; +} + +/* Align the pos to the next byte */ +void br_align(struct bitbuff_reader *br) +{ + _dbg_read_magic(br, DBG_MAGIC_ALIGN, 0); + br->cur_bit += (8 - (br->cur_bit & 7)) & 7; +} + +#if BITBUFF_DEBUG +INTERNAL u64 br_read_ubits_nomagic(struct bitbuff_reader *br, u8 num_bits) +#else +u64 br_read_ubits(struct bitbuff_reader *br, u8 num_bits) +#endif +{ + if (br_check_overflow_bits(br, num_bits)) { + return 0; + } + + u64 res = 0; + + u8 offset = br->cur_bit & 7; + u8 num_trailing_bits = 0; + if (offset) { + u8 *at = br->base + (br->cur_bit >> 3); + num_trailing_bits = min_u8(8 - offset, num_bits); + u8 mix_byte = *at; + mix_byte >>= offset; + mix_byte &= (1 << num_trailing_bits) - 1; + res = mix_byte; + num_bits -= num_trailing_bits; + br->cur_bit += num_trailing_bits; + } + + /* cur_bit is now aligned to byte */ + u8 *at = br->base + (br->cur_bit >> 3); + u8 num_bytes = (num_bits + 7) >> 3; + u64 tmp = 0; + MEMCPY(&tmp, at, num_bytes); + u64 mask = U64_MAX; + if (num_bits < 64) { + mask = ~(U64_MAX << num_bits); + } + tmp &= mask; + res |= tmp << num_trailing_bits; + br->cur_bit += num_bits; + return res; +} + +#if BITBUFF_DEBUG +u64 br_read_ubits(struct bitbuff_reader *br, u8 num_bits) +{ + _dbg_read_magic(br, DBG_MAGIC_UBITS, num_bits); + return br_read_ubits_nomagic(br, num_bits); +} +#endif + +i64 br_read_ibits(struct bitbuff_reader *br, u8 num_bits) +{ + ASSERT(num_bits > 1); + _dbg_read_magic(br, DBG_MAGIC_IBITS, num_bits); + u64 tc = br_read_ubits(br, num_bits); + return sint_from_twos_compliment(tc, num_bits); +} + +u8 br_read_bit(struct bitbuff_reader *br) +{ + return br_read_ubits(br, 1); +} + +/* Read a variable length unsigned integer. + * See bw_write_uv for details. */ +u64 br_read_uv(struct bitbuff_reader *br) +{ + _dbg_read_magic(br, DBG_MAGIC_UV, 0); + + u64 res = 0; + for (u64 i = 0; i <= 9; ++i) { + u64 part = br_read_ubits(br, 8); + u8 is_last_part = part <= 0x7F; + res |= (part & 0x7F) << (i * 7); + if (is_last_part) { + break; + } + } + return res; +} + +/* Read a variable length signed integer. + * See bw_write_iv for details. */ +i64 br_read_iv(struct bitbuff_reader *br) +{ + _dbg_read_magic(br, DBG_MAGIC_IV, 0); + u8 first_byte = br_read_ubits(br, 8); + u8 cont_bit = first_byte & 0x80; + u8 sign_bit = first_byte & 0x40; + + u8 num_bits = 6; + u64 tc = first_byte & 0x3F; + if (cont_bit) { + for (u64 i = 0; i <= 9; ++i) { + u64 part = br_read_ubits(br, 8); + u8 is_last_part = part <= 0x7F; + tc |= (part & 0x7F) << num_bits; + num_bits += 7; + if (is_last_part) { + break; + } + } + } + num_bits = min_u8(num_bits, 64); + + i64 res; + if (sign_bit) { + /* Sign bit is 1, indicating result is stored in twos compliment */ + res = sint_from_twos_compliment(tc, num_bits); + } else { + res = (i64)tc; + } + + return res; +} + +f32 br_read_f32(struct bitbuff_reader *br) +{ + _dbg_read_magic(br, DBG_MAGIC_F32, 0); + u32 ubits = br_read_ubits(br, 32); + return *(f32 *)&ubits; +} + +f64 br_read_f64(struct bitbuff_reader *br) +{ + _dbg_read_magic(br, DBG_MAGIC_F64, 0); + u64 ubits = br_read_ubits(br, 64); + return *(f64 *)&ubits; +} + +struct string br_read_string(struct arena *arena, struct bitbuff_reader *br) +{ + _dbg_read_magic(br, DBG_MAGIC_STRING, 0); + struct string res = ZI; + u64 len = br_read_uv(br); + u8 *src = br_read_bytes_raw(br, len); + if (src != NULL) { + res.len = len; + res.text = arena_push_array(arena, u8, len); + MEMCPY(res.text, src, len); + } + return res; +} + +/* Will fill dst with zeroes if bitbuff overflows */ +void br_read_bytes(struct bitbuff_reader *br, struct string out) +{ + u8 *src = br_read_bytes_raw(br, out.len); + if (src) { + MEMCPY(out.text, src, out.len); + } else { + MEMZERO(out.text, out.len); + } +} + +/* NULL will return on bitbuff overflow, result should be checked. */ +u8 *br_read_bytes_raw(struct bitbuff_reader *br, u64 num_bytes) +{ + _dbg_read_magic(br, DBG_MAGIC_BYTES, 0); + br_align(br); + + u64 num_bits = num_bytes << 3; + if (br_check_overflow_bits(br, num_bits)) { + return NULL; + } + + u8 *raw = br->base + (br->cur_bit >> 3); + br->cur_bit += num_bits; + + return raw; +} + +void br_seek_bytes(struct bitbuff_reader *br, u64 num_bytes) +{ + br_align(br); + + u64 num_bits = num_bytes << 3; + if (br_check_overflow_bits(br, num_bits)) { + return; + } + + br->cur_bit += num_bits; +} + +void br_seek_to_byte(struct bitbuff_reader *br, u64 pos) +{ + u64 cur_byte_pos = br->cur_bit >> 3; + if (pos >= cur_byte_pos) { + br_seek_bytes(br, pos - cur_byte_pos); + } else { + /* Tried to seek byte backwards in reader */ + ASSERT(false); + br->overflowed = true; + br->cur_bit = (br->base_len << 3); + } + +} + +/* ========================== * + * Test + * ========================== */ + +#if BITBUFF_TEST + +#include "string.h" +#include "scratch.h" + +void bitbuff_test(void) +{ + struct temp_arena scratch = scratch_begin_no_conflict(); + + u8 kind_ubits = 0; + u8 kind_ibits = 1; + u8 kind_uv = 2; + u8 kind_iv = 3; + u8 kind_string = 4; + + struct test_case_ubits { u64 v; u64 num_bits; }; + struct test_case_ibits { i64 v; u64 num_bits; }; + struct test_case_uv { u64 v; }; + struct test_case_iv { i64 v; }; + struct test_case_string { struct string v; }; + struct test_case { + u8 kind; + union { + struct test_case_ubits ubits; + struct test_case_ibits ibits; + struct test_case_uv uv; + struct test_case_iv iv; + struct test_case_string s; + }; + }; + + struct test_case cases[] = { + { kind_ubits, .ubits = { 40, 8 } }, + { kind_ubits, .ubits = { 32, 8 } }, + { kind_ubits, .ubits = { 100, 7 } }, + { kind_ubits, .ubits = { 4, 3 } }, + { kind_ubits, .ubits = { 13, 8 } }, + + { kind_ibits, .ibits = { 0, 8 } }, + { kind_ibits, .ibits = { -1, 8 } }, + { kind_ibits, .ibits = { -2, 8 } }, + { kind_ibits, .ibits = { -3, 8 } }, + { kind_ibits, .ibits = { -100, 8 } }, + { kind_ibits, .ibits = { -50, 7 } }, + { kind_ibits, .ibits = { 50, 7 } }, + { kind_ibits, .ibits = { 4, 7 } }, + { kind_ibits, .ibits = { 1, 7 } }, + { kind_ibits, .ibits = { 3, 3 } }, + + { kind_uv, .uv = { 0 } }, + { kind_uv, .uv = { 100 } }, + { kind_uv, .uv = { 10000 } }, + { kind_uv, .uv = { 10000000000000 } }, + { kind_uv, .uv = { U64_MAX } }, + + { kind_iv, .iv = { 0 } }, + { kind_iv, .iv = { -1 } }, + { kind_iv, .iv = { 10000000000000 } }, + { kind_iv, .iv = { -10000000000000 } }, + { kind_iv, .iv = { I64_MAX } }, + { kind_iv, .iv = { I64_MIN } }, + + { kind_string, .s = { LIT("Hello there! Hope you're doing well.") } }, + { kind_ibits, .ibits = { 3, 3 } }, + { kind_string, .s = { LIT("Alriiiiiiiiiiiiiiiiiiighty then") } }, + { kind_string, .s = { LIT("Alriiiiiiiiiiiiiiiiiiighty then") } }, + }; + + struct string encoded = ZI; + { + struct bitbuff bb = bitbuff_alloc(GIGABYTE(64)); + struct bitbuff_writer bw = bw_from_bitbuff(&bb); + for (u64 i = 0; i < ARRAY_COUNT(cases); ++i) { + struct test_case c = cases[i]; + if (c.kind == kind_ubits) { + bw_write_ubits(&bw, c.ubits.v, c.ubits.num_bits); + } else if (c.kind == kind_ibits) { + bw_write_ibits(&bw, c.ibits.v, c.ibits.num_bits); + } else if (c.kind == kind_uv) { + bw_write_uv(&bw, c.uv.v); + } else if (c.kind == kind_iv) { + bw_write_iv(&bw, c.iv.v); + } else if (c.kind == kind_string) { + bw_write_string(&bw, c.s.v); + } else { + ASSERT(false); + } + } + encoded = bw_get_written(scratch.arena, &bw); + } + + { + struct bitbuff bb = bitbuff_from_string(encoded); + struct bitbuff_reader br = br_from_bitbuff(&bb); + for (u64 i = 0; i < ARRAY_COUNT(cases); ++i) { + struct test_case c = cases[i]; + if (c.kind == kind_ubits) { + u64 w = c.ubits.v; + u64 r = br_read_ubits(&br, c.ubits.num_bits); + ASSERT(r == w); + } else if (c.kind == kind_ibits) { + i64 w = c.ibits.v; + i64 r = br_read_ibits(&br, c.ubits.num_bits); + ASSERT(r == w); + } else if (c.kind == kind_uv) { + u64 w = c.uv.v; + u64 r = br_read_uv(&br); + ASSERT(r == w); + } else if (c.kind == kind_iv) { + i64 w = c.iv.v; + i64 r = br_read_iv(&br); + ASSERT(r == w); + } else if (c.kind == kind_string) { + struct string w = c.s.v; + struct string r = br_read_string(scratch.arena, &br); + ASSERT(string_eq(r, w)); + } else { + ASSERT(false); + } + } + } + + scratch_end(scratch); +} +#endif diff --git a/src/bitbuff.h b/src/bitbuff.h new file mode 100644 index 00000000..c9f9b11b --- /dev/null +++ b/src/bitbuff.h @@ -0,0 +1,112 @@ +#ifndef BITBUFF_H +#define BITBUFF_H + +/* ========================== * + * Bitbuff + * ========================== */ + +struct bitbuff { + b32 is_backed_by_arena; + + /* If `is_arena_bitbuff` is true, this dynamically-sized arena will be used for reading & writing (meaning writing cannot overflow) */ + struct arena arena; + + /* If `is_arena_bitbuff` is false, this fixed-sized buffer willl be used for reading & writing */ + struct string fixed_buffer; +}; + +struct bitbuff bitbuff_alloc(u64 arena_reserve); +void bitbuff_release(struct bitbuff *bitbuff); + +struct bitbuff bitbuff_from_string(struct string s); + +/* ========================== * + * Writer + * ========================== */ + +/* NOTE: base_len is not stored in writer (as it is in the reader) since a dynamic arena-backed bitbuff could grow meaning len needs to be re-checked */ +struct bitbuff_writer { + b32 overflowed; + struct bitbuff *bb; + u8 *base; + u64 cur_bit; +#if BITBUFF_DEBUG + b32 debug_enabled; +#endif +}; + +struct bitbuff_writer bw_from_bitbuff(struct bitbuff *bb); +struct bitbuff_writer bw_from_bitbuff_no_debug(struct bitbuff *bb); + +u64 bw_num_bits_written(struct bitbuff_writer *bw); +u64 bw_num_bytes_written(struct bitbuff_writer *bw); + +struct string bw_get_written(struct arena *arena, struct bitbuff_writer *bw); + +b32 bw_check_overflow_bits(struct bitbuff_writer *bw, u64 num_bits); + +void bw_align(struct bitbuff_writer *bw); + +void bw_write_ubits(struct bitbuff_writer *bw, u64 value, u8 num_bits); +void bw_write_ibits(struct bitbuff_writer *bw, i64 value, u8 num_bits); +void bw_write_bit(struct bitbuff_writer *bw, u8 value); + +void bw_write_uv(struct bitbuff_writer *bw, u64 value); +void bw_write_iv(struct bitbuff_writer *bw, i64 value); + +void bw_write_f32(struct bitbuff_writer *bw, f32 value); +void bw_write_f64(struct bitbuff_writer *bw, f64 value); + +void bw_write_string(struct bitbuff_writer *bw, struct string s); + +void bw_write_bytes(struct bitbuff_writer *bw, struct string bytes); + +/* ========================== * + * Reader + * ========================== */ + +struct bitbuff_reader { + b32 overflowed; + u64 base_len; + u8 *base; + u64 cur_bit; +#if BITBUFF_DEBUG + b32 debug_enabled; +#endif +}; + +struct bitbuff_reader br_from_bitbuff(struct bitbuff *bb); +struct bitbuff_reader br_from_bitbuff_no_debug(struct bitbuff *bb); + +u64 br_cur_bit(struct bitbuff_reader *br); +u64 br_cur_byte(struct bitbuff_reader *br); + +u64 br_num_bits_left(struct bitbuff_reader *br); +u64 br_num_bytes_left(struct bitbuff_reader *br); + +b32 br_check_overflow_bits(struct bitbuff_reader *br, u64 num_bits); + +void br_align(struct bitbuff_reader *br); + +u64 br_read_ubits(struct bitbuff_reader *br, u8 num_bits); +i64 br_read_ibits(struct bitbuff_reader *br, u8 num_bits); +u8 br_read_bit(struct bitbuff_reader *br); + +u64 br_read_uv(struct bitbuff_reader *br); +i64 br_read_iv(struct bitbuff_reader *br); + +f32 br_read_f32(struct bitbuff_reader *br); +f64 br_read_f64(struct bitbuff_reader *br); + +struct string br_read_string(struct arena *arena, struct bitbuff_reader *br); + +void br_read_bytes(struct bitbuff_reader *br, struct string dst); +u8 *br_read_bytes_raw(struct bitbuff_reader *br, u64 num_bytes); +void br_seek_bytes(struct bitbuff_reader *br, u64 num_bytes); +void br_seek_to_byte(struct bitbuff_reader *br, u64 pos); + +#if BITBUFF_TEST +void bitbuff_test(void); +#endif + +#endif diff --git a/src/buddy.c b/src/buddy.c index f1742704..84c2fb4f 100644 --- a/src/buddy.c +++ b/src/buddy.c @@ -1,6 +1,8 @@ #include "buddy.h" #include "arena.h" +/* TODO: Elminiate meta arena. Just store levels in first 4096 bytes of buddy arena, and then zone header data at the beginning of each allocation. */ + /* ========================== * * Ctx * ========================== */ diff --git a/src/byteio.c b/src/byteio.c deleted file mode 100644 index 3cbff301..00000000 --- a/src/byteio.c +++ /dev/null @@ -1,334 +0,0 @@ -#include "byteio.h" -#include "memory.h" -#include "arena.h" - -/* ========================== * - * Writer - * ========================== */ - -INTERNAL b32 write_check_overflow(struct byte_writer *bw, i64 amount) -{ - b32 overflowed = bw->overflowed; - if (overflowed || amount < 0) { - overflowed = true; - bw->overflowed = true; - } else { - u8 *end = bw->buff.text + bw->buff.len; - u8 *new_pos = bw->at + amount; - i64 new_space_left = end - new_pos; - if (new_space_left < 0) { - struct arena *arena = bw->arena; - if (arena) { - if ((arena->base + arena->pos) == end) { - arena_push_array(arena, u8, -new_space_left); - bw->buff.len += -new_space_left; - } else { - /* Writer memory must be contiguous in arena */ - overflowed = true; - bw->overflowed = true; - } - } else { - overflowed = true; - bw->overflowed = true; - } - } - } - ASSERT(!overflowed); - return overflowed; -} - -INTERNAL void write(struct byte_writer *bw, void *v, u64 size) -{ - if (write_check_overflow(bw, size)) { - return; - } - MEMCPY(bw->at, v, size); - bw->at += size; -} - -struct byte_writer bw_from_buffer(struct string buff) -{ - struct byte_writer bw = ZI; - bw.buff = buff; - bw.at = buff.text; - return bw; -} - -/* Returns a writer that will allocate to arena instead of overflowing (writes must stay contiguous in arena) */ -struct byte_writer bw_from_arena(struct arena *arena) -{ - struct byte_writer bw = ZI; - bw.arena = arena; - bw.buff.text = arena->base + arena->pos; - bw.buff.len = 0; - bw.at = bw.buff.text; - return bw; -} - -/* Seeks forward and returns a new writer pointing to the skipped bytes */ -struct byte_writer bw_branch(struct byte_writer *bw, u64 size) -{ - struct string buff = STRING(size, bw->at); - struct byte_writer branch = bw_from_buffer(buff); - bw_seek(bw, size); - branch.overflowed = bw->overflowed; - return branch; -} - -void bw_seek(struct byte_writer *bw, u64 amount) -{ - if (write_check_overflow(bw, amount)) { - return; - } - bw->at += amount; -} - -void bw_seek_to(struct byte_writer *bw, u64 pos) -{ - if (write_check_overflow(bw, (i64)pos - (i64)(bw->at - bw->buff.text))) { - return; - } - bw->at = bw->buff.text + pos; -} - -void bw_write_bytes(struct byte_writer *bw, struct string bytes) -{ - write(bw, bytes.text, bytes.len); -} - -void bw_write_u8(struct byte_writer *bw, u8 v) -{ - write(bw, &v, sizeof(v)); -} - -void bw_write_u16(struct byte_writer *bw, u16 v) -{ - write(bw, &v, sizeof(v)); -} - -void bw_write_u32(struct byte_writer *bw, u32 v) -{ - write(bw, &v, sizeof(v)); -} - -void bw_write_u64(struct byte_writer *bw, u64 v) -{ - write(bw, &v, sizeof(v)); -} - -void bw_write_i8(struct byte_writer *bw, i8 v) -{ - write(bw, &v, sizeof(v)); -} - -void bw_write_i16(struct byte_writer *bw, i16 v) -{ - write(bw, &v, sizeof(v)); -} - -void bw_write_i32(struct byte_writer *bw, i32 v) -{ - write(bw, &v, sizeof(v)); -} - -void bw_write_i64(struct byte_writer *bw, i64 v) -{ - write(bw, &v, sizeof(v)); -} - -void bw_write_var_uint(struct byte_writer *bw, u64 v) -{ - /* TODO: real varint write */ - bw_write_u64(bw, v); -} - -void bw_write_var_sint(struct byte_writer *bw, i64 v) -{ - /* TODO: real varint write */ - bw_write_i64(bw, v); -} - -void bw_write_f32(struct byte_writer *bw, f32 v) -{ - write(bw, &v, sizeof(v)); -} - -void bw_write_f64(struct byte_writer *bw, f64 v) -{ - write(bw, &v, sizeof(v)); -} - -void bw_write_v2(struct byte_writer *bw, struct v2 v) -{ - bw_write_f32(bw, v.x); - bw_write_f32(bw, v.y); -} - -void bw_write_string(struct byte_writer *bw, struct string str) -{ - bw_write_var_uint(bw, str.len); - bw_write_bytes(bw, str); -} - -/* ========================== * - * Reader - * ========================== */ - -INTERNAL b32 read_check_overflow(struct byte_reader *br, i64 amount) -{ - b32 overflowed = br->overflowed; - if (overflowed || amount < 0 || (br->at + amount) > (br->buff.text + br->buff.len) ){ - ASSERT(false); - br->overflowed = true; - overflowed = true; - } - return overflowed; -} - -INTERNAL void read(struct byte_reader *br, void *dst, u64 size) -{ - if (read_check_overflow(br, size)) { - MEMZERO(dst, size); - } - MEMCPY(dst, br->at, size); - br->at += size; -} - -struct byte_reader br_from_buffer(struct string buff) -{ - struct byte_reader br = ZI; - br.buff = buff; - br.at = buff.text; - return br; -} - -/* Returns pointer to old position, or NULL on overflow */ -void *br_seek(struct byte_reader *br, u64 amount) -{ - if (read_check_overflow(br, amount)) { - return NULL; - } - void *ptr = br->at; - br->at += amount; - return ptr; -} - -/* Returns pointer to old position, or NULL on overflow */ -void *br_seek_to(struct byte_reader *br, u64 pos) -{ - if (read_check_overflow(br, (i64)pos - (i64)(br->at - br->buff.text))) { - return NULL; - } - void *ptr = br->at; - br->at = br->buff.text + pos; - return ptr; -} - -/* Will fill buff with zeroes on overflow */ -void br_read_to_buffer(struct byte_reader *br, struct string buff) -{ - read(br, buff.text, buff.len); -} - -u8 br_read_u8(struct byte_reader *br) -{ - u8 res; - read(br, &res, sizeof(res)); - return res; -} - -u16 br_read_u16(struct byte_reader *br) -{ - u16 res; - read(br, &res, sizeof(res)); - return res; -} - -u32 br_read_u32(struct byte_reader *br) -{ - u32 res; - read(br, &res, sizeof(res)); - return res; -} - -u64 br_read_u64(struct byte_reader *br) -{ - u64 res; - read(br, &res, sizeof(res)); - return res; -} - -i8 br_read_i8(struct byte_reader *br) -{ - i8 res; - read(br, &res, sizeof(res)); - return res; -} - -i16 br_read_i16(struct byte_reader *br) -{ - i16 res = 0; - read(br, &res, sizeof(res)); - return res; -} - -i32 br_read_i32(struct byte_reader *br) -{ - i32 res = 0; - read(br, &res, sizeof(res)); - return res; -} - -i64 br_read_i64(struct byte_reader *br) -{ - i64 res; - read(br, &res, sizeof(res)); - return res; -} - -u64 br_read_var_uint(struct byte_reader *br) -{ - /* TODO: real variable length read */ - return br_read_u64(br); -} - -i64 br_read_var_sint(struct byte_reader *br) -{ - /* TODO: real variable length read */ - return br_read_i64(br); -} - -f32 br_read_f32(struct byte_reader *br) -{ - f32 res; - read(br, &res, sizeof(res)); - return res; -} - -f64 br_read_f64(struct byte_reader *br) -{ - f64 res; - read(br, &res, sizeof(res)); - return res; -} - -struct v2 br_read_v2(struct byte_reader *br) -{ - struct v2 res; - res.x = br_read_f32(br); - res.y = br_read_f32(br); - return res; -} - -struct string br_read_string(struct arena *arena, struct byte_reader *br) -{ - struct string res = ZI; - u64 len = br_read_var_uint(br); - u8 *text = br_seek(br, len); - if (text != NULL) { - res.len = len; - res.text = arena_push_array(arena, u8, len); - MEMCPY(res.text, text, len); - } - return res; -} diff --git a/src/byteio.h b/src/byteio.h deleted file mode 100644 index 48a521bf..00000000 --- a/src/byteio.h +++ /dev/null @@ -1,96 +0,0 @@ -#ifndef BYTEIO_H -#define BYTEIO_H - -struct byte_writer { - struct arena *arena; /* If arena is set, then the writer cannot overflow and will instead allocate more memory as it grows */ - struct string buff; - b32 overflowed; - u8 *at; -}; - -struct byte_reader { - struct string buff; - b32 overflowed; - u8 *at; -}; - -/* ========================== * - * Writer - * ========================== */ - -struct byte_writer bw_from_buffer(struct string buff); -struct byte_writer bw_from_arena(struct arena *arena); -struct byte_writer bw_branch(struct byte_writer *bw, u64 size); - -void bw_seek(struct byte_writer *bw, u64 amount); -void bw_seek_to(struct byte_writer *bw, u64 pos); - -void bw_write_bytes(struct byte_writer *bw, struct string bytes); -void bw_write_u8(struct byte_writer *bw, u8 v); -void bw_write_u16(struct byte_writer *bw, u16 v); -void bw_write_u32(struct byte_writer *bw, u32 v); -void bw_write_u64(struct byte_writer *bw, u64 v); -void bw_write_i8(struct byte_writer *bw, i8 v); -void bw_write_i16(struct byte_writer *bw, i16 v); -void bw_write_i32(struct byte_writer *bw, i32 v); -void bw_write_i64(struct byte_writer *bw, i64 v); -void bw_write_var_uint(struct byte_writer *bw, u64 v); -void bw_write_var_sint(struct byte_writer *bw, i64 v); -void bw_write_f32(struct byte_writer *bw, f32 v); -void bw_write_f64(struct byte_writer *bw, f64 v); -void bw_write_v2(struct byte_writer *bw, struct v2 v); -void bw_write_string(struct byte_writer *bw, struct string str); - -/* Returns a string containing written bytes only */ -INLINE struct string bw_get_written(struct byte_writer *bw) -{ - struct string buff = ZI; - buff.text = bw->buff.text; - buff.len = bw->at - bw->buff.text; - return buff; -} - -INLINE u64 bw_pos(struct byte_writer *bw) -{ - return bw->at - bw->buff.text; -} - -/* ========================== * - * Reader - * ========================== */ - - /* Will fill struct with zeroes on overflow */ -#define br_read_to_struct(br_ptr, var_ptr) (br_read_to_buffer(br_ptr, STRING(sizeof(*var_ptr), (u8 *)var_ptr))) - -struct byte_reader br_from_buffer(struct string buff); - -void *br_seek(struct byte_reader *br, u64 amount); -void *br_seek_to(struct byte_reader *br, u64 pos); - -void br_read_to_buffer(struct byte_reader *br, struct string s); -u8 br_read_u8(struct byte_reader *br); -u16 br_read_u16(struct byte_reader *br); -u32 br_read_u32(struct byte_reader *br); -u64 br_read_u64(struct byte_reader *br); -i8 br_read_i8(struct byte_reader *br); -i16 br_read_i16(struct byte_reader *br); -i32 br_read_i32(struct byte_reader *br); -i64 br_read_i64(struct byte_reader *br); -u64 br_read_var_uint(struct byte_reader *br); -i64 br_read_var_sint(struct byte_reader *br); -f32 br_read_f32(struct byte_reader *br); -f64 br_read_f64(struct byte_reader *br); -struct v2 br_read_v2(struct byte_reader *br); -struct string br_read_string(struct arena *arena, struct byte_reader *br); - -INLINE u64 br_bytes_left(const struct byte_reader *br) -{ - return br->overflowed ? 0 : br->buff.len - (br->at - br->buff.text); -} - -INLINE u64 br_pos(struct byte_reader *br) -{ - return br->at - br->buff.text; -} - -#endif diff --git a/src/common.h b/src/common.h index d52077bc..e6b15ab1 100644 --- a/src/common.h +++ b/src/common.h @@ -346,7 +346,7 @@ typedef i32 b32; #define I8_MIN ((i8)-0x80) #define I16_MIN ((i16)-0x8000) #define I32_MIN ((i32)-0x80000000) -#define I64_MIN ((i64)-0x8000000000000000LL) +#define I64_MIN ((i64)-0x8000000000000000ULL) GLOBAL const u32 _f32_infinity_u32 = 0x7f800000; GLOBAL const f32 *_f32_infinity = (f32 *)&_f32_infinity_u32; @@ -656,8 +656,8 @@ struct collider_shape { * Common utilities * ========================== */ -INLINE u8 min_u8(u8 a, u8 b) { return a <= b ? a : b; } -INLINE u8 max_u8(u8 a, u8 b) { return a >= b ? a : b; } +INLINE u8 min_u8(u8 a, u8 b) { return a <= b ? a : b; } +INLINE u8 max_u8(u8 a, u8 b) { return a >= b ? a : b; } INLINE u32 min_u32(u32 a, u32 b) { return a <= b ? a : b; } INLINE u32 max_u32(u32 a, u32 b) { return a >= b ? a : b; } INLINE u64 min_u64(u64 a, u64 b) { return a <= b ? a : b; } diff --git a/src/config.h b/src/config.h index ecf213bd..9dfafd41 100644 --- a/src/config.h +++ b/src/config.h @@ -61,6 +61,10 @@ #define COLLIDER_DEBUG_DETAILED 0 #define COLLIDER_DEBUG_DETAILED_DRAW_MENKOWSKI 0 +/* If enabled, bitbuffs will insert/verify magic numbers & length for each read & write */ +#define BITBUFF_DEBUG 0 +#define BITBUFF_TEST 0 + /* ========================== * * Settings * ========================== */ diff --git a/src/host.c b/src/host.c index 89ecc5eb..ad809ee6 100644 --- a/src/host.c +++ b/src/host.c @@ -1,7 +1,7 @@ #include "arena.h" #include "host.h" #include "scratch.h" -#include "byteio.h" +#include "bitbuff.h" #include "sys.h" #include "util.h" #include "log.h" @@ -400,7 +400,7 @@ INTERNAL struct host_msg_assembler *host_msg_assembler_alloc(struct host_channel ma->num_chunks_total = chunk_count; - u64 chunk_bitmap_size = ((chunk_count - 1) / 8) + 1; + u64 chunk_bitmap_size = (chunk_count + 7) >> 3; if ((chunk_bitmap_size % 16) != 0) { /* Align chunk bitmap to 16 so msg data is aligned */ chunk_bitmap_size += 16 - (chunk_bitmap_size % 16); @@ -655,15 +655,16 @@ void host_update(struct host *host) for (struct host_rcv_packet *packet = rcv_buffer->first_packet; packet; packet = packet->next) { //struct sock *sock = packet->sock; struct sock_address address = packet->address; - struct byte_reader br = br_from_buffer(packet->data); - u32 magic = br_read_u32(&br); + struct bitbuff bb = bitbuff_from_string(packet->data); + struct bitbuff_reader br = br_from_bitbuff(&bb); + u32 magic = br_read_ubits(&br, 32); if (magic == PACKET_MAGIC) { /* TODO: Combine kind byte with flags byte */ struct host_channel *channel = host_channel_from_address(host, address); - enum host_packet_kind host_packet_kind = br_read_i8(&br); - u8 packet_flags = br_read_u8(&br); + enum host_packet_kind host_packet_kind = br_read_ibits(&br, 8); + u8 packet_flags = br_read_ubits(&br, 8); - u64 their_acked_seq = br_read_var_uint(&br); + u64 their_acked_seq = br_read_uv(&br); if (their_acked_seq > channel->their_acked_seq) { channel->their_acked_seq = their_acked_seq; } @@ -672,7 +673,7 @@ void host_update(struct host *host) b32 is_reliable = packet_flags & HOST_PACKET_FLAG_RELIABLE; if (channel->valid) { if (is_reliable) { - u64 packet_seq = br_read_var_uint(&br); + u64 packet_seq = br_read_uv(&br); if (packet_seq == channel->our_acked_seq + 1) { channel->our_acked_seq = packet_seq; } else { @@ -725,11 +726,11 @@ void host_update(struct host *host) { if (channel->valid && channel->connected) { /* Packet is chunk out of belonging to message */ - u64 msg_id = br_read_var_uint(&br); - u64 chunk_id = br_read_var_uint(&br); - u64 chunk_count = br_read_var_uint(&br); + u64 msg_id = br_read_uv(&br); + u64 chunk_id = br_read_uv(&br); + u64 chunk_count = br_read_uv(&br); b32 is_last_chunk = (chunk_id + 1) == chunk_count; - u64 data_len = is_last_chunk ? br_read_var_uint(&br) : PACKET_MSG_CHUNK_MAX_LEN; + u64 chunk_len = is_last_chunk ? br_read_uv(&br) : PACKET_MSG_CHUNK_MAX_LEN; struct host_msg_assembler *ma = host_get_msg_assembler(host, channel->id, msg_id); if (!ma) { @@ -738,12 +739,12 @@ void host_update(struct host *host) if (chunk_count == ma->num_chunks_total && chunk_id < chunk_count) { if (!host_msg_assembler_is_chunk_filled(ma, chunk_id)) { - u8 *src = br_seek(&br, data_len); + u8 *src = br_read_bytes_raw(&br, chunk_len); if (src) { u8 *dst = &ma->chunk_data[chunk_id * PACKET_MSG_CHUNK_MAX_LEN]; - MEMCPY(dst, src, data_len); + MEMCPY(dst, src, chunk_len); if (is_last_chunk) { - ma->last_chunk_len = data_len; + ma->last_chunk_len = chunk_len; } host_msg_assembler_set_chunk_received(ma, chunk_id); ++ma->num_chunks_received; @@ -765,10 +766,12 @@ void host_update(struct host *host) } } } else { + /* Overflow reading chunk */ ASSERT(false); } } } else { + /* Chunk id/count mismatch */ ASSERT(false); } } @@ -801,19 +804,19 @@ void host_update(struct host *host) /* Release acked reliable packets */ { u64 acked_seq = channel->their_acked_seq; - struct host_snd_packet *host_packet = channel->first_reliable_packet; - while (host_packet) { - struct host_snd_packet *next = host_packet->next; - u64 seq = host_packet->seq; + struct host_snd_packet *packet = channel->first_reliable_packet; + while (packet) { + struct host_snd_packet *next = packet->next; + u64 seq = packet->seq; if (seq < acked_seq) { - host_packet->next = host->first_free_packet; - host->first_free_packet = host_packet; + packet->next = host->first_free_packet; + host->first_free_packet = packet; channel->first_reliable_packet = next; --channel->num_reliable_packets; } else { break; } - host_packet = next; + packet = next; } if (channel->first_reliable_packet == NULL) { channel->last_reliable_packet = NULL; @@ -854,37 +857,40 @@ void host_update(struct host *host) case HOST_CMD_KIND_TRY_CONNECT: { u8 packet_flags = 0; - struct host_snd_packet *host_packet = host_channel_snd_packet_alloc(channel, false); - struct byte_writer bw = bw_from_buffer(STRING_FROM_ARRAY(host_packet->data)); - bw_write_u32(&bw, PACKET_MAGIC); - bw_write_i8(&bw, HOST_PACKET_KIND_TRY_CONNECT); - bw_write_u8(&bw, packet_flags); - bw_write_var_uint(&bw, channel->our_acked_seq); - host_packet->data_len = bw_pos(&bw); + struct host_snd_packet *packet = host_channel_snd_packet_alloc(channel, false); + struct bitbuff bb = bitbuff_from_string(STRING_FROM_ARRAY(packet->data)); + struct bitbuff_writer bw = bw_from_bitbuff(&bb); + bw_write_ubits(&bw, PACKET_MAGIC, 32); + bw_write_ibits(&bw, HOST_PACKET_KIND_TRY_CONNECT, 8); + bw_write_ubits(&bw, packet_flags, 8); + bw_write_uv(&bw, channel->our_acked_seq); + packet->data_len = bw_num_bytes_written(&bw); } break; case HOST_CMD_KIND_CONNECT_SUCCESS: { u8 packet_flags = 0; - struct host_snd_packet *host_packet = host_channel_snd_packet_alloc(channel, false); - struct byte_writer bw = bw_from_buffer(STRING_FROM_ARRAY(host_packet->data)); - bw_write_u32(&bw, PACKET_MAGIC); - bw_write_i8(&bw, HOST_PACKET_KIND_CONNECT_SUCCESS); - bw_write_u8(&bw, packet_flags); - bw_write_var_uint(&bw, channel->our_acked_seq); - host_packet->data_len = bw_pos(&bw); + struct host_snd_packet *packet = host_channel_snd_packet_alloc(channel, false); + struct bitbuff bb = bitbuff_from_string(STRING_FROM_ARRAY(packet->data)); + struct bitbuff_writer bw = bw_from_bitbuff(&bb); + bw_write_ubits(&bw, PACKET_MAGIC, 32); + bw_write_ibits(&bw, HOST_PACKET_KIND_CONNECT_SUCCESS, 8); + bw_write_ubits(&bw, packet_flags, 8); + bw_write_uv(&bw, channel->our_acked_seq); + packet->data_len = bw_num_bytes_written(&bw); } break; case HOST_CMD_KIND_DISCONNECT: { u8 packet_flags = 0; - struct host_snd_packet *host_packet = host_channel_snd_packet_alloc(channel, false); - struct byte_writer bw = bw_from_buffer(STRING_FROM_ARRAY(host_packet->data)); - bw_write_u32(&bw, PACKET_MAGIC); - bw_write_i8(&bw, HOST_PACKET_KIND_DISCONNECT); - bw_write_u8(&bw, packet_flags); - bw_write_var_uint(&bw, channel->our_acked_seq); - host_packet->data_len = bw_pos(&bw); + struct host_snd_packet *packet = host_channel_snd_packet_alloc(channel, false); + struct bitbuff bb = bitbuff_from_string(STRING_FROM_ARRAY(packet->data)); + struct bitbuff_writer bw = bw_from_bitbuff(&bb); + bw_write_ubits(&bw, PACKET_MAGIC, 32); + bw_write_ibits(&bw, HOST_PACKET_KIND_DISCONNECT, 8); + bw_write_ubits(&bw, packet_flags, 8); + bw_write_uv(&bw, channel->our_acked_seq); + packet->data_len = bw_num_bytes_written(&bw); } break; case HOST_CMD_KIND_WRITE: @@ -901,33 +907,33 @@ void host_update(struct host *host) u64 msg_id = ++channel->last_sent_msg_id; for (u64 i = 0; i < chunk_count; ++i) { - u64 data_len = PACKET_MSG_CHUNK_MAX_LEN; + u64 chunk_len = PACKET_MSG_CHUNK_MAX_LEN; b32 is_last_chunk = i + 1 == chunk_count; if (is_last_chunk) { - data_len = msg.len % PACKET_MSG_CHUNK_MAX_LEN; - if (data_len == 0) { - data_len = PACKET_MSG_CHUNK_MAX_LEN; + chunk_len = msg.len % PACKET_MSG_CHUNK_MAX_LEN; + if (chunk_len == 0) { + chunk_len = PACKET_MSG_CHUNK_MAX_LEN; } } - u8 *data = msg.text + (i * PACKET_MSG_CHUNK_MAX_LEN); - struct host_snd_packet *host_packet = host_channel_snd_packet_alloc(channel, is_reliable); - struct byte_writer bw = bw_from_buffer(STRING_FROM_ARRAY(host_packet->data)); - bw_write_u32(&bw, PACKET_MAGIC); - bw_write_i8(&bw, HOST_PACKET_KIND_MSG_CHUNK); - bw_write_u8(&bw, packet_flags); - bw_write_var_uint(&bw, channel->our_acked_seq); + struct host_snd_packet *packet = host_channel_snd_packet_alloc(channel, is_reliable); + struct bitbuff bb = bitbuff_from_string(STRING_FROM_ARRAY(packet->data)); + struct bitbuff_writer bw = bw_from_bitbuff(&bb); + bw_write_ubits(&bw, PACKET_MAGIC, 32); + bw_write_ibits(&bw, HOST_PACKET_KIND_MSG_CHUNK, 8); + bw_write_ubits(&bw, packet_flags, 8); + bw_write_uv(&bw, channel->our_acked_seq); if (is_reliable) { - bw_write_var_uint(&bw, host_packet->seq); + bw_write_uv(&bw, packet->seq); } - bw_write_var_uint(&bw, msg_id); - bw_write_var_uint(&bw, i); - bw_write_var_uint(&bw, chunk_count); + bw_write_uv(&bw, msg_id); + bw_write_uv(&bw, i); + bw_write_uv(&bw, chunk_count); if (is_last_chunk) { - /* FIXME: Ensure data_len can never be 0 */ - bw_write_var_uint(&bw, data_len); + bw_write_uv(&bw, chunk_len); } - bw_write_bytes(&bw, STRING(data_len, data)); - host_packet->data_len = bw_pos(&bw); + u8 *chunk_data = msg.text + (i * PACKET_MSG_CHUNK_MAX_LEN); + bw_write_bytes(&bw, STRING(chunk_len, chunk_data)); + packet->data_len = bw_num_bytes_written(&bw); } } break; diff --git a/src/memory.c b/src/memory.c index cd171c4e..499460ef 100644 --- a/src/memory.c +++ b/src/memory.c @@ -3,23 +3,23 @@ #if !CRTLIB __attribute((section(".text.memcpy"))) -void *memcpy(void *__restrict dest, const void *__restrict src, u64 n) +void *memcpy(void *__restrict dst, const void *__restrict src, u64 n) { /* TODO: Faster memcpy */ for (u64 i = 0; i < n; ++i) { - ((u8 *)dest)[i] = ((u8 *)src)[i]; + ((u8 *)dst)[i] = ((u8 *)src)[i]; } - return dest; + return dst; } __attribute((section(".text.memset"))) -void *memset(void *dest, i32 c, u64 n) +void *memset(void *dst, i32 c, u64 n) { /* TODO: Faster memset */ for (u64 i = 0; i < n; ++i) { - ((u8 *)dest)[i] = c; + ((u8 *)dst)[i] = c; } - return dest; + return dst; } __attribute((section(".text.memcmp"))) diff --git a/src/memory.h b/src/memory.h index fc924f20..7b9c1ea6 100644 --- a/src/memory.h +++ b/src/memory.h @@ -5,8 +5,8 @@ #define MEMZERO_ARRAY(a) MEMZERO((a), sizeof((a))) #define MEMZERO(ptr, count) MEMSET((ptr), 0, (count)) -#define MEMCPY_STRUCT(ptr_dest, ptr_src) MEMCPY((ptr_dest), (ptr_src), sizeof(*(ptr_dest))); -#define MEMCPY(dest, src, count) memcpy((dest), (src), (count)) +#define MEMCPY_STRUCT(ptr_dst, ptr_src) MEMCPY((ptr_dst), (ptr_src), sizeof(*(ptr_dst))); +#define MEMCPY(dst, src, count) memcpy((dst), (src), (count)) #define MEMCMP_STRUCT(p1, p2) MEMCMP((p1), (p2), sizeof(*p1)) #define MEMCMP(p1, p2, n) memcmp((p1), (p2), (n)) @@ -19,8 +19,8 @@ #if CRTLIB # include #else -void *memcpy(void *__restrict dest, const void *__restrict src, u64 n); -void *memset(void *dest, i32 c, u64 n); +void *memcpy(void *__restrict dst, const void *__restrict src, u64 n); +void *memset(void *dst, i32 c, u64 n); i32 memcmp(const void *p1, const void *p2, u64 n); #endif diff --git a/src/scratch.h b/src/scratch.h index 5776a5c7..66b35b0d 100644 --- a/src/scratch.h +++ b/src/scratch.h @@ -77,7 +77,7 @@ INLINE struct temp_arena _scratch_begin(struct arena *potential_conflict) #define scratch_begin_no_conflict() \ _scratch_begin_no_conflict(); \ do { \ - struct arena *arena = NULL; \ + u8 arena = 0; \ (UNUSED)arena; \ } while (0) diff --git a/src/sim.c b/src/sim.c index 40ad7900..1137f4aa 100644 --- a/src/sim.c +++ b/src/sim.c @@ -14,7 +14,7 @@ #include "collider.h" #include "rng.h" #include "space.h" -#include "byteio.h" +#include "bitbuff.h" #include "host.h" /* ========================== * @@ -39,6 +39,9 @@ struct sim_ctx *sim_ctx_alloc(struct sprite_startup_receipt *sprite_sr, /* Intialize host */ ctx->host = host_alloc(host_port); + /* Allocate encoder bitbuff */ + ctx->encoder_bitbuff = bitbuff_alloc(GIGABYTE(64)); + /* Create bookkeeping */ ctx->contact_lookup = sim_ent_lookup_alloc(4096); #if COLLIDER_DEBUG @@ -67,7 +70,12 @@ void sim_ctx_release(struct sim_ctx *ctx) #endif sim_ent_lookup_release(&ctx->contact_lookup); + /* Release encoder bitbuff */ + bitbuff_release(&ctx->encoder_bitbuff); + + /* Release host */ host_release(ctx->host); + arena_release(&ctx->arena); } @@ -513,7 +521,7 @@ void sim_update(struct sim_ctx *ctx, i64 target_dt_ns) struct host_event_array host_events = host_pop_events(scratch.arena, ctx->host); struct sim_cmd_list sim_cmds = ZI; - sim_cmds_from_host_events(scratch.arena, host_events, &sim_cmds); + sim_cmds_decode(scratch.arena, host_events, &sim_cmds); /* Create connecting clients */ for (struct sim_cmd *cmd = sim_cmds.first; cmd; cmd = cmd->next) { @@ -1378,30 +1386,6 @@ void sim_update(struct sim_ctx *ctx, i64 target_dt_ns) * Publish tick * ========================== */ -#if 0 - for (u64 i = 0; i < ctx->world->num_clients_reserved; ++i) { - struct sim_client *client = &ctx->world->clients[i]; - if (client->valid) { - struct temp_arena temp = arena_temp_begin(scratch.arena); - - /* TODO: Not like this */ - struct sim_event snapshot_event = ZI; - snapshot_event.tick = ctx->world->tick; - snapshot_event.kind = SIM_EVENT_KIND_SNAPSHOT; - snapshot_event.encoded_snapshot = sim_snapshot_encode(temp.arena, client, ctx->world); - - struct sim_event_list l = ZI; - l.first = &snapshot_event; - l.last = &snapshot_event; - struct string msg = sim_string_from_events(temp.arena, l); - - host_queue_write(ctx->host, client->channel_id, msg, 0); - //host_queue_write(ctx->host, HOST_CHANNEL_ID_ALL, msg, 0); - - arena_temp_end(temp); - } - } -#else for (u64 i = 0; i < ctx->world->num_clients_reserved; ++i) { struct sim_client *client = &ctx->world->clients[i]; if (client->valid) { @@ -1413,33 +1397,43 @@ void sim_update(struct sim_ctx *ctx, i64 target_dt_ns) struct sim_snapshot *ss1 = ctx->world; ss0_tick = ss0->tick; /* In case ack tick is no longer in store we need to do a full resend */ - struct sim_event snapshot_event = ZI; - snapshot_event.kind = SIM_EVENT_KIND_SNAPSHOT; - snapshot_event.tick = ctx->world->tick; - - snapshot_event.snapshot_tick_start = ss0_tick; - snapshot_event.snapshot_tick_end = ss1_tick; - - struct sim_encoder encoder = ZI; - encoder.client = client; - /* FIXME: Don't store external arena in bw. Could end up with hidden scratch conflicts. */ - encoder.bw = bw_from_arena(temp.arena); - - sim_snapshot_encode(&encoder, ss0, ss1); - snapshot_event.snapshot_encoded = bw_get_written(&encoder.bw); - struct sim_event_list l = ZI; - l.first = &snapshot_event; - l.last = &snapshot_event; - struct string msg = sim_string_from_events(temp.arena, l); - host_queue_write(ctx->host, client->channel_id, msg, 0); - //host_queue_write(ctx->host, HOST_CHANNEL_ID_ALL, msg, 0); + /* Create & encode snapshot event */ + { + struct sim_event snapshot_event = ZI; + snapshot_event.kind = SIM_EVENT_KIND_SNAPSHOT; + snapshot_event.tick = ctx->world->tick; + snapshot_event.snapshot_tick_start = ss0_tick; + snapshot_event.snapshot_tick_end = ss1_tick; + + { + struct bitbuff_writer bw = bw_from_bitbuff(&ctx->encoder_bitbuff); + sim_snapshot_encode(&bw, ss0, ss1, client); + snapshot_event.snapshot_encoded = bw_get_written(temp.arena, &bw); + } + + if (l.last) { + l.last->next = &snapshot_event; + } else { + l.first = &snapshot_event; + } + l.last = &snapshot_event; + } + + /* Encode events */ + struct string events_msg = ZI; + { + struct bitbuff_writer bw = bw_from_bitbuff(&ctx->encoder_bitbuff); + sim_events_encode(&bw, l); + events_msg = bw_get_written(temp.arena, &bw); + } + + host_queue_write(ctx->host, client->channel_id, events_msg, 0); arena_temp_end(temp); } } -#endif host_update(ctx->host); __profframe("Sim"); @@ -1457,46 +1451,39 @@ void sim_update(struct sim_ctx *ctx, i64 target_dt_ns) * Sim cmd * ========================== */ -struct string sim_string_from_cmds(struct arena *arena, struct sim_cmd_list cmds, u64 ack_tick) +void sim_cmds_encode(struct bitbuff_writer *bw, struct sim_cmd_list cmds, u64 ack_tick) { __prof; - struct byte_writer bw = bw_from_arena(arena); - for (struct sim_cmd *cmd = cmds.first; cmd; cmd = cmd->next) { - struct byte_writer bw_size = bw_branch(&bw, sizeof(u64)); - u64 start = bw_pos(&bw); - - bw_write_i8(&bw, cmd->kind); - bw_write_i8(&bw, cmd->state); - bw_write_var_uint(&bw, ack_tick); + bw_write_ibits(bw, cmd->kind, 8); + bw_write_ibits(bw, cmd->state, 8); + bw_write_uv(bw, ack_tick); #if COLLIDER_DEBUG - bw_write_u32(&bw, cmd->collider_gjk_steps); + bw_write_ubits(bw, cmd->collider_gjk_steps, 32); #endif switch (cmd->kind) { case SIM_CMD_KIND_PLAYER_CONTROL: { - bw_write_v2(&bw, cmd->move_dir); - bw_write_v2(&bw, cmd->aim_dir); + bw_write_f32(bw, cmd->move_dir.x); + bw_write_f32(bw, cmd->move_dir.y); + bw_write_f32(bw, cmd->aim_dir.x); + bw_write_f32(bw, cmd->aim_dir.y); } break; case SIM_CMD_KIND_CURSOR_MOVE: { - bw_write_v2(&bw, cmd->cursor_pos); + bw_write_f32(bw, cmd->cursor_pos.x); + bw_write_f32(bw, cmd->cursor_pos.y); } break; default: break; } - - u64 size = bw_pos(&bw) - start; - bw_write_u64(&bw_size, size); } - - return bw_get_written(&bw); } -void sim_cmds_from_host_events(struct arena *arena, struct host_event_array host_events, struct sim_cmd_list *cmds_out) +void sim_cmds_decode(struct arena *arena, struct host_event_array host_events, struct sim_cmd_list *cmds_out) { __prof; for (u64 i = 0; i < host_events.count; ++i) { @@ -1531,37 +1518,36 @@ void sim_cmds_from_host_events(struct arena *arena, struct host_event_array host case HOST_EVENT_KIND_MSG: { - struct byte_reader br = br_from_buffer(host_event.msg); - while (br_bytes_left(&br) > 0) { + struct bitbuff bb = bitbuff_from_string(host_event.msg); + struct bitbuff_reader br = br_from_bitbuff(&bb); + while (br_num_bits_left(&br) > 0) { struct sim_cmd *cmd = arena_push_zero(arena, struct sim_cmd); - u64 cmd_size = br_read_u64(&br); - u64 cmd_pos_end = br_pos(&br) + cmd_size; - cmd->kind = br_read_i8(&br); + cmd->kind = br_read_ibits(&br, 8); cmd->channel_id = host_event.channel_id; - cmd->state = br_read_i8(&br); - cmd->ack_tick = br_read_var_uint(&br); + cmd->state = br_read_ibits(&br, 8); + cmd->ack_tick = br_read_uv(&br); #if COLLIDER_DEBUG - cmd->collider_gjk_steps = br_read_u32(&br); + cmd->collider_gjk_steps = br_read_ubits(&br, 32); #endif switch (cmd->kind) { case SIM_CMD_KIND_PLAYER_CONTROL: { - cmd->move_dir = br_read_v2(&br); - cmd->aim_dir = br_read_v2(&br); + cmd->move_dir.x = br_read_f32(&br); + cmd->move_dir.y = br_read_f32(&br); + cmd->aim_dir.x = br_read_f32(&br); + cmd->aim_dir.y = br_read_f32(&br); } break; case SIM_CMD_KIND_CURSOR_MOVE: { - cmd->cursor_pos = br_read_v2(&br); + cmd->cursor_pos.x = br_read_f32(&br); + cmd->cursor_pos.y = br_read_f32(&br); } break; default: break; } - ASSERT(br_pos(&br) == cmd_pos_end); - br_seek_to(&br, cmd_pos_end); - if (cmds_out->last) { cmds_out->last->next = cmd; } else { @@ -1580,34 +1566,27 @@ void sim_cmds_from_host_events(struct arena *arena, struct host_event_array host * Sim event * ========================== */ -struct string sim_string_from_events(struct arena *arena, struct sim_event_list events) +void sim_events_encode(struct bitbuff_writer *bw, struct sim_event_list events) { __prof; - struct byte_writer bw = bw_from_arena(arena); for (struct sim_event *event = events.first; event; event = event->next) { - struct byte_writer bw_size = bw_branch(&bw, sizeof(u64)); - u64 start = bw_pos(&bw); - bw_write_var_uint(&bw, event->tick); - bw_write_i8(&bw, event->kind); + bw_write_uv(bw, event->tick); + bw_write_ibits(bw, event->kind, 8); switch (event->kind) { case SIM_EVENT_KIND_SNAPSHOT: { - bw_write_var_uint(&bw, event->snapshot_tick_start); - bw_write_var_uint(&bw, event->snapshot_tick_end); - bw_write_string(&bw, event->snapshot_encoded); + bw_write_uv(bw, event->snapshot_tick_start); + bw_write_uv(bw, event->snapshot_tick_end); + bw_write_string(bw, event->snapshot_encoded); } break; default: break; } - - u64 size = bw_pos(&bw) - start; - bw_write_u64(&bw_size, size); } - return bw_get_written(&bw); } -void sim_events_from_host_events(struct arena *arena, struct host_event_array host_events, struct sim_event_list *events_out) +void sim_events_decode(struct arena *arena, struct host_event_array host_events, struct sim_event_list *events_out) { __prof; for (u64 i = 0; i < host_events.count; ++i) { @@ -1629,26 +1608,21 @@ void sim_events_from_host_events(struct arena *arena, struct host_event_array ho case HOST_EVENT_KIND_MSG: { - struct byte_reader br = br_from_buffer(host_event.msg); - while (br_bytes_left(&br) > 0) { - u64 event_size = br_read_u64(&br); - u64 event_pos_end = br_pos(&br) + event_size; - - sim_event->tick = br_read_var_uint(&br); - sim_event->kind = br_read_i8(&br); + struct bitbuff bb = bitbuff_from_string(host_event.msg); + struct bitbuff_reader br = br_from_bitbuff(&bb); + while (br_num_bits_left(&br) > 0) { + sim_event->tick = br_read_uv(&br); + sim_event->kind = br_read_ibits(&br, 8); switch (sim_event->kind) { case SIM_EVENT_KIND_SNAPSHOT: { - sim_event->snapshot_tick_start = br_read_var_uint(&br); - sim_event->snapshot_tick_end = br_read_var_uint(&br); + sim_event->snapshot_tick_start = br_read_uv(&br); + sim_event->snapshot_tick_end = br_read_uv(&br); sim_event->snapshot_encoded = br_read_string(arena, &br); } break; default: break; } - - ASSERT(br_pos(&br) == event_pos_end); - br_seek_to(&br, event_pos_end); } } break; diff --git a/src/sim.h b/src/sim.h index 290ef547..782160fc 100644 --- a/src/sim.h +++ b/src/sim.h @@ -3,6 +3,7 @@ #include "sim_ent.h" #include "sim_snapshot.h" +#include "bitbuff.h" struct sprite_startup_receipt; struct phys_startup_receipt; @@ -124,7 +125,11 @@ struct sim_event_list { struct sim_ctx { struct arena arena; - i64 last_tick_ns; /* When did the last tick simulate in program time */ + /* Dynamic bitbuff used by encoders */ + struct bitbuff encoder_bitbuff; + + /* When did the last tick simulate in program time */ + i64 last_tick_ns; struct sprite_scope *sprite_frame_scope; @@ -166,23 +171,13 @@ void sim_update(struct sim_ctx *ctx, i64 target_dt_ns); /* TODO: Move this */ -#include "byteio.h" #include "host.h" -struct sim_encoder { - struct byte_writer bw; - struct sim_client *client; -}; +void sim_cmds_encode(struct bitbuff_writer *bw, struct sim_cmd_list cmds, u64 ack_tick); +void sim_cmds_decode(struct arena *arena, struct host_event_array host_events, struct sim_cmd_list *cmds_out); -struct sim_decoder { - struct byte_reader br; -}; - -struct string sim_string_from_cmds(struct arena *arena, struct sim_cmd_list cmds, u64 ack_tick); -void sim_cmds_from_host_events(struct arena *arena, struct host_event_array host_events, struct sim_cmd_list *cmds_out); - -struct string sim_string_from_events(struct arena *arena, struct sim_event_list events); -void sim_events_from_host_events(struct arena *arena, struct host_event_array host_events, struct sim_event_list *events_out); +void sim_events_encode(struct bitbuff_writer *bw, struct sim_event_list events); +void sim_events_decode(struct arena *arena, struct host_event_array host_events, struct sim_event_list *events_out); diff --git a/src/sim_client.c b/src/sim_client.c index d978f587..a175cb8d 100644 --- a/src/sim_client.c +++ b/src/sim_client.c @@ -124,19 +124,27 @@ void sim_client_lerp(struct sim_client *c, struct sim_client *c0, struct sim_cli * Encode * ========================== */ -void sim_client_encode(struct sim_encoder *enc, struct sim_client *c0, struct sim_client *c1) +void sim_client_encode(struct bitbuff_writer *bw, struct sim_client *c0, struct sim_client *c1) { - struct byte_writer *bw = &enc->bw; struct sim_snapshot *ss = c1->ss; + /* TODO: Granular delta encoding */ + + u64 pos = 0; c1->ss = c0->ss; - if (MEMEQ_STRUCT(c0, c1)) { - bw_write_u8(bw, 0); - } else { - bw_write_u8(bw, 1); - struct string bytes = STRING_FROM_STRUCT(c1); - bw_write_var_uint(bw, bytes.len); - bw_write_bytes(bw, bytes); + while (pos < sizeof(*c1)) { + u64 chunk_size = min_u64(pos + 8, sizeof(*c1)) - pos; + u8 *chunk0 = (u8 *)c0 + pos; + u8 *chunk1 = (u8 *)c1 + pos; + if (MEMEQ(chunk0, chunk1, chunk_size)) { + bw_write_bit(bw, 0); + } else { + bw_write_bit(bw, 1); + u64 bits = 0; + MEMCPY(&bits, chunk1, chunk_size); + bw_write_ubits(bw, bits, 64); + } + pos += 8; } c1->ss = ss; } @@ -145,14 +153,20 @@ void sim_client_encode(struct sim_encoder *enc, struct sim_client *c0, struct si * Decode * ========================== */ -void sim_client_decode(struct sim_decoder *dec, struct sim_client *c) +void sim_client_decode(struct bitbuff_reader *br, struct sim_client *e) { - struct byte_reader *br = &dec->br; - struct sim_snapshot *ss = c->ss; + struct sim_snapshot *ss = e->ss; - if (br_read_u8(br)) { - u64 size = br_read_var_uint(br); - *c = *(struct sim_client *)br_seek(br, size); - c->ss = ss; + u64 pos = 0; + while (pos < sizeof(*e)) { + u8 *chunk = (u8 *)e + pos; + if (br_read_bit(br)) { + u64 chunk_size = min_u64(pos + 8, sizeof(*e)) - pos; + u64 bits = br_read_ubits(br, 64); + MEMCPY(chunk, &bits, chunk_size); + } + pos += 8; } + + e->ss = ss; } diff --git a/src/sim_client.h b/src/sim_client.h index f2dfa84c..ece3a446 100644 --- a/src/sim_client.h +++ b/src/sim_client.h @@ -45,14 +45,29 @@ INLINE struct sim_client *sim_client_nil(void) return *_g_sim_client_nil; } +/* ========================== * + * Client + * ========================== */ + struct sim_client *sim_client_from_handle(struct sim_snapshot *ss, struct sim_client_handle handle); struct sim_client *sim_client_from_channel_id(struct sim_snapshot *ss, struct host_channel_id channel_id); struct sim_client *sim_client_alloc(struct sim_snapshot *ss, struct host_channel_id channel_id); void sim_client_release(struct sim_client *client); +/* ========================== * + * Lerp + * ========================== */ + void sim_client_lerp(struct sim_client *c, struct sim_client *c0, struct sim_client *c1, f64 blend); -void sim_client_encode(struct sim_encoder *enc, struct sim_client *c0, struct sim_client *c1); -void sim_client_decode(struct sim_decoder *dec, struct sim_client *c); +/* ========================== * + * Encode + * ========================== */ + +struct bitbuff_writer; +struct bitbuff_reader; + +void sim_client_encode(struct bitbuff_writer *bw, struct sim_client *c0, struct sim_client *c1); +void sim_client_decode(struct bitbuff_reader *br, struct sim_client *c); #endif diff --git a/src/sim_ent.c b/src/sim_ent.c index 24dac1ee..897351be 100644 --- a/src/sim_ent.c +++ b/src/sim_ent.c @@ -480,36 +480,49 @@ void sim_ent_lerp(struct sim_ent *e, struct sim_ent *e0, struct sim_ent *e1, f64 * Encode * ========================== */ -void sim_ent_encode(struct sim_encoder *enc, struct sim_ent *e0, struct sim_ent *e1) +void sim_ent_encode(struct bitbuff_writer *bw, struct sim_ent *e0, struct sim_ent *e1) { - struct byte_writer *bw = &enc->bw; struct sim_snapshot *ss = e1->ss; + /* TODO: Granular delta encoding */ + + u64 pos = 0; e1->ss = e0->ss; - if (MEMEQ_STRUCT(e0, e1)) { - bw_write_u8(bw, 0); - } else { - bw_write_u8(bw, 1); - struct string bytes = STRING_FROM_STRUCT(e1); - bw_write_var_uint(bw, bytes.len); - bw_write_bytes(bw, bytes); + while (pos < sizeof(*e1)) { + u64 chunk_size = min_u64(pos + 8, sizeof(*e1)) - pos; + u8 *chunk0 = (u8 *)e0 + pos; + u8 *chunk1 = (u8 *)e1 + pos; + if (MEMEQ(chunk0, chunk1, chunk_size)) { + bw_write_bit(bw, 0); + } else { + bw_write_bit(bw, 1); + u64 bits = 0; + MEMCPY(&bits, chunk1, chunk_size); + bw_write_ubits(bw, bits, 64); + } + pos += 8; } e1->ss = ss; - } /* ========================== * * Decode * ========================== */ -void sim_ent_decode(struct sim_decoder *dec, struct sim_ent *e) +void sim_ent_decode(struct bitbuff_reader *br, struct sim_ent *e) { - struct byte_reader *br = &dec->br; struct sim_snapshot *ss = e->ss; - if (br_read_u8(br)) { - u64 size = br_read_var_uint(br); - *e = *(struct sim_ent *)br_seek(br, size); - e->ss = ss; + u64 pos = 0; + while (pos < sizeof(*e)) { + u8 *chunk = (u8 *)e + pos; + if (br_read_bit(br)) { + u64 chunk_size = min_u64(pos + 8, sizeof(*e)) - pos; + u64 bits = br_read_ubits(br, 64); + MEMCPY(chunk, &bits, chunk_size); + } + pos += 8; } + + e->ss = ss; } diff --git a/src/sim_ent.h b/src/sim_ent.h index 0b93be5d..e746a054 100644 --- a/src/sim_ent.h +++ b/src/sim_ent.h @@ -8,8 +8,8 @@ #define SIM_ENT_NIL_HANDLE ((struct sim_ent_handle){ .gen = 0, .idx = 0 }) #define SIM_ENT_ROOT_HANDLE ((struct sim_ent_handle){ .gen = 1, .idx = 0 }) -struct sim_encoder; -struct sim_decoder; +struct bitbuff_writer; +struct bitbuff_reader; enum sim_ent_prop { SIM_ENT_PROP_NONE, @@ -406,7 +406,7 @@ void sim_ent_activate(struct sim_ent *ent, u64 current_tick); void sim_ent_lerp(struct sim_ent *e, struct sim_ent *e0, struct sim_ent *e1, f64 blend); /* Encode / decode */ -void sim_ent_encode(struct sim_encoder *enc, struct sim_ent *e0, struct sim_ent *e1); -void sim_ent_decode(struct sim_decoder *dec, struct sim_ent *e); +void sim_ent_encode(struct bitbuff_writer *bw, struct sim_ent *e0, struct sim_ent *e1); +void sim_ent_decode(struct bitbuff_reader *br, struct sim_ent *e); #endif diff --git a/src/sim_snapshot.c b/src/sim_snapshot.c index 02074274..9064ecb7 100644 --- a/src/sim_snapshot.c +++ b/src/sim_snapshot.c @@ -412,36 +412,33 @@ struct sim_snapshot *sim_snapshot_alloc_from_lerp(struct sim_snapshot_store *sto * Encode * ========================== */ -void sim_snapshot_encode(struct sim_encoder *enc, struct sim_snapshot *ss0, struct sim_snapshot *ss1) +void sim_snapshot_encode(struct bitbuff_writer *bw, struct sim_snapshot *ss0, struct sim_snapshot *ss1, struct sim_client *client) { __prof; - struct byte_writer *bw = &enc->bw; - struct sim_client *client = enc->client; + bw_write_uv(bw, ss1->continuity_gen); - bw_write_var_uint(bw, ss1->continuity_gen); - - bw_write_var_sint(bw, ss1->real_dt_ns); - bw_write_var_sint(bw, ss1->real_time_ns); + bw_write_iv(bw, ss1->real_dt_ns); + bw_write_iv(bw, ss1->real_time_ns); bw_write_f64(bw, ss1->world_timescale); - bw_write_var_sint(bw, ss1->world_dt_ns); - bw_write_var_sint(bw, ss1->world_time_ns); + bw_write_iv(bw, ss1->world_dt_ns); + bw_write_iv(bw, ss1->world_time_ns); - bw_write_var_uint(bw, client->handle.gen); - bw_write_var_uint(bw, client->handle.idx); + bw_write_uv(bw, client->handle.gen); + bw_write_uv(bw, client->handle.idx); /* Clients */ if (ss1->num_clients_allocated == ss0->num_clients_allocated) { - bw_write_u8(bw, 0); + bw_write_bit(bw, 0); } else { - bw_write_u8(bw, 1); - bw_write_var_uint(bw, ss1->num_clients_allocated); + bw_write_bit(bw, 1); + bw_write_uv(bw, ss1->num_clients_allocated); } if (ss1->num_clients_reserved == ss0->num_clients_reserved) { - bw_write_u8(bw, 0); + bw_write_bit(bw, 0); } else { - bw_write_u8(bw, 1); - bw_write_var_uint(bw, ss1->num_clients_reserved); + bw_write_bit(bw, 1); + bw_write_uv(bw, ss1->num_clients_reserved); } for (u64 i = 0; i < ss1->num_clients_reserved; ++i) { struct sim_client *c0 = sim_client_nil(); @@ -449,21 +446,21 @@ void sim_snapshot_encode(struct sim_encoder *enc, struct sim_snapshot *ss0, stru c0 = &ss0->clients[i]; } struct sim_client *c1 = &ss1->clients[i]; - sim_client_encode(enc, c0, c1); + sim_client_encode(bw, c0, c1); } /* Ents */ if (ss1->num_ents_allocated == ss0->num_ents_allocated) { - bw_write_u8(bw, 0); + bw_write_bit(bw, 0); } else { - bw_write_u8(bw, 1); - bw_write_var_uint(bw, ss1->num_ents_allocated); + bw_write_bit(bw, 1); + bw_write_uv(bw, ss1->num_ents_allocated); } if (ss1->num_ents_reserved == ss0->num_ents_reserved) { - bw_write_u8(bw, 0); + bw_write_bit(bw, 0); } else { - bw_write_u8(bw, 1); - bw_write_var_uint(bw, ss1->num_ents_reserved); + bw_write_bit(bw, 1); + bw_write_uv(bw, ss1->num_ents_reserved); } for (u64 i = 0; i < ss1->num_ents_reserved; ++i) { struct sim_ent *e0 = sim_ent_nil(); @@ -471,7 +468,7 @@ void sim_snapshot_encode(struct sim_encoder *enc, struct sim_snapshot *ss0, stru e0 = &ss0->ents[i]; } struct sim_ent *e1 = &ss1->ents[i]; - sim_ent_encode(enc, e0, e1); + sim_ent_encode(bw, e0, e1); } } @@ -479,30 +476,29 @@ void sim_snapshot_encode(struct sim_encoder *enc, struct sim_snapshot *ss0, stru * Decode * ========================== */ -void sim_snapshot_decode(struct sim_decoder *dec, struct sim_snapshot *ss) +void sim_snapshot_decode(struct bitbuff_reader *br, struct sim_snapshot *ss) { __prof; - struct byte_reader *br = &dec->br; - ss->continuity_gen = br_read_var_uint(br); + ss->continuity_gen = br_read_uv(br); - ss->real_dt_ns = br_read_var_sint(br); - ss->real_time_ns = br_read_var_sint(br); + ss->real_dt_ns = br_read_iv(br); + ss->real_time_ns = br_read_iv(br); ss->world_timescale = br_read_f64(br); - ss->world_dt_ns = br_read_var_sint(br); - ss->world_time_ns = br_read_var_sint(br); + ss->world_dt_ns = br_read_iv(br); + ss->world_time_ns = br_read_iv(br); - ss->local_client.gen = br_read_var_uint(br); - ss->local_client.idx = br_read_var_uint(br); + ss->local_client.gen = br_read_uv(br); + ss->local_client.idx = br_read_uv(br); /* Clients */ - if (br_read_u8(br)) { - ss->num_clients_allocated = br_read_var_uint(br); + if (br_read_bit(br)) { + ss->num_clients_allocated = br_read_uv(br); } - if (br_read_u8(br)) { + if (br_read_bit(br)) { u64 old_num_clients_reserved = ss->num_clients_reserved; - ss->num_clients_reserved = br_read_var_uint(br); + ss->num_clients_reserved = br_read_uv(br); i64 reserve_diff = (i64)ss->num_clients_reserved - (i64)old_num_clients_reserved; if (reserve_diff > 0) { arena_push_array(&ss->clients_arena, struct sim_client, reserve_diff); @@ -515,16 +511,16 @@ void sim_snapshot_decode(struct sim_decoder *dec, struct sim_snapshot *ss) } for (u64 i = 0; i < ss->num_clients_reserved; ++i) { struct sim_client *c = &ss->clients[i]; - sim_client_decode(dec, c); + sim_client_decode(br, c); } /* Ents */ - if (br_read_u8(br)) { - ss->num_ents_allocated = br_read_var_uint(br); + if (br_read_bit(br)) { + ss->num_ents_allocated = br_read_uv(br); } - if (br_read_u8(br)) { + if (br_read_bit(br)) { u64 old_num_ents_reserved = ss->num_ents_reserved; - ss->num_ents_reserved = br_read_var_uint(br); + ss->num_ents_reserved = br_read_uv(br); i64 reserve_diff = (i64)ss->num_ents_reserved - (i64)old_num_ents_reserved; if (reserve_diff > 0) { arena_push_array(&ss->ents_arena, struct sim_ent, reserve_diff); @@ -537,6 +533,6 @@ void sim_snapshot_decode(struct sim_decoder *dec, struct sim_snapshot *ss) } for (u64 i = 0; i < ss->num_ents_reserved; ++i) { struct sim_ent *e = &ss->ents[i]; - sim_ent_decode(dec, e); + sim_ent_decode(br, e); } } diff --git a/src/sim_snapshot.h b/src/sim_snapshot.h index 1af9b565..a8be8077 100644 --- a/src/sim_snapshot.h +++ b/src/sim_snapshot.h @@ -4,9 +4,6 @@ #include "sim_ent.h" #include "sim_client.h" -struct sim_encoder; -struct sim_decoder; - struct sim_snapshot_store { b32 valid; struct arena arena; @@ -106,6 +103,9 @@ INLINE struct sim_snapshot_store *sim_snapshot_store_nil(void) * Snapshot * ========================== */ +struct bitbuff_writer; +struct bitbuff_reader; + /* Alloc */ struct sim_snapshot *sim_snapshot_alloc(struct sim_snapshot_store *store, struct sim_snapshot *src, u64 tick); void sim_snapshot_release(struct sim_snapshot *sim_snapshot); @@ -117,8 +117,8 @@ struct sim_snapshot *sim_snapshot_from_tick(struct sim_snapshot_store *store, u6 struct sim_snapshot *sim_snapshot_alloc_from_lerp(struct sim_snapshot_store *store, struct sim_snapshot *ss0, struct sim_snapshot *ss1, f64 blend); /* Encode / decode */ -void sim_snapshot_encode(struct sim_encoder *enc, struct sim_snapshot *ss0, struct sim_snapshot *ss1); -void sim_snapshot_decode(struct sim_decoder *dec, struct sim_snapshot *ss); +void sim_snapshot_encode(struct bitbuff_writer *bw, struct sim_snapshot *ss0, struct sim_snapshot *ss1, struct sim_client *client); +void sim_snapshot_decode(struct bitbuff_reader *br, struct sim_snapshot *ss); #endif diff --git a/src/sys_win32.c b/src/sys_win32.c index d7ce41dd..59928bd3 100644 --- a/src/sys_win32.c +++ b/src/sys_win32.c @@ -2192,7 +2192,7 @@ int CALLBACK wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance, abort: if (error_msg) { - MessageBoxExW(NULL, error_msg, L"Fatal error", MB_ICONSTOP | MB_SETFOREGROUND, 0); + MessageBoxExW(NULL, error_msg, L"Fatal error", MB_ICONSTOP | MB_SETFOREGROUND | MB_TOPMOST, 0); ASSERT(false); return 1; } diff --git a/src/tar.c b/src/tar.c index fa012650..153efcaa 100644 --- a/src/tar.c +++ b/src/tar.c @@ -1,5 +1,5 @@ #include "tar.h" -#include "byteio.h" +#include "bitbuff.h" #include "string.h" #include "util.h" @@ -70,13 +70,14 @@ struct tar_archive tar_parse(struct arena *arena, struct string data, struct str __prof; struct tar_archive archive = ZI; - struct byte_reader br = br_from_buffer(data); + struct bitbuff bb = bitbuff_from_string(data); + struct bitbuff_reader br = br_from_bitbuff_no_debug(&bb); u64 num_files = 0; - while (br_bytes_left(&br) > 1024) { + while (br_num_bytes_left(&br) > 1024) { struct tar_header header = ZI; - br_read_to_struct(&br, &header); + br_read_bytes(&br, STRING_FROM_STRUCT(&header)); if (!string_eq(STRING_FROM_ARRAY(header.ustar_indicator), LIT("ustar\0"))) { /* Invalid header */ @@ -93,7 +94,7 @@ struct tar_archive tar_parse(struct arena *arena, struct string data, struct str struct string file_size_oct_str = { .len = 11, .text = header.file_size }; u64 file_size = str_oct_to_u64(file_size_oct_str); - u8 *file_data_ptr = br_seek(&br, file_size); + u8 *file_data_ptr = br_read_bytes_raw(&br, file_size); if (!file_data_ptr) { file_size = 0; } @@ -101,7 +102,7 @@ struct tar_archive tar_parse(struct arena *arena, struct string data, struct str /* Skip sector padding */ u64 remaining = (512 - (file_size % 512)) % 512; - br_seek(&br, remaining); + br_seek_bytes(&br, remaining); b32 is_dir = header.file_type == TAR_TYPE_DIRECTORY; if (!is_dir && header.file_type != TAR_TYPE_FILE) { diff --git a/src/user.c b/src/user.c index 17f7cc7e..e2b35000 100644 --- a/src/user.c +++ b/src/user.c @@ -21,7 +21,7 @@ #include "log.h" #include "sock.h" #include "host.h" -#include "byteio.h" +#include "bitbuff.h" struct bind_state { b32 is_held; /* Is this bind held down this frame */ @@ -53,6 +53,9 @@ GLOBAL struct { struct sim_snapshot_store *world_snapshot_store; /* Contains single world snapshot from result of blending sim snapshots */ struct sim_snapshot *world; + /* Dynamic bitbuff used by encoders */ + struct bitbuff encoder_bitbuff; + /* Usage stats */ i64 last_second_reset_ns; struct second_stat client_bytes_read; @@ -188,6 +191,8 @@ struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr, //struct sock_address bind_addr = sock_address_from_any_local_interface_with_dynamic_port(); G.host = host_alloc(0); + G.encoder_bitbuff = bitbuff_alloc(GIGABYTE(64)); + G.world_to_ui_xf = XFORM_IDENT; G.world_cmd_buffer = renderer_cmd_buffer_alloc(); G.ui_cmd_buffer = renderer_cmd_buffer_alloc(); @@ -405,7 +410,7 @@ INTERNAL void user_update(void) { host_update(G.host); struct host_event_array host_events = host_pop_events(scratch.arena, G.host); - sim_events_from_host_events(scratch.arena, host_events, &sim_events); + sim_events_decode(scratch.arena, host_events, &sim_events); } /* ========================== * @@ -438,24 +443,6 @@ INTERNAL void user_update(void) case SIM_EVENT_KIND_SNAPSHOT: { -#if 0 - if (event_tick > G.world->tick) { - u64 ss0_tick = event->snapshot_tick_start; - u64 ss1_tick = event->snapshot_tick_end; - struct sim_snapshot *ss0 = sim_snapshot_from_tick(G.sim_snapshot_store, ss0_tick); - struct sim_snapshot *ss1 = sim_snapshot_from_tick(G.sim_snapshot_store, ss1_tick); - if (ss0->tick == ss0_tick) { - if (!ss1->valid) { - ss1 = sim_snapshot_alloc(G.sim_snapshot_store, ss0, ss1_tick); - sim_snapshot_decode(event->snapshot_encoded, ss1); - ss1->received_at_ns = G.real_time_ns; - } - } else { - /* User should always have src tick present */ - ASSERT(false); - } - } -#else if (event_tick > G.world->tick) { u64 ss0_tick = event->snapshot_tick_start; u64 ss1_tick = event->snapshot_tick_end; @@ -465,16 +452,16 @@ INTERNAL void user_update(void) if (!ss1->valid) { ss1 = sim_snapshot_alloc(G.sim_snapshot_store, ss0, ss1_tick); ss1->received_at_ns = G.real_time_ns; - struct sim_decoder decoder = ZI; - decoder.br = br_from_buffer(event->snapshot_encoded); - sim_snapshot_decode(&decoder, ss1); + + struct bitbuff bb = bitbuff_from_string(event->snapshot_encoded); + struct bitbuff_reader br = br_from_bitbuff(&bb); + sim_snapshot_decode(&br, ss1); } } else { /* User should always have src tick present */ ASSERT(false); } } -#endif } break; default: break; @@ -1526,6 +1513,11 @@ INTERNAL void user_update(void) if (font) { struct temp_arena temp = arena_temp_begin(scratch.arena); +#if BITBUFF_DEBUG + draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("(bitbuff debug enabled)"))); + pos.y += spacing; +#endif + draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Read from local sim: %F mbit/s"), FMT_FLOAT_P((f64)G.client_bytes_read.last_second * 8 / 1000 / 1000, 3))); pos.y += spacing; @@ -1622,12 +1614,10 @@ INTERNAL void user_update(void) /* Publish sim cmds */ { - struct temp_arena temp = arena_temp_begin(scratch.arena); - - struct string cmds_str = sim_string_from_cmds(temp.arena, cmd_list, G.local_sim_last_known_tick); + struct bitbuff_writer bw = bw_from_bitbuff(&G.encoder_bitbuff); + sim_cmds_encode(&bw, cmd_list, G.local_sim_last_known_tick); + struct string cmds_str = bw_get_written(scratch.arena, &bw); host_queue_write(G.host, HOST_CHANNEL_ID_ALL, cmds_str, 0); - - arena_temp_end(temp); } host_update(G.host);