replace byteio with bitbuff

This commit is contained in:
jacob 2025-02-13 17:15:23 -06:00
parent 6d58619fbe
commit be5d10e2b0
25 changed files with 1379 additions and 895 deletions

79
build.c
View File

@ -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,14 +214,15 @@ void BuildStepSimpleCommand(void *arg_raw)
{
TempArena scratch = ScratchBeginNoConflict();
Step *s = arg_raw;
BuildStepSimpleCommandArg *arg = s->arg;
SH_CommandResult result = RunCommand(scratch.arena, arg->cmd);
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);
{
OS_Lock res_lock = OS_MutexLockE(&s->res_mutex);
String result_output_cleaned = CleanResultOutput(scratch.arena, result.output);
if (result_output_cleaned.len > 0) {
OS_Lock sl_lock = OS_MutexLockE(&sl->mutex);
{
@ -227,9 +233,22 @@ void BuildStepSimpleCommand(void *arg_raw)
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);
}
OS_ConditionVariableBroadcast(&s->res_cv);
OS_MutexUnlock(&res_lock);
}
ScratchEnd(scratch);
}
@ -238,15 +257,16 @@ void BuildStepMsvcCompileCommand(void *arg_raw)
{
TempArena scratch = ScratchBeginNoConflict();
Step *s = arg_raw;
BuildStepMsvcCompileCommandArg *arg = s->arg;
SH_CommandResult result = RunCommand(scratch.arena, arg->cmd);
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);
@ -260,6 +280,18 @@ void BuildStepMsvcCompileCommand(void *arg_raw)
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);
}
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);

View File

@ -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));

193
src/ase.c
View File

@ -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);

750
src/bitbuff.c Normal file
View File

@ -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

112
src/bitbuff.h Normal file
View File

@ -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

View File

@ -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
* ========================== */

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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
* ========================== */

View File

@ -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 <chunk_id> out of <chunk_count> belonging to message <msg_id> */
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;

View File

@ -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")))

View File

@ -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 <memory.h>
#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

View File

@ -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)

170
src/sim.c
View File

@ -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_list l = ZI;
/* 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 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);
{
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);
}
sim_snapshot_encode(&encoder, ss0, ss1);
snapshot_event.snapshot_encoded = bw_get_written(&encoder.bw);
struct sim_event_list l = ZI;
if (l.last) {
l.last->next = &snapshot_event;
} else {
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);
/* 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;

View File

@ -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);

View File

@ -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);
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_u8(bw, 1);
struct string bytes = STRING_FROM_STRUCT(c1);
bw_write_var_uint(bw, bytes.len);
bw_write_bytes(bw, bytes);
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;
}

View File

@ -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

View File

@ -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);
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_u8(bw, 1);
struct string bytes = STRING_FROM_STRUCT(e1);
bw_write_var_uint(bw, bytes.len);
bw_write_bytes(bw, bytes);
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;
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -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) {

View File

@ -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);