diff --git a/build.bat b/build.bat index f9dee01..62db48f 100644 --- a/build.bat +++ b/build.bat @@ -1,6 +1,6 @@ @echo off -REM Setup ========================================================================================== +REM Setup ////////////////////////////////////////////////////////////////////////////////////////// set script_dir_backslash=%~dp0 set script_dir=%script_dir_backslash:~0,-1% set build_dir=%script_dir%\Build @@ -12,15 +12,19 @@ if not exist %part1_build_dir% mkdir %part1_build_dir% set part2_dir=%script_dir%\part2 set part2_build_dir=%build_dir%\part2 if not exist %part2_build_dir% mkdir %part2_build_dir% -goto :part2 -REM Part 1 ========================================================================================= -REM Build ========================================================================================== +set part3_dir=%script_dir%\part3 +set part3_build_dir=%build_dir%\part3 +if not exist %part3_build_dir% mkdir %part3_build_dir% +goto :part3 + +REM Part 1 ///////////////////////////////////////////////////////////////////////////////////////// +REM Build ////////////////////////////////////////////////////////////////////////////////////////// pushd %part1_build_dir% cl %part1_dir%\sim8086.c /W4 /WX /Z7 /nologo || exit /b 1 popd -REM Tests ========================================================================================== +REM Tests ////////////////////////////////////////////////////////////////////////////////////////// set listing_0037=listing_0037_single_register_mov set part1_build_dir_listing_0037=%part1_build_dir%\%listing_0037% @@ -30,9 +34,9 @@ copy /Y %part1_dir%\%listing_0037% %part1_build_dir% 1>NUL nasm %part1_build_dir_listing_0037%_disassembled.asm fc /B %part1_build_dir_listing_0037% %part1_build_dir_listing_0037%_disassembled || exit /b 1 -REM ================================================================================================ -set listing_0038=listing_0038_many_register_mov -set part1_build_dir_listing_0038=%part1_build_dir%\%listing_0038% +REM //////////////////////////////////////////////////////////////////////////////////////////////// +set listing_0038/listing_0038_many_register_mov +set part1_build_dir_listing_0038/%part1_build_dir%\%listing_0038% copy /Y %part1_dir%\%listing_0038% %part1_build_dir% 1>NUL @@ -40,9 +44,9 @@ copy /Y %part1_dir%\%listing_0038% %part1_build_dir% 1>NUL nasm %part1_build_dir_listing_0038%_disassembled.asm fc /B %part1_build_dir_listing_0038% %part1_build_dir_listing_0038%_disassembled || exit /b 1 -REM ================================================================================================ -set listing_0039=listing_0039_more_movs -set part1_build_dir_listing_0039=%part1_build_dir%\%listing_0039% +REM //////////////////////////////////////////////////////////////////////////////////////////////// +set listing_0039/listing_0039_more_movs +set part1_build_dir_listing_0039/%part1_build_dir%\%listing_0039% copy /Y %part1_dir%\%listing_0039% %part1_build_dir% 1>NUL @@ -50,9 +54,9 @@ copy /Y %part1_dir%\%listing_0039% %part1_build_dir% 1>NUL nasm %part1_build_dir_listing_0039%_disassembled.asm fc /B %part1_build_dir_listing_0039% %part1_build_dir_listing_0039%_disassembled || exit /b 1 -REM ================================================================================================ -set listing_0040=listing_0040_challenge_movs -set part1_build_dir_listing_0040=%part1_build_dir%\%listing_0040% +REM //////////////////////////////////////////////////////////////////////////////////////////////// +set listing_0040/listing_0040_challenge_movs +set part1_build_dir_listing_0040/%part1_build_dir%\%listing_0040% copy /Y %part1_dir%\%listing_0040% %part1_build_dir% 1>NUL @@ -60,9 +64,9 @@ copy /Y %part1_dir%\%listing_0040% %part1_build_dir% 1>NUL nasm %part1_build_dir_listing_0040%_disassembled.asm fc /B %part1_build_dir_listing_0040% %part1_build_dir_listing_0040%_disassembled || exit /b 1 -REM ================================================================================================ -set listing_0041=listing_0041_add_sub_cmp_jnz -set part1_build_dir_listing_0041=%part1_build_dir%\%listing_0041% +REM //////////////////////////////////////////////////////////////////////////////////////////////// +set listing_0041/listing_0041_add_sub_cmp_jnz +set part1_build_dir_listing_0041/%part1_build_dir%\%listing_0041% copy /Y %part1_dir%\%listing_0041% %part1_build_dir% 1>NUL @@ -70,9 +74,9 @@ copy /Y %part1_dir%\%listing_0041% %part1_build_dir% 1>NUL nasm %part1_build_dir_listing_0041%_disassembled.asm fc /B %part1_build_dir_listing_0041% %part1_build_dir_listing_0041%_disassembled || exit /b 1 -REM ================================================================================================ -set listing_0042=listing_0042_completionist_decode -set part1_build_dir_listing_0042=%part1_build_dir%\%listing_0042% +REM //////////////////////////////////////////////////////////////////////////////////////////////// +set listing_0042/listing_0042_completionist_decode +set part1_build_dir_listing_0042/%part1_build_dir%\%listing_0042% copy /Y %part1_dir%\%listing_0042% %part1_build_dir% 1>NUL @@ -80,9 +84,9 @@ copy /Y %part1_dir%\%listing_0042% %part1_build_dir% 1>NUL nasm %part1_build_dir_listing_0042%_disassembled.asm fc /B %part1_build_dir_listing_0042% %part1_build_dir_listing_0042%_disassembled || exit /b 1 -REM ================================================================================================ -set listing_0043=listing_0043_immediate_movs -set part1_build_dir_listing_0043=%part1_build_dir%\%listing_0043% +REM //////////////////////////////////////////////////////////////////////////////////////////////// +set listing_0043/listing_0043_immediate_movs +set part1_build_dir_listing_0043/%part1_build_dir%\%listing_0043% copy /Y %part1_dir%\%listing_0043% %part1_build_dir% 1>NUL copy /Y %part1_dir%\%listing_0043%.txt %part1_build_dir% 1>NUL @@ -95,9 +99,9 @@ nasm %part1_build_dir_listing_0043%_disassembled.asm fc /B %part1_build_dir_listing_0043% %part1_build_dir_listing_0043%_disassembled || exit /b 1 fc /N %part1_build_dir_listing_0043%.txt %part1_build_dir_listing_0043%_disassembled.txt || exit /b 1 -REM ================================================================================================ -set listing_0044=listing_0044_register_movs -set part1_build_dir_listing_0044=%part1_build_dir%\%listing_0044% +REM //////////////////////////////////////////////////////////////////////////////////////////////// +set listing_0044/listing_0044_register_movs +set part1_build_dir_listing_0044/%part1_build_dir%\%listing_0044% copy /Y %part1_dir%\%listing_0044% %part1_build_dir% 1>NUL copy /Y %part1_dir%\%listing_0044%.txt %part1_build_dir% 1>NUL @@ -110,9 +114,9 @@ nasm %part1_build_dir_listing_0044%_disassembled.asm fc /B %part1_build_dir_listing_0044% %part1_build_dir_listing_0044%_disassembled || exit /b 1 fc /N %part1_build_dir_listing_0044%.txt %part1_build_dir_listing_0044%_disassembled.txt || exit /b 1 -REM ================================================================================================ -set listing_0045=listing_0045_challenge_register_movs -set part1_build_dir_listing_0045=%part1_build_dir%\%listing_0045% +REM //////////////////////////////////////////////////////////////////////////////////////////////// +set listing_0045/listing_0045_challenge_register_movs +set part1_build_dir_listing_0045/%part1_build_dir%\%listing_0045% copy /Y %part1_dir%\%listing_0045% %part1_build_dir% 1>NUL copy /Y %part1_dir%\%listing_0045%.txt %part1_build_dir% 1>NUL @@ -125,9 +129,9 @@ nasm %part1_build_dir_listing_0045%_disassembled.asm fc /B %part1_build_dir_listing_0045% %part1_build_dir_listing_0045%_disassembled || exit /b 1 fc /N %part1_build_dir_listing_0045%.txt %part1_build_dir_listing_0045%_disassembled.txt || exit /b 1 -REM ================================================================================================ -set listing_0046=listing_0046_add_sub_cmp -set part1_build_dir_listing_0046=%part1_build_dir%\%listing_0046% +REM //////////////////////////////////////////////////////////////////////////////////////////////// +set listing_0046/listing_0046_add_sub_cmp +set part1_build_dir_listing_0046/%part1_build_dir%\%listing_0046% copy /Y %part1_dir%\%listing_0046% %part1_build_dir% 1>NUL copy /Y %part1_dir%\%listing_0046%.txt %part1_build_dir% 1>NUL @@ -140,9 +144,9 @@ nasm %part1_build_dir_listing_0046%_disassembled.asm fc /B %part1_build_dir_listing_0046% %part1_build_dir_listing_0046%_disassembled || exit /b 1 fc /N %part1_build_dir_listing_0046%.txt %part1_build_dir_listing_0046%_disassembled.txt || exit /b 1 -REM ================================================================================================ -set listing_0047=listing_0047_challenge_flags -set part1_build_dir_listing_0047=%part1_build_dir%\%listing_0047% +REM //////////////////////////////////////////////////////////////////////////////////////////////// +set listing_0047/listing_0047_challenge_flags +set part1_build_dir_listing_0047/%part1_build_dir%\%listing_0047% copy /Y %part1_dir%\%listing_0047% %part1_build_dir% 1>NUL copy /Y %part1_dir%\%listing_0047%.txt %part1_build_dir% 1>NUL @@ -155,9 +159,9 @@ nasm %part1_build_dir_listing_0047%_disassembled.asm fc /B %part1_build_dir_listing_0047% %part1_build_dir_listing_0047%_disassembled || exit /b 1 fc /N %part1_build_dir_listing_0047%.txt %part1_build_dir_listing_0047%_disassembled.txt || exit /b 1 -REM ================================================================================================ -set listing_0048=listing_0048_ip_register -set part1_build_dir_listing_0048=%part1_build_dir%\%listing_0048% +REM //////////////////////////////////////////////////////////////////////////////////////////////// +set listing_0048/listing_0048_ip_register +set part1_build_dir_listing_0048/%part1_build_dir%\%listing_0048% copy /Y %part1_dir%\%listing_0048% %part1_build_dir% 1>NUL copy /Y %part1_dir%\%listing_0048%.txt %part1_build_dir% 1>NUL @@ -170,9 +174,9 @@ nasm %part1_build_dir_listing_0048%_disassembled.asm fc /B %part1_build_dir_listing_0048% %part1_build_dir_listing_0048%_disassembled || exit /b 1 fc /N %part1_build_dir_listing_0048%.txt %part1_build_dir_listing_0048%_disassembled.txt || exit /b 1 -REM ================================================================================================ -set listing_0049=listing_0049_conditional_jumps -set part1_build_dir_listing_0049=%part1_build_dir%\%listing_0049% +REM //////////////////////////////////////////////////////////////////////////////////////////////// +set listing_0049/listing_0049_conditional_jumps +set part1_build_dir_listing_0049/%part1_build_dir%\%listing_0049% copy /Y %part1_dir%\%listing_0049% %part1_build_dir% 1>NUL copy /Y %part1_dir%\%listing_0049%.txt %part1_build_dir% 1>NUL @@ -185,9 +189,9 @@ nasm %part1_build_dir_listing_0049%_disassembled.asm fc /B %part1_build_dir_listing_0049% %part1_build_dir_listing_0049%_disassembled || exit /b 1 fc /N %part1_build_dir_listing_0049%.txt %part1_build_dir_listing_0049%_disassembled.txt || exit /b 1 -REM ================================================================================================ -set listing_0050=listing_0050_challenge_jumps -set part1_build_dir_listing_0050=%part1_build_dir%\%listing_0050% +REM //////////////////////////////////////////////////////////////////////////////////////////////// +set listing_0050/listing_0050_challenge_jumps +set part1_build_dir_listing_0050/%part1_build_dir%\%listing_0050% copy /Y %part1_dir%\%listing_0050% %part1_build_dir% 1>NUL copy /Y %part1_dir%\%listing_0050%.txt %part1_build_dir% 1>NUL @@ -200,9 +204,9 @@ nasm %part1_build_dir_listing_0050%_disassembled.asm fc /B %part1_build_dir_listing_0050% %part1_build_dir_listing_0050%_disassembled || exit /b 1 fc /N %part1_build_dir_listing_0050%.txt %part1_build_dir_listing_0050%_disassembled.txt || exit /b 1 -REM ================================================================================================ -set listing_0051=listing_0051_memory_mov -set part1_build_dir_listing_0051=%part1_build_dir%\%listing_0051% +REM //////////////////////////////////////////////////////////////////////////////////////////////// +set listing_0051/listing_0051_memory_mov +set part1_build_dir_listing_0051/%part1_build_dir%\%listing_0051% copy /Y %part1_dir%\%listing_0051% %part1_build_dir% 1>NUL copy /Y %part1_dir%\%listing_0051%.txt %part1_build_dir% 1>NUL @@ -215,9 +219,9 @@ nasm %part1_build_dir_listing_0051%_disassembled.asm fc /B %part1_build_dir_listing_0051% %part1_build_dir_listing_0051%_disassembled || exit /b 1 fc /N %part1_build_dir_listing_0051%.txt %part1_build_dir_listing_0051%_disassembled.txt || exit /b 1 -REM ================================================================================================ -set listing_0052=listing_0052_memory_add_loop -set part1_build_dir_listing_0052=%part1_build_dir%\%listing_0052% +REM //////////////////////////////////////////////////////////////////////////////////////////////// +set listing_0052/listing_0052_memory_add_loop +set part1_build_dir_listing_0052/%part1_build_dir%\%listing_0052% copy /Y %part1_dir%\%listing_0052% %part1_build_dir% 1>NUL copy /Y %part1_dir%\%listing_0052%.txt %part1_build_dir% 1>NUL @@ -230,9 +234,9 @@ nasm %part1_build_dir_listing_0052%_disassembled.asm fc /B %part1_build_dir_listing_0052% %part1_build_dir_listing_0052%_disassembled || exit /b 1 fc /N %part1_build_dir_listing_0052%.txt %part1_build_dir_listing_0052%_disassembled.txt || exit /b 1 -REM ================================================================================================ -set listing_0053=listing_0053_add_loop_challenge -set part1_build_dir_listing_0053=%part1_build_dir%\%listing_0053% +REM //////////////////////////////////////////////////////////////////////////////////////////////// +set listing_0053/listing_0053_add_loop_challenge +set part1_build_dir_listing_0053/%part1_build_dir%\%listing_0053% copy /Y %part1_dir%\%listing_0053% %part1_build_dir% 1>NUL copy /Y %part1_dir%\%listing_0053%.txt %part1_build_dir% 1>NUL @@ -245,9 +249,9 @@ nasm %part1_build_dir_listing_0053%_disassembled.asm fc /B %part1_build_dir_listing_0053% %part1_build_dir_listing_0053%_disassembled || exit /b 1 fc /N %part1_build_dir_listing_0053%.txt %part1_build_dir_listing_0053%_disassembled.txt || exit /b 1 -REM ================================================================================================ -set listing_0054=listing_0054_draw_rectangle -set part1_build_dir_listing_0054=%part1_build_dir%\%listing_0054% +REM //////////////////////////////////////////////////////////////////////////////////////////////// +set listing_0054/listing_0054_draw_rectangle +set part1_build_dir_listing_0054/%part1_build_dir%\%listing_0054% copy /Y %part1_dir%\%listing_0054% %part1_build_dir% 1>NUL copy /Y %part1_dir%\%listing_0054%.txt %part1_build_dir% 1>NUL @@ -262,9 +266,9 @@ nasm %part1_build_dir_listing_0054%_disassembled.asm fc /B %part1_build_dir_listing_0054% %part1_build_dir_listing_0054%_disassembled || exit /b 1 fc /N %part1_build_dir_listing_0054%.txt %part1_build_dir_listing_0054%_disassembled.txt || exit /b 1 -REM ================================================================================================ -set listing_0055=listing_0055_challenge_rectangle -set part1_build_dir_listing_0055=%part1_build_dir%\%listing_0055% +REM //////////////////////////////////////////////////////////////////////////////////////////////// +set listing_0055/listing_0055_challenge_rectangle +set part1_build_dir_listing_0055/%part1_build_dir%\%listing_0055% copy /Y %part1_dir%\%listing_0055% %part1_build_dir% 1>NUL copy /Y %part1_dir%\%listing_0055%.txt %part1_build_dir% 1>NUL @@ -279,9 +283,9 @@ nasm %part1_build_dir_listing_0055%_disassembled.asm fc /B %part1_build_dir_listing_0055% %part1_build_dir_listing_0055%_disassembled || exit /b 1 fc /N %part1_build_dir_listing_0055%.txt %part1_build_dir_listing_0055%_disassembled.txt || exit /b 1 -REM ================================================================================================ -set listing_0056=listing_0056_estimating_cycles -set part1_build_dir_listing_0056=%part1_build_dir%\%listing_0056% +REM //////////////////////////////////////////////////////////////////////////////////////////////// +set listing_0056/listing_0056_estimating_cycles +set part1_build_dir_listing_0056/%part1_build_dir%\%listing_0056% copy /Y %part1_dir%\%listing_0056% %part1_build_dir% 1>NUL copy /Y %part1_dir%\%listing_0056%.txt %part1_build_dir% 1>NUL @@ -297,9 +301,9 @@ nasm %part1_build_dir_listing_0056%_disassembled.asm fc /B %part1_build_dir_listing_0056% %part1_build_dir_listing_0056%_disassembled || exit /b 1 fc /N %part1_build_dir_listing_0056%.txt %part1_build_dir_listing_0056%_disassembled.txt || exit /b 1 -REM ================================================================================================ -set listing_0057=listing_0057_challenge_cycles -set part1_build_dir_listing_0057=%part1_build_dir%\%listing_0057% +REM //////////////////////////////////////////////////////////////////////////////////////////////// +set listing_0057/listing_0057_challenge_cycles +set part1_build_dir_listing_0057/%part1_build_dir%\%listing_0057% copy /Y %part1_dir%\%listing_0057% %part1_build_dir% 1>NUL copy /Y %part1_dir%\%listing_0057%.txt %part1_build_dir% 1>NUL @@ -315,8 +319,8 @@ nasm %part1_build_dir_listing_0057%_disassembled.asm fc /B %part1_build_dir_listing_0057% %part1_build_dir_listing_0057%_disassembled || exit /b 1 fc /N %part1_build_dir_listing_0057%.txt %part1_build_dir_listing_0057%_disassembled.txt || exit /b 1 -REM Part 2 ========================================================================================= -REM Build ========================================================================================== +REM Part 2 ///////////////////////////////////////////////////////////////////////////////////////// +REM Build ////////////////////////////////////////////////////////////////////////////////////////// :part2 pushd %part2_build_dir% cl %part2_dir%\haversine_generator.c /W4 /WX /Z7 /nologo /Fe:haversine_generator_debug || exit /b 1 @@ -335,3 +339,11 @@ cl %part2_dir%\listing_0071_os_timer_main.cpp /W4 /WX /Z7 /O2 /nologo /Fe:listin cl %part2_dir%\listing_0072_cpu_timer_main.cpp /W4 /WX /Z7 /O2 /nologo /Fe:listing_0072_cpu_timer_main_release || exit /b 1 cl %part2_dir%\listing_0073_cpu_timer_guessfreq_main.cpp /W4 /WX /Z7 /O2 /nologo /Fe:listing_0073_cpu_timer_guessfreq_release || exit /b 1 popd + +REM Part 3 ///////////////////////////////////////////////////////////////////////////////////////// +REM Build ////////////////////////////////////////////////////////////////////////////////////////// +pushd %part3_build_dir% +cl %part3_dir%\repetition_tester.c /W4 /WX /Z7 /nologo /Fe:repetition_tester_debug || exit /b 1 +cl %part3_dir%\repetition_tester.c /W4 /WX /Z7 /nologo /O2 /Fe:repetition_tester_release || exit /b 1 +popd + diff --git a/part1/sim8086.c b/part1/sim8086.c index 7c96603..036e1db 100644 --- a/part1/sim8086.c +++ b/part1/sim8086.c @@ -382,8 +382,7 @@ S86_Opcode S86_DecodeOpcode(S86_BufferIterator *buffer_it, size_t op_code_size = 0; op_code_bytes[op_code_size++] = S86_BufferIteratorNextByte(buffer_it); - // NOTE: Match the assembly bytes to the desired instruction - // ===================================================================== + // NOTE: Match the assembly bytes to the desired instruction /////////////////////////////////// S86_OpDecodeType op_decode_type = S86_OpDecodeType_Count; S86_OpDecode const *op_decode = NULL; for (size_t op_index = 0; @@ -392,13 +391,11 @@ S86_Opcode S86_DecodeOpcode(S86_BufferIterator *buffer_it, { S86_OpDecode const *item = decode_table + op_index; - // NOTE: Check first instruction byte - // ================================================================= + // NOTE: Check first instruction byte ////////////////////////////////////////////////////// if ((op_code_bytes[0] & item->op_mask0) != item->op_bits0) continue; - // NOTE Check multi-byte instruction - // ================================================================= + // NOTE Check multi-byte instruction /////////////////////////////////////////////////////// // If the matched instruction has a bit mask for the 2nd byte, this // is a multi-byte instruction. Check if the 2nd byte checks out. bool op_match = true; @@ -418,8 +415,7 @@ S86_Opcode S86_DecodeOpcode(S86_BufferIterator *buffer_it, } } - // NOTE: Disassemble bytes to assembly mnemonics - // ================================================================= + // NOTE: Disassemble bytes to assembly mnemonics /////////////////////////////////////////////// S86_ASSERT(op_code_size > 0 && op_code_size <= S86_ARRAY_UCOUNT(op_code_bytes)); S86_ASSERT(op_decode_type != S86_OpDecodeType_Count && "Unknown instruction"); @@ -677,8 +673,7 @@ S86_Opcode S86_DecodeOpcode(S86_BufferIterator *buffer_it, case S86_OpDecodeType_ORImmediateToAccum: /*FALLTHRU*/ case S86_OpDecodeType_XORImmediateToAccum: /*FALLTHRU*/ case S86_OpDecodeType_MOVImmediateToReg: { - // NOTE: Parse opcode control bits - // ============================================================= + // NOTE: Parse opcode control bits ///////////////////////////////////////////////////// S86_ASSERT(op_code_size == 1); uint8_t w = 0; if (op_decode_type == S86_OpDecodeType_ADDImmediateToAccum || @@ -695,16 +690,14 @@ S86_Opcode S86_DecodeOpcode(S86_BufferIterator *buffer_it, w = (op_code_bytes[0] & 0b0000'1000) >> 3; } - // NOTE: Parse data payload - // ============================================================= + // NOTE: Parse data payload //////////////////////////////////////////////////////////// uint16_t data = S86_BufferIteratorNextByte(buffer_it); if (w) { // 16 bit data uint8_t data_hi = S86_BufferIteratorNextByte(buffer_it); data |= (uint16_t)(data_hi) << 8; } - // NOTE: Disassemble - // ============================================================= + // NOTE: Disassemble /////////////////////////////////////////////////////////////////// result.effective_addr = S86_EffectiveAddress_Dest; result.src = S86_MnemonicOp_Immediate; result.wide = w; @@ -893,8 +886,7 @@ char const CLI_ARG_DUMP[] = "--dump"; int main(int argc, char **argv) { - // NOTE: Argument handling - // ========================================================================= + // NOTE: Argument handling ///////////////////////////////////////////////////////////////////// if (argc < 2) { PRINT_USAGE; return -1; @@ -965,8 +957,7 @@ int main(int argc, char **argv) return -1; } - // NOTE: Sim8086 - // ========================================================================= + // NOTE: Sim8086 /////////////////////////////////////////////////////////////////////////////// S86_OpDecode const DECODE_TABLE[] = { [S86_OpDecodeType_MOVRegOrMemToOrFromReg] = {.op_mask0 = 0b1111'1100, .op_mask1 = 0b0000'0000, .op_bits0 = 0b1000'1000, .op_bits1 = 0b0000'0000, .mnemonic = S86_Mnemonic_MOV}, @@ -1257,8 +1248,7 @@ int main(int argc, char **argv) .op_bits0 = 0b0010'0110, .op_bits1 = 0b0000'0000, .mnemonic = S86_Mnemonic_SEGMENT}, }; - // NOTE: Decode assembly - // ========================================================================= + // NOTE: Decode assembly /////////////////////////////////////////////////////////////////////// if (exec_mode) { if (log_cycle_counts != CycleCount_None) { // NOTE: Print disclaimer + header S86_PrintLn(S86_STR8("**************")); @@ -1337,7 +1327,7 @@ int main(int argc, char **argv) continue; } - // NOTE: Simulate instruction ============================================================== + // NOTE: Simulate instruction ////////////////////////////////////////////////////////////// bool cycle_count_8088 = log_cycle_counts == CycleCount_8088; uint32_t base_clocks = 0; uint32_t effective_address_clocks = 0; @@ -1800,7 +1790,7 @@ int main(int argc, char **argv) clocks_counter += base_clocks + effective_address_clocks + transfer_penalty_clocks; - // NOTE: Printing ========================================================================== + // NOTE: Printing ////////////////////////////////////////////////////////////////////////// S86_PrintFmt(" ; "); // NOTE: Clocks diff --git a/part1/sim8086.h b/part1/sim8086.h index b3f12f1..4d93778 100644 --- a/part1/sim8086.h +++ b/part1/sim8086.h @@ -1,5 +1,4 @@ -// NOTE: Sim8086 -// ============================================================================ +// NOTE: Sim8086 /////////////////////////////////////////////////////////////////////////////////// typedef enum S86_OpDecodeType { S86_OpDecodeType_MOVRegOrMemToOrFromReg, S86_OpDecodeType_MOVImmediateToRegOrMem, diff --git a/part2/haversine_generator.c b/part2/haversine_generator.c index 190dabc..78a2209 100644 --- a/part2/haversine_generator.c +++ b/part2/haversine_generator.c @@ -13,8 +13,7 @@ #define PRINT_USAGE HAV_PrintLnFmt("Usage: %s [uniform/cluster] [random seed] [number of coordinate pairs to generate]", argv[0]) int main(int argc, char **argv) { - // NOTE: Unit Tests - // ========================================================================= + // NOTE: Unit Tests //////////////////////////////////////////////////////////////////////////// { { HAV_Str8ToU64Result result = HAV_Str8_ToU64(HAV_STR8("00")); @@ -44,8 +43,7 @@ int main(int argc, char **argv) } } - // NOTE: Arg Parsing - // ========================================================================= + // NOTE: Arg Parsing /////////////////////////////////////////////////////////////////////////// if (argc != 4) { PRINT_USAGE; return -1; @@ -95,8 +93,7 @@ int main(int argc, char **argv) return -1; } - // NOTE: Generator - // ========================================================================= + // NOTE: Generator ///////////////////////////////////////////////////////////////////////////// uint64_t point_count = number_of_coordinate_pairs_to_generate_u64_result.value; uint64_t random_seed = random_seed_u64_result.value; uint64_t rng_state = random_seed; diff --git a/part2/haversine_stdlib.c b/part2/haversine_stdlib.c index 97022b9..455bb68 100644 --- a/part2/haversine_stdlib.c +++ b/part2/haversine_stdlib.c @@ -1,5 +1,4 @@ -// NOTE: Implementation -// ============================================================================ +// NOTE: Implementation //////////////////////////////////////////////////////////////////////////// bool HAV_Str8_Equals(HAV_Str8 lhs, HAV_Str8 rhs) { bool result = lhs.size == rhs.size && memcmp(lhs.data, rhs.data, lhs.size) == 0; @@ -181,14 +180,12 @@ HAV_Buffer HAV_FileRead(char const *file_path) { HAV_Buffer result = {0}; - // NOTE: Determine file size - // ========================================================================= + // NOTE: Determine file size /////////////////////////////////////////////////////////////////// WIN32_FILE_ATTRIBUTE_DATA file_attrib_data = {0}; if (GetFileAttributesEx(file_path, GetFileExInfoStandard, &file_attrib_data) == 0) return result; - // NOTE: Open file - // ========================================================================= + // NOTE: Open file ///////////////////////////////////////////////////////////////////////////// HANDLE file_handle = CreateFile( /*LPCSTR lpFileName*/ file_path, /*DWORD dwDesiredAccess*/ GENERIC_READ, @@ -202,8 +199,7 @@ HAV_Buffer HAV_FileRead(char const *file_path) if (file_handle == INVALID_HANDLE_VALUE) return result; - // NOTE: Allocate buffer - // ========================================================================= + // NOTE: Allocate buffer /////////////////////////////////////////////////////////////////////// uint64_t file_size = (uint64_t)file_attrib_data.nFileSizeHigh << 32 | (uint64_t)file_attrib_data.nFileSizeLow << 0; HAV_ASSERT(file_size < (DWORD)-1); char *buffer = VirtualAlloc( @@ -216,8 +212,7 @@ HAV_Buffer HAV_FileRead(char const *file_path) if (!buffer) goto end; - // NOTE: Read file to buffer - // ========================================================================= + // NOTE: Read file to buffer /////////////////////////////////////////////////////////////////// DWORD bytes_read = 0; HAV_ProfilerZone prof_file_read_zone = HAV_Profiler_BeginZoneBandwidth("File Read", file_size); BOOL read_file_result = ReadFile( @@ -229,8 +224,7 @@ HAV_Buffer HAV_FileRead(char const *file_path) ); HAV_Profiler_EndZone(prof_file_read_zone); - // NOTE: Handle read result - // ========================================================================= + // NOTE: Handle read result //////////////////////////////////////////////////////////////////// if (read_file_result == 0) { VirtualFree(buffer, 0, MEM_RELEASE); } else { @@ -253,8 +247,7 @@ bool HAV_FileWrite(char const *file_path, void const *buffer, size_t buffer_size { bool result = false; - // NOTE: Open file - // ========================================================================= + // NOTE: Open file ///////////////////////////////////////////////////////////////////////////// HANDLE file_handle = CreateFile( /*LPCSTR lpFileName*/ file_path, /*DWORD dwDesiredAccess*/ GENERIC_WRITE, @@ -268,8 +261,7 @@ bool HAV_FileWrite(char const *file_path, void const *buffer, size_t buffer_size if (file_handle == INVALID_HANDLE_VALUE) return result; - // NOTE: Write file to disk - // ========================================================================= + // NOTE: Write file to disk //////////////////////////////////////////////////////////////////// DWORD bytes_written = 0; BOOL write_file_result = WriteFile( /*HANDLE hFile*/ file_handle, diff --git a/part2/haversine_stdlib.h b/part2/haversine_stdlib.h index 3b6e502..9eb4a4f 100644 --- a/part2/haversine_stdlib.h +++ b/part2/haversine_stdlib.h @@ -1,7 +1,6 @@ #include -// NOTE: Macros -// ============================================================================ +// NOTE: Macros //////////////////////////////////////////////////////////////////////////////////// #define HAV_STRINGIFY2(token) #token #define HAV_STRINGIFY(token) HAV_STRINGIFY2(token) @@ -24,8 +23,7 @@ typedef float f32; typedef double f64; typedef uint64_t u64; -// NOTE: Globals -// ============================================================================ +// NOTE: Globals /////////////////////////////////////////////////////////////////////////////////// typedef struct HAV_Globals { HANDLE stdout_handle; bool write_to_console; @@ -33,8 +31,7 @@ typedef struct HAV_Globals { HAV_Globals pap_globals; -// NOTE: Strings -// ============================================================================ +// NOTE: Strings /////////////////////////////////////////////////////////////////////////////////// typedef struct HAV_Str8 { char *data; size_t size; @@ -60,8 +57,7 @@ HAV_Str8BinarySplitResult HAV_Str8_BinarySplit(HAV_Str8 buffer, HAV_Str8 find); bool HAV_CharIsWhiteSpace(char ch); bool HAV_CharIsDigit(char ch); -// NOTE: Profiler -// ============================================================================ +// NOTE: Profiler ////////////////////////////////////////////////////////////////////////////////// typedef struct HAV_ProfilerAnchor { HAV_Str8 label; u64 elapsed_tsc_exclusive; // Does not include children @@ -91,12 +87,11 @@ static HAV_Profiler g_profiler; #define HAV_Profiler_BeginZone(label) HAV_Profiler_BeginZone_(HAV_STR8(label), __COUNTER__ + 1, 0) #define HAV_Profiler_BeginZoneBandwidth(label, byte_count) HAV_Profiler_BeginZone_(HAV_STR8(label), __COUNTER__ + 1, byte_count) -static void HAV_Profiler_Dump(); +static void HAV_Profiler_Dump (); static HAV_ProfilerZone HAV_Profiler_BeginZone_(HAV_Str8 label, uint32_t index, u64 byte_count); -static void HAV_Profiler_EndZone(HAV_ProfilerZone zone); +static void HAV_Profiler_EndZone (HAV_ProfilerZone zone); -// NOTE: PCG32 -// ============================================================================ +// NOTE: PCG32 ///////////////////////////////////////////////////////////////////////////////////// // NOTE: PCG RNG from Demetri Spanos: https://github.com/demetri/scribbles // pcg32_pie, based on the minimal C version from O'Neill at pcg-random.org; // I've made a few (subjective) UX improvements for beginner use @@ -119,8 +114,7 @@ static void HAV_Profiler_EndZone(HAV_ProfilerZone zone); uint32_t HAV_PCG32_Pie (uint64_t *state); f64 HAV_PCG32_PieF64(uint64_t *state, f64 min, f64 max); -// NOTE: Buffer -// ============================================================================ +// NOTE: Buffer //////////////////////////////////////////////////////////////////////////////////// typedef struct HAV_Buffer { char *data; size_t size; @@ -136,14 +130,12 @@ HAV_BufferIterator HAV_BufferIteratorInit (HAV_Buffer buffer); bool HAV_BufferIteratorHasMoreBytes(HAV_BufferIterator it); uint8_t HAV_BufferIteratorNextByte (HAV_BufferIterator *it); -// NOTE: File -// ============================================================================ +// NOTE: File ////////////////////////////////////////////////////////////////////////////////////// HAV_Buffer HAV_FileRead (char const *file_path); void HAV_FileFree (HAV_Buffer buffer); bool HAV_FileWrite(char const *file_path, void const *buffer, size_t buffer_size); -// NOTE: Print -// ============================================================================ +// NOTE: Print ///////////////////////////////////////////////////////////////////////////////////// void HAV_PrintHandle(void *handle, HAV_Str8 string); void HAV_PrintLn (HAV_Str8 string); void HAV_PrintLnFmt (char const *fmt, ...); diff --git a/part2/listing_0089_allfuncs_lookup_json_parser.cpp b/part2/listing_0089_allfuncs_lookup_json_parser.cpp new file mode 100644 index 0000000..637dbac --- /dev/null +++ b/part2/listing_0089_allfuncs_lookup_json_parser.cpp @@ -0,0 +1,537 @@ +/* ======================================================================== + + (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Please see https://computerenhance.com for more information + + ======================================================================== */ + +/* ======================================================================== + LISTING 89 + ======================================================================== */ + +enum json_token_type +{ + Token_end_of_stream, + Token_error, + + Token_open_brace, + Token_open_bracket, + Token_close_brace, + Token_close_bracket, + Token_comma, + Token_colon, + Token_string_literal, + Token_number, + Token_true, + Token_false, + Token_null, + + Token_count, +}; + +struct json_token +{ + json_token_type Type; + buffer Value; +}; + +struct json_element +{ + buffer Label; + buffer Value; + json_element *FirstSubElement; + + json_element *NextSibling; +}; + +struct json_parser +{ + buffer Source; + u64 At; + b32 HadError; +}; + +static b32 IsJSONDigit(buffer Source, u64 At) +{ + TimeFunction; + + b32 Result = false; + if(IsInBounds(Source, At)) + { + u8 Val = Source.Data[At]; + Result = ((Val >= '0') && (Val <= '9')); + } + + return Result; +} + +static b32 IsJSONWhitespace(buffer Source, u64 At) +{ + TimeFunction; + + b32 Result = false; + if(IsInBounds(Source, At)) + { + u8 Val = Source.Data[At]; + Result = ((Val == ' ') || (Val == '\t') || (Val == '\n') || (Val == '\r')); + } + + return Result; +} + +static b32 IsParsing(json_parser *Parser) +{ + TimeFunction; + + b32 Result = !Parser->HadError && IsInBounds(Parser->Source, Parser->At); + return Result; +} + +static void Error(json_parser *Parser, json_token Token, char const *Message) +{ + TimeFunction; + + Parser->HadError = true; + fprintf(stderr, "ERROR: \"%.*s\" - %s\n", (u32)Token.Value.Count, (char *)Token.Value.Data, Message); +} + +static void ParseKeyword(buffer Source, u64 *At, buffer KeywordRemaining, json_token_type Type, json_token *Result) +{ + TimeFunction; + + if((Source.Count - *At) >= KeywordRemaining.Count) + { + buffer Check = Source; + Check.Data += *At; + Check.Count = KeywordRemaining.Count; + if(AreEqual(Check, KeywordRemaining)) + { + Result->Type = Type; + Result->Value.Count += KeywordRemaining.Count; + *At += KeywordRemaining.Count; + } + } +} + +static json_token GetJSONToken(json_parser *Parser) +{ + TimeFunction; + + json_token Result = {}; + + buffer Source = Parser->Source; + u64 At = Parser->At; + + while(IsJSONWhitespace(Source, At)) + { + ++At; + } + + if(IsInBounds(Source, At)) + { + Result.Type = Token_error; + Result.Value.Count = 1; + Result.Value.Data = Source.Data + At; + u8 Val = Source.Data[At++]; + switch(Val) + { + case '{': {Result.Type = Token_open_brace;} break; + case '[': {Result.Type = Token_open_bracket;} break; + case '}': {Result.Type = Token_close_brace;} break; + case ']': {Result.Type = Token_close_bracket;} break; + case ',': {Result.Type = Token_comma;} break; + case ':': {Result.Type = Token_colon;} break; + + case 'f': + { + ParseKeyword(Source, &At, CONSTANT_STRING("alse"), Token_false, &Result); + } break; + + case 'n': + { + ParseKeyword(Source, &At, CONSTANT_STRING("ull"), Token_null, &Result); + } break; + + case 't': + { + ParseKeyword(Source, &At, CONSTANT_STRING("rue"), Token_true, &Result); + } break; + + case '"': + { + Result.Type = Token_string_literal; + + u64 StringStart = At; + + while(IsInBounds(Source, At) && (Source.Data[At] != '"')) + { + if(IsInBounds(Source, (At + 1)) && + (Source.Data[At] == '\\') && + (Source.Data[At + 1] == '"')) + { + // NOTE(casey): Skip escaped quotation marks + ++At; + } + + ++At; + } + + Result.Value.Data = Source.Data + StringStart; + Result.Value.Count = At - StringStart; + if(IsInBounds(Source, At)) + { + ++At; + } + } break; + + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + u64 Start = At - 1; + Result.Type = Token_number; + + // NOTE(casey): Move past a leading negative sign if one exists + if((Val == '-') && IsInBounds(Source, At)) + { + Val = Source.Data[At++]; + } + + // NOTE(casey): If the leading digit wasn't 0, parse any digits before the decimal point + if(Val != '0') + { + while(IsJSONDigit(Source, At)) + { + ++At; + } + } + + // NOTE(casey): If there is a decimal point, parse any digits after the decimal point + if(IsInBounds(Source, At) && (Source.Data[At] == '.')) + { + ++At; + while(IsJSONDigit(Source, At)) + { + ++At; + } + } + + // NOTE(casey): If it's in scientific notation, parse any digits after the "e" + if(IsInBounds(Source, At) && ((Source.Data[At] == 'e') || (Source.Data[At] == 'E'))) + { + ++At; + + if(IsInBounds(Source, At) && ((Source.Data[At] == '+') || (Source.Data[At] == '-'))) + { + ++At; + } + + while(IsJSONDigit(Source, At)) + { + ++At; + } + } + + Result.Value.Count = At - Start; + } break; + + default: + { + } break; + } + } + + Parser->At = At; + + return Result; +} + +static json_element *ParseJSONList(json_parser *Parser, json_token_type EndType, b32 HasLabels); +static json_element *ParseJSONElement(json_parser *Parser, buffer Label, json_token Value) +{ + TimeFunction; + + b32 Valid = true; + + json_element *SubElement = 0; + if(Value.Type == Token_open_bracket) + { + SubElement = ParseJSONList(Parser, Token_close_bracket, false); + } + else if(Value.Type == Token_open_brace) + { + SubElement = ParseJSONList(Parser, Token_close_brace, true); + } + else if((Value.Type == Token_string_literal) || + (Value.Type == Token_true) || + (Value.Type == Token_false) || + (Value.Type == Token_null) || + (Value.Type == Token_number)) + { + // NOTE(casey): Nothing to do here, since there is no additional data + } + else + { + Valid = false; + } + + json_element *Result = 0; + + if(Valid) + { + Result = (json_element *)malloc(sizeof(json_element)); + Result->Label = Label; + Result->Value = Value.Value; + Result->FirstSubElement = SubElement; + Result->NextSibling = 0; + } + + return Result; +} + +static json_element *ParseJSONList(json_parser *Parser, json_token_type EndType, b32 HasLabels) +{ + TimeFunction; + + json_element *FirstElement = {}; + json_element *LastElement = {}; + + while(IsParsing(Parser)) + { + buffer Label = {}; + json_token Value = GetJSONToken(Parser); + if(HasLabels) + { + if(Value.Type == Token_string_literal) + { + Label = Value.Value; + + json_token Colon = GetJSONToken(Parser); + if(Colon.Type == Token_colon) + { + Value = GetJSONToken(Parser); + } + else + { + Error(Parser, Colon, "Expected colon after field name"); + } + } + else if(Value.Type != EndType) + { + Error(Parser, Value, "Unexpected token in JSON"); + } + } + + json_element *Element = ParseJSONElement(Parser, Label, Value); + if(Element) + { + LastElement = (LastElement ? LastElement->NextSibling : FirstElement) = Element; + } + else if(Value.Type == EndType) + { + break; + } + else + { + Error(Parser, Value, "Unexpected token in JSON"); + } + + json_token Comma = GetJSONToken(Parser); + if(Comma.Type == EndType) + { + break; + } + else if(Comma.Type != Token_comma) + { + Error(Parser, Comma, "Unexpected token in JSON"); + } + } + + return FirstElement; +} + +static json_element *ParseJSON(buffer InputJSON) +{ + TimeFunction; + + json_parser Parser = {}; + Parser.Source = InputJSON; + + json_element *Result = ParseJSONElement(&Parser, {}, GetJSONToken(&Parser)); + return Result; +} + +static void FreeJSON(json_element *Element) +{ + TimeFunction; + + while(Element) + { + json_element *FreeElement = Element; + Element = Element->NextSibling; + + FreeJSON(FreeElement->FirstSubElement); + free(FreeElement); + } +} + +static json_element *LookupElement(json_element *Object, buffer ElementName) +{ + TimeFunction; + + json_element *Result = 0; + + if(Object) + { + for(json_element *Search = Object->FirstSubElement; Search; Search = Search->NextSibling) + { + if(AreEqual(Search->Label, ElementName)) + { + Result = Search; + break; + } + } + } + + return Result; +} + +static f64 ConvertJSONSign(buffer Source, u64 *AtResult) +{ + TimeFunction; + + u64 At = *AtResult; + + f64 Result = 1.0; + if(IsInBounds(Source, At) && (Source.Data[At] == '-')) + { + Result = -1.0; + ++At; + } + + *AtResult = At; + + return Result; +} + +static f64 ConvertJSONNumber(buffer Source, u64 *AtResult) +{ + TimeFunction; + + u64 At = *AtResult; + + f64 Result = 0.0; + while(IsInBounds(Source, At)) + { + u8 Char = Source.Data[At] - (u8)'0'; + if(Char < 10) + { + Result = 10.0*Result + (f64)Char; + ++At; + } + else + { + break; + } + } + + *AtResult = At; + + return Result; +} + +static f64 ConvertElementToF64(json_element *Object, buffer ElementName) +{ + TimeFunction; + + f64 Result = 0.0; + + json_element *Element = LookupElement(Object, ElementName); + if(Element) + { + buffer Source = Element->Value; + u64 At = 0; + + f64 Sign = ConvertJSONSign(Source, &At); + f64 Number = ConvertJSONNumber(Source, &At); + + if(IsInBounds(Source, At) && (Source.Data[At] == '.')) + { + ++At; + f64 C = 1.0 / 10.0; + while(IsInBounds(Source, At)) + { + u8 Char = Source.Data[At] - (u8)'0'; + if(Char < 10) + { + Number = Number + C*(f64)Char; + C *= 1.0 / 10.0; + ++At; + } + else + { + break; + } + } + } + + if(IsInBounds(Source, At) && ((Source.Data[At] == 'e') || (Source.Data[At] == 'E'))) + { + ++At; + if(IsInBounds(Source, At) && (Source.Data[At] == '+')) + { + ++At; + } + + f64 ExponentSign = ConvertJSONSign(Source, &At); + f64 Exponent = ExponentSign*ConvertJSONNumber(Source, &At); + Number *= pow(10.0, Exponent); + } + + Result = Sign*Number; + } + + return Result; +} + +static u64 ParseHaversinePairs(buffer InputJSON, u64 MaxPairCount, haversine_pair *Pairs) +{ + TimeFunction; + + u64 PairCount = 0; + + json_element *JSON = ParseJSON(InputJSON); + + json_element *PairsArray = LookupElement(JSON, CONSTANT_STRING("pairs")); + if(PairsArray) + { + for(json_element *Element = PairsArray->FirstSubElement; + Element && (PairCount < MaxPairCount); + Element = Element->NextSibling) + { + haversine_pair *Pair = Pairs + PairCount++; + + Pair->X0 = ConvertElementToF64(Element, CONSTANT_STRING("x0")); + Pair->Y0 = ConvertElementToF64(Element, CONSTANT_STRING("y0")); + Pair->X1 = ConvertElementToF64(Element, CONSTANT_STRING("x1")); + Pair->Y1 = ConvertElementToF64(Element, CONSTANT_STRING("y1")); + } + } + + FreeJSON(JSON); + + return PairCount; +} diff --git a/part2/listing_0090_allfuncs_counted_haversine_main.cpp b/part2/listing_0090_allfuncs_counted_haversine_main.cpp new file mode 100644 index 0000000..50596c2 --- /dev/null +++ b/part2/listing_0090_allfuncs_counted_haversine_main.cpp @@ -0,0 +1,184 @@ +/* ======================================================================== + + (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Please see https://computerenhance.com for more information + + ======================================================================== */ + +/* ======================================================================== + LISTING 90 + ======================================================================== */ + +/* NOTE(casey): _CRT_SECURE_NO_WARNINGS is here because otherwise we cannot + call fopen(). If we replace fopen() with fopen_s() to avoid the warning, + then the code doesn't compile on Linux anymore, since fopen_s() does not + exist there. + + What exactly the CRT maintainers were thinking when they made this choice, + I have no idea. */ +#define _CRT_SECURE_NO_WARNINGS + +#include +#include +#include +#include +#include + +typedef uint8_t u8; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef int32_t b32; + +typedef float f32; +typedef double f64; + +#define ArrayCount(Array) (sizeof(Array)/sizeof((Array)[0])) + +struct haversine_pair +{ + f64 X0, Y0; + f64 X1, Y1; +}; + +#include "listing_0087_simplified_profiler.cpp" +#include "listing_0065_haversine_formula.cpp" +#include "listing_0068_buffer.cpp" +#include "listing_0089_allfuncs_lookup_json_parser.cpp" + +static buffer ReadEntireFile(char *FileName) +{ + TimeFunction; + + buffer Result = {}; + + FILE *File = fopen(FileName, "rb"); + if(File) + { +#if _WIN32 + struct __stat64 Stat; + _stat64(FileName, &Stat); +#else + struct stat Stat; + stat(FileName, &Stat); +#endif + + Result = AllocateBuffer(Stat.st_size); + if(Result.Data) + { + if(fread(Result.Data, Result.Count, 1, File) != 1) + { + fprintf(stderr, "ERROR: Unable to read \"%s\".\n", FileName); + FreeBuffer(&Result); + } + } + + fclose(File); + } + else + { + fprintf(stderr, "ERROR: Unable to open \"%s\".\n", FileName); + } + + return Result; +} + +static f64 SumHaversineDistances(u64 PairCount, haversine_pair *Pairs) +{ + TimeFunction; + + f64 Sum = 0; + + f64 SumCoef = 1 / (f64)PairCount; + for(u64 PairIndex = 0; PairIndex < PairCount; ++PairIndex) + { + haversine_pair Pair = Pairs[PairIndex]; + f64 EarthRadius = 6372.8; + f64 Dist = ReferenceHaversine(Pair.X0, Pair.Y0, Pair.X1, Pair.Y1, EarthRadius); + Sum += SumCoef*Dist; + } + + return Sum; +} + +int main(int ArgCount, char **Args) +{ + BeginProfile(); + + int Result = 1; + + if((ArgCount == 2) || (ArgCount == 3)) + { + buffer InputJSON = ReadEntireFile(Args[1]); + + u32 MinimumJSONPairEncoding = 6*4; + u64 MaxPairCount = InputJSON.Count / MinimumJSONPairEncoding; + if(MaxPairCount) + { + buffer ParsedValues = AllocateBuffer(MaxPairCount * sizeof(haversine_pair)); + if(ParsedValues.Count) + { + haversine_pair *Pairs = (haversine_pair *)ParsedValues.Data; + + u64 PairCount = ParseHaversinePairs(InputJSON, MaxPairCount, Pairs); + f64 Sum = SumHaversineDistances(PairCount, Pairs); + + Result = 0; + + fprintf(stdout, "Input size: %llu\n", InputJSON.Count); + fprintf(stdout, "Pair count: %llu\n", PairCount); + fprintf(stdout, "Haversine sum: %.16f\n", Sum); + + if(ArgCount == 3) + { + buffer AnswersF64 = ReadEntireFile(Args[2]); + if(AnswersF64.Count >= sizeof(f64)) + { + f64 *AnswerValues = (f64 *)AnswersF64.Data; + + fprintf(stdout, "\nValidation:\n"); + + u64 RefAnswerCount = (AnswersF64.Count - sizeof(f64)) / sizeof(f64); + if(PairCount != RefAnswerCount) + { + fprintf(stdout, "FAILED - pair count doesn't match %llu.\n", RefAnswerCount); + } + + f64 RefSum = AnswerValues[RefAnswerCount]; + fprintf(stdout, "Reference sum: %.16f\n", RefSum); + fprintf(stdout, "Difference: %.16f\n", Sum - RefSum); + + fprintf(stdout, "\n"); + } + } + } + + FreeBuffer(&ParsedValues); + } + else + { + fprintf(stderr, "ERROR: Malformed input JSON\n"); + } + + FreeBuffer(&InputJSON); + } + else + { + fprintf(stderr, "Usage: %s [haversine_input.json]\n", Args[0]); + fprintf(stderr, " %s [haversine_input.json] [answers.f64]\n", Args[0]); + } + + if(Result == 0) + { + EndAndPrintProfile(); + } + + return Result; +} + +static_assert(__COUNTER__ < ArrayCount(profiler::Anchors), "Number of profile points exceeds size of profiler::Anchors array"); \ No newline at end of file diff --git a/part2/listing_0091_switchable_profiler.cpp b/part2/listing_0091_switchable_profiler.cpp new file mode 100644 index 0000000..dcc95c2 --- /dev/null +++ b/part2/listing_0091_switchable_profiler.cpp @@ -0,0 +1,142 @@ +/* ======================================================================== + + (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Please see https://computerenhance.com for more information + + ======================================================================== */ + +/* ======================================================================== + LISTING 91 + ======================================================================== */ + +#include "listing_0074_platform_metrics.cpp" + +#ifndef PROFILER +#define PROFILER 0 +#endif + +#if PROFILER + +struct profile_anchor +{ + u64 TSCElapsedExclusive; // NOTE(casey): Does NOT include children + u64 TSCElapsedInclusive; // NOTE(casey): DOES include children + u64 HitCount; + char const *Label; +}; +static profile_anchor GlobalProfilerAnchors[4096]; +static u32 GlobalProfilerParent; + +struct profile_block +{ + profile_block(char const *Label_, u32 AnchorIndex_) + { + ParentIndex = GlobalProfilerParent; + + AnchorIndex = AnchorIndex_; + Label = Label_; + + profile_anchor *Anchor = GlobalProfilerAnchors + AnchorIndex; + OldTSCElapsedInclusive = Anchor->TSCElapsedInclusive; + + GlobalProfilerParent = AnchorIndex; + StartTSC = ReadCPUTimer(); + } + + ~profile_block(void) + { + u64 Elapsed = ReadCPUTimer() - StartTSC; + GlobalProfilerParent = ParentIndex; + + profile_anchor *Parent = GlobalProfilerAnchors + ParentIndex; + profile_anchor *Anchor = GlobalProfilerAnchors + AnchorIndex; + + Parent->TSCElapsedExclusive -= Elapsed; + Anchor->TSCElapsedExclusive += Elapsed; + Anchor->TSCElapsedInclusive = OldTSCElapsedInclusive + Elapsed; + ++Anchor->HitCount; + + /* NOTE(casey): This write happens every time solely because there is no + straightforward way in C++ to have the same ease-of-use. In a better programming + language, it would be simple to have the anchor points gathered and labeled at compile + time, and this repetative write would be eliminated. */ + Anchor->Label = Label; + } + + char const *Label; + u64 OldTSCElapsedInclusive; + u64 StartTSC; + u32 ParentIndex; + u32 AnchorIndex; +}; + +#define NameConcat2(A, B) A##B +#define NameConcat(A, B) NameConcat2(A, B) +#define TimeBlock(Name) profile_block NameConcat(Block, __LINE__)(Name, __COUNTER__ + 1); +#define ProfilerEndOfCompilationUnit static_assert(__COUNTER__ < ArrayCount(GlobalProfilerAnchors), "Number of profile points exceeds size of profiler::Anchors array") + +static void PrintTimeElapsed(u64 TotalTSCElapsed, profile_anchor *Anchor) +{ + f64 Percent = 100.0 * ((f64)Anchor->TSCElapsedExclusive / (f64)TotalTSCElapsed); + printf(" %s[%llu]: %llu (%.2f%%", Anchor->Label, Anchor->HitCount, Anchor->TSCElapsedExclusive, Percent); + if(Anchor->TSCElapsedInclusive != Anchor->TSCElapsedExclusive) + { + f64 PercentWithChildren = 100.0 * ((f64)Anchor->TSCElapsedInclusive / (f64)TotalTSCElapsed); + printf(", %.2f%% w/children", PercentWithChildren); + } + printf(")\n"); +} + +static void PrintAnchorData(u64 TotalCPUElapsed) +{ + for(u32 AnchorIndex = 0; AnchorIndex < ArrayCount(GlobalProfilerAnchors); ++AnchorIndex) + { + profile_anchor *Anchor = GlobalProfilerAnchors + AnchorIndex; + if(Anchor->TSCElapsedInclusive) + { + PrintTimeElapsed(TotalCPUElapsed, Anchor); + } + } +} + +#else + +#define TimeBlock(...) +#define PrintAnchorData(...) +#define ProfilerEndOfCompilationUnit + +#endif + +struct profiler +{ + u64 StartTSC; + u64 EndTSC; +}; +static profiler GlobalProfiler; + +#define TimeFunction TimeBlock(__func__) + +static void BeginProfile(void) +{ + GlobalProfiler.StartTSC = ReadCPUTimer(); +} + +static void EndAndPrintProfile() +{ + GlobalProfiler.EndTSC = ReadCPUTimer(); + u64 CPUFreq = EstimateCPUTimerFreq(); + + u64 TotalCPUElapsed = GlobalProfiler.EndTSC - GlobalProfiler.StartTSC; + + if(CPUFreq) + { + printf("\nTotal time: %0.4fms (CPU freq %llu)\n", 1000.0 * (f64)TotalCPUElapsed / (f64)CPUFreq, CPUFreq); + } + + PrintAnchorData(TotalCPUElapsed); +} diff --git a/part2/listing_0092_profiler_on_haversine_main.cpp b/part2/listing_0092_profiler_on_haversine_main.cpp new file mode 100644 index 0000000..68cc61d --- /dev/null +++ b/part2/listing_0092_profiler_on_haversine_main.cpp @@ -0,0 +1,185 @@ +/* ======================================================================== + + (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Please see https://computerenhance.com for more information + + ======================================================================== */ + +/* ======================================================================== + LISTING 92 + ======================================================================== */ + +/* NOTE(casey): _CRT_SECURE_NO_WARNINGS is here because otherwise we cannot + call fopen(). If we replace fopen() with fopen_s() to avoid the warning, + then the code doesn't compile on Linux anymore, since fopen_s() does not + exist there. + + What exactly the CRT maintainers were thinking when they made this choice, + I have no idea. */ +#define _CRT_SECURE_NO_WARNINGS + +#include +#include +#include +#include +#include + +typedef uint8_t u8; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef int32_t b32; + +typedef float f32; +typedef double f64; + +#define ArrayCount(Array) (sizeof(Array)/sizeof((Array)[0])) + +struct haversine_pair +{ + f64 X0, Y0; + f64 X1, Y1; +}; + +#define PROFILER 1 +#include "listing_0091_switchable_profiler.cpp" +#include "listing_0065_haversine_formula.cpp" +#include "listing_0068_buffer.cpp" +#include "listing_0089_allfuncs_lookup_json_parser.cpp" + +static buffer ReadEntireFile(char *FileName) +{ + TimeFunction; + + buffer Result = {}; + + FILE *File = fopen(FileName, "rb"); + if(File) + { +#if _WIN32 + struct __stat64 Stat; + _stat64(FileName, &Stat); +#else + struct stat Stat; + stat(FileName, &Stat); +#endif + + Result = AllocateBuffer(Stat.st_size); + if(Result.Data) + { + if(fread(Result.Data, Result.Count, 1, File) != 1) + { + fprintf(stderr, "ERROR: Unable to read \"%s\".\n", FileName); + FreeBuffer(&Result); + } + } + + fclose(File); + } + else + { + fprintf(stderr, "ERROR: Unable to open \"%s\".\n", FileName); + } + + return Result; +} + +static f64 SumHaversineDistances(u64 PairCount, haversine_pair *Pairs) +{ + TimeFunction; + + f64 Sum = 0; + + f64 SumCoef = 1 / (f64)PairCount; + for(u64 PairIndex = 0; PairIndex < PairCount; ++PairIndex) + { + haversine_pair Pair = Pairs[PairIndex]; + f64 EarthRadius = 6372.8; + f64 Dist = ReferenceHaversine(Pair.X0, Pair.Y0, Pair.X1, Pair.Y1, EarthRadius); + Sum += SumCoef*Dist; + } + + return Sum; +} + +int main(int ArgCount, char **Args) +{ + BeginProfile(); + + int Result = 1; + + if((ArgCount == 2) || (ArgCount == 3)) + { + buffer InputJSON = ReadEntireFile(Args[1]); + + u32 MinimumJSONPairEncoding = 6*4; + u64 MaxPairCount = InputJSON.Count / MinimumJSONPairEncoding; + if(MaxPairCount) + { + buffer ParsedValues = AllocateBuffer(MaxPairCount * sizeof(haversine_pair)); + if(ParsedValues.Count) + { + haversine_pair *Pairs = (haversine_pair *)ParsedValues.Data; + + u64 PairCount = ParseHaversinePairs(InputJSON, MaxPairCount, Pairs); + f64 Sum = SumHaversineDistances(PairCount, Pairs); + + Result = 0; + + fprintf(stdout, "Input size: %llu\n", InputJSON.Count); + fprintf(stdout, "Pair count: %llu\n", PairCount); + fprintf(stdout, "Haversine sum: %.16f\n", Sum); + + if(ArgCount == 3) + { + buffer AnswersF64 = ReadEntireFile(Args[2]); + if(AnswersF64.Count >= sizeof(f64)) + { + f64 *AnswerValues = (f64 *)AnswersF64.Data; + + fprintf(stdout, "\nValidation:\n"); + + u64 RefAnswerCount = (AnswersF64.Count - sizeof(f64)) / sizeof(f64); + if(PairCount != RefAnswerCount) + { + fprintf(stdout, "FAILED - pair count doesn't match %llu.\n", RefAnswerCount); + } + + f64 RefSum = AnswerValues[RefAnswerCount]; + fprintf(stdout, "Reference sum: %.16f\n", RefSum); + fprintf(stdout, "Difference: %.16f\n", Sum - RefSum); + + fprintf(stdout, "\n"); + } + } + } + + FreeBuffer(&ParsedValues); + } + else + { + fprintf(stderr, "ERROR: Malformed input JSON\n"); + } + + FreeBuffer(&InputJSON); + } + else + { + fprintf(stderr, "Usage: %s [haversine_input.json]\n", Args[0]); + fprintf(stderr, " %s [haversine_input.json] [answers.f64]\n", Args[0]); + } + + if(Result == 0) + { + EndAndPrintProfile(); + } + + return Result; +} + +ProfilerEndOfCompilationUnit; diff --git a/part2/listing_0093_profiler_off_haversine_main.cpp b/part2/listing_0093_profiler_off_haversine_main.cpp new file mode 100644 index 0000000..9924487 --- /dev/null +++ b/part2/listing_0093_profiler_off_haversine_main.cpp @@ -0,0 +1,184 @@ +/* ======================================================================== + + (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Please see https://computerenhance.com for more information + + ======================================================================== */ + +/* ======================================================================== + LISTING 93 + ======================================================================== */ + +/* NOTE(casey): _CRT_SECURE_NO_WARNINGS is here because otherwise we cannot + call fopen(). If we replace fopen() with fopen_s() to avoid the warning, + then the code doesn't compile on Linux anymore, since fopen_s() does not + exist there. + + What exactly the CRT maintainers were thinking when they made this choice, + I have no idea. */ +#define _CRT_SECURE_NO_WARNINGS + +#include +#include +#include +#include +#include + +typedef uint8_t u8; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef int32_t b32; + +typedef float f32; +typedef double f64; + +#define ArrayCount(Array) (sizeof(Array)/sizeof((Array)[0])) + +struct haversine_pair +{ + f64 X0, Y0; + f64 X1, Y1; +}; + +#include "listing_0091_switchable_profiler.cpp" +#include "listing_0065_haversine_formula.cpp" +#include "listing_0068_buffer.cpp" +#include "listing_0089_allfuncs_lookup_json_parser.cpp" + +static buffer ReadEntireFile(char *FileName) +{ + TimeFunction; + + buffer Result = {}; + + FILE *File = fopen(FileName, "rb"); + if(File) + { +#if _WIN32 + struct __stat64 Stat; + _stat64(FileName, &Stat); +#else + struct stat Stat; + stat(FileName, &Stat); +#endif + + Result = AllocateBuffer(Stat.st_size); + if(Result.Data) + { + if(fread(Result.Data, Result.Count, 1, File) != 1) + { + fprintf(stderr, "ERROR: Unable to read \"%s\".\n", FileName); + FreeBuffer(&Result); + } + } + + fclose(File); + } + else + { + fprintf(stderr, "ERROR: Unable to open \"%s\".\n", FileName); + } + + return Result; +} + +static f64 SumHaversineDistances(u64 PairCount, haversine_pair *Pairs) +{ + TimeFunction; + + f64 Sum = 0; + + f64 SumCoef = 1 / (f64)PairCount; + for(u64 PairIndex = 0; PairIndex < PairCount; ++PairIndex) + { + haversine_pair Pair = Pairs[PairIndex]; + f64 EarthRadius = 6372.8; + f64 Dist = ReferenceHaversine(Pair.X0, Pair.Y0, Pair.X1, Pair.Y1, EarthRadius); + Sum += SumCoef*Dist; + } + + return Sum; +} + +int main(int ArgCount, char **Args) +{ + BeginProfile(); + + int Result = 1; + + if((ArgCount == 2) || (ArgCount == 3)) + { + buffer InputJSON = ReadEntireFile(Args[1]); + + u32 MinimumJSONPairEncoding = 6*4; + u64 MaxPairCount = InputJSON.Count / MinimumJSONPairEncoding; + if(MaxPairCount) + { + buffer ParsedValues = AllocateBuffer(MaxPairCount * sizeof(haversine_pair)); + if(ParsedValues.Count) + { + haversine_pair *Pairs = (haversine_pair *)ParsedValues.Data; + + u64 PairCount = ParseHaversinePairs(InputJSON, MaxPairCount, Pairs); + f64 Sum = SumHaversineDistances(PairCount, Pairs); + + Result = 0; + + fprintf(stdout, "Input size: %llu\n", InputJSON.Count); + fprintf(stdout, "Pair count: %llu\n", PairCount); + fprintf(stdout, "Haversine sum: %.16f\n", Sum); + + if(ArgCount == 3) + { + buffer AnswersF64 = ReadEntireFile(Args[2]); + if(AnswersF64.Count >= sizeof(f64)) + { + f64 *AnswerValues = (f64 *)AnswersF64.Data; + + fprintf(stdout, "\nValidation:\n"); + + u64 RefAnswerCount = (AnswersF64.Count - sizeof(f64)) / sizeof(f64); + if(PairCount != RefAnswerCount) + { + fprintf(stdout, "FAILED - pair count doesn't match %llu.\n", RefAnswerCount); + } + + f64 RefSum = AnswerValues[RefAnswerCount]; + fprintf(stdout, "Reference sum: %.16f\n", RefSum); + fprintf(stdout, "Difference: %.16f\n", Sum - RefSum); + + fprintf(stdout, "\n"); + } + } + } + + FreeBuffer(&ParsedValues); + } + else + { + fprintf(stderr, "ERROR: Malformed input JSON\n"); + } + + FreeBuffer(&InputJSON); + } + else + { + fprintf(stderr, "Usage: %s [haversine_input.json]\n", Args[0]); + fprintf(stderr, " %s [haversine_input.json] [answers.f64]\n", Args[0]); + } + + if(Result == 0) + { + EndAndPrintProfile(); + } + + return Result; +} + +ProfilerEndOfCompilationUnit; diff --git a/part2/listing_0094_profiled_lookup_json_parser.cpp b/part2/listing_0094_profiled_lookup_json_parser.cpp new file mode 100644 index 0000000..56c8f57 --- /dev/null +++ b/part2/listing_0094_profiled_lookup_json_parser.cpp @@ -0,0 +1,516 @@ +/* ======================================================================== + + (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Please see https://computerenhance.com for more information + + ======================================================================== */ + +/* ======================================================================== + LISTING 94 + ======================================================================== */ + +enum json_token_type +{ + Token_end_of_stream, + Token_error, + + Token_open_brace, + Token_open_bracket, + Token_close_brace, + Token_close_bracket, + Token_comma, + Token_colon, + Token_string_literal, + Token_number, + Token_true, + Token_false, + Token_null, + + Token_count, +}; + +struct json_token +{ + json_token_type Type; + buffer Value; +}; + +struct json_element +{ + buffer Label; + buffer Value; + json_element *FirstSubElement; + + json_element *NextSibling; +}; + +struct json_parser +{ + buffer Source; + u64 At; + b32 HadError; +}; + +static b32 IsJSONDigit(buffer Source, u64 At) +{ + b32 Result = false; + if(IsInBounds(Source, At)) + { + u8 Val = Source.Data[At]; + Result = ((Val >= '0') && (Val <= '9')); + } + + return Result; +} + +static b32 IsJSONWhitespace(buffer Source, u64 At) +{ + b32 Result = false; + if(IsInBounds(Source, At)) + { + u8 Val = Source.Data[At]; + Result = ((Val == ' ') || (Val == '\t') || (Val == '\n') || (Val == '\r')); + } + + return Result; +} + +static b32 IsParsing(json_parser *Parser) +{ + b32 Result = !Parser->HadError && IsInBounds(Parser->Source, Parser->At); + return Result; +} + +static void Error(json_parser *Parser, json_token Token, char const *Message) +{ + Parser->HadError = true; + fprintf(stderr, "ERROR: \"%.*s\" - %s\n", (u32)Token.Value.Count, (char *)Token.Value.Data, Message); +} + +static void ParseKeyword(buffer Source, u64 *At, buffer KeywordRemaining, json_token_type Type, json_token *Result) +{ + if((Source.Count - *At) >= KeywordRemaining.Count) + { + buffer Check = Source; + Check.Data += *At; + Check.Count = KeywordRemaining.Count; + if(AreEqual(Check, KeywordRemaining)) + { + Result->Type = Type; + Result->Value.Count += KeywordRemaining.Count; + *At += KeywordRemaining.Count; + } + } +} + +static json_token GetJSONToken(json_parser *Parser) +{ + json_token Result = {}; + + buffer Source = Parser->Source; + u64 At = Parser->At; + + while(IsJSONWhitespace(Source, At)) + { + ++At; + } + + if(IsInBounds(Source, At)) + { + Result.Type = Token_error; + Result.Value.Count = 1; + Result.Value.Data = Source.Data + At; + u8 Val = Source.Data[At++]; + switch(Val) + { + case '{': {Result.Type = Token_open_brace;} break; + case '[': {Result.Type = Token_open_bracket;} break; + case '}': {Result.Type = Token_close_brace;} break; + case ']': {Result.Type = Token_close_bracket;} break; + case ',': {Result.Type = Token_comma;} break; + case ':': {Result.Type = Token_colon;} break; + + case 'f': + { + ParseKeyword(Source, &At, CONSTANT_STRING("alse"), Token_false, &Result); + } break; + + case 'n': + { + ParseKeyword(Source, &At, CONSTANT_STRING("ull"), Token_null, &Result); + } break; + + case 't': + { + ParseKeyword(Source, &At, CONSTANT_STRING("rue"), Token_true, &Result); + } break; + + case '"': + { + Result.Type = Token_string_literal; + + u64 StringStart = At; + + while(IsInBounds(Source, At) && (Source.Data[At] != '"')) + { + if(IsInBounds(Source, (At + 1)) && + (Source.Data[At] == '\\') && + (Source.Data[At + 1] == '"')) + { + // NOTE(casey): Skip escaped quotation marks + ++At; + } + + ++At; + } + + Result.Value.Data = Source.Data + StringStart; + Result.Value.Count = At - StringStart; + if(IsInBounds(Source, At)) + { + ++At; + } + } break; + + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + u64 Start = At - 1; + Result.Type = Token_number; + + // NOTE(casey): Move past a leading negative sign if one exists + if((Val == '-') && IsInBounds(Source, At)) + { + Val = Source.Data[At++]; + } + + // NOTE(casey): If the leading digit wasn't 0, parse any digits before the decimal point + if(Val != '0') + { + while(IsJSONDigit(Source, At)) + { + ++At; + } + } + + // NOTE(casey): If there is a decimal point, parse any digits after the decimal point + if(IsInBounds(Source, At) && (Source.Data[At] == '.')) + { + ++At; + while(IsJSONDigit(Source, At)) + { + ++At; + } + } + + // NOTE(casey): If it's in scientific notation, parse any digits after the "e" + if(IsInBounds(Source, At) && ((Source.Data[At] == 'e') || (Source.Data[At] == 'E'))) + { + ++At; + + if(IsInBounds(Source, At) && ((Source.Data[At] == '+') || (Source.Data[At] == '-'))) + { + ++At; + } + + while(IsJSONDigit(Source, At)) + { + ++At; + } + } + + Result.Value.Count = At - Start; + } break; + + default: + { + } break; + } + } + + Parser->At = At; + + return Result; +} + +static json_element *ParseJSONList(json_parser *Parser, json_token_type EndType, b32 HasLabels); +static json_element *ParseJSONElement(json_parser *Parser, buffer Label, json_token Value) +{ + b32 Valid = true; + + json_element *SubElement = 0; + if(Value.Type == Token_open_bracket) + { + SubElement = ParseJSONList(Parser, Token_close_bracket, false); + } + else if(Value.Type == Token_open_brace) + { + SubElement = ParseJSONList(Parser, Token_close_brace, true); + } + else if((Value.Type == Token_string_literal) || + (Value.Type == Token_true) || + (Value.Type == Token_false) || + (Value.Type == Token_null) || + (Value.Type == Token_number)) + { + // NOTE(casey): Nothing to do here, since there is no additional data + } + else + { + Valid = false; + } + + json_element *Result = 0; + + if(Valid) + { + Result = (json_element *)malloc(sizeof(json_element)); + Result->Label = Label; + Result->Value = Value.Value; + Result->FirstSubElement = SubElement; + Result->NextSibling = 0; + } + + return Result; +} + +static json_element *ParseJSONList(json_parser *Parser, json_token_type EndType, b32 HasLabels) +{ + json_element *FirstElement = {}; + json_element *LastElement = {}; + + while(IsParsing(Parser)) + { + buffer Label = {}; + json_token Value = GetJSONToken(Parser); + if(HasLabels) + { + if(Value.Type == Token_string_literal) + { + Label = Value.Value; + + json_token Colon = GetJSONToken(Parser); + if(Colon.Type == Token_colon) + { + Value = GetJSONToken(Parser); + } + else + { + Error(Parser, Colon, "Expected colon after field name"); + } + } + else if(Value.Type != EndType) + { + Error(Parser, Value, "Unexpected token in JSON"); + } + } + + json_element *Element = ParseJSONElement(Parser, Label, Value); + if(Element) + { + LastElement = (LastElement ? LastElement->NextSibling : FirstElement) = Element; + } + else if(Value.Type == EndType) + { + break; + } + else + { + Error(Parser, Value, "Unexpected token in JSON"); + } + + json_token Comma = GetJSONToken(Parser); + if(Comma.Type == EndType) + { + break; + } + else if(Comma.Type != Token_comma) + { + Error(Parser, Comma, "Unexpected token in JSON"); + } + } + + return FirstElement; +} + +static json_element *ParseJSON(buffer InputJSON) +{ + TimeFunction; + + json_parser Parser = {}; + Parser.Source = InputJSON; + + json_element *Result = ParseJSONElement(&Parser, {}, GetJSONToken(&Parser)); + return Result; +} + +static void FreeJSON(json_element *Element) +{ + while(Element) + { + json_element *FreeElement = Element; + Element = Element->NextSibling; + + FreeJSON(FreeElement->FirstSubElement); + free(FreeElement); + } +} + +static json_element *LookupElement(json_element *Object, buffer ElementName) +{ + json_element *Result = 0; + + if(Object) + { + for(json_element *Search = Object->FirstSubElement; Search; Search = Search->NextSibling) + { + if(AreEqual(Search->Label, ElementName)) + { + Result = Search; + break; + } + } + } + + return Result; +} + +static f64 ConvertJSONSign(buffer Source, u64 *AtResult) +{ + u64 At = *AtResult; + + f64 Result = 1.0; + if(IsInBounds(Source, At) && (Source.Data[At] == '-')) + { + Result = -1.0; + ++At; + } + + *AtResult = At; + + return Result; +} + +static f64 ConvertJSONNumber(buffer Source, u64 *AtResult) +{ + u64 At = *AtResult; + + f64 Result = 0.0; + while(IsInBounds(Source, At)) + { + u8 Char = Source.Data[At] - (u8)'0'; + if(Char < 10) + { + Result = 10.0*Result + (f64)Char; + ++At; + } + else + { + break; + } + } + + *AtResult = At; + + return Result; +} + +static f64 ConvertElementToF64(json_element *Object, buffer ElementName) +{ + f64 Result = 0.0; + + json_element *Element = LookupElement(Object, ElementName); + if(Element) + { + buffer Source = Element->Value; + u64 At = 0; + + f64 Sign = ConvertJSONSign(Source, &At); + f64 Number = ConvertJSONNumber(Source, &At); + + if(IsInBounds(Source, At) && (Source.Data[At] == '.')) + { + ++At; + f64 C = 1.0 / 10.0; + while(IsInBounds(Source, At)) + { + u8 Char = Source.Data[At] - (u8)'0'; + if(Char < 10) + { + Number = Number + C*(f64)Char; + C *= 1.0 / 10.0; + ++At; + } + else + { + break; + } + } + } + + if(IsInBounds(Source, At) && ((Source.Data[At] == 'e') || (Source.Data[At] == 'E'))) + { + ++At; + if(IsInBounds(Source, At) && (Source.Data[At] == '+')) + { + ++At; + } + + f64 ExponentSign = ConvertJSONSign(Source, &At); + f64 Exponent = ExponentSign*ConvertJSONNumber(Source, &At); + Number *= pow(10.0, Exponent); + } + + Result = Sign*Number; + } + + return Result; +} + +static u64 ParseHaversinePairs(buffer InputJSON, u64 MaxPairCount, haversine_pair *Pairs) +{ + TimeFunction; + + u64 PairCount = 0; + + json_element *JSON = ParseJSON(InputJSON); + + json_element *PairsArray = LookupElement(JSON, CONSTANT_STRING("pairs")); + if(PairsArray) + { + TimeBlock("Lookup and Convert"); + + for(json_element *Element = PairsArray->FirstSubElement; + Element && (PairCount < MaxPairCount); + Element = Element->NextSibling) + { + haversine_pair *Pair = Pairs + PairCount++; + + Pair->X0 = ConvertElementToF64(Element, CONSTANT_STRING("x0")); + Pair->Y0 = ConvertElementToF64(Element, CONSTANT_STRING("y0")); + Pair->X1 = ConvertElementToF64(Element, CONSTANT_STRING("x1")); + Pair->Y1 = ConvertElementToF64(Element, CONSTANT_STRING("y1")); + } + } + + { + TimeBlock("FreeJSON"); + FreeJSON(JSON); + } + + return PairCount; +} diff --git a/part2/listing_0095_profiled_haversine_main.cpp b/part2/listing_0095_profiled_haversine_main.cpp new file mode 100644 index 0000000..ea7b00d --- /dev/null +++ b/part2/listing_0095_profiled_haversine_main.cpp @@ -0,0 +1,185 @@ +/* ======================================================================== + + (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Please see https://computerenhance.com for more information + + ======================================================================== */ + +/* ======================================================================== + LISTING 95 + ======================================================================== */ + +/* NOTE(casey): _CRT_SECURE_NO_WARNINGS is here because otherwise we cannot + call fopen(). If we replace fopen() with fopen_s() to avoid the warning, + then the code doesn't compile on Linux anymore, since fopen_s() does not + exist there. + + What exactly the CRT maintainers were thinking when they made this choice, + I have no idea. */ +#define _CRT_SECURE_NO_WARNINGS + +#include +#include +#include +#include +#include + +typedef uint8_t u8; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef int32_t b32; + +typedef float f32; +typedef double f64; + +#define ArrayCount(Array) (sizeof(Array)/sizeof((Array)[0])) + +struct haversine_pair +{ + f64 X0, Y0; + f64 X1, Y1; +}; + +#define PROFILER 1 +#include "listing_0091_switchable_profiler.cpp" +#include "listing_0065_haversine_formula.cpp" +#include "listing_0068_buffer.cpp" +#include "listing_0094_profiled_lookup_json_parser.cpp" + +static buffer ReadEntireFile(char *FileName) +{ + TimeFunction; + + buffer Result = {}; + + FILE *File = fopen(FileName, "rb"); + if(File) + { +#if _WIN32 + struct __stat64 Stat; + _stat64(FileName, &Stat); +#else + struct stat Stat; + stat(FileName, &Stat); +#endif + + Result = AllocateBuffer(Stat.st_size); + if(Result.Data) + { + if(fread(Result.Data, Result.Count, 1, File) != 1) + { + fprintf(stderr, "ERROR: Unable to read \"%s\".\n", FileName); + FreeBuffer(&Result); + } + } + + fclose(File); + } + else + { + fprintf(stderr, "ERROR: Unable to open \"%s\".\n", FileName); + } + + return Result; +} + +static f64 SumHaversineDistances(u64 PairCount, haversine_pair *Pairs) +{ + TimeFunction; + + f64 Sum = 0; + + f64 SumCoef = 1 / (f64)PairCount; + for(u64 PairIndex = 0; PairIndex < PairCount; ++PairIndex) + { + haversine_pair Pair = Pairs[PairIndex]; + f64 EarthRadius = 6372.8; + f64 Dist = ReferenceHaversine(Pair.X0, Pair.Y0, Pair.X1, Pair.Y1, EarthRadius); + Sum += SumCoef*Dist; + } + + return Sum; +} + +int main(int ArgCount, char **Args) +{ + BeginProfile(); + + int Result = 1; + + if((ArgCount == 2) || (ArgCount == 3)) + { + buffer InputJSON = ReadEntireFile(Args[1]); + + u32 MinimumJSONPairEncoding = 6*4; + u64 MaxPairCount = InputJSON.Count / MinimumJSONPairEncoding; + if(MaxPairCount) + { + buffer ParsedValues = AllocateBuffer(MaxPairCount * sizeof(haversine_pair)); + if(ParsedValues.Count) + { + haversine_pair *Pairs = (haversine_pair *)ParsedValues.Data; + + u64 PairCount = ParseHaversinePairs(InputJSON, MaxPairCount, Pairs); + f64 Sum = SumHaversineDistances(PairCount, Pairs); + + Result = 0; + + fprintf(stdout, "Input size: %llu\n", InputJSON.Count); + fprintf(stdout, "Pair count: %llu\n", PairCount); + fprintf(stdout, "Haversine sum: %.16f\n", Sum); + + if(ArgCount == 3) + { + buffer AnswersF64 = ReadEntireFile(Args[2]); + if(AnswersF64.Count >= sizeof(f64)) + { + f64 *AnswerValues = (f64 *)AnswersF64.Data; + + fprintf(stdout, "\nValidation:\n"); + + u64 RefAnswerCount = (AnswersF64.Count - sizeof(f64)) / sizeof(f64); + if(PairCount != RefAnswerCount) + { + fprintf(stdout, "FAILED - pair count doesn't match %llu.\n", RefAnswerCount); + } + + f64 RefSum = AnswerValues[RefAnswerCount]; + fprintf(stdout, "Reference sum: %.16f\n", RefSum); + fprintf(stdout, "Difference: %.16f\n", Sum - RefSum); + + fprintf(stdout, "\n"); + } + } + } + + FreeBuffer(&ParsedValues); + } + else + { + fprintf(stderr, "ERROR: Malformed input JSON\n"); + } + + FreeBuffer(&InputJSON); + } + else + { + fprintf(stderr, "Usage: %s [haversine_input.json]\n", Args[0]); + fprintf(stderr, " %s [haversine_input.json] [answers.f64]\n", Args[0]); + } + + if(Result == 0) + { + EndAndPrintProfile(); + } + + return Result; +} + +ProfilerEndOfCompilationUnit; diff --git a/part2/listing_0096_selectable_profiler.cpp b/part2/listing_0096_selectable_profiler.cpp new file mode 100644 index 0000000..d216ea5 --- /dev/null +++ b/part2/listing_0096_selectable_profiler.cpp @@ -0,0 +1,176 @@ +/* ======================================================================== + + (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Please see https://computerenhance.com for more information + + ======================================================================== */ + +/* ======================================================================== + LISTING 96 + ======================================================================== */ + +#include "listing_0074_platform_metrics.cpp" + +#ifndef PROFILER +#define PROFILER 0 +#endif + +#ifndef READ_BLOCK_TIMER +#define READ_BLOCK_TIMER ReadCPUTimer +#endif + +#if PROFILER + +struct profile_anchor +{ + u64 TSCElapsedExclusive; // NOTE(casey): Does NOT include children + u64 TSCElapsedInclusive; // NOTE(casey): DOES include children + u64 HitCount; + char const *Label; +}; +static profile_anchor GlobalProfilerAnchors[4096]; +static u32 GlobalProfilerParent; + +struct profile_block +{ + profile_block(char const *Label_, u32 AnchorIndex_) + { + ParentIndex = GlobalProfilerParent; + + AnchorIndex = AnchorIndex_; + Label = Label_; + + profile_anchor *Anchor = GlobalProfilerAnchors + AnchorIndex; + OldTSCElapsedInclusive = Anchor->TSCElapsedInclusive; + + GlobalProfilerParent = AnchorIndex; + StartTSC = READ_BLOCK_TIMER(); + } + + ~profile_block(void) + { + u64 Elapsed = READ_BLOCK_TIMER() - StartTSC; + GlobalProfilerParent = ParentIndex; + + profile_anchor *Parent = GlobalProfilerAnchors + ParentIndex; + profile_anchor *Anchor = GlobalProfilerAnchors + AnchorIndex; + + Parent->TSCElapsedExclusive -= Elapsed; + Anchor->TSCElapsedExclusive += Elapsed; + Anchor->TSCElapsedInclusive = OldTSCElapsedInclusive + Elapsed; + ++Anchor->HitCount; + + /* NOTE(casey): This write happens every time solely because there is no + straightforward way in C++ to have the same ease-of-use. In a better programming + language, it would be simple to have the anchor points gathered and labeled at compile + time, and this repetative write would be eliminated. */ + Anchor->Label = Label; + } + + char const *Label; + u64 OldTSCElapsedInclusive; + u64 StartTSC; + u32 ParentIndex; + u32 AnchorIndex; +}; + +#define NameConcat2(A, B) A##B +#define NameConcat(A, B) NameConcat2(A, B) +#define TimeBlock(Name) profile_block NameConcat(Block, __LINE__)(Name, __COUNTER__ + 1); +#define ProfilerEndOfCompilationUnit static_assert(__COUNTER__ < ArrayCount(GlobalProfilerAnchors), "Number of profile points exceeds size of profiler::Anchors array") + +static void PrintTimeElapsed(u64 TotalTSCElapsed, profile_anchor *Anchor) +{ + f64 Percent = 100.0 * ((f64)Anchor->TSCElapsedExclusive / (f64)TotalTSCElapsed); + printf(" %s[%llu]: %llu (%.2f%%", Anchor->Label, Anchor->HitCount, Anchor->TSCElapsedExclusive, Percent); + if(Anchor->TSCElapsedInclusive != Anchor->TSCElapsedExclusive) + { + f64 PercentWithChildren = 100.0 * ((f64)Anchor->TSCElapsedInclusive / (f64)TotalTSCElapsed); + printf(", %.2f%% w/children", PercentWithChildren); + } + printf(")\n"); +} + +static void PrintAnchorData(u64 TotalCPUElapsed) +{ + for(u32 AnchorIndex = 0; AnchorIndex < ArrayCount(GlobalProfilerAnchors); ++AnchorIndex) + { + profile_anchor *Anchor = GlobalProfilerAnchors + AnchorIndex; + if(Anchor->TSCElapsedInclusive) + { + PrintTimeElapsed(TotalCPUElapsed, Anchor); + } + } +} + +#else + +#define TimeBlock(...) +#define PrintAnchorData(...) +#define ProfilerEndOfCompilationUnit + +#endif + +struct profiler +{ + u64 StartTSC; + u64 EndTSC; +}; +static profiler GlobalProfiler; + +#define TimeFunction TimeBlock(__func__) + +static u64 EstimateBlockTimerFreq(void) +{ + (void)&EstimateCPUTimerFreq; // NOTE(casey): This has to be voided here to prevent compilers from warning us that it is not used + + u64 MillisecondsToWait = 100; + u64 OSFreq = GetOSTimerFreq(); + + u64 BlockStart = READ_BLOCK_TIMER(); + u64 OSStart = ReadOSTimer(); + u64 OSEnd = 0; + u64 OSElapsed = 0; + u64 OSWaitTime = OSFreq * MillisecondsToWait / 1000; + while(OSElapsed < OSWaitTime) + { + OSEnd = ReadOSTimer(); + OSElapsed = OSEnd - OSStart; + } + + u64 BlockEnd = READ_BLOCK_TIMER(); + u64 BlockElapsed = BlockEnd - BlockStart; + + u64 BlockFreq = 0; + if(OSElapsed) + { + BlockFreq = OSFreq * BlockElapsed / OSElapsed; + } + + return BlockFreq; +} + +static void BeginProfile(void) +{ + GlobalProfiler.StartTSC = READ_BLOCK_TIMER(); +} + +static void EndAndPrintProfile() +{ + GlobalProfiler.EndTSC = READ_BLOCK_TIMER(); + u64 TimerFreq = EstimateBlockTimerFreq(); + + u64 TotalTSCElapsed = GlobalProfiler.EndTSC - GlobalProfiler.StartTSC; + + if(TimerFreq) + { + printf("\nTotal time: %0.4fms (timer freq %llu)\n", 1000.0 * (f64)TotalTSCElapsed / (f64)TimerFreq, TimerFreq); + } + + PrintAnchorData(TotalTSCElapsed); +} diff --git a/part2/listing_0097_tsc_select_main.cpp b/part2/listing_0097_tsc_select_main.cpp new file mode 100644 index 0000000..db3724c --- /dev/null +++ b/part2/listing_0097_tsc_select_main.cpp @@ -0,0 +1,185 @@ +/* ======================================================================== + + (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Please see https://computerenhance.com for more information + + ======================================================================== */ + +/* ======================================================================== + LISTING 97 + ======================================================================== */ + +/* NOTE(casey): _CRT_SECURE_NO_WARNINGS is here because otherwise we cannot + call fopen(). If we replace fopen() with fopen_s() to avoid the warning, + then the code doesn't compile on Linux anymore, since fopen_s() does not + exist there. + + What exactly the CRT maintainers were thinking when they made this choice, + I have no idea. */ +#define _CRT_SECURE_NO_WARNINGS + +#include +#include +#include +#include +#include + +typedef uint8_t u8; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef int32_t b32; + +typedef float f32; +typedef double f64; + +#define ArrayCount(Array) (sizeof(Array)/sizeof((Array)[0])) + +struct haversine_pair +{ + f64 X0, Y0; + f64 X1, Y1; +}; + +#define PROFILER 1 +#include "listing_0096_selectable_profiler.cpp" +#include "listing_0065_haversine_formula.cpp" +#include "listing_0068_buffer.cpp" +#include "listing_0083_recursive_timed_lookup_json_parser.cpp" + +static buffer ReadEntireFile(char *FileName) +{ + TimeFunction; + + buffer Result = {}; + + FILE *File = fopen(FileName, "rb"); + if(File) + { +#if _WIN32 + struct __stat64 Stat; + _stat64(FileName, &Stat); +#else + struct stat Stat; + stat(FileName, &Stat); +#endif + + Result = AllocateBuffer(Stat.st_size); + if(Result.Data) + { + if(fread(Result.Data, Result.Count, 1, File) != 1) + { + fprintf(stderr, "ERROR: Unable to read \"%s\".\n", FileName); + FreeBuffer(&Result); + } + } + + fclose(File); + } + else + { + fprintf(stderr, "ERROR: Unable to open \"%s\".\n", FileName); + } + + return Result; +} + +static f64 SumHaversineDistances(u64 PairCount, haversine_pair *Pairs) +{ + TimeFunction; + + f64 Sum = 0; + + f64 SumCoef = 1 / (f64)PairCount; + for(u64 PairIndex = 0; PairIndex < PairCount; ++PairIndex) + { + haversine_pair Pair = Pairs[PairIndex]; + f64 EarthRadius = 6372.8; + f64 Dist = ReferenceHaversine(Pair.X0, Pair.Y0, Pair.X1, Pair.Y1, EarthRadius); + Sum += SumCoef*Dist; + } + + return Sum; +} + +int main(int ArgCount, char **Args) +{ + BeginProfile(); + + int Result = 1; + + if((ArgCount == 2) || (ArgCount == 3)) + { + buffer InputJSON = ReadEntireFile(Args[1]); + + u32 MinimumJSONPairEncoding = 6*4; + u64 MaxPairCount = InputJSON.Count / MinimumJSONPairEncoding; + if(MaxPairCount) + { + buffer ParsedValues = AllocateBuffer(MaxPairCount * sizeof(haversine_pair)); + if(ParsedValues.Count) + { + haversine_pair *Pairs = (haversine_pair *)ParsedValues.Data; + + u64 PairCount = ParseHaversinePairs(InputJSON, MaxPairCount, Pairs); + f64 Sum = SumHaversineDistances(PairCount, Pairs); + + Result = 0; + + fprintf(stdout, "Input size: %llu\n", InputJSON.Count); + fprintf(stdout, "Pair count: %llu\n", PairCount); + fprintf(stdout, "Haversine sum: %.16f\n", Sum); + + if(ArgCount == 3) + { + buffer AnswersF64 = ReadEntireFile(Args[2]); + if(AnswersF64.Count >= sizeof(f64)) + { + f64 *AnswerValues = (f64 *)AnswersF64.Data; + + fprintf(stdout, "\nValidation:\n"); + + u64 RefAnswerCount = (AnswersF64.Count - sizeof(f64)) / sizeof(f64); + if(PairCount != RefAnswerCount) + { + fprintf(stdout, "FAILED - pair count doesn't match %llu.\n", RefAnswerCount); + } + + f64 RefSum = AnswerValues[RefAnswerCount]; + fprintf(stdout, "Reference sum: %.16f\n", RefSum); + fprintf(stdout, "Difference: %.16f\n", Sum - RefSum); + + fprintf(stdout, "\n"); + } + } + } + + FreeBuffer(&ParsedValues); + } + else + { + fprintf(stderr, "ERROR: Malformed input JSON\n"); + } + + FreeBuffer(&InputJSON); + } + else + { + fprintf(stderr, "Usage: %s [haversine_input.json]\n", Args[0]); + fprintf(stderr, " %s [haversine_input.json] [answers.f64]\n", Args[0]); + } + + if(Result == 0) + { + EndAndPrintProfile(); + } + + return Result; +} + +ProfilerEndOfCompilationUnit; diff --git a/part2/listing_0098_qpc_select_main.cpp b/part2/listing_0098_qpc_select_main.cpp new file mode 100644 index 0000000..9e933b7 --- /dev/null +++ b/part2/listing_0098_qpc_select_main.cpp @@ -0,0 +1,186 @@ +/* ======================================================================== + + (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Please see https://computerenhance.com for more information + + ======================================================================== */ + +/* ======================================================================== + LISTING 98 + ======================================================================== */ + +/* NOTE(casey): _CRT_SECURE_NO_WARNINGS is here because otherwise we cannot + call fopen(). If we replace fopen() with fopen_s() to avoid the warning, + then the code doesn't compile on Linux anymore, since fopen_s() does not + exist there. + + What exactly the CRT maintainers were thinking when they made this choice, + I have no idea. */ +#define _CRT_SECURE_NO_WARNINGS + +#include +#include +#include +#include +#include + +typedef uint8_t u8; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef int32_t b32; + +typedef float f32; +typedef double f64; + +#define ArrayCount(Array) (sizeof(Array)/sizeof((Array)[0])) + +struct haversine_pair +{ + f64 X0, Y0; + f64 X1, Y1; +}; + +#define PROFILER 1 +#define READ_BLOCK_TIMER ReadOSTimer +#include "listing_0096_selectable_profiler.cpp" +#include "listing_0065_haversine_formula.cpp" +#include "listing_0068_buffer.cpp" +#include "listing_0083_recursive_timed_lookup_json_parser.cpp" + +static buffer ReadEntireFile(char *FileName) +{ + TimeFunction; + + buffer Result = {}; + + FILE *File = fopen(FileName, "rb"); + if(File) + { +#if _WIN32 + struct __stat64 Stat; + _stat64(FileName, &Stat); +#else + struct stat Stat; + stat(FileName, &Stat); +#endif + + Result = AllocateBuffer(Stat.st_size); + if(Result.Data) + { + if(fread(Result.Data, Result.Count, 1, File) != 1) + { + fprintf(stderr, "ERROR: Unable to read \"%s\".\n", FileName); + FreeBuffer(&Result); + } + } + + fclose(File); + } + else + { + fprintf(stderr, "ERROR: Unable to open \"%s\".\n", FileName); + } + + return Result; +} + +static f64 SumHaversineDistances(u64 PairCount, haversine_pair *Pairs) +{ + TimeFunction; + + f64 Sum = 0; + + f64 SumCoef = 1 / (f64)PairCount; + for(u64 PairIndex = 0; PairIndex < PairCount; ++PairIndex) + { + haversine_pair Pair = Pairs[PairIndex]; + f64 EarthRadius = 6372.8; + f64 Dist = ReferenceHaversine(Pair.X0, Pair.Y0, Pair.X1, Pair.Y1, EarthRadius); + Sum += SumCoef*Dist; + } + + return Sum; +} + +int main(int ArgCount, char **Args) +{ + BeginProfile(); + + int Result = 1; + + if((ArgCount == 2) || (ArgCount == 3)) + { + buffer InputJSON = ReadEntireFile(Args[1]); + + u32 MinimumJSONPairEncoding = 6*4; + u64 MaxPairCount = InputJSON.Count / MinimumJSONPairEncoding; + if(MaxPairCount) + { + buffer ParsedValues = AllocateBuffer(MaxPairCount * sizeof(haversine_pair)); + if(ParsedValues.Count) + { + haversine_pair *Pairs = (haversine_pair *)ParsedValues.Data; + + u64 PairCount = ParseHaversinePairs(InputJSON, MaxPairCount, Pairs); + f64 Sum = SumHaversineDistances(PairCount, Pairs); + + Result = 0; + + fprintf(stdout, "Input size: %llu\n", InputJSON.Count); + fprintf(stdout, "Pair count: %llu\n", PairCount); + fprintf(stdout, "Haversine sum: %.16f\n", Sum); + + if(ArgCount == 3) + { + buffer AnswersF64 = ReadEntireFile(Args[2]); + if(AnswersF64.Count >= sizeof(f64)) + { + f64 *AnswerValues = (f64 *)AnswersF64.Data; + + fprintf(stdout, "\nValidation:\n"); + + u64 RefAnswerCount = (AnswersF64.Count - sizeof(f64)) / sizeof(f64); + if(PairCount != RefAnswerCount) + { + fprintf(stdout, "FAILED - pair count doesn't match %llu.\n", RefAnswerCount); + } + + f64 RefSum = AnswerValues[RefAnswerCount]; + fprintf(stdout, "Reference sum: %.16f\n", RefSum); + fprintf(stdout, "Difference: %.16f\n", Sum - RefSum); + + fprintf(stdout, "\n"); + } + } + } + + FreeBuffer(&ParsedValues); + } + else + { + fprintf(stderr, "ERROR: Malformed input JSON\n"); + } + + FreeBuffer(&InputJSON); + } + else + { + fprintf(stderr, "Usage: %s [haversine_input.json]\n", Args[0]); + fprintf(stderr, " %s [haversine_input.json] [answers.f64]\n", Args[0]); + } + + if(Result == 0) + { + EndAndPrintProfile(); + } + + return Result; +} + +ProfilerEndOfCompilationUnit; diff --git a/part2/listing_0099_qpc_minimal_blocks_main.cpp b/part2/listing_0099_qpc_minimal_blocks_main.cpp new file mode 100644 index 0000000..fdd3dff --- /dev/null +++ b/part2/listing_0099_qpc_minimal_blocks_main.cpp @@ -0,0 +1,186 @@ +/* ======================================================================== + + (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Please see https://computerenhance.com for more information + + ======================================================================== */ + +/* ======================================================================== + LISTING 99 + ======================================================================== */ + +/* NOTE(casey): _CRT_SECURE_NO_WARNINGS is here because otherwise we cannot + call fopen(). If we replace fopen() with fopen_s() to avoid the warning, + then the code doesn't compile on Linux anymore, since fopen_s() does not + exist there. + + What exactly the CRT maintainers were thinking when they made this choice, + I have no idea. */ +#define _CRT_SECURE_NO_WARNINGS + +#include +#include +#include +#include +#include + +typedef uint8_t u8; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef int32_t b32; + +typedef float f32; +typedef double f64; + +#define ArrayCount(Array) (sizeof(Array)/sizeof((Array)[0])) + +struct haversine_pair +{ + f64 X0, Y0; + f64 X1, Y1; +}; + +#define PROFILER 1 +#define READ_BLOCK_TIMER ReadOSTimer +#include "listing_0091_switchable_profiler.cpp" +#include "listing_0065_haversine_formula.cpp" +#include "listing_0068_buffer.cpp" +#include "listing_0094_profiled_lookup_json_parser.cpp" + +static buffer ReadEntireFile(char *FileName) +{ + TimeFunction; + + buffer Result = {}; + + FILE *File = fopen(FileName, "rb"); + if(File) + { +#if _WIN32 + struct __stat64 Stat; + _stat64(FileName, &Stat); +#else + struct stat Stat; + stat(FileName, &Stat); +#endif + + Result = AllocateBuffer(Stat.st_size); + if(Result.Data) + { + if(fread(Result.Data, Result.Count, 1, File) != 1) + { + fprintf(stderr, "ERROR: Unable to read \"%s\".\n", FileName); + FreeBuffer(&Result); + } + } + + fclose(File); + } + else + { + fprintf(stderr, "ERROR: Unable to open \"%s\".\n", FileName); + } + + return Result; +} + +static f64 SumHaversineDistances(u64 PairCount, haversine_pair *Pairs) +{ + TimeFunction; + + f64 Sum = 0; + + f64 SumCoef = 1 / (f64)PairCount; + for(u64 PairIndex = 0; PairIndex < PairCount; ++PairIndex) + { + haversine_pair Pair = Pairs[PairIndex]; + f64 EarthRadius = 6372.8; + f64 Dist = ReferenceHaversine(Pair.X0, Pair.Y0, Pair.X1, Pair.Y1, EarthRadius); + Sum += SumCoef*Dist; + } + + return Sum; +} + +int main(int ArgCount, char **Args) +{ + BeginProfile(); + + int Result = 1; + + if((ArgCount == 2) || (ArgCount == 3)) + { + buffer InputJSON = ReadEntireFile(Args[1]); + + u32 MinimumJSONPairEncoding = 6*4; + u64 MaxPairCount = InputJSON.Count / MinimumJSONPairEncoding; + if(MaxPairCount) + { + buffer ParsedValues = AllocateBuffer(MaxPairCount * sizeof(haversine_pair)); + if(ParsedValues.Count) + { + haversine_pair *Pairs = (haversine_pair *)ParsedValues.Data; + + u64 PairCount = ParseHaversinePairs(InputJSON, MaxPairCount, Pairs); + f64 Sum = SumHaversineDistances(PairCount, Pairs); + + Result = 0; + + fprintf(stdout, "Input size: %llu\n", InputJSON.Count); + fprintf(stdout, "Pair count: %llu\n", PairCount); + fprintf(stdout, "Haversine sum: %.16f\n", Sum); + + if(ArgCount == 3) + { + buffer AnswersF64 = ReadEntireFile(Args[2]); + if(AnswersF64.Count >= sizeof(f64)) + { + f64 *AnswerValues = (f64 *)AnswersF64.Data; + + fprintf(stdout, "\nValidation:\n"); + + u64 RefAnswerCount = (AnswersF64.Count - sizeof(f64)) / sizeof(f64); + if(PairCount != RefAnswerCount) + { + fprintf(stdout, "FAILED - pair count doesn't match %llu.\n", RefAnswerCount); + } + + f64 RefSum = AnswerValues[RefAnswerCount]; + fprintf(stdout, "Reference sum: %.16f\n", RefSum); + fprintf(stdout, "Difference: %.16f\n", Sum - RefSum); + + fprintf(stdout, "\n"); + } + } + } + + FreeBuffer(&ParsedValues); + } + else + { + fprintf(stderr, "ERROR: Malformed input JSON\n"); + } + + FreeBuffer(&InputJSON); + } + else + { + fprintf(stderr, "Usage: %s [haversine_input.json]\n", Args[0]); + fprintf(stderr, " %s [haversine_input.json] [answers.f64]\n", Args[0]); + } + + if(Result == 0) + { + EndAndPrintProfile(); + } + + return Result; +} + +ProfilerEndOfCompilationUnit; diff --git a/part3/base.c b/part3/base.c new file mode 100644 index 0000000..511312e --- /dev/null +++ b/part3/base.c @@ -0,0 +1,353 @@ +// NOTE: Implementation //////////////////////////////////////////////////////////////////////////// +bool Str8_Equals(Str8 lhs, Str8 rhs) +{ + bool result = lhs.size == rhs.size && memcmp(lhs.data, rhs.data, lhs.size) == 0; + return result; +} + +Str8ToU64Result Str8_ToU64(Str8 string) +{ + Str8ToU64Result result = {0}; + + size_t ch_index = 0; + while (ch_index < string.size && CharIsWhiteSpace(string.data[ch_index])) + ch_index++; + + for (; ch_index < string.size; ch_index++) { + char ch = string.data[ch_index]; + if (ch >= '0' && ch <= '9') { + result.value = (result.value * 10) + (ch - '0'); + } else { + return result; + } + } + + result.success = true; + return result; +} + +Str8BinarySplitResult Str8_BinarySplit(Str8 buffer, Str8 find) +{ + Str8BinarySplitResult result = {0}; + result.lhs = buffer; + for (size_t index = 0; (index + find.size) <= buffer.size; index++) { + Str8 check = {buffer.data + index, find.size}; + if (Str8_Equals(find, check)) { + result.lhs.size = index; + result.rhs.data = check.data + find.size; + result.rhs.size = buffer.size - (index + find.size); + break; + } + } + return result; +} + +bool CharIsWhiteSpace(char ch) +{ + bool result = ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'; + return result; +} + +bool CharIsDigit(char ch) +{ + bool result = ch >= '0' && ch <= '9'; + return result; +} + +void Profiler_Dump() +{ + u64 total_elapsed_tsc = g_profiler.end_tsc - g_profiler.begin_tsc; + u64 cpu_frequency = EstimateCPUTimerFreq(); + if (cpu_frequency) + printf("\nTotal time: %0.4fms (CPU freq %llu)\n", 1000.0 * (f64)total_elapsed_tsc / (f64)cpu_frequency, cpu_frequency); + + for (uint32_t index = 1; index < ARRAY_UCOUNT(g_profiler.anchors); index++) { + ProfilerAnchor const *anchor = g_profiler.anchors + index; + if (!anchor->elapsed_tsc_inclusive) + break; + + f64 percent = total_elapsed_tsc ? (f64)anchor->elapsed_tsc_exclusive / (f64)total_elapsed_tsc * 100.0 : 100.0; + printf(" %.*s[%zu]: %llu (%.2f%%", STR8_FMT(anchor->label), anchor->hits, anchor->elapsed_tsc_exclusive, percent); + if (anchor->elapsed_tsc_inclusive != anchor->elapsed_tsc_exclusive) { + f64 percent_w_children = total_elapsed_tsc ? ((f64)anchor->elapsed_tsc_inclusive / (f64)total_elapsed_tsc * 100.0) : 100.0; + printf(", %.2f%% w/children", percent_w_children); + } + printf(")"); + + if (anchor->byte_count) { + f64 megabytes_processed = anchor->byte_count / (1024.f * 1024.f); + f64 elapsed_s = anchor->elapsed_tsc_inclusive / CAST(f64)cpu_frequency; + f64 bytes_per_s = anchor->byte_count / elapsed_s; + f64 gigabytes_bandwidth = bytes_per_s / (1024.f * 1024.f * 1024.f); + printf(" %.3fmb at %.2fgb/s", megabytes_processed, gigabytes_bandwidth); + } + printf("\n"); + } +} + +ProfilerZone Profiler_BeginZone_(Str8 label, uint32_t index, u64 byte_count) +{ + ProfilerZone result = {0}; + #if defined(PROFILER) + result.index = index; + result.label = label; + result.tsc = ReadCPUTimer(); + result.elapsed_tsc_inclusive = g_profiler.anchors[index].elapsed_tsc_inclusive; + result.byte_count = byte_count; + result.parent_index = g_profiler.parent_index; + g_profiler.parent_index = index; + #else + (void)label; (void)index; (void)byte_count; + #endif + return result; +} + +void Profiler_EndZone(ProfilerZone zone) +{ + #if defined(PROFILER) + u64 elapsed_tsc = ReadCPUTimer() - zone.tsc; + ProfilerAnchor* anchor = g_profiler.anchors + zone.index; + ProfilerAnchor* parent = g_profiler.anchors + zone.parent_index; + + anchor->elapsed_tsc_exclusive += elapsed_tsc; + anchor->elapsed_tsc_inclusive = zone.elapsed_tsc_inclusive + elapsed_tsc; + anchor->label = zone.label; + anchor->byte_count += zone.byte_count; + anchor->hits++; + parent->elapsed_tsc_exclusive -= elapsed_tsc; + g_profiler.parent_index = zone.parent_index; + #else + (void)zone; + #endif +} + +#pragma warning(push) +#pragma warning(disable: 4146) // warning C4146: unary minus operator applied to unsigned type, result still unsigned +uint32_t PCG32_Pie (uint64_t *state) +{ + uint64_t old = *state ^ 0xc90fdaa2adf85459ULL; + *state = *state * 6364136223846793005ULL + 0xc90fdaa2adf85459ULL; + uint32_t xorshifted = (uint32_t)(((old >> 18u) ^ old) >> 27u); + uint32_t rot = old >> 59u; + return (xorshifted >> rot) | (xorshifted << ((-rot) & 31)); +} +#pragma warning(pop) + +f64 PCG32_PieF64(uint64_t *state, f64 min, f64 max) +{ + uint32_t u32_value = PCG32_Pie(state); + f64 t01 = CAST(f64)u32_value / CAST(f64)CAST(uint32_t)-1; + f64 result = min + (max - min) * t01; + return result; +} + +bool BufferIsValid(Buffer buffer) +{ + bool result = buffer.data && buffer.size; + return result; +} + +BufferIterator BufferIteratorInit(Buffer buffer) +{ + BufferIterator result = {0}; + result.buffer = buffer; + return result; +} + +bool BufferIteratorHasMoreBytes(BufferIterator it) +{ + bool result = BufferIsValid(it.buffer) && it.index < it.buffer.size; + return result; +} + +uint8_t BufferIteratorPeekByte(BufferIterator *it) +{ + ASSERT(it); + ASSERT(BufferIsValid(it->buffer)); + ASSERT(it->index < it->buffer.size); + uint8_t result = it->buffer.data[it->index]; + return result; +} + +uint8_t BufferIteratorNextByte(BufferIterator *it) +{ + uint8_t result = BufferIteratorPeekByte(it); + it->index++; + return result; +} + +Buffer FileRead(char const *file_path) +{ + Buffer result = {0}; + + // NOTE: Determine file size /////////////////////////////////////////////////////////////////// + WIN32_FILE_ATTRIBUTE_DATA file_attrib_data = {0}; + if (GetFileAttributesEx(file_path, GetFileExInfoStandard, &file_attrib_data) == 0) + return result; + + // NOTE: Open file ///////////////////////////////////////////////////////////////////////////// + HANDLE file_handle = CreateFile( + /*LPCSTR lpFileName*/ file_path, + /*DWORD dwDesiredAccess*/ GENERIC_READ, + /*DWORD dwShareMode*/ 0, + /*LPSECURITY_ATTRIBUTES lpSecurityAttributes*/ NULL, + /*DWORD dwCreationDisposition*/ OPEN_EXISTING, + /*DWORD dwFlagsAndAttributes*/ 0, + /*HANDLE hTemplateFile*/ NULL + ); + + if (file_handle == INVALID_HANDLE_VALUE) + return result; + + // NOTE: Allocate buffer /////////////////////////////////////////////////////////////////////// + uint64_t file_size = (uint64_t)file_attrib_data.nFileSizeHigh << 32 | (uint64_t)file_attrib_data.nFileSizeLow << 0; + ASSERT(file_size < (DWORD)-1); + char *buffer = VirtualAlloc( + /*LPVOID lpAddress*/ NULL, + /*SIZE_T dwSize*/ file_size, + /*DWORD flAllocationType*/ MEM_COMMIT | MEM_RESERVE, + /*DWORD flProtect*/ PAGE_READWRITE + ); + + if (!buffer) + goto end; + + // NOTE: Read file to buffer /////////////////////////////////////////////////////////////////// + DWORD bytes_read = 0; + ProfilerZone prof_file_read_zone = Profiler_BeginZoneBandwidth("File Read", file_size); + BOOL read_file_result = ReadFile( + /*HANDLE hFile*/ file_handle, + /*LPVOID lpBuffer*/ buffer, + /*DWORD nNumberOfBytesToRead*/ CAST(DWORD)file_size, + /*LPDWORD lpNumberOfBytesRead*/ &bytes_read, + /*LPOVERLAPPED lpOverlapped*/ NULL + ); + Profiler_EndZone(prof_file_read_zone); + + // NOTE: Handle read result //////////////////////////////////////////////////////////////////// + if (read_file_result == 0) { + VirtualFree(buffer, 0, MEM_RELEASE); + } else { + result.data = buffer; + result.size = file_size; + } + +end: + CloseHandle(file_handle); + return result; +}; + +void FileFree(Buffer buffer) +{ + if (BufferIsValid(buffer)) + VirtualFree(buffer.data, 0, MEM_RELEASE); +} + +bool FileWrite(char const *file_path, void const *buffer, size_t buffer_size) +{ + bool result = false; + + // NOTE: Open file ///////////////////////////////////////////////////////////////////////////// + HANDLE file_handle = CreateFile( + /*LPCSTR lpFileName*/ file_path, + /*DWORD dwDesiredAccess*/ GENERIC_WRITE, + /*DWORD dwShareMode*/ 0, + /*LPSECURITY_ATTRIBUTES lpSecurityAttributes*/ NULL, + /*DWORD dwCreationDisposition*/ CREATE_ALWAYS, + /*DWORD dwFlagsAndAttributes*/ 0, + /*HANDLE hTemplateFile*/ NULL + ); + + if (file_handle == INVALID_HANDLE_VALUE) + return result; + + // NOTE: Write file to disk //////////////////////////////////////////////////////////////////// + DWORD bytes_written = 0; + BOOL write_file_result = WriteFile( + /*HANDLE hFile*/ file_handle, + /*LPVOID lpBuffer*/ buffer, + /*DWORD nNumberOfBytesToWrite*/ CAST(DWORD)buffer_size, + /*LPDWORD lpNumberOfBytesWrite*/ &bytes_written, + /*LPOVERLAPPED lpOverlapped*/ NULL + ); + + ASSERT(bytes_written == buffer_size); + result = write_file_result && bytes_written == buffer_size; + CloseHandle(file_handle); + return result; +}; + +void PrintHandle(void *handle, Str8 string) +{ + DWORD bytes_written = 0; + WriteFile(handle, string.data, CAST(DWORD)string.size, &bytes_written, NULL); + (void)bytes_written; +} + +void Print(Str8 string) +{ + if (pap_globals.stdout_handle == NULL) { + pap_globals.stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD mode = 0; + BOOL get_console_mode_result = GetConsoleMode( + /*HANDLE hConsoleHandle*/ pap_globals.stdout_handle, + /*LPDWORD lpMode*/ &mode + ); + pap_globals.write_to_console = get_console_mode_result != 0; + } + + + ASSERT(string.size < CAST(DWORD)-1); + if (pap_globals.write_to_console) { + DWORD chars_written = 0; + WriteConsoleA(pap_globals.stdout_handle, string.data, (DWORD)string.size, &chars_written, NULL); + } else { + PrintHandle(pap_globals.stdout_handle, string); + } +} + +void PrintFmt(char const *fmt, ...) +{ + va_list args, args_copy; + va_start(args, fmt); + + va_copy(args_copy, args); + int string_size = vsnprintf(NULL, 0, fmt, args_copy); + va_end(args_copy); + + char buffer[8192]; + ASSERT(string_size >= 0 && string_size < ARRAY_UCOUNT(buffer)); + if (string_size) { + vsnprintf(buffer, sizeof(buffer), fmt, args); + Str8 string = {.data = buffer, .size = string_size}; + Print(string); + } + + va_end(args); +} + +void PrintLn(Str8 string) +{ + Print(string); + Print(STR8("\n")); +} + +void PrintLnFmt(char const *fmt, ...) +{ + va_list args, args_copy; + va_start(args, fmt); + + va_copy(args_copy, args); + int string_size = vsnprintf(NULL, 0, fmt, args_copy); + va_end(args_copy); + + char buffer[8192]; + ASSERT(string_size >= 0 && string_size < ARRAY_UCOUNT(buffer)); + if (string_size) { + vsnprintf(buffer, sizeof(buffer), fmt, args); + Str8 string = {.data = buffer, .size = string_size}; + PrintLn(string); + } + + va_end(args); +} diff --git a/part3/base.h b/part3/base.h new file mode 100644 index 0000000..d4b6990 --- /dev/null +++ b/part3/base.h @@ -0,0 +1,141 @@ +#include + +// NOTE: Macros //////////////////////////////////////////////////////////////////////////////////// +#define STRINGIFY2(token) #token +#define STRINGIFY(token) STRINGIFY2(token) + +#if defined(NDEBUG) + #define ASSERT(expr) +#else + #define ASSERT(expr) \ + if (!(expr)) { \ + PrintLnFmt("Assertion triggered [file=\"" __FILE__ ":" STRINGIFY(__LINE__) "\", expr=\"" #expr "\"]"); \ + __debugbreak(); \ + } +#endif + +#define ARRAY_UCOUNT(array) sizeof((array)) / sizeof((array)[0]) +#define CAST(Type) (Type) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +typedef float f32; +typedef double f64; +typedef uint64_t u64; + +// NOTE: Globals /////////////////////////////////////////////////////////////////////////////////// +typedef struct Globals { + HANDLE stdout_handle; + bool write_to_console; +} Globals; + +Globals pap_globals; + +// NOTE: Strings /////////////////////////////////////////////////////////////////////////////////// +typedef struct Str8 { + char *data; + size_t size; +} Str8; + +typedef struct Str8ToU64Result { + bool success; + uint64_t value; +} Str8ToU64Result; + +typedef struct Str8BinarySplitResult { + Str8 lhs; + Str8 rhs; +} Str8BinarySplitResult; + +#define STR8(string) (Str8){.data = (string), .size = ARRAY_UCOUNT(string) - 1 } +#define STR8_FMT(string) (int)((string).size), (string).data + +bool Str8_Equals(Str8 lhs, Str8 rhs); +Str8ToU64Result Str8_ToU64(Str8 string); +Str8BinarySplitResult Str8_BinarySplit(Str8 buffer, Str8 find); + +bool CharIsWhiteSpace(char ch); +bool CharIsDigit(char ch); + +// NOTE: Profiler ////////////////////////////////////////////////////////////////////////////////// +typedef struct ProfilerAnchor { + Str8 label; + u64 elapsed_tsc_exclusive; // Does not include children + u64 elapsed_tsc_inclusive; // Includes children + u64 byte_count; + u64 hits; +} ProfilerAnchor; + +typedef struct Profiler { + ProfilerAnchor anchors[4096]; + u64 begin_tsc; + u64 end_tsc; + u64 parent_index; +} Profiler; + +typedef struct ProfilerZone { + u64 parent_index; + uint32_t index; + Str8 label; + u64 elapsed_tsc_inclusive; + u64 tsc; + u64 byte_count; +} ProfilerZone; + +static Profiler g_profiler; + +#define Profiler_BeginZone(label) Profiler_BeginZone_(STR8(label), __COUNTER__ + 1, 0) +#define Profiler_BeginZoneBandwidth(label, byte_count) Profiler_BeginZone_(STR8(label), __COUNTER__ + 1, byte_count) + +static void Profiler_Dump (); +static ProfilerZone Profiler_BeginZone_(Str8 label, uint32_t index, u64 byte_count); +static void Profiler_EndZone (ProfilerZone zone); + +// NOTE: PCG32 ///////////////////////////////////////////////////////////////////////////////////// +// NOTE: PCG RNG from Demetri Spanos: https://github.com/demetri/scribbles +// pcg32_pie, based on the minimal C version from O'Neill at pcg-random.org; +// I've made a few (subjective) UX improvements for beginner use +// +// I'm not allowing the user to pick the stream/increment constant at all, +// since there is almost never a reason for doing this in common applications. +// This means that the prng state is reduced to a single uint64_t which also +// means we can avoid having a state struct at all. The (fixed) stream constant +// uses the leading hex digits of pi and e multipled by 2^30 (c90fdaa2 and +// adf85459). +// +// I have also added an XOR with the same digits on the output path prior +// to xorshift mixing. This prevents the "surprising" result that the +// first "random 32-bit number" from a (very common) 0 seed is 0. +// +// use: +// uint64_t state = 12345; // whatever you like can go here +// uint32_t some_random_32_bits = pcg32_pie(&state); +// uint32_t more_random_32_bits = pcg32_pie(&state); +uint32_t PCG32_Pie (uint64_t *state); +f64 PCG32_PieF64(uint64_t *state, f64 min, f64 max); + +// NOTE: Buffer //////////////////////////////////////////////////////////////////////////////////// +typedef struct Buffer { + char *data; + size_t size; +} Buffer; + +typedef struct BufferIterator { + Buffer buffer; + size_t index; +} BufferIterator; + +bool BufferIsValid (Buffer buffer); +BufferIterator BufferIteratorInit (Buffer buffer); +bool BufferIteratorHasMoreBytes(BufferIterator it); +uint8_t BufferIteratorNextByte (BufferIterator *it); + +// NOTE: File ////////////////////////////////////////////////////////////////////////////////////// +Buffer FileRead (char const *file_path); +void FileFree (Buffer buffer); +bool FileWrite(char const *file_path, void const *buffer, size_t buffer_size); + +// NOTE: Print ///////////////////////////////////////////////////////////////////////////////////// +void PrintHandle(void *handle, Str8 string); +void PrintLn (Str8 string); +void PrintLnFmt (char const *fmt, ...); diff --git a/part3/repetition_tester.c b/part3/repetition_tester.c new file mode 100644 index 0000000..2a235e5 --- /dev/null +++ b/part3/repetition_tester.c @@ -0,0 +1,10 @@ +#include +#include + +#include "base.h" +#include "base.c" + +int main() +{ + return 0; +}