Prepare for part 3

This commit is contained in:
2024-03-03 22:33:57 +11:00
parent d01cf53ff8
commit 8083a7c782
20 changed files with 3283 additions and 131 deletions
+3 -6
View File
@@ -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;
+8 -16
View File
@@ -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,
+10 -18
View File
@@ -1,7 +1,6 @@
#include <stdint.h>
// 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, ...);
@@ -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;
}
@@ -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 <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include <sys/stat.h>
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");
+142
View File
@@ -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);
}
@@ -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 <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include <sys/stat.h>
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;
@@ -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 <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include <sys/stat.h>
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;
@@ -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;
}
@@ -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 <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include <sys/stat.h>
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;
+176
View File
@@ -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);
}
+185
View File
@@ -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 <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include <sys/stat.h>
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;
+186
View File
@@ -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 <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include <sys/stat.h>
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;
@@ -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 <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include <sys/stat.h>
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;