diff --git a/code/build.bat b/code/build.bat index 125bfc5..e886679 100644 --- a/code/build.bat +++ b/code/build.bat @@ -5,9 +5,8 @@ set CommonCompilerOptions=/Zi /FC /nologo /DVN_INTERNAL=1 /DVN_SLOW=1 /Oi /W4 /W if not exist "../build" mkdir "../build" pushd "../build/" - -rem cl %CommonCompilerOptions% ../code/gen.cpp -rem gen.exe +rem cl /Zi /nologo /FC ../code/third_party/codegen/codegen.c +codegen ../code/ cl %CommonCompilerOptions% ../code/vn.cpp /LD /link /export:VN_UpdateAndRender /incremental:no cl %CommonCompilerOptions% ../code/win32_main.cpp /link user32.lib gdi32.lib winmm.lib opengl32.lib diff --git a/code/core/core.h b/code/core/core.h index 858d1ed..fd6d77b 100644 --- a/code/core/core.h +++ b/code/core/core.h @@ -90,6 +90,12 @@ Index = ___; break;\ }\ }\ +#define Bytes(n) (n) +#define Kilobytes(n) (n << 10) +#define Megabytes(n) (n << 20) +#define Gigabytes(n) (((u64)n) << 30) +#define Terabytes(n) (((u64)n) << 40) + //- sixten: Min, max & clamp #define Minimum(A, B) (((A)<(B))?(A):(B)) @@ -103,6 +109,23 @@ Index = ___; break;\ #define IsNull(x) ((x) == 0) #define SetNull(x) ((x) = 0) +#define QueuePush_N(f,l,n,next) (IsNull(f)?\ +(((f)=(l)=(n)), SetNull((n)->next)):\ +((l)->next=(n),(l)=(n),SetNull((n)->next))) +#define QueuePushFront_N(f,l,n,next) (IsNull(f) ? (((f) = (l) = (n)), SetNull((n)->next)) :\ +((n)->next = (f)), ((f) = (n))) +#define QueuePop_N(f,l,next) ((f)==(l)?\ +(SetNull(f),SetNull(l)):\ +((f)=(f)->next)) +#define StackPush_N(f,n,next) ((n)->next=(f),(f)=(n)) +#define StackPop_N(f,next) (IsNull(f)?0:((f)=(f)->next)) + +#define QueuePush(f,l,n) QueuePush_N(f,l,n,Next) +#define QueuePushFront(f,l,n) QueuePushFront_N(f,l,n,Next) +#define QueuePop(f,l) QueuePop_N(f,l,Next) +#define StackPush(f,n) StackPush_N(f,n,Next) +#define StackPop(f) StackPop_N(f,Next) + #define DLLInsert_NP(f,l,p,n,next,prev) \ (IsNull(f) ? (((f) = (l) = (n)), SetNull((n)->next), SetNull((n)->prev)) :\ IsNull(p) ? (SetNull((n)->prev), (n)->next = (f), (IsNull(f) ? (0) : ((f)->prev = (n))), (f) = (n)) :\ @@ -168,7 +191,6 @@ inline void EndTicketMutex(ticket_mutex *Mutex) } //- sixten: Axes - enum axis2 { Axis2_X, @@ -178,4 +200,15 @@ enum axis2 inline axis2 Opposite(axis2 Axis) { axis2 Result = (axis2)(!(u32)Axis); return(Result); } +//////////////////////////////// +//- sixten: Corners +enum corner +{ + Corner_00 = 0, + Corner_10 = (1 << Axis2_X), + Corner_01 = (1 << Axis2_Y), + Corner_11 = (1 << Axis2_X)|(1 << Axis2_Y), + Corner_Count, +}; + #endif //CORE_H diff --git a/code/core/core_math.cpp b/code/core/core_math.cpp index a99a57f..ceb2175 100644 --- a/code/core/core_math.cpp +++ b/code/core/core_math.cpp @@ -489,9 +489,9 @@ inline v4_s64 Max(v4_s64 A, v4_s64 B) //- sixten: Range functions -inline range1_r32 Range1R32(r32 Min, r32 Max) +inline range1_r32 Range1R32(r32 A, r32 B) { - range1_r32 Result = {Min, Max}; + range1_r32 Result = {Min(A, B), Max(A, B)}; return(Result); } @@ -501,6 +501,12 @@ inline b32 InRange(range1_r32 Range, r32 Value) return(Result); } +inline b32 Contains(range1_r32 Range, r32 Value) +{ + b32 Result = (Value >= Range.Min) && (Value <= Range.Max); + return(Result); +} + inline r32 DimOfRange(range1_r32 Range) { r32 Result = Range.Max - Range.Min; @@ -513,9 +519,9 @@ inline range1_r32 Intersection(range1_r32 A, range1_r32 B) return(Result); } -inline range1_s32 Range1S32(s32 Min, s32 Max) +inline range1_s32 Range1S32(s32 A, s32 B) { - range1_s32 Result = {Min, Max}; + range1_s32 Result = {Min(A, B), Max(A, B)}; return(Result); } @@ -525,6 +531,12 @@ inline b32 InRange(range1_s32 Range, s32 Value) return(Result); } +inline b32 Contains(range1_s32 Range, s32 Value) +{ + b32 Result = (Value >= Range.Min) && (Value <= Range.Max); + return(Result); +} + inline s32 DimOfRange(range1_s32 Range) { s32 Result = Range.Max - Range.Min; @@ -537,9 +549,9 @@ inline range1_s32 Intersection(range1_s32 A, range1_s32 B) return(Result); } -inline range1_s64 Range1S64(s64 Min, s64 Max) +inline range1_s64 Range1S64(s64 A, s64 B) { - range1_s64 Result = {Min, Max}; + range1_s64 Result = {Min(A, B), Max(A, B)}; return(Result); } @@ -549,6 +561,12 @@ inline b32 InRange(range1_s64 Range, s64 Value) return(Result); } +inline b32 Contains(range1_s64 Range, s64 Value) +{ + b32 Result = (Value >= Range.Min) && (Value <= Range.Max); + return(Result); +} + inline s64 DimOfRange(range1_s64 Range) { s64 Result = Range.Max - Range.Min; @@ -561,9 +579,9 @@ inline range1_s64 Intersection(range1_s64 A, range1_s64 B) return(Result); } -inline range2_r32 Range2R32(v2_r32 Min, v2_r32 Max) +inline range2_r32 Range2R32(v2_r32 A, v2_r32 B) { - range2_r32 Result = {Min, Max}; + range2_r32 Result = {Min(A, B), Max(A, B)}; return(Result); } @@ -574,6 +592,13 @@ inline b32 InRange(range2_r32 Range, v2_r32 Value) return(Result); } +inline b32 Contains(range2_r32 Range, v2_r32 Value) +{ + b32 Result = ((Value.x >= Range.Min.x) && (Value.y >= Range.Min.y) && + (Value.x <= Range.Max.x) && (Value.y <= Range.Max.y)); + return(Result); +} + inline v2_r32 DimOfRange(range2_r32 Range) { v2_r32 Result = Range.Max - Range.Min; @@ -586,9 +611,16 @@ inline range2_r32 Intersection(range2_r32 A, range2_r32 B) return(Result); } -inline range2_s32 Range2S32(v2_s32 Min, v2_s32 Max) +inline v2_r32 CornerFromRange(range2_r32 Range, corner Corner) { - range2_s32 Result = {Min, Max}; + v2_r32 Result = V2R32((Corner & (1 << Axis2_X))?Range.Min.x:Range.Max.x, + (Corner & (1 << Axis2_Y))?Range.Min.y:Range.Max.y); + return(Result); +} + +inline range2_s32 Range2S32(v2_s32 A, v2_s32 B) +{ + range2_s32 Result = {Min(A, B), Max(A, B)}; return(Result); } @@ -599,6 +631,13 @@ inline b32 InRange(range2_s32 Range, v2_s32 Value) return(Result); } +inline b32 Contains(range2_s32 Range, v2_s32 Value) +{ + b32 Result = ((Value.x >= Range.Min.x) && (Value.y >= Range.Min.y) && + (Value.x <= Range.Max.x) && (Value.y <= Range.Max.y)); + return(Result); +} + inline v2_s32 DimOfRange(range2_s32 Range) { v2_s32 Result = Range.Max - Range.Min; @@ -611,9 +650,16 @@ inline range2_s32 Intersection(range2_s32 A, range2_s32 B) return(Result); } -inline range2_s64 Range2S64(v2_s64 Min, v2_s64 Max) +inline v2_s32 CornerFromRange(range2_s32 Range, corner Corner) { - range2_s64 Result = {Min, Max}; + v2_s32 Result = V2S32((Corner & (1 << Axis2_X))?Range.Min.x:Range.Max.x, + (Corner & (1 << Axis2_Y))?Range.Min.y:Range.Max.y); + return(Result); +} + +inline range2_s64 Range2S64(v2_s64 A, v2_s64 B) +{ + range2_s64 Result = {Min(A, B), Max(A, B)}; return(Result); } @@ -624,6 +670,13 @@ inline b32 InRange(range2_s64 Range, v2_s64 Value) return(Result); } +inline b32 Contains(range2_s64 Range, v2_s64 Value) +{ + b32 Result = ((Value.x >= Range.Min.x) && (Value.y >= Range.Min.y) && + (Value.x <= Range.Max.x) && (Value.y <= Range.Max.y)); + return(Result); +} + inline v2_s64 DimOfRange(range2_s64 Range) { v2_s64 Result = Range.Max - Range.Min; @@ -634,4 +687,11 @@ inline range2_s64 Intersection(range2_s64 A, range2_s64 B) { range2_s64 Result = {Max(A.Min, B.Min), Min(A.Max, B.Max)}; return(Result); +} + +inline v2_s64 CornerFromRange(range2_s64 Range, corner Corner) +{ + v2_s64 Result = V2S64((Corner & (1 << Axis2_X))?Range.Min.x:Range.Max.x, + (Corner & (1 << Axis2_Y))?Range.Min.y:Range.Max.y); + return(Result); } \ No newline at end of file diff --git a/code/core/core_math.h b/code/core/core_math.h index 3294946..741d7b7 100644 --- a/code/core/core_math.h +++ b/code/core/core_math.h @@ -325,35 +325,47 @@ inline v4_s64 Max(v4_s64 A, v4_s64 B); //- sixten: Range functions -inline range1_r32 Range1R32(r32 Min, r32 Max); +// sixten(NOTE): InRange means that the value is in [Min, Max) and +// Contains means that the value is in [Min, Max] + +inline range1_r32 Range1R32(r32 A, r32 B); inline b32 InRange(range1_r32 Range, r32 Value); +inline b32 Contains(range1_r32 Range, r32 Value); inline r32 DimOfRange(range1_r32 Range); inline range1_r32 Intersection(range1_r32 A, range1_r32 B); -inline range1_s32 Range1S32(s32 Min, s32 Max); +inline range1_s32 Range1S32(s32 A, s32 B); inline b32 InRange(range1_s32 Range, s32 Value); +inline b32 Contains(range1_s32 Range, s32 Value); inline s32 DimOfRange(range1_s32 Range); inline range1_s32 Intersection(range1_s32 A, range1_s32 B); -inline range1_s64 Range1S64(s64 Min, s64 Max); +inline range1_s64 Range1S64(s64 A, s64 B); inline b32 InRange(range1_s64 Range, s64 Value); +inline b32 Contains(range1_s64 Range, s64 Value); inline s64 DimOfRange(range1_s64 Range); inline range1_s64 Intersection(range1_s64 A, range1_s64 B); -inline range2_r32 Range2R32(v2_r32 Min, v2_r32 Max); +inline range2_r32 Range2R32(v2_r32 A, v2_r32 B); inline b32 InRange(range2_r32 Range, v2_r32 Value); +inline b32 Contains(range2_r32 Range, v2_r32 Value); inline v2_r32 DimOfRange(range2_r32 Range); inline range2_r32 Intersection(range2_r32 A, range2_r32 B); +inline v2_r32 CornerFromRange(range2_r32 Range, corner Corner); -inline range2_s32 Range2S32(v2_s32 Min, v2_s32 Max); +inline range2_s32 Range2S32(v2_s32 A, v2_s32 B); inline b32 InRange(range2_s32 Range, v2_s32 Value); +inline b32 Contains(range2_s32 Range, v2_s32 Value); inline v2_s32 DimOfRange(range2_s32 Range); inline range2_s32 Intersection(range2_s32 A, range2_s32 B); +inline v2_s32 CornerFromRange(range2_s32 Range, corner Corner); -inline range2_s64 Range2S64(v2_s64 Min, v2_s64 Max); +inline range2_s64 Range2S64(v2_s64 A, v2_s64 B); inline b32 InRange(range2_s64 Range, v2_s64 Value); +inline b32 Contains(range2_s64 Range, v2_s64 Value); inline v2_s64 DimOfRange(range2_s64 Range); inline range2_s64 Intersection(range2_s64 A, range2_s64 B); +inline v2_s64 CornerFromRange(range2_s64 Range, corner Corner); //- sixten: Shorthand base types diff --git a/code/core/core_memory.cpp b/code/core/core_memory.cpp index e2718b7..6dc3bb5 100644 --- a/code/core/core_memory.cpp +++ b/code/core/core_memory.cpp @@ -1,153 +1,142 @@ -//- sixten: Common memory functions +//////////////////////////////// +//- sixten: Common Memory Functions static void Copy(void *Dest, void *Source, umm Count) { - u8 *Dest8 = (u8 *)Dest; - u8 *Source8 = (u8 *)Source; - - while(Count--) + if(Count <= S64_Max) { - *Dest8++ = *Source8++; + u8 *Dest8 = (u8 *)Dest; + u8 *Source8 = (u8 *)Source; + + while(Count--) + { + *Dest8++ = *Source8++; + } + } +} + +static void CopyReverse(void *Dest, void *Source, umm Count) +{ + if(Count <= S64_Max) + { + u8 *Dest8 = (u8 *)Dest + Count; + u8 *Source8 = (u8 *)Source + Count; + + while(Count--) + { + *--Dest8 = *--Source8; + } } } static void Fill(void *Dest, u8 Value, umm Count) { - u8 *Dest8 = (u8 *)Dest; - - while(Count--) + if(Count <= S64_Max) { - *Dest8++ = Value; - } -} - -//- sixten: Memory arena functions - -static void Release(memory_arena *Arena) -{ - while(Arena->CurrentBlock != 0) - { - platform_memory_block *MemoryBlock = Arena->CurrentBlock; - b32 IsLastBlock = (MemoryBlock->ArenaPrev == 0); - Arena->CurrentBlock = MemoryBlock->ArenaPrev; - Platform.DeallocateMemory(MemoryBlock); + u8 *Dest8 = (u8 *)Dest; - if(IsLastBlock) + while(Count--) { - break; + *Dest8++ = Value; } } } -inline umm GetAlignmentOffset(memory_arena *Arena, umm Alignment) +//////////////////////////////// +//- sixten: Memory Arena Functions + +static memory_arena *ArenaAllocate(u64 Size) { - umm AlignmentOffset = 0; - - umm ResultPointer = (umm)Arena->CurrentBlock + Arena->CurrentBlock->Used; - umm AlignmentMask = Alignment - 1; - if(ResultPointer & AlignmentMask) - { - AlignmentOffset = Alignment - (ResultPointer & AlignmentMask); - } - - return(AlignmentOffset); + u64 RoundedSize = Size+Megabytes(64)-1; + RoundedSize -= RoundedSize&Megabytes(64); + void *Memory = Platform.Reserve(RoundedSize); + u64 InitialCommitSize = MEMORY_ARENA_COMMIT_SIZE; + Platform.Commit(Memory, InitialCommitSize); + memory_arena *Arena = (memory_arena *)Memory; + Arena->Position = sizeof(memory_arena); + Arena->CommitPosition = InitialCommitSize; + Arena->Size = Size; + Arena->Align = 8; + return(Arena); } -inline umm GetEffectiveSizeFor(memory_arena *Arena, umm InitialSize, arena_push_params Params) +static void ArenaRelease(memory_arena *Arena) { - umm Size = InitialSize; - - umm AlignmentOffset = GetAlignmentOffset(Arena, Params.Alignment); - Size += AlignmentOffset; - - return(Size); + Platform.Release(Arena); } -static void *PushSize_(memory_arena *Arena, umm InitialSize, arena_push_params Params) +static void *ArenaPushNoClear(memory_arena *Arena, u64 Size) { void *Result = 0; - - umm Size = 0; - if(Arena->CurrentBlock) + if(Arena->Position + Size <= Arena->Size) { - Size = GetEffectiveSizeFor(Arena, InitialSize, Params); - } - - if(!Arena->CurrentBlock || ((Arena->CurrentBlock->Used + Size) > Arena->CurrentBlock->Size)) - { - Size = InitialSize; - - if(!Arena->MinimumBlockSize) + u8 *Base = (u8 *)Arena; + u64 PostAlignPos = (Arena->Position+Arena->Align-1); + PostAlignPos -= PostAlignPos%Arena->Align; + u64 Align = PostAlignPos - Arena->Position; + Result = Base + Arena->Position + Align; + Arena->Position += Size + Align; + if(Arena->CommitPosition < Arena->Position) { - Arena->MinimumBlockSize = 1024*1024; + u64 ToCommit = Arena->Position - Arena->CommitPosition; + ToCommit += MEMORY_ARENA_COMMIT_SIZE - 1; + ToCommit -= ToCommit%MEMORY_ARENA_COMMIT_SIZE; + Platform.Commit(Base + Arena->CommitPosition, ToCommit); + Arena->CommitPosition += ToCommit; } - - umm BlockSize = Maximum(Size, Arena->MinimumBlockSize); - - platform_memory_block *NewBlock = Platform.AllocateMemory(BlockSize); - NewBlock->ArenaPrev = Arena->CurrentBlock; - Arena->CurrentBlock = NewBlock; } - - Assert((Arena->CurrentBlock->Used + Size) <= Arena->CurrentBlock->Size); - - umm AlignmentOffset = GetAlignmentOffset(Arena, Params.Alignment); - umm OffsetInBlock = Arena->CurrentBlock->Used + AlignmentOffset; - Result = Arena->CurrentBlock->Base + OffsetInBlock; - Arena->CurrentBlock->Used += Size; - - Assert(Size >= InitialSize); - Assert(Arena->CurrentBlock->Used <= Arena->CurrentBlock->Size); - - if(Params.Flags & ArenaFlag_ClearToZero) + else { - Fill(Result, 0, InitialSize); + InvalidCodepath; } - return(Result); } -//- sixten: Bootsrapping helpers - -static void *BootstrapPushSize(umm Size, umm OffsetToArena) +static void *ArenaPush(memory_arena *Arena, u64 Size) { - memory_arena Arena = {}; - void *Result = PushSize(&Arena, Size); - *(memory_arena *)((u8 *)Result + OffsetToArena) = Arena; - + void *Result = ArenaPushNoClear(Arena, Size); + Fill(Result, 0, Size); return(Result); } -//- sixten: Temporary memory functions +static void ArenaPopTo(memory_arena *Arena, u64 Position) +{ + u64 MinPosition = sizeof(*Arena); + u64 NewPosition = Maximum(MinPosition, Position); + Arena->Position = NewPosition; + u64 CommitAlignedPosition = Arena->Position+MEMORY_ARENA_COMMIT_SIZE-1; + CommitAlignedPosition -= CommitAlignedPosition%MEMORY_ARENA_COMMIT_SIZE; + if(CommitAlignedPosition + MEMORY_ARENA_DECOMMIT_THRESHOLD <= Arena->CommitPosition) + { + u8 *Base = (u8 *)Arena; + u64 ToDecommit = Arena->CommitPosition-CommitAlignedPosition; + Platform.Decommit(Base+CommitAlignedPosition, ToDecommit); + Arena->CommitPosition -= ToDecommit; + } +} + +static void ArenaClear(memory_arena *Arena) +{ + ArenaPopTo(Arena, sizeof(*Arena)); +} + +static void ArenaSetAlign(memory_arena *Arena, u64 Align) +{ + Arena->Align = Align; +} + +//////////////////////////////// +//- sixten: Temporary Memory Functions static temporary_memory BeginTemporaryMemory(memory_arena *Arena) { - temporary_memory Result; - Result.Arena = Arena; - Result.Block = Arena->CurrentBlock; - Result.Used = Arena->CurrentBlock ? Arena->CurrentBlock->Used : 0; - - ++Arena->TemporaryMemoryCount; - - return(Result); + temporary_memory Temp; + Temp.Arena = Arena; + Temp.Position = Arena->Position; + return(Temp); } static void EndTemporaryMemory(temporary_memory Temp) { - memory_arena *Arena = Temp.Arena; - while(Arena->CurrentBlock != Temp.Block) - { - platform_memory_block *MemoryBlock = Arena->CurrentBlock; - Arena->CurrentBlock = MemoryBlock->ArenaPrev; - Platform.DeallocateMemory(MemoryBlock); - } - - if(Arena->CurrentBlock) - { - Assert(Arena->CurrentBlock->Used >= Temp.Used); - Arena->CurrentBlock->Used = Temp.Used; - } - - Assert(Arena->TemporaryMemoryCount > 0); - --Arena->TemporaryMemoryCount; + ArenaPopTo(Temp.Arena, Temp.Position); } \ No newline at end of file diff --git a/code/core/core_memory.h b/code/core/core_memory.h index fdad661..d9e9e60 100644 --- a/code/core/core_memory.h +++ b/code/core/core_memory.h @@ -3,72 +3,54 @@ #ifndef CORE_MEMORY_H #define CORE_MEMORY_H -//- sixten: Common memory functions +//////////////////////////////// +//- sixten: Common Memory Functions static void Copy(void *Dest, void *Source, umm Count); +static void CopyReverse(void *Dest, void *Source, umm Count); static void Fill(void *Dest, u8 Value, umm Count); +#define Move(Dest, Source, Count) memmove(Dest, Source, Count) -//- sixten: Memory arena types - -struct platform_memory_block; +//////////////////////////////// +//- sixten: Memory Arena Types struct memory_arena { - platform_memory_block *CurrentBlock; - umm MinimumBlockSize; - s32 TemporaryMemoryCount; + u64 Position; + u64 CommitPosition; + u64 Size; + u64 Align; + u64 Unused[4]; }; struct temporary_memory { memory_arena *Arena; - platform_memory_block *Block; - umm Used; + u64 Position; }; -//- sixten: Arena push flags +//////////////////////////////// +//~ sixten: Memory Arena Settings +#define MEMORY_ARENA_COMMIT_SIZE Kilobytes(4) +#define MEMORY_ARENA_DECOMMIT_THRESHOLD Megabytes(64) -enum arena_push_flag -{ - ArenaFlag_ClearToZero = 0x1, -}; +//////////////////////////////// +//- sixten: Memory Arena Functions -struct arena_push_params -{ - u32 Flags; - u32 Alignment; -}; +static memory_arena *ArenaAllocate(u64 Size); +static void ArenaRelease(memory_arena *Arena); +static void *ArenaPushNoClear(memory_arena *Arena, u64 Size); +static void *ArenaPush(memory_arena *Arena, u64 Size); +static void ArenaPopTo(memory_arena *Arena, u64 Position); +static void ArenaClear(memory_arena *Arena); +static void ArenaSetAlign(memory_arena *Arena, u64 Align); +#define PushArray(Arena, type, Count) (type *)ArenaPush((Arena), sizeof(type)*(Count)) +#define PushArrayNoClear(Arena, type, Count) (type *)ArenaPushNoClear((Arena), sizeof(type)*(Count)) +#define PushStruct(Arena, type) (type *)ArenaPush((Arena), sizeof(type)) +#define PushStructNoClear(Arena, type) (type *)ArenaPushNoClear((Arena), sizeof(type)) -inline arena_push_params DefaultArenaParams(void) -{ - arena_push_params Params = {}; - Params.Flags = ArenaFlag_ClearToZero; - Params.Alignment = 4; - return(Params); -} - -inline arena_push_params NoClear(void) -{ - arena_push_params Params = DefaultArenaParams(); - Params.Flags &= ~ArenaFlag_ClearToZero; - return(Params); -} - -//- sixten: Memory arena functions - -static void Release(memory_arena *Arena); - -#define PushSize(Arena, InitialSize, ...) PushSize_(Arena, InitialSize, __VA_ARGS__) -#define PushStruct(Arena, type, ...) (type *)PushSize_(Arena, sizeof(type), __VA_ARGS__) -#define PushArray(Arena, type, Count, ...) (type *)PushSize_(Arena, sizeof(type)*Count, __VA_ARGS__) -static void *PushSize_(memory_arena *Arena, umm InitialSize, arena_push_params Params = DefaultArenaParams()); - -//- sixten: Bootstrapping helpers - -#define BootstrapPushStruct(type, Member) (type *)BootstrapPushSize(sizeof(type), OffsetOf(type, Member)) -static void *BootstrapPushSize(umm Size, umm OffsetToArena); - -//- sixten: Temporary memory functions +//////////////////////////////// +//- sixten: Temporary Memory Functions static temporary_memory BeginTemporaryMemory(memory_arena *Arena); static void EndTemporaryMemory(temporary_memory Temp); diff --git a/code/core/core_string.cpp b/code/core/core_string.cpp index 88349a1..3374b29 100644 --- a/code/core/core_string.cpp +++ b/code/core/core_string.cpp @@ -186,7 +186,7 @@ static s64 LastIndexOf(string String, string Sub) static string PushString(memory_arena *Arena, string String) { string Result; - Result.Data = PushArray(Arena, u8, String.Count, NoClear()); + Result.Data = PushArrayNoClear(Arena, u8, String.Count); Result.Count = String.Count; Copy(Result.Data, String.Data, String.Count); @@ -200,7 +200,7 @@ static string PushFormatVariadic(memory_arena *Arena, char *Format, va_list Argu string Result; Result.Count = stbsp_vsnprintf(0, 0, Format, ArgumentsCopy); - Result.Data = PushArray(Arena, u8, Result.Count + 1, NoClear()); + Result.Data = PushArrayNoClear(Arena, u8, Result.Count + 1); Result.Data[Result.Count] = 0; stbsp_vsnprintf((char *)Result.Data, (s32)Result.Count + 1, Format, Arguments); @@ -294,6 +294,41 @@ static string StringFromCodepoint(memory_arena *Arena, u32 Codepoint) return(Result); } +//- sixten: Replacing + +static string RemoveAll(memory_arena *Arena, string Text, char ToRemove) +{ + //- sixten: get new count + s64 Occurrences = 0; + u8 *TextBegin = Text.Data; + u8 *TextEnd = TextBegin+Text.Count; + for(u8 *Char = TextBegin; Char != TextEnd; Char += 1) + { + if(*Char == ToRemove) + { + Occurrences += 1; + } + } + + //- sixten: copy over all other bytes + s64 Count = Text.Count - Occurrences; + u8 *Data = PushArrayNoClear(Arena, u8, Count + 1); + Data[Count] = 0; + + s64 Index = 0; + for(u8 *Char = TextBegin; Char != TextEnd; Char += 1) + { + if(*Char != ToRemove) + { + Data[Index] = *Char; + Index += 1; + } + } + + string Result = MakeString(Data, Count); + return(Result); +} + //- sixten: "C Style" strings static s64 StringLength(char *String) @@ -344,9 +379,136 @@ static string JoinStringList(string_list *List, memory_arena *Arena) return(Result); } +///////////////////////////////////// +//~ sixten: String Chunk Functions +static string_chunk_list MakeStringChunkList(s64 ChunkSize) +{ + string_chunk_list Result = {}; + Result.ChunkSize = ChunkSize; + return(Result); +} + +static string JoinStringChunkList(memory_arena *Arena, string_chunk_list *List) +{ + string Result = {}; + Result.Count = List->TotalCount; + Result.Data = PushArrayNoClear(Arena, u8, List->TotalCount + 1); + s64 Index = 0; + s64 CountRemaining = List->TotalCount; + for(string_node *Node = List->First; Node != 0; Node = Node->Next) + { + string String = Node->String; + Copy(Result.Data + Index, String.Data, Min(CountRemaining, List->ChunkSize)); + CountRemaining -= List->ChunkSize; + Index += String.Count; + } + return(Result); +} + +// sixten(TODO): Incomplete, remove maybe? +static void ReplaceRange(memory_arena *Arena, string_chunk_list *List, string Text, range1_s64 Range) +{ + s64 NewTotalCount = Max(0ULL, List->TotalCount - DimOfRange(Range)) + Text.Count; + + //- sixten: do we need to allocate more chunks? + if(List->ChunkSize*List->ChunkCount < NewTotalCount) + { + s64 ChunksToAlloc = (NewTotalCount - List->ChunkSize*List->ChunkCount)/List->ChunkSize + 1; + for(s64 Index = 0; Index < ChunksToAlloc; Index += 1) + { + if(DLLIsEmpty(List->FirstFree)) + { + string_node *Node = PushStructNoClear(Arena, string_node); + Node->String.Count = 0; + Node->String.Data = PushArrayNoClear(Arena, u8, List->ChunkSize); + DLLInsertLast(List->First, List->Last, Node); + } + else + { + string_node *Node = List->FirstFree; + Node->String.Count = 0; + DLLRemove(List->FirstFree, List->LastFree, Node); + DLLInsertLast(List->First, List->Last, Node); + } + } + + List->ChunkCount += ChunksToAlloc; + } + + s64 CountDelta = NewTotalCount - List->TotalCount; + + // sixten: I cannot be bothered enough to figure out the correct implementation for this. However, if I do this - remember that you can rearrange + // the ordering of the linked list, instead of actually copying over the bytes for the majority of this. + Assert(AbsoluteValue(CountDelta) < List->ChunkSize); + + //- sixten: find the first and last affected nodes + s64 FirstAffectedNodeIndex = Range.Min/List->ChunkSize; + s64 LastAffectedNodeIndex = Range.Max/List->ChunkSize; + string_node *FirstAffectedNode = List->First; + for(s64 WalkIndex = 0; WalkIndex < FirstAffectedNodeIndex; WalkIndex += 1) + { + FirstAffectedNode = FirstAffectedNode->Next; + } + string_node *LastAffectedNode = FirstAffectedNode; + for(s64 WalkIndex = 0; WalkIndex < LastAffectedNodeIndex-FirstAffectedNodeIndex; WalkIndex += 1) + { + LastAffectedNode = LastAffectedNode->Next; + } + + if(CountDelta >= 0) + { + //- sixten: insertion - make room and the copy the data + s64 WriteOffset = Range.Min%List->ChunkSize; + for(string_node *Node = List->Last; Node != 0; Node = Node->Prev) + { + CopyReverse(Node->String.Data+CountDelta+WriteOffset, Node->String.Data+WriteOffset, List->ChunkSize-CountDelta-WriteOffset); + + if(Node == LastAffectedNode) + { + break; + } + else + { + Copy(Node->String.Data, Node->Prev->String.Data+List->ChunkSize-CountDelta, CountDelta); + } + } + s64 SourceOffset = 0; + for(string_node *Node = FirstAffectedNode; Node != 0; Node = Node->Next) + { + Copy(Node->String.Data+WriteOffset, Text.Data+SourceOffset, Min(List->ChunkSize-WriteOffset, Text.Count-SourceOffset)); + SourceOffset += List->ChunkSize; + if(Node == LastAffectedNode) + { + break; + } + } + } + else if(CountDelta < 0) + { + //- sixten: deletion + } + + List->TotalCount = NewTotalCount; +} + + //~ sixten: Unicode +read_only u8 UTF8Lengths[] = +{ + 1, 1, 1, 1, // 000xx + 1, 1, 1, 1, + 1, 1, 1, 1, + 1, 1, 1, 1, + 0, 0, 0, 0, // 100xx + 0, 0, 0, 0, + 2, 2, 2, 2, // 110xx + 3, 3, // 1110x + 4, // 11110 + 0, // 11111 +}; + static utf8_iterator IterateUTF8String(string String) { utf8_iterator Iter = {}; @@ -431,4 +593,115 @@ static s64 UTF8FromCodepoint(u8 *Out, u32 Codepoint) } return(Length); -} \ No newline at end of file +} + +//~ sixten: Text point + +static text_point TextPointFromOffset(string String, s64 Offset) +{ + text_point Point = {1, 1}; + for(s64 Index = 0; + Index < String.Count && Index < Offset; + ++Index) + { + if(String.Data[Index] == '\n') + { + ++Point.Line; + Point.Column = 1; + } + else + { + if(String.Data[Index] != '\r') + { + ++Point.Column; + } + } + } + + return(Point); +} + +static s64 OffsetFromTextPoint(string String, text_point Point) +{ + s64 Offset = 0; + + Point.Line -= 1; + Point.Column -= 1; + + u8 *StringBegin = String.Data; + u8 *StringEnd = StringBegin+String.Count; + u8 *Char = StringBegin; + //- sixten: find the start of the correct line + for(;Char < StringEnd && Point.Line > 0; Char += 1, Offset += 1) + { + if(*Char == '\n') + { + Point.Line -= 1; + } + } + + for(;Char < StringEnd && Point.Column > 0; Char += 1, Offset += 1) + { + //- sixten: if a newline has been reached, the initial column was out of bounds + if(*Char == '\n') + { + break; + } + //- sixten: tabs are two-spaces, so we must take that into account +#if 0 + if(*Char == '\t') + { + Point.Column -= 1; + } +#endif + Point.Column -= 1; + } + + return(Offset); +} + +static text_range TextRange(text_point A, text_point B) +{ + text_range Result; + if(A.Line > B.Line || (A.Line == B.Line && A.Column > B.Column)) + { + Result = {B, A}; + } + else + { + Result = {A, B}; + } + return(Result); +} + +//////////////////////////////// +//~ sixten: 1D Interval List & Array Functions +static void Range1S64ListPush(memory_arena *Arena, range1_s64_list *List, range1_s64 Range) +{ + range1_s64_node *Node = PushStructNoClear(Arena, range1_s64_node); + Node->Range = Range; + QueuePush(List->First, List->Last, Node); + List->Count += 1; +} + +static range1_s64_array Range1S64ArrayFromList(memory_arena *Arena, range1_s64_list *List) +{ + range1_s64_array Result = {}; + Result.Count = List->Count; + Result.Ranges = PushArray(Arena, range1_s64, List->Count); + s64 Index = 0; + for(range1_s64_node *Node = List->First; Node != 0; Node = Node->Next, Index += 1) + { + Result.Ranges[Index] = Node->Range; + } + return(Result); +} + +static s64 OffsetFromTextPoint(string String, range1_s64_array Lines, text_point Point) +{ + s64 LineIndex = Clamp(Point.Line, 1, Lines.Count) - 1; + range1_s64 Range = Lines.Ranges[LineIndex]; + s64 ColumnIndex = Clamp(Point.Column, 1, DimOfRange(Range)) - 1; + s64 Offset = Range.Min+ColumnIndex; + return(Offset); +} diff --git a/code/core/core_string.h b/code/core/core_string.h index 8dd571a..3cbcca0 100644 --- a/code/core/core_string.h +++ b/code/core/core_string.h @@ -29,7 +29,22 @@ struct string_list s64 TotalCount; }; +///////////////////////////////////// +//~ sixten: String Chunk Types +struct string_chunk_list +{ + s64 ChunkSize; + s64 ChunkCount; + s64 TotalCount; + + string_node *First; + string_node *Last; + string_node *FirstFree; + string_node *LastFree; +}; + +///////////////////////////////////// //~ sixten: Char funcitons inline b32 IsWhitespace(char C); @@ -37,6 +52,7 @@ inline b32 IsDigit(char C); inline b32 IsLetter(char C); +///////////////////////////////////// //~ sixten: String functions //- sixten: Basic constructors @@ -79,11 +95,21 @@ static s64 ConvertStringToS64(string String); static string ConvertS64ToString(memory_arena *Arena, s64 Value); static string StringFromCodepoint(memory_arena *Arena, u32 Codepoint); +//- sixten: Replacing + +static string RemoveAll(memory_arena *Arena, string Text, char ToRemove); + //- sixten: "C Style" strings static s64 StringLength(char *String); +///////////////////////////////////// +//~ sixten: String Chunk Functions +static string_chunk_list MakeStringChunkList(s64 ChunkSize); +static string JoinStringChunkList(memory_arena *Arena, string_chunk_list *List); +static void ReplaceRange(memory_arena *Arena, string_chunk_list *List, string Text, range1_s64 Range); + //~ sixten: String list static void AppendString(string_list *List, string String, memory_arena *Arena); @@ -108,4 +134,50 @@ static b32 IsValid(utf8_iterator *Iter); static s64 UTF8FromCodepoint(u8 *Out, u32 Codepoint); +//~ sixten: Text point + +struct text_point +{ + s64 Line; + s64 Column; +}; + +struct text_range +{ + text_point Min; + text_point Max; +}; + +static text_point TextPointFromOffset(string String, s64 Offset); +static s64 OffsetFromTextPoint(string String, text_point Point); + +static text_range TextRange(text_point A, text_point B); + +//////////////////////////////// +//~ sixten: 1D Interval List & Array Types +struct range1_s64_node +{ + range1_s64 Range; + range1_s64_node *Next; +}; + +struct range1_s64_list +{ + s64 Count; + range1_s64_node *First; + range1_s64_node *Last; +}; + +struct range1_s64_array +{ + s64 Count; + range1_s64 *Ranges; +}; + +//////////////////////////////// +//~ sixten: 1D Interval List & Array Functions +static void Range1S64ListPush(memory_arena *Arena, range1_s64_list *List, range1_s64 Range); +static range1_s64_array Range1S64ArrayFromList(memory_arena *Arena, range1_s64_list *List); +static s64 OffsetFromTextPoint(string Text, range1_s64_array Lines, text_point Point); + #endif //CORE_STRING_H diff --git a/code/core/core_thread_context.cpp b/code/core/core_thread_context.cpp index 26321e6..eedd61f 100644 --- a/code/core/core_thread_context.cpp +++ b/code/core/core_thread_context.cpp @@ -1,5 +1,15 @@ per_thread thread_context *ThreadLocal_ThreadContext = 0; +static thread_context AllocateThreadContext(void) +{ + thread_context Context = {}; + for(s64 ArenaIndex = 0; ArenaIndex < ArrayCount(Context.Arenas); ArenaIndex += 1) + { + Context.Arenas[ArenaIndex] = ArenaAllocate(Gigabytes(1)); + } + return(Context); +} + static void SetThreadContext(thread_context *Context) { ThreadLocal_ThreadContext = Context; @@ -25,7 +35,7 @@ static temporary_memory GetScratch(memory_arena **Conflicts, u64 ConflictCount) ++ConflictIndex) { memory_arena *Conflict = Conflicts[ConflictIndex]; - if(Conflict == Context->Arenas + ArenaIndex) + if(Conflict == Context->Arenas[ArenaIndex]) { FoundConflict = true; break; @@ -34,7 +44,7 @@ static temporary_memory GetScratch(memory_arena **Conflicts, u64 ConflictCount) if(!FoundConflict) { - Scratch = BeginTemporaryMemory(Context->Arenas + ArenaIndex); + Scratch = BeginTemporaryMemory(Context->Arenas[ArenaIndex]); break; } } diff --git a/code/core/core_thread_context.h b/code/core/core_thread_context.h index da79b16..e54a8e7 100644 --- a/code/core/core_thread_context.h +++ b/code/core/core_thread_context.h @@ -5,7 +5,7 @@ struct thread_context { - memory_arena Arenas[2]; + memory_arena *Arenas[2]; }; //- sixten: Thread state management diff --git a/code/gen.cpp b/code/gen.cpp deleted file mode 100644 index 0de6dd2..0000000 --- a/code/gen.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "core/core.h" -#include - -#define ArrayCount(Array) (sizeof(Array)/sizeof((Array)[0])) - -#include "gen_opengl.cpp" -#include "gen_ui.cpp" - -int main(int ArgumentCount, char **Arguments) -{ - GenOpenGL(); - GenUI(); - - return(0); -} \ No newline at end of file diff --git a/code/gen_opengl.cpp b/code/gen_opengl.cpp deleted file mode 100644 index 05aa922..0000000 --- a/code/gen_opengl.cpp +++ /dev/null @@ -1,140 +0,0 @@ -struct opengl_function -{ - char *Name; - char *Type; - char *Arguments; -}; - -struct opengl_function OpenGLFunctionEntries[] = -{ - {"BindTexture", "void", "GLenum target, GLuint texture"}, - {"BlendFunc", "void", "GLenum sfactor, GLenum dfactor"}, - {"Clear", "void", "GLbitfield mask"}, - {"ClearAccum", "void", "GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha"}, - {"ClearColor", "void", "GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha"}, - {"ClearDepth", "void", "GLclampd depth"}, - {"ClearIndex", "void", "GLfloat c"}, - {"ClearStencil", "void", "GLint s"}, - {"ClipPlane", "void", "GLenum plane, const GLdouble *equation"}, - {"CullFace", "void", "GLenum mode"}, - {"DeleteTextures", "void", "GLsizei n, const GLuint *textures"}, - {"Disable", "void", "GLenum cap"}, - {"DrawArrays", "void", "GLenum mode, GLint first, GLsizei count"}, - {"DrawBuffer", "void", "GLenum mode"}, - {"DrawElements", "void", "GLenum mode, GLsizei count, GLenum type, const GLvoid *indices"}, - {"DrawArraysInstanced", "void", "GLenum mode, GLint first, GLsizei count, GLsizei instancecount"}, - {"Enable", "void", "GLenum cap"}, - {"GenTextures", "void", "GLsizei n, GLuint *textures"}, - {"GetClipPlane", "void", "GLenum plane, GLdouble *equation"}, - {"GetDoublev", "void", "GLenum pname, GLdouble *params"}, - {"GetError", "GLenum", "void"}, - {"GetFloatv", "void", "GLenum pname, GLfloat *params"}, - {"GetIntegerv", "void", "GLenum pname, GLint *params"}, - {"GetPointerv", "void", "GLenum pname, GLvoid* *params"}, - {"GetString", "const GLubyte *", "GLenum name"}, - {"GetTexEnvfv", "void", "GLenum target, GLenum pname, GLfloat *params"}, - {"GetTexEnviv", "void", "GLenum target, GLenum pname, GLint *params"}, - {"GetTexGendv", "void", "GLenum coord, GLenum pname, GLdouble *params"}, - {"GetTexGenfv", "void", "GLenum coord, GLenum pname, GLfloat *params"}, - {"GetTexGeniv", "void", "GLenum coord, GLenum pname, GLint *params"}, - {"GetTexImage", "void", "GLenum target, GLint level, GLenum format, GLenum type, GLvoid *pixels"}, - {"GetTexLevelParameterfv", "void", "GLenum target, GLint level, GLenum pname, GLfloat *params"}, - {"GetTexLevelParameteriv", "void", "GLenum target, GLint level, GLenum pname, GLint *params"}, - {"GetTexParameterfv", "void", "GLenum target, GLenum pname, GLfloat *params"}, - {"GetTexParameteriv", "void", "GLenum target, GLenum pname, GLint *params"}, - {"Hint", "void", "GLenum target, GLenum mode"}, - {"IsTexture", "GLboolean", "GLuint texture"}, - {"LineWidth", "void", "GLfloat width"}, - {"ListBase", "void", "GLuint base"}, - {"LoadName", "void", "GLuint name"}, - {"LogicOp", "void", "GLenum opcode"}, - {"PointSize", "void", "GLfloat size"}, - {"PolygonMode", "void", "GLenum face, GLenum mode"}, - {"Scissor", "void", "GLint x, GLint y, GLsizei width, GLsizei height"}, - {"TexImage1D", "void", "GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels"}, - {"TexImage2D", "void", "GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels"}, - {"TexParameterf", "void", "GLenum target, GLenum pname, GLfloat param"}, - {"TexParameterfv", "void", "GLenum target, GLenum pname, const GLfloat *params"}, - {"TexParameteri", "void", "GLenum target, GLenum pname, GLint param"}, - {"TexParameteriv", "void", "GLenum target, GLenum pname, const GLint *params"}, - {"TexSubImage1D", "void", "GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels"}, - {"TexSubImage2D", "void", "GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels"}, - {"CompressedTexImage2D", "void", "GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *pixels"}, - {"ActiveTexture", "void", "GLenum texture"}, - {"Viewport", "void", "GLint x, GLint y, GLsizei width, GLsizei height"}, - {"GenBuffers", "void", "GLsizei n, GLuint *buffers"}, - {"BindBuffer", "void", "GLenum target, GLuint buffer"}, - {"BufferData", "void", "GLenum target, GLsizeiptr size, const void *data, GLenum usage"}, - {"BufferSubData", "void", "GLenum target, GLintptr offset, GLsizeiptr size, const void *data"}, - {"GenVertexArrays", "void", "GLsizei n, GLuint *arrays"}, - {"BindVertexArray", "void", "GLenum array"}, - {"GetAttribLocation", "GLint", "GLuint program, const GLchar *name"}, - {"EnableVertexAttribArray", "void", "GLuint index"}, - {"DisableVertexAttribArray", "void", "GLuint index"}, - {"VertexAttribPointer", "void", "GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer"}, - {"VertexAttribIPointer", "void", "GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer"}, - {"VertexAttribLPointer", "void", "GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer"}, - {"VertexAttribDivisor", "void", "GLuint index, GLuint divisor"}, - {"CreateShader", "GLuint", "GLenum type"}, - {"ShaderSource", "void", "GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length"}, - {"CompileShader", "void", "GLuint shader"}, - {"DeleteShader", "void", "GLuint shader"}, - {"GetShaderiv", "void", "GLuint shader, GLenum pname, GLint *params"}, - {"GetShaderInfoLog", "void", "GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog"}, - {"CreateProgram", "GLuint", "void"}, - {"UseProgram", "void", "GLuint program"}, - {"AttachShader", "void", "GLuint program, GLuint shader"}, - {"DeleteProgram", "void", "GLuint program"}, - {"LinkProgram", "void", "GLuint program"}, - {"GetProgramiv", "void", "GLuint program, GLenum pname, GLint *params"}, - {"GetProgramInfoLog", "void", "GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog"}, - {"GetUniformLocation", "GLint", "GLuint program, const GLchar *name"}, - {"Uniform1i", "void", "GLint location, GLint v0"}, - {"Uniform2f", "void", "GLint location, GLfloat v0, GLfloat v1"}, - {"Uniform3f", "void", "GLint location, GLfloat v0, GLfloat v1, GLfloat v2"}, - {"UniformMatrix4fv", "void", "GLint location, GLsizei count, GLboolean transpose, const GLfloat *value"}, - {"DebugMessageCallback", "void", "void (*)(GLenum, GLenum, GLuint, GLenum, GLsizei, const GLchar *, const void *), const void *userParam"}, -}; - -static void GenOpenGL(void) -{ - FILE *Out = fopen("../code/generated/vn_opengl_functions.h", "wb"); - if(Out) - { - for(s32 Index = 0; - Index < ArrayCount(OpenGLFunctionEntries); - ++Index) - { - struct opengl_function *Function = OpenGLFunctionEntries + Index; - fprintf(Out, "typedef %s opengl_%s(%s);\n", Function->Type, Function->Name, Function->Arguments); - } - - fprintf(Out, "\n"); - - for(s32 Index = 0; - Index < ArrayCount(OpenGLFunctionEntries); - ++Index) - { - struct opengl_function *Function = OpenGLFunctionEntries + Index; - fprintf(Out, "global opengl_%s *gl%s = 0;\n", Function->Name, Function->Name); - } - - fprintf(Out, "\n"); - fprintf(Out, "extern void *OpenGL_LoadFunction(char *);\n"); - fprintf(Out, "\n"); - fprintf(Out, "static void OpenGL_LoadAllFunctions(void)\n"); - fprintf(Out, "{\n"); - - for(s32 Index = 0; - Index < ArrayCount(OpenGLFunctionEntries); - ++Index) - { - struct opengl_function *Function = OpenGLFunctionEntries + Index; - fprintf(Out, "\tgl%s = (opengl_%s *)OpenGL_LoadFunction(\"gl%s\");\n", Function->Name, Function->Name, Function->Name); - } - - fprintf(Out, "}\n"); - - fclose(Out); - } -} \ No newline at end of file diff --git a/code/gen_ui.cpp b/code/gen_ui.cpp deleted file mode 100644 index 00daa6f..0000000 --- a/code/gen_ui.cpp +++ /dev/null @@ -1,129 +0,0 @@ -struct ui_style_stack -{ - char *Name; - char *Type; - char *MemberName; -}; - -ui_style_stack UIStyleStacks[] = -{ - { "Parent", "ui_box *", "Parent" }, - { "Width", "ui_size", "SemanticSize[Axis2_X]" }, - { "Height", "ui_size", "SemanticSize[Axis2_Y]" }, - { "FixedX", "r32", "FixedP.E[Axis2_X]" }, - { "FixedY", "r32", "FixedP.E[Axis2_Y]" }, - { "TextColor", "v4", "TextColor" }, - { "BackgroundColor", "v4", "BackgroundColor" }, - { "BorderColor", "v4", "BorderColor" }, - { "BorderThickness", "r32", "BorderThickness" }, - { "LayoutAxis", "axis2", "LayoutAxis" }, - { "CornerRadius", "r32", "CornerRadius" }, - { "Font", "font_id", "Font" }, - { "FontSize", "r32", "FontSize" }, - { "OffsetX", "r32", "Offset.x" }, - { "OffsetY", "r32", "Offset.y" }, -}; - -static void GenUI(void) -{ - FILE *Out = fopen("../code/generated/vn_generated_ui.h", "wb"); - if(Out) - { - fprintf(Out, "struct ui_style_stacks\n"); - fprintf(Out, "{\n"); - for(s32 Index = 0; - Index < ArrayCount(UIStyleStacks); - ++Index) - { - ui_style_stack Stack = UIStyleStacks[Index]; - fprintf(Out, "\t%s %sStack[64];\n", Stack.Type, Stack.Name); - fprintf(Out, "\ts32 %sStackUsed;\n", Stack.Name); - fprintf(Out, "\tb32 AutoPop%s;\n", Stack.Name); - } - fprintf(Out, "};\n"); - fclose(Out); - } - - Out = fopen("../code/generated/vn_generated_ui.cpp", "wb"); - if(Out) - { - for(s32 Index = 0; - Index < ArrayCount(UIStyleStacks); - ++Index) - { - ui_style_stack Stack = UIStyleStacks[Index]; - fprintf(Out, "inline void UI_Push%s(%s Element)\n", Stack.Name, Stack.Type); - fprintf(Out, "{\n"); - fprintf(Out, "\tui *UI = UI_GetState();\n"); - fprintf(Out, "\tAssert(UI->Stacks.%sStackUsed + 1 < ArrayCount(UI->Stacks.%sStack));\n", Stack.Name, Stack.Name); - fprintf(Out, "\tUI->Stacks.%sStack[UI->Stacks.%sStackUsed++] = Element;\n", Stack.Name, Stack.Name); - fprintf(Out, "}\n"); - - fprintf(Out, "\n"); - - fprintf(Out, "inline void UI_Pop%s(void)\n", Stack.Name); - fprintf(Out, "{\n"); - fprintf(Out, "\tui *UI = UI_GetState();\n"); - fprintf(Out, "\tAssert(UI->Stacks.%sStackUsed > 0);\n", Stack.Name); - fprintf(Out, "\t--UI->Stacks.%sStackUsed;\n", Stack.Name); - fprintf(Out, "}\n"); - - fprintf(Out, "\n"); - - fprintf(Out, "inline void UI_SetNext%s(%s Element)\n", Stack.Name, Stack.Type); - fprintf(Out, "{\n"); - fprintf(Out, "\tui *UI = UI_GetState();\n"); - fprintf(Out, "\tUI_Push%s(Element);\n", Stack.Name); - fprintf(Out, "\tUI->Stacks.AutoPop%s = true;\n", Stack.Name); - fprintf(Out, "}\n"); - - fprintf(Out, "\n"); - - fprintf(Out, "inline %s UI_First%s(void)\n", Stack.Type, Stack.Name); - fprintf(Out, "{\n"); - fprintf(Out, "\tui *UI = UI_GetState();\n"); - fprintf(Out, "\treturn(UI->Stacks.%sStack[0]);\n", Stack.Name); - fprintf(Out, "}\n"); - - fprintf(Out, "\n"); - - fprintf(Out, "inline %s UI_Top%s(void)\n", Stack.Type, Stack.Name); - fprintf(Out, "{\n"); - fprintf(Out, "\tui *UI = UI_GetState();\n"); - fprintf(Out, "\treturn(UI->Stacks.%sStack[UI->Stacks.%sStackUsed - 1]);\n", Stack.Name, Stack.Name); - fprintf(Out, "}\n"); - - fprintf(Out, "\n"); - - fprintf(Out, "#define UI_%s(Element) DeferLoop(UI_Push%s(Element), UI_Pop%s())\n", - Stack.Name, Stack.Name, Stack.Name); - - fprintf(Out, "\n"); - } - - fprintf(Out, "\n"); - - fprintf(Out, "inline void UI_ApplyStyles(ui_box *Box)\n"); - fprintf(Out, "{\n"); - fprintf(Out, "\tui *UI = UI_GetState();\n"); - for(s32 Index = 0; - Index < ArrayCount(UIStyleStacks); - ++Index) - { - ui_style_stack Stack = UIStyleStacks[Index]; - fprintf(Out, "\tAssert(UI->Stacks.%sStackUsed > 0);\n", Stack.Name); - fprintf(Out, "\tBox->%s = UI->Stacks.%sStack[UI->Stacks.%sStackUsed - 1];\n", - Stack.MemberName, Stack.Name, Stack.Name); - fprintf(Out, "\tif(UI->Stacks.AutoPop%s)\n", Stack.Name); - fprintf(Out, "\t{\n"); - fprintf(Out, "\t\tUI_Pop%s();\n", Stack.Name); - fprintf(Out, "\t\tUI->Stacks.AutoPop%s = false;\n", Stack.Name); - fprintf(Out, "\t}\n\n"); - } - - fprintf(Out, "}\n"); - - - fclose(Out); - } -} \ No newline at end of file diff --git a/code/generated/vn_generated_ui.cpp b/code/generated/vn_generated_ui.cpp deleted file mode 100644 index d275bb9..0000000 --- a/code/generated/vn_generated_ui.cpp +++ /dev/null @@ -1,650 +0,0 @@ -inline void UI_PushParent(ui_box * Element) -{ - ui *UI = UI_GetState(); - Assert(UI->Stacks.ParentStackUsed + 1 < ArrayCount(UI->Stacks.ParentStack)); - UI->Stacks.ParentStack[UI->Stacks.ParentStackUsed++] = Element; -} - -inline void UI_PopParent(void) -{ - ui *UI = UI_GetState(); - Assert(UI->Stacks.ParentStackUsed > 0); - --UI->Stacks.ParentStackUsed; -} - -inline void UI_SetNextParent(ui_box * Element) -{ - ui *UI = UI_GetState(); - UI_PushParent(Element); - UI->Stacks.AutoPopParent = true; -} - -inline ui_box * UI_FirstParent(void) -{ - ui *UI = UI_GetState(); - return(UI->Stacks.ParentStack[0]); -} - -inline ui_box * UI_TopParent(void) -{ - ui *UI = UI_GetState(); - return(UI->Stacks.ParentStack[UI->Stacks.ParentStackUsed - 1]); -} - -#define UI_Parent(Element) DeferLoop(UI_PushParent(Element), UI_PopParent()) - -inline void UI_PushWidth(ui_size Element) -{ - ui *UI = UI_GetState(); - Assert(UI->Stacks.WidthStackUsed + 1 < ArrayCount(UI->Stacks.WidthStack)); - UI->Stacks.WidthStack[UI->Stacks.WidthStackUsed++] = Element; -} - -inline void UI_PopWidth(void) -{ - ui *UI = UI_GetState(); - Assert(UI->Stacks.WidthStackUsed > 0); - --UI->Stacks.WidthStackUsed; -} - -inline void UI_SetNextWidth(ui_size Element) -{ - ui *UI = UI_GetState(); - UI_PushWidth(Element); - UI->Stacks.AutoPopWidth = true; -} - -inline ui_size UI_FirstWidth(void) -{ - ui *UI = UI_GetState(); - return(UI->Stacks.WidthStack[0]); -} - -inline ui_size UI_TopWidth(void) -{ - ui *UI = UI_GetState(); - return(UI->Stacks.WidthStack[UI->Stacks.WidthStackUsed - 1]); -} - -#define UI_Width(Element) DeferLoop(UI_PushWidth(Element), UI_PopWidth()) - -inline void UI_PushHeight(ui_size Element) -{ - ui *UI = UI_GetState(); - Assert(UI->Stacks.HeightStackUsed + 1 < ArrayCount(UI->Stacks.HeightStack)); - UI->Stacks.HeightStack[UI->Stacks.HeightStackUsed++] = Element; -} - -inline void UI_PopHeight(void) -{ - ui *UI = UI_GetState(); - Assert(UI->Stacks.HeightStackUsed > 0); - --UI->Stacks.HeightStackUsed; -} - -inline void UI_SetNextHeight(ui_size Element) -{ - ui *UI = UI_GetState(); - UI_PushHeight(Element); - UI->Stacks.AutoPopHeight = true; -} - -inline ui_size UI_FirstHeight(void) -{ - ui *UI = UI_GetState(); - return(UI->Stacks.HeightStack[0]); -} - -inline ui_size UI_TopHeight(void) -{ - ui *UI = UI_GetState(); - return(UI->Stacks.HeightStack[UI->Stacks.HeightStackUsed - 1]); -} - -#define UI_Height(Element) DeferLoop(UI_PushHeight(Element), UI_PopHeight()) - -inline void UI_PushFixedX(r32 Element) -{ - ui *UI = UI_GetState(); - Assert(UI->Stacks.FixedXStackUsed + 1 < ArrayCount(UI->Stacks.FixedXStack)); - UI->Stacks.FixedXStack[UI->Stacks.FixedXStackUsed++] = Element; -} - -inline void UI_PopFixedX(void) -{ - ui *UI = UI_GetState(); - Assert(UI->Stacks.FixedXStackUsed > 0); - --UI->Stacks.FixedXStackUsed; -} - -inline void UI_SetNextFixedX(r32 Element) -{ - ui *UI = UI_GetState(); - UI_PushFixedX(Element); - UI->Stacks.AutoPopFixedX = true; -} - -inline r32 UI_FirstFixedX(void) -{ - ui *UI = UI_GetState(); - return(UI->Stacks.FixedXStack[0]); -} - -inline r32 UI_TopFixedX(void) -{ - ui *UI = UI_GetState(); - return(UI->Stacks.FixedXStack[UI->Stacks.FixedXStackUsed - 1]); -} - -#define UI_FixedX(Element) DeferLoop(UI_PushFixedX(Element), UI_PopFixedX()) - -inline void UI_PushFixedY(r32 Element) -{ - ui *UI = UI_GetState(); - Assert(UI->Stacks.FixedYStackUsed + 1 < ArrayCount(UI->Stacks.FixedYStack)); - UI->Stacks.FixedYStack[UI->Stacks.FixedYStackUsed++] = Element; -} - -inline void UI_PopFixedY(void) -{ - ui *UI = UI_GetState(); - Assert(UI->Stacks.FixedYStackUsed > 0); - --UI->Stacks.FixedYStackUsed; -} - -inline void UI_SetNextFixedY(r32 Element) -{ - ui *UI = UI_GetState(); - UI_PushFixedY(Element); - UI->Stacks.AutoPopFixedY = true; -} - -inline r32 UI_FirstFixedY(void) -{ - ui *UI = UI_GetState(); - return(UI->Stacks.FixedYStack[0]); -} - -inline r32 UI_TopFixedY(void) -{ - ui *UI = UI_GetState(); - return(UI->Stacks.FixedYStack[UI->Stacks.FixedYStackUsed - 1]); -} - -#define UI_FixedY(Element) DeferLoop(UI_PushFixedY(Element), UI_PopFixedY()) - -inline void UI_PushTextColor(v4 Element) -{ - ui *UI = UI_GetState(); - Assert(UI->Stacks.TextColorStackUsed + 1 < ArrayCount(UI->Stacks.TextColorStack)); - UI->Stacks.TextColorStack[UI->Stacks.TextColorStackUsed++] = Element; -} - -inline void UI_PopTextColor(void) -{ - ui *UI = UI_GetState(); - Assert(UI->Stacks.TextColorStackUsed > 0); - --UI->Stacks.TextColorStackUsed; -} - -inline void UI_SetNextTextColor(v4 Element) -{ - ui *UI = UI_GetState(); - UI_PushTextColor(Element); - UI->Stacks.AutoPopTextColor = true; -} - -inline v4 UI_FirstTextColor(void) -{ - ui *UI = UI_GetState(); - return(UI->Stacks.TextColorStack[0]); -} - -inline v4 UI_TopTextColor(void) -{ - ui *UI = UI_GetState(); - return(UI->Stacks.TextColorStack[UI->Stacks.TextColorStackUsed - 1]); -} - -#define UI_TextColor(Element) DeferLoop(UI_PushTextColor(Element), UI_PopTextColor()) - -inline void UI_PushBackgroundColor(v4 Element) -{ - ui *UI = UI_GetState(); - Assert(UI->Stacks.BackgroundColorStackUsed + 1 < ArrayCount(UI->Stacks.BackgroundColorStack)); - UI->Stacks.BackgroundColorStack[UI->Stacks.BackgroundColorStackUsed++] = Element; -} - -inline void UI_PopBackgroundColor(void) -{ - ui *UI = UI_GetState(); - Assert(UI->Stacks.BackgroundColorStackUsed > 0); - --UI->Stacks.BackgroundColorStackUsed; -} - -inline void UI_SetNextBackgroundColor(v4 Element) -{ - ui *UI = UI_GetState(); - UI_PushBackgroundColor(Element); - UI->Stacks.AutoPopBackgroundColor = true; -} - -inline v4 UI_FirstBackgroundColor(void) -{ - ui *UI = UI_GetState(); - return(UI->Stacks.BackgroundColorStack[0]); -} - -inline v4 UI_TopBackgroundColor(void) -{ - ui *UI = UI_GetState(); - return(UI->Stacks.BackgroundColorStack[UI->Stacks.BackgroundColorStackUsed - 1]); -} - -#define UI_BackgroundColor(Element) DeferLoop(UI_PushBackgroundColor(Element), UI_PopBackgroundColor()) - -inline void UI_PushBorderColor(v4 Element) -{ - ui *UI = UI_GetState(); - Assert(UI->Stacks.BorderColorStackUsed + 1 < ArrayCount(UI->Stacks.BorderColorStack)); - UI->Stacks.BorderColorStack[UI->Stacks.BorderColorStackUsed++] = Element; -} - -inline void UI_PopBorderColor(void) -{ - ui *UI = UI_GetState(); - Assert(UI->Stacks.BorderColorStackUsed > 0); - --UI->Stacks.BorderColorStackUsed; -} - -inline void UI_SetNextBorderColor(v4 Element) -{ - ui *UI = UI_GetState(); - UI_PushBorderColor(Element); - UI->Stacks.AutoPopBorderColor = true; -} - -inline v4 UI_FirstBorderColor(void) -{ - ui *UI = UI_GetState(); - return(UI->Stacks.BorderColorStack[0]); -} - -inline v4 UI_TopBorderColor(void) -{ - ui *UI = UI_GetState(); - return(UI->Stacks.BorderColorStack[UI->Stacks.BorderColorStackUsed - 1]); -} - -#define UI_BorderColor(Element) DeferLoop(UI_PushBorderColor(Element), UI_PopBorderColor()) - -inline void UI_PushBorderThickness(r32 Element) -{ - ui *UI = UI_GetState(); - Assert(UI->Stacks.BorderThicknessStackUsed + 1 < ArrayCount(UI->Stacks.BorderThicknessStack)); - UI->Stacks.BorderThicknessStack[UI->Stacks.BorderThicknessStackUsed++] = Element; -} - -inline void UI_PopBorderThickness(void) -{ - ui *UI = UI_GetState(); - Assert(UI->Stacks.BorderThicknessStackUsed > 0); - --UI->Stacks.BorderThicknessStackUsed; -} - -inline void UI_SetNextBorderThickness(r32 Element) -{ - ui *UI = UI_GetState(); - UI_PushBorderThickness(Element); - UI->Stacks.AutoPopBorderThickness = true; -} - -inline r32 UI_FirstBorderThickness(void) -{ - ui *UI = UI_GetState(); - return(UI->Stacks.BorderThicknessStack[0]); -} - -inline r32 UI_TopBorderThickness(void) -{ - ui *UI = UI_GetState(); - return(UI->Stacks.BorderThicknessStack[UI->Stacks.BorderThicknessStackUsed - 1]); -} - -#define UI_BorderThickness(Element) DeferLoop(UI_PushBorderThickness(Element), UI_PopBorderThickness()) - -inline void UI_PushLayoutAxis(axis2 Element) -{ - ui *UI = UI_GetState(); - Assert(UI->Stacks.LayoutAxisStackUsed + 1 < ArrayCount(UI->Stacks.LayoutAxisStack)); - UI->Stacks.LayoutAxisStack[UI->Stacks.LayoutAxisStackUsed++] = Element; -} - -inline void UI_PopLayoutAxis(void) -{ - ui *UI = UI_GetState(); - Assert(UI->Stacks.LayoutAxisStackUsed > 0); - --UI->Stacks.LayoutAxisStackUsed; -} - -inline void UI_SetNextLayoutAxis(axis2 Element) -{ - ui *UI = UI_GetState(); - UI_PushLayoutAxis(Element); - UI->Stacks.AutoPopLayoutAxis = true; -} - -inline axis2 UI_FirstLayoutAxis(void) -{ - ui *UI = UI_GetState(); - return(UI->Stacks.LayoutAxisStack[0]); -} - -inline axis2 UI_TopLayoutAxis(void) -{ - ui *UI = UI_GetState(); - return(UI->Stacks.LayoutAxisStack[UI->Stacks.LayoutAxisStackUsed - 1]); -} - -#define UI_LayoutAxis(Element) DeferLoop(UI_PushLayoutAxis(Element), UI_PopLayoutAxis()) - -inline void UI_PushCornerRadius(r32 Element) -{ - ui *UI = UI_GetState(); - Assert(UI->Stacks.CornerRadiusStackUsed + 1 < ArrayCount(UI->Stacks.CornerRadiusStack)); - UI->Stacks.CornerRadiusStack[UI->Stacks.CornerRadiusStackUsed++] = Element; -} - -inline void UI_PopCornerRadius(void) -{ - ui *UI = UI_GetState(); - Assert(UI->Stacks.CornerRadiusStackUsed > 0); - --UI->Stacks.CornerRadiusStackUsed; -} - -inline void UI_SetNextCornerRadius(r32 Element) -{ - ui *UI = UI_GetState(); - UI_PushCornerRadius(Element); - UI->Stacks.AutoPopCornerRadius = true; -} - -inline r32 UI_FirstCornerRadius(void) -{ - ui *UI = UI_GetState(); - return(UI->Stacks.CornerRadiusStack[0]); -} - -inline r32 UI_TopCornerRadius(void) -{ - ui *UI = UI_GetState(); - return(UI->Stacks.CornerRadiusStack[UI->Stacks.CornerRadiusStackUsed - 1]); -} - -#define UI_CornerRadius(Element) DeferLoop(UI_PushCornerRadius(Element), UI_PopCornerRadius()) - -inline void UI_PushFont(font_id Element) -{ - ui *UI = UI_GetState(); - Assert(UI->Stacks.FontStackUsed + 1 < ArrayCount(UI->Stacks.FontStack)); - UI->Stacks.FontStack[UI->Stacks.FontStackUsed++] = Element; -} - -inline void UI_PopFont(void) -{ - ui *UI = UI_GetState(); - Assert(UI->Stacks.FontStackUsed > 0); - --UI->Stacks.FontStackUsed; -} - -inline void UI_SetNextFont(font_id Element) -{ - ui *UI = UI_GetState(); - UI_PushFont(Element); - UI->Stacks.AutoPopFont = true; -} - -inline font_id UI_FirstFont(void) -{ - ui *UI = UI_GetState(); - return(UI->Stacks.FontStack[0]); -} - -inline font_id UI_TopFont(void) -{ - ui *UI = UI_GetState(); - return(UI->Stacks.FontStack[UI->Stacks.FontStackUsed - 1]); -} - -#define UI_Font(Element) DeferLoop(UI_PushFont(Element), UI_PopFont()) - -inline void UI_PushFontSize(r32 Element) -{ - ui *UI = UI_GetState(); - Assert(UI->Stacks.FontSizeStackUsed + 1 < ArrayCount(UI->Stacks.FontSizeStack)); - UI->Stacks.FontSizeStack[UI->Stacks.FontSizeStackUsed++] = Element; -} - -inline void UI_PopFontSize(void) -{ - ui *UI = UI_GetState(); - Assert(UI->Stacks.FontSizeStackUsed > 0); - --UI->Stacks.FontSizeStackUsed; -} - -inline void UI_SetNextFontSize(r32 Element) -{ - ui *UI = UI_GetState(); - UI_PushFontSize(Element); - UI->Stacks.AutoPopFontSize = true; -} - -inline r32 UI_FirstFontSize(void) -{ - ui *UI = UI_GetState(); - return(UI->Stacks.FontSizeStack[0]); -} - -inline r32 UI_TopFontSize(void) -{ - ui *UI = UI_GetState(); - return(UI->Stacks.FontSizeStack[UI->Stacks.FontSizeStackUsed - 1]); -} - -#define UI_FontSize(Element) DeferLoop(UI_PushFontSize(Element), UI_PopFontSize()) - -inline void UI_PushOffsetX(r32 Element) -{ - ui *UI = UI_GetState(); - Assert(UI->Stacks.OffsetXStackUsed + 1 < ArrayCount(UI->Stacks.OffsetXStack)); - UI->Stacks.OffsetXStack[UI->Stacks.OffsetXStackUsed++] = Element; -} - -inline void UI_PopOffsetX(void) -{ - ui *UI = UI_GetState(); - Assert(UI->Stacks.OffsetXStackUsed > 0); - --UI->Stacks.OffsetXStackUsed; -} - -inline void UI_SetNextOffsetX(r32 Element) -{ - ui *UI = UI_GetState(); - UI_PushOffsetX(Element); - UI->Stacks.AutoPopOffsetX = true; -} - -inline r32 UI_FirstOffsetX(void) -{ - ui *UI = UI_GetState(); - return(UI->Stacks.OffsetXStack[0]); -} - -inline r32 UI_TopOffsetX(void) -{ - ui *UI = UI_GetState(); - return(UI->Stacks.OffsetXStack[UI->Stacks.OffsetXStackUsed - 1]); -} - -#define UI_OffsetX(Element) DeferLoop(UI_PushOffsetX(Element), UI_PopOffsetX()) - -inline void UI_PushOffsetY(r32 Element) -{ - ui *UI = UI_GetState(); - Assert(UI->Stacks.OffsetYStackUsed + 1 < ArrayCount(UI->Stacks.OffsetYStack)); - UI->Stacks.OffsetYStack[UI->Stacks.OffsetYStackUsed++] = Element; -} - -inline void UI_PopOffsetY(void) -{ - ui *UI = UI_GetState(); - Assert(UI->Stacks.OffsetYStackUsed > 0); - --UI->Stacks.OffsetYStackUsed; -} - -inline void UI_SetNextOffsetY(r32 Element) -{ - ui *UI = UI_GetState(); - UI_PushOffsetY(Element); - UI->Stacks.AutoPopOffsetY = true; -} - -inline r32 UI_FirstOffsetY(void) -{ - ui *UI = UI_GetState(); - return(UI->Stacks.OffsetYStack[0]); -} - -inline r32 UI_TopOffsetY(void) -{ - ui *UI = UI_GetState(); - return(UI->Stacks.OffsetYStack[UI->Stacks.OffsetYStackUsed - 1]); -} - -#define UI_OffsetY(Element) DeferLoop(UI_PushOffsetY(Element), UI_PopOffsetY()) - - -inline void UI_ApplyStyles(ui_box *Box) -{ - ui *UI = UI_GetState(); - Assert(UI->Stacks.ParentStackUsed > 0); - Box->Parent = UI->Stacks.ParentStack[UI->Stacks.ParentStackUsed - 1]; - if(UI->Stacks.AutoPopParent) - { - UI_PopParent(); - UI->Stacks.AutoPopParent = false; - } - - Assert(UI->Stacks.WidthStackUsed > 0); - Box->SemanticSize[Axis2_X] = UI->Stacks.WidthStack[UI->Stacks.WidthStackUsed - 1]; - if(UI->Stacks.AutoPopWidth) - { - UI_PopWidth(); - UI->Stacks.AutoPopWidth = false; - } - - Assert(UI->Stacks.HeightStackUsed > 0); - Box->SemanticSize[Axis2_Y] = UI->Stacks.HeightStack[UI->Stacks.HeightStackUsed - 1]; - if(UI->Stacks.AutoPopHeight) - { - UI_PopHeight(); - UI->Stacks.AutoPopHeight = false; - } - - Assert(UI->Stacks.FixedXStackUsed > 0); - Box->FixedP.E[Axis2_X] = UI->Stacks.FixedXStack[UI->Stacks.FixedXStackUsed - 1]; - if(UI->Stacks.AutoPopFixedX) - { - UI_PopFixedX(); - UI->Stacks.AutoPopFixedX = false; - } - - Assert(UI->Stacks.FixedYStackUsed > 0); - Box->FixedP.E[Axis2_Y] = UI->Stacks.FixedYStack[UI->Stacks.FixedYStackUsed - 1]; - if(UI->Stacks.AutoPopFixedY) - { - UI_PopFixedY(); - UI->Stacks.AutoPopFixedY = false; - } - - Assert(UI->Stacks.TextColorStackUsed > 0); - Box->TextColor = UI->Stacks.TextColorStack[UI->Stacks.TextColorStackUsed - 1]; - if(UI->Stacks.AutoPopTextColor) - { - UI_PopTextColor(); - UI->Stacks.AutoPopTextColor = false; - } - - Assert(UI->Stacks.BackgroundColorStackUsed > 0); - Box->BackgroundColor = UI->Stacks.BackgroundColorStack[UI->Stacks.BackgroundColorStackUsed - 1]; - if(UI->Stacks.AutoPopBackgroundColor) - { - UI_PopBackgroundColor(); - UI->Stacks.AutoPopBackgroundColor = false; - } - - Assert(UI->Stacks.BorderColorStackUsed > 0); - Box->BorderColor = UI->Stacks.BorderColorStack[UI->Stacks.BorderColorStackUsed - 1]; - if(UI->Stacks.AutoPopBorderColor) - { - UI_PopBorderColor(); - UI->Stacks.AutoPopBorderColor = false; - } - - Assert(UI->Stacks.BorderThicknessStackUsed > 0); - Box->BorderThickness = UI->Stacks.BorderThicknessStack[UI->Stacks.BorderThicknessStackUsed - 1]; - if(UI->Stacks.AutoPopBorderThickness) - { - UI_PopBorderThickness(); - UI->Stacks.AutoPopBorderThickness = false; - } - - Assert(UI->Stacks.LayoutAxisStackUsed > 0); - Box->LayoutAxis = UI->Stacks.LayoutAxisStack[UI->Stacks.LayoutAxisStackUsed - 1]; - if(UI->Stacks.AutoPopLayoutAxis) - { - UI_PopLayoutAxis(); - UI->Stacks.AutoPopLayoutAxis = false; - } - - Assert(UI->Stacks.CornerRadiusStackUsed > 0); - Box->CornerRadius = UI->Stacks.CornerRadiusStack[UI->Stacks.CornerRadiusStackUsed - 1]; - if(UI->Stacks.AutoPopCornerRadius) - { - UI_PopCornerRadius(); - UI->Stacks.AutoPopCornerRadius = false; - } - - Assert(UI->Stacks.FontStackUsed > 0); - Box->Font = UI->Stacks.FontStack[UI->Stacks.FontStackUsed - 1]; - if(UI->Stacks.AutoPopFont) - { - UI_PopFont(); - UI->Stacks.AutoPopFont = false; - } - - Assert(UI->Stacks.FontSizeStackUsed > 0); - Box->FontSize = UI->Stacks.FontSizeStack[UI->Stacks.FontSizeStackUsed - 1]; - if(UI->Stacks.AutoPopFontSize) - { - UI_PopFontSize(); - UI->Stacks.AutoPopFontSize = false; - } - - Assert(UI->Stacks.OffsetXStackUsed > 0); - Box->Offset.x = UI->Stacks.OffsetXStack[UI->Stacks.OffsetXStackUsed - 1]; - if(UI->Stacks.AutoPopOffsetX) - { - UI_PopOffsetX(); - UI->Stacks.AutoPopOffsetX = false; - } - - Assert(UI->Stacks.OffsetYStackUsed > 0); - Box->Offset.y = UI->Stacks.OffsetYStack[UI->Stacks.OffsetYStackUsed - 1]; - if(UI->Stacks.AutoPopOffsetY) - { - UI_PopOffsetY(); - UI->Stacks.AutoPopOffsetY = false; - } - -} diff --git a/code/generated/vn_generated_ui.h b/code/generated/vn_generated_ui.h deleted file mode 100644 index 44cbece..0000000 --- a/code/generated/vn_generated_ui.h +++ /dev/null @@ -1,48 +0,0 @@ -struct ui_style_stacks -{ - ui_box * ParentStack[64]; - s32 ParentStackUsed; - b32 AutoPopParent; - ui_size WidthStack[64]; - s32 WidthStackUsed; - b32 AutoPopWidth; - ui_size HeightStack[64]; - s32 HeightStackUsed; - b32 AutoPopHeight; - r32 FixedXStack[64]; - s32 FixedXStackUsed; - b32 AutoPopFixedX; - r32 FixedYStack[64]; - s32 FixedYStackUsed; - b32 AutoPopFixedY; - v4 TextColorStack[64]; - s32 TextColorStackUsed; - b32 AutoPopTextColor; - v4 BackgroundColorStack[64]; - s32 BackgroundColorStackUsed; - b32 AutoPopBackgroundColor; - v4 BorderColorStack[64]; - s32 BorderColorStackUsed; - b32 AutoPopBorderColor; - r32 BorderThicknessStack[64]; - s32 BorderThicknessStackUsed; - b32 AutoPopBorderThickness; - axis2 LayoutAxisStack[64]; - s32 LayoutAxisStackUsed; - b32 AutoPopLayoutAxis; - r32 CornerRadiusStack[64]; - s32 CornerRadiusStackUsed; - b32 AutoPopCornerRadius; - font_id FontStack[64]; - s32 FontStackUsed; - b32 AutoPopFont; - r32 FontSizeStack[64]; - s32 FontSizeStackUsed; - b32 AutoPopFontSize; - r32 OffsetXStack[64]; - s32 OffsetXStackUsed; - b32 AutoPopOffsetX; - r32 OffsetYStack[64]; - s32 OffsetYStackUsed; - b32 AutoPopOffsetY; -}; diff --git a/code/generated/vn_opengl_functions.meta.c b/code/generated/vn_opengl_functions.meta.c new file mode 100644 index 0000000..e69de29 diff --git a/code/generated/vn_opengl_functions.h b/code/generated/vn_opengl_functions.meta.h similarity index 61% rename from code/generated/vn_opengl_functions.h rename to code/generated/vn_opengl_functions.meta.h index 53cbbcd..eea81cd 100644 --- a/code/generated/vn_opengl_functions.h +++ b/code/generated/vn_opengl_functions.meta.h @@ -175,94 +175,94 @@ global opengl_UniformMatrix4fv *glUniformMatrix4fv = 0; global opengl_DebugMessageCallback *glDebugMessageCallback = 0; extern void *OpenGL_LoadFunction(char *); - static void OpenGL_LoadAllFunctions(void) { - glBindTexture = (opengl_BindTexture *)OpenGL_LoadFunction("glBindTexture"); - glBlendFunc = (opengl_BlendFunc *)OpenGL_LoadFunction("glBlendFunc"); - glClear = (opengl_Clear *)OpenGL_LoadFunction("glClear"); - glClearAccum = (opengl_ClearAccum *)OpenGL_LoadFunction("glClearAccum"); - glClearColor = (opengl_ClearColor *)OpenGL_LoadFunction("glClearColor"); - glClearDepth = (opengl_ClearDepth *)OpenGL_LoadFunction("glClearDepth"); - glClearIndex = (opengl_ClearIndex *)OpenGL_LoadFunction("glClearIndex"); - glClearStencil = (opengl_ClearStencil *)OpenGL_LoadFunction("glClearStencil"); - glClipPlane = (opengl_ClipPlane *)OpenGL_LoadFunction("glClipPlane"); - glCullFace = (opengl_CullFace *)OpenGL_LoadFunction("glCullFace"); - glDeleteTextures = (opengl_DeleteTextures *)OpenGL_LoadFunction("glDeleteTextures"); - glDisable = (opengl_Disable *)OpenGL_LoadFunction("glDisable"); - glDrawArrays = (opengl_DrawArrays *)OpenGL_LoadFunction("glDrawArrays"); - glDrawBuffer = (opengl_DrawBuffer *)OpenGL_LoadFunction("glDrawBuffer"); - glDrawElements = (opengl_DrawElements *)OpenGL_LoadFunction("glDrawElements"); - glDrawArraysInstanced = (opengl_DrawArraysInstanced *)OpenGL_LoadFunction("glDrawArraysInstanced"); - glEnable = (opengl_Enable *)OpenGL_LoadFunction("glEnable"); - glGenTextures = (opengl_GenTextures *)OpenGL_LoadFunction("glGenTextures"); - glGetClipPlane = (opengl_GetClipPlane *)OpenGL_LoadFunction("glGetClipPlane"); - glGetDoublev = (opengl_GetDoublev *)OpenGL_LoadFunction("glGetDoublev"); - glGetError = (opengl_GetError *)OpenGL_LoadFunction("glGetError"); - glGetFloatv = (opengl_GetFloatv *)OpenGL_LoadFunction("glGetFloatv"); - glGetIntegerv = (opengl_GetIntegerv *)OpenGL_LoadFunction("glGetIntegerv"); - glGetPointerv = (opengl_GetPointerv *)OpenGL_LoadFunction("glGetPointerv"); - glGetString = (opengl_GetString *)OpenGL_LoadFunction("glGetString"); - glGetTexEnvfv = (opengl_GetTexEnvfv *)OpenGL_LoadFunction("glGetTexEnvfv"); - glGetTexEnviv = (opengl_GetTexEnviv *)OpenGL_LoadFunction("glGetTexEnviv"); - glGetTexGendv = (opengl_GetTexGendv *)OpenGL_LoadFunction("glGetTexGendv"); - glGetTexGenfv = (opengl_GetTexGenfv *)OpenGL_LoadFunction("glGetTexGenfv"); - glGetTexGeniv = (opengl_GetTexGeniv *)OpenGL_LoadFunction("glGetTexGeniv"); - glGetTexImage = (opengl_GetTexImage *)OpenGL_LoadFunction("glGetTexImage"); - glGetTexLevelParameterfv = (opengl_GetTexLevelParameterfv *)OpenGL_LoadFunction("glGetTexLevelParameterfv"); - glGetTexLevelParameteriv = (opengl_GetTexLevelParameteriv *)OpenGL_LoadFunction("glGetTexLevelParameteriv"); - glGetTexParameterfv = (opengl_GetTexParameterfv *)OpenGL_LoadFunction("glGetTexParameterfv"); - glGetTexParameteriv = (opengl_GetTexParameteriv *)OpenGL_LoadFunction("glGetTexParameteriv"); - glHint = (opengl_Hint *)OpenGL_LoadFunction("glHint"); - glIsTexture = (opengl_IsTexture *)OpenGL_LoadFunction("glIsTexture"); - glLineWidth = (opengl_LineWidth *)OpenGL_LoadFunction("glLineWidth"); - glListBase = (opengl_ListBase *)OpenGL_LoadFunction("glListBase"); - glLoadName = (opengl_LoadName *)OpenGL_LoadFunction("glLoadName"); - glLogicOp = (opengl_LogicOp *)OpenGL_LoadFunction("glLogicOp"); - glPointSize = (opengl_PointSize *)OpenGL_LoadFunction("glPointSize"); - glPolygonMode = (opengl_PolygonMode *)OpenGL_LoadFunction("glPolygonMode"); - glScissor = (opengl_Scissor *)OpenGL_LoadFunction("glScissor"); - glTexImage1D = (opengl_TexImage1D *)OpenGL_LoadFunction("glTexImage1D"); - glTexImage2D = (opengl_TexImage2D *)OpenGL_LoadFunction("glTexImage2D"); - glTexParameterf = (opengl_TexParameterf *)OpenGL_LoadFunction("glTexParameterf"); - glTexParameterfv = (opengl_TexParameterfv *)OpenGL_LoadFunction("glTexParameterfv"); - glTexParameteri = (opengl_TexParameteri *)OpenGL_LoadFunction("glTexParameteri"); - glTexParameteriv = (opengl_TexParameteriv *)OpenGL_LoadFunction("glTexParameteriv"); - glTexSubImage1D = (opengl_TexSubImage1D *)OpenGL_LoadFunction("glTexSubImage1D"); - glTexSubImage2D = (opengl_TexSubImage2D *)OpenGL_LoadFunction("glTexSubImage2D"); - glCompressedTexImage2D = (opengl_CompressedTexImage2D *)OpenGL_LoadFunction("glCompressedTexImage2D"); - glActiveTexture = (opengl_ActiveTexture *)OpenGL_LoadFunction("glActiveTexture"); - glViewport = (opengl_Viewport *)OpenGL_LoadFunction("glViewport"); - glGenBuffers = (opengl_GenBuffers *)OpenGL_LoadFunction("glGenBuffers"); - glBindBuffer = (opengl_BindBuffer *)OpenGL_LoadFunction("glBindBuffer"); - glBufferData = (opengl_BufferData *)OpenGL_LoadFunction("glBufferData"); - glBufferSubData = (opengl_BufferSubData *)OpenGL_LoadFunction("glBufferSubData"); - glGenVertexArrays = (opengl_GenVertexArrays *)OpenGL_LoadFunction("glGenVertexArrays"); - glBindVertexArray = (opengl_BindVertexArray *)OpenGL_LoadFunction("glBindVertexArray"); - glGetAttribLocation = (opengl_GetAttribLocation *)OpenGL_LoadFunction("glGetAttribLocation"); - glEnableVertexAttribArray = (opengl_EnableVertexAttribArray *)OpenGL_LoadFunction("glEnableVertexAttribArray"); - glDisableVertexAttribArray = (opengl_DisableVertexAttribArray *)OpenGL_LoadFunction("glDisableVertexAttribArray"); - glVertexAttribPointer = (opengl_VertexAttribPointer *)OpenGL_LoadFunction("glVertexAttribPointer"); - glVertexAttribIPointer = (opengl_VertexAttribIPointer *)OpenGL_LoadFunction("glVertexAttribIPointer"); - glVertexAttribLPointer = (opengl_VertexAttribLPointer *)OpenGL_LoadFunction("glVertexAttribLPointer"); - glVertexAttribDivisor = (opengl_VertexAttribDivisor *)OpenGL_LoadFunction("glVertexAttribDivisor"); - glCreateShader = (opengl_CreateShader *)OpenGL_LoadFunction("glCreateShader"); - glShaderSource = (opengl_ShaderSource *)OpenGL_LoadFunction("glShaderSource"); - glCompileShader = (opengl_CompileShader *)OpenGL_LoadFunction("glCompileShader"); - glDeleteShader = (opengl_DeleteShader *)OpenGL_LoadFunction("glDeleteShader"); - glGetShaderiv = (opengl_GetShaderiv *)OpenGL_LoadFunction("glGetShaderiv"); - glGetShaderInfoLog = (opengl_GetShaderInfoLog *)OpenGL_LoadFunction("glGetShaderInfoLog"); - glCreateProgram = (opengl_CreateProgram *)OpenGL_LoadFunction("glCreateProgram"); - glUseProgram = (opengl_UseProgram *)OpenGL_LoadFunction("glUseProgram"); - glAttachShader = (opengl_AttachShader *)OpenGL_LoadFunction("glAttachShader"); - glDeleteProgram = (opengl_DeleteProgram *)OpenGL_LoadFunction("glDeleteProgram"); - glLinkProgram = (opengl_LinkProgram *)OpenGL_LoadFunction("glLinkProgram"); - glGetProgramiv = (opengl_GetProgramiv *)OpenGL_LoadFunction("glGetProgramiv"); - glGetProgramInfoLog = (opengl_GetProgramInfoLog *)OpenGL_LoadFunction("glGetProgramInfoLog"); - glGetUniformLocation = (opengl_GetUniformLocation *)OpenGL_LoadFunction("glGetUniformLocation"); - glUniform1i = (opengl_Uniform1i *)OpenGL_LoadFunction("glUniform1i"); - glUniform2f = (opengl_Uniform2f *)OpenGL_LoadFunction("glUniform2f"); - glUniform3f = (opengl_Uniform3f *)OpenGL_LoadFunction("glUniform3f"); - glUniformMatrix4fv = (opengl_UniformMatrix4fv *)OpenGL_LoadFunction("glUniformMatrix4fv"); - glDebugMessageCallback = (opengl_DebugMessageCallback *)OpenGL_LoadFunction("glDebugMessageCallback"); +glBindTexture = (opengl_BindTexture *)OpenGL_LoadFunction("glBindTexture"); +glBlendFunc = (opengl_BlendFunc *)OpenGL_LoadFunction("glBlendFunc"); +glClear = (opengl_Clear *)OpenGL_LoadFunction("glClear"); +glClearAccum = (opengl_ClearAccum *)OpenGL_LoadFunction("glClearAccum"); +glClearColor = (opengl_ClearColor *)OpenGL_LoadFunction("glClearColor"); +glClearDepth = (opengl_ClearDepth *)OpenGL_LoadFunction("glClearDepth"); +glClearIndex = (opengl_ClearIndex *)OpenGL_LoadFunction("glClearIndex"); +glClearStencil = (opengl_ClearStencil *)OpenGL_LoadFunction("glClearStencil"); +glClipPlane = (opengl_ClipPlane *)OpenGL_LoadFunction("glClipPlane"); +glCullFace = (opengl_CullFace *)OpenGL_LoadFunction("glCullFace"); +glDeleteTextures = (opengl_DeleteTextures *)OpenGL_LoadFunction("glDeleteTextures"); +glDisable = (opengl_Disable *)OpenGL_LoadFunction("glDisable"); +glDrawArrays = (opengl_DrawArrays *)OpenGL_LoadFunction("glDrawArrays"); +glDrawBuffer = (opengl_DrawBuffer *)OpenGL_LoadFunction("glDrawBuffer"); +glDrawElements = (opengl_DrawElements *)OpenGL_LoadFunction("glDrawElements"); +glDrawArraysInstanced = (opengl_DrawArraysInstanced *)OpenGL_LoadFunction("glDrawArraysInstanced"); +glEnable = (opengl_Enable *)OpenGL_LoadFunction("glEnable"); +glGenTextures = (opengl_GenTextures *)OpenGL_LoadFunction("glGenTextures"); +glGetClipPlane = (opengl_GetClipPlane *)OpenGL_LoadFunction("glGetClipPlane"); +glGetDoublev = (opengl_GetDoublev *)OpenGL_LoadFunction("glGetDoublev"); +glGetError = (opengl_GetError *)OpenGL_LoadFunction("glGetError"); +glGetFloatv = (opengl_GetFloatv *)OpenGL_LoadFunction("glGetFloatv"); +glGetIntegerv = (opengl_GetIntegerv *)OpenGL_LoadFunction("glGetIntegerv"); +glGetPointerv = (opengl_GetPointerv *)OpenGL_LoadFunction("glGetPointerv"); +glGetString = (opengl_GetString *)OpenGL_LoadFunction("glGetString"); +glGetTexEnvfv = (opengl_GetTexEnvfv *)OpenGL_LoadFunction("glGetTexEnvfv"); +glGetTexEnviv = (opengl_GetTexEnviv *)OpenGL_LoadFunction("glGetTexEnviv"); +glGetTexGendv = (opengl_GetTexGendv *)OpenGL_LoadFunction("glGetTexGendv"); +glGetTexGenfv = (opengl_GetTexGenfv *)OpenGL_LoadFunction("glGetTexGenfv"); +glGetTexGeniv = (opengl_GetTexGeniv *)OpenGL_LoadFunction("glGetTexGeniv"); +glGetTexImage = (opengl_GetTexImage *)OpenGL_LoadFunction("glGetTexImage"); +glGetTexLevelParameterfv = (opengl_GetTexLevelParameterfv *)OpenGL_LoadFunction("glGetTexLevelParameterfv"); +glGetTexLevelParameteriv = (opengl_GetTexLevelParameteriv *)OpenGL_LoadFunction("glGetTexLevelParameteriv"); +glGetTexParameterfv = (opengl_GetTexParameterfv *)OpenGL_LoadFunction("glGetTexParameterfv"); +glGetTexParameteriv = (opengl_GetTexParameteriv *)OpenGL_LoadFunction("glGetTexParameteriv"); +glHint = (opengl_Hint *)OpenGL_LoadFunction("glHint"); +glIsTexture = (opengl_IsTexture *)OpenGL_LoadFunction("glIsTexture"); +glLineWidth = (opengl_LineWidth *)OpenGL_LoadFunction("glLineWidth"); +glListBase = (opengl_ListBase *)OpenGL_LoadFunction("glListBase"); +glLoadName = (opengl_LoadName *)OpenGL_LoadFunction("glLoadName"); +glLogicOp = (opengl_LogicOp *)OpenGL_LoadFunction("glLogicOp"); +glPointSize = (opengl_PointSize *)OpenGL_LoadFunction("glPointSize"); +glPolygonMode = (opengl_PolygonMode *)OpenGL_LoadFunction("glPolygonMode"); +glScissor = (opengl_Scissor *)OpenGL_LoadFunction("glScissor"); +glTexImage1D = (opengl_TexImage1D *)OpenGL_LoadFunction("glTexImage1D"); +glTexImage2D = (opengl_TexImage2D *)OpenGL_LoadFunction("glTexImage2D"); +glTexParameterf = (opengl_TexParameterf *)OpenGL_LoadFunction("glTexParameterf"); +glTexParameterfv = (opengl_TexParameterfv *)OpenGL_LoadFunction("glTexParameterfv"); +glTexParameteri = (opengl_TexParameteri *)OpenGL_LoadFunction("glTexParameteri"); +glTexParameteriv = (opengl_TexParameteriv *)OpenGL_LoadFunction("glTexParameteriv"); +glTexSubImage1D = (opengl_TexSubImage1D *)OpenGL_LoadFunction("glTexSubImage1D"); +glTexSubImage2D = (opengl_TexSubImage2D *)OpenGL_LoadFunction("glTexSubImage2D"); +glCompressedTexImage2D = (opengl_CompressedTexImage2D *)OpenGL_LoadFunction("glCompressedTexImage2D"); +glActiveTexture = (opengl_ActiveTexture *)OpenGL_LoadFunction("glActiveTexture"); +glViewport = (opengl_Viewport *)OpenGL_LoadFunction("glViewport"); +glGenBuffers = (opengl_GenBuffers *)OpenGL_LoadFunction("glGenBuffers"); +glBindBuffer = (opengl_BindBuffer *)OpenGL_LoadFunction("glBindBuffer"); +glBufferData = (opengl_BufferData *)OpenGL_LoadFunction("glBufferData"); +glBufferSubData = (opengl_BufferSubData *)OpenGL_LoadFunction("glBufferSubData"); +glGenVertexArrays = (opengl_GenVertexArrays *)OpenGL_LoadFunction("glGenVertexArrays"); +glBindVertexArray = (opengl_BindVertexArray *)OpenGL_LoadFunction("glBindVertexArray"); +glGetAttribLocation = (opengl_GetAttribLocation *)OpenGL_LoadFunction("glGetAttribLocation"); +glEnableVertexAttribArray = (opengl_EnableVertexAttribArray *)OpenGL_LoadFunction("glEnableVertexAttribArray"); +glDisableVertexAttribArray = (opengl_DisableVertexAttribArray *)OpenGL_LoadFunction("glDisableVertexAttribArray"); +glVertexAttribPointer = (opengl_VertexAttribPointer *)OpenGL_LoadFunction("glVertexAttribPointer"); +glVertexAttribIPointer = (opengl_VertexAttribIPointer *)OpenGL_LoadFunction("glVertexAttribIPointer"); +glVertexAttribLPointer = (opengl_VertexAttribLPointer *)OpenGL_LoadFunction("glVertexAttribLPointer"); +glVertexAttribDivisor = (opengl_VertexAttribDivisor *)OpenGL_LoadFunction("glVertexAttribDivisor"); +glCreateShader = (opengl_CreateShader *)OpenGL_LoadFunction("glCreateShader"); +glShaderSource = (opengl_ShaderSource *)OpenGL_LoadFunction("glShaderSource"); +glCompileShader = (opengl_CompileShader *)OpenGL_LoadFunction("glCompileShader"); +glDeleteShader = (opengl_DeleteShader *)OpenGL_LoadFunction("glDeleteShader"); +glGetShaderiv = (opengl_GetShaderiv *)OpenGL_LoadFunction("glGetShaderiv"); +glGetShaderInfoLog = (opengl_GetShaderInfoLog *)OpenGL_LoadFunction("glGetShaderInfoLog"); +glCreateProgram = (opengl_CreateProgram *)OpenGL_LoadFunction("glCreateProgram"); +glUseProgram = (opengl_UseProgram *)OpenGL_LoadFunction("glUseProgram"); +glAttachShader = (opengl_AttachShader *)OpenGL_LoadFunction("glAttachShader"); +glDeleteProgram = (opengl_DeleteProgram *)OpenGL_LoadFunction("glDeleteProgram"); +glLinkProgram = (opengl_LinkProgram *)OpenGL_LoadFunction("glLinkProgram"); +glGetProgramiv = (opengl_GetProgramiv *)OpenGL_LoadFunction("glGetProgramiv"); +glGetProgramInfoLog = (opengl_GetProgramInfoLog *)OpenGL_LoadFunction("glGetProgramInfoLog"); +glGetUniformLocation = (opengl_GetUniformLocation *)OpenGL_LoadFunction("glGetUniformLocation"); +glUniform1i = (opengl_Uniform1i *)OpenGL_LoadFunction("glUniform1i"); +glUniform2f = (opengl_Uniform2f *)OpenGL_LoadFunction("glUniform2f"); +glUniform3f = (opengl_Uniform3f *)OpenGL_LoadFunction("glUniform3f"); +glUniformMatrix4fv = (opengl_UniformMatrix4fv *)OpenGL_LoadFunction("glUniformMatrix4fv"); +glDebugMessageCallback = (opengl_DebugMessageCallback *)OpenGL_LoadFunction("glDebugMessageCallback"); } + diff --git a/code/generated/vn_platform.meta.c b/code/generated/vn_platform.meta.c new file mode 100644 index 0000000..e69de29 diff --git a/code/generated/vn_platform.meta.h b/code/generated/vn_platform.meta.h new file mode 100644 index 0000000..06999ef --- /dev/null +++ b/code/generated/vn_platform.meta.h @@ -0,0 +1,56 @@ +#define PLATFORM_RESERVE(name) void * name(u64 Size) +#define PLATFORM_RELEASE(name) void name(void *Pointer) +#define PLATFORM_COMMIT(name) void name(void *Pointer, u64 Size) +#define PLATFORM_DECOMMIT(name) void name(void *Pointer, u64 Size) +#define PLATFORM_OPEN_FILE(name) platform_file_handle name(string Path, platform_access_flags FileAccess) +#define PLATFORM_CLOSE_FILE(name) void name(platform_file_handle Handle) +#define PLATFORM_READ_FILE(name) void name(platform_file_handle Handle, void *Dest, u64 Offset, u64 Size) +#define PLATFORM_WRITE_FILE(name) void name(platform_file_handle Handle, void *Source, u64 Offset, u64 Size) +#define PLATFORM_GET_FILE_SIZE(name) u64 name(platform_file_handle Handle) +#define PLATFORM_SET_CURSOR(name) void name(platform_cursor Cursor) +#define PLATFORM_TOGGLE_FULLSCREEN(name) void name(void) +#define PLATFORM_SHOW_MESSAGE(name) void name(string Message, platform_message_type Type) + +typedef PLATFORM_RESERVE(platform_reserve); +typedef PLATFORM_RELEASE(platform_release); +typedef PLATFORM_COMMIT(platform_commit); +typedef PLATFORM_DECOMMIT(platform_decommit); +typedef PLATFORM_OPEN_FILE(platform_open_file); +typedef PLATFORM_CLOSE_FILE(platform_close_file); +typedef PLATFORM_READ_FILE(platform_read_file); +typedef PLATFORM_WRITE_FILE(platform_write_file); +typedef PLATFORM_GET_FILE_SIZE(platform_get_file_size); +typedef PLATFORM_SET_CURSOR(platform_set_cursor); +typedef PLATFORM_TOGGLE_FULLSCREEN(platform_toggle_fullscreen); +typedef PLATFORM_SHOW_MESSAGE(platform_show_message); + +struct platform_api +{ +platform_reserve *Reserve; +platform_release *Release; +platform_commit *Commit; +platform_decommit *Decommit; +platform_open_file *OpenFile; +platform_close_file *CloseFile; +platform_read_file *ReadFile; +platform_write_file *WriteFile; +platform_get_file_size *GetFileSize; +platform_set_cursor *SetCursor; +platform_toggle_fullscreen *ToggleFullscreen; +platform_show_message *ShowMessage; +}; + +#define RegisterPlatformFunctions(PlatformName)\ +Platform.Reserve = PlatformName##_Reserve;\ +Platform.Release = PlatformName##_Release;\ +Platform.Commit = PlatformName##_Commit;\ +Platform.Decommit = PlatformName##_Decommit;\ +Platform.OpenFile = PlatformName##_OpenFile;\ +Platform.CloseFile = PlatformName##_CloseFile;\ +Platform.ReadFile = PlatformName##_ReadFile;\ +Platform.WriteFile = PlatformName##_WriteFile;\ +Platform.GetFileSize = PlatformName##_GetFileSize;\ +Platform.SetCursor = PlatformName##_SetCursor;\ +Platform.ToggleFullscreen = PlatformName##_ToggleFullscreen;\ +Platform.ShowMessage = PlatformName##_ShowMessage;\ + diff --git a/code/generated/vn_scene.meta.c b/code/generated/vn_scene.meta.c new file mode 100644 index 0000000..e69de29 diff --git a/code/generated/vn_scene.meta.h b/code/generated/vn_scene.meta.h new file mode 100644 index 0000000..485ebef --- /dev/null +++ b/code/generated/vn_scene.meta.h @@ -0,0 +1,25 @@ +enum scene_ast_node_type +{ +S_AstNode_Invalid, +S_AstNode_BlockStatement, +S_AstNode_SceneDecl, +S_AstNode_Count, +}; + +struct scene_ast_node; + +struct scene_ast_node_invalid {}; +struct scene_ast_node_block_statement {scene_ast_node *First; scene_ast_node *Last;}; +struct scene_ast_node_scene_declaration {}; + +struct scene_ast_node +{ +scene_ast_node_type Type; +union +{ +scene_ast_node_invalid InvalidData; +scene_ast_node_block_statement BlockStatementData; +scene_ast_node_scene_declaration SceneDeclData; +}; +}; + diff --git a/code/generated/vn_ui.meta.c b/code/generated/vn_ui.meta.c new file mode 100644 index 0000000..36d32f2 --- /dev/null +++ b/code/generated/vn_ui.meta.c @@ -0,0 +1,111 @@ +inline void UI_PushParent(ui_box * Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.ParentStackUsed + 1 < ArrayCount(UI->Stacks.ParentStack)); UI->Stacks.ParentStack[UI->Stacks.ParentStackUsed++] = Element; } +inline void UI_PushWidth(ui_size Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.WidthStackUsed + 1 < ArrayCount(UI->Stacks.WidthStack)); UI->Stacks.WidthStack[UI->Stacks.WidthStackUsed++] = Element; } +inline void UI_PushHeight(ui_size Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.HeightStackUsed + 1 < ArrayCount(UI->Stacks.HeightStack)); UI->Stacks.HeightStack[UI->Stacks.HeightStackUsed++] = Element; } +inline void UI_PushFixedX(r32 Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.FixedXStackUsed + 1 < ArrayCount(UI->Stacks.FixedXStack)); UI->Stacks.FixedXStack[UI->Stacks.FixedXStackUsed++] = Element; } +inline void UI_PushFixedY(r32 Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.FixedYStackUsed + 1 < ArrayCount(UI->Stacks.FixedYStack)); UI->Stacks.FixedYStack[UI->Stacks.FixedYStackUsed++] = Element; } +inline void UI_PushTextColor(v4 Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.TextColorStackUsed + 1 < ArrayCount(UI->Stacks.TextColorStack)); UI->Stacks.TextColorStack[UI->Stacks.TextColorStackUsed++] = Element; } +inline void UI_PushBackgroundColor(v4 Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.BackgroundColorStackUsed + 1 < ArrayCount(UI->Stacks.BackgroundColorStack)); UI->Stacks.BackgroundColorStack[UI->Stacks.BackgroundColorStackUsed++] = Element; } +inline void UI_PushBorderColor(v4 Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.BorderColorStackUsed + 1 < ArrayCount(UI->Stacks.BorderColorStack)); UI->Stacks.BorderColorStack[UI->Stacks.BorderColorStackUsed++] = Element; } +inline void UI_PushBorderThickness(r32 Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.BorderThicknessStackUsed + 1 < ArrayCount(UI->Stacks.BorderThicknessStack)); UI->Stacks.BorderThicknessStack[UI->Stacks.BorderThicknessStackUsed++] = Element; } +inline void UI_PushLayoutAxis(axis2 Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.LayoutAxisStackUsed + 1 < ArrayCount(UI->Stacks.LayoutAxisStack)); UI->Stacks.LayoutAxisStack[UI->Stacks.LayoutAxisStackUsed++] = Element; } +inline void UI_PushCornerRadius(r32 Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.CornerRadiusStackUsed + 1 < ArrayCount(UI->Stacks.CornerRadiusStack)); UI->Stacks.CornerRadiusStack[UI->Stacks.CornerRadiusStackUsed++] = Element; } +inline void UI_PushFont(font_id Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.FontStackUsed + 1 < ArrayCount(UI->Stacks.FontStack)); UI->Stacks.FontStack[UI->Stacks.FontStackUsed++] = Element; } +inline void UI_PushFontSize(r32 Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.FontSizeStackUsed + 1 < ArrayCount(UI->Stacks.FontSizeStack)); UI->Stacks.FontSizeStack[UI->Stacks.FontSizeStackUsed++] = Element; } +inline void UI_PushOffsetX(r32 Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.OffsetXStackUsed + 1 < ArrayCount(UI->Stacks.OffsetXStack)); UI->Stacks.OffsetXStack[UI->Stacks.OffsetXStackUsed++] = Element; } +inline void UI_PushOffsetY(r32 Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.OffsetYStackUsed + 1 < ArrayCount(UI->Stacks.OffsetYStack)); UI->Stacks.OffsetYStack[UI->Stacks.OffsetYStackUsed++] = Element; } +inline void UI_PopParent(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.ParentStackUsed > 0); --UI->Stacks.ParentStackUsed; } +inline void UI_PopWidth(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.WidthStackUsed > 0); --UI->Stacks.WidthStackUsed; } +inline void UI_PopHeight(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.HeightStackUsed > 0); --UI->Stacks.HeightStackUsed; } +inline void UI_PopFixedX(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.FixedXStackUsed > 0); --UI->Stacks.FixedXStackUsed; } +inline void UI_PopFixedY(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.FixedYStackUsed > 0); --UI->Stacks.FixedYStackUsed; } +inline void UI_PopTextColor(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.TextColorStackUsed > 0); --UI->Stacks.TextColorStackUsed; } +inline void UI_PopBackgroundColor(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.BackgroundColorStackUsed > 0); --UI->Stacks.BackgroundColorStackUsed; } +inline void UI_PopBorderColor(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.BorderColorStackUsed > 0); --UI->Stacks.BorderColorStackUsed; } +inline void UI_PopBorderThickness(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.BorderThicknessStackUsed > 0); --UI->Stacks.BorderThicknessStackUsed; } +inline void UI_PopLayoutAxis(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.LayoutAxisStackUsed > 0); --UI->Stacks.LayoutAxisStackUsed; } +inline void UI_PopCornerRadius(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.CornerRadiusStackUsed > 0); --UI->Stacks.CornerRadiusStackUsed; } +inline void UI_PopFont(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.FontStackUsed > 0); --UI->Stacks.FontStackUsed; } +inline void UI_PopFontSize(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.FontSizeStackUsed > 0); --UI->Stacks.FontSizeStackUsed; } +inline void UI_PopOffsetX(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.OffsetXStackUsed > 0); --UI->Stacks.OffsetXStackUsed; } +inline void UI_PopOffsetY(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.OffsetYStackUsed > 0); --UI->Stacks.OffsetYStackUsed; } +inline void UI_SetNextParent(ui_box * Element) { ui *UI = UI_GetState(); UI_PushParent(Element); UI->Stacks.AutoPopParent = true; } +inline void UI_SetNextWidth(ui_size Element) { ui *UI = UI_GetState(); UI_PushWidth(Element); UI->Stacks.AutoPopWidth = true; } +inline void UI_SetNextHeight(ui_size Element) { ui *UI = UI_GetState(); UI_PushHeight(Element); UI->Stacks.AutoPopHeight = true; } +inline void UI_SetNextFixedX(r32 Element) { ui *UI = UI_GetState(); UI_PushFixedX(Element); UI->Stacks.AutoPopFixedX = true; } +inline void UI_SetNextFixedY(r32 Element) { ui *UI = UI_GetState(); UI_PushFixedY(Element); UI->Stacks.AutoPopFixedY = true; } +inline void UI_SetNextTextColor(v4 Element) { ui *UI = UI_GetState(); UI_PushTextColor(Element); UI->Stacks.AutoPopTextColor = true; } +inline void UI_SetNextBackgroundColor(v4 Element) { ui *UI = UI_GetState(); UI_PushBackgroundColor(Element); UI->Stacks.AutoPopBackgroundColor = true; } +inline void UI_SetNextBorderColor(v4 Element) { ui *UI = UI_GetState(); UI_PushBorderColor(Element); UI->Stacks.AutoPopBorderColor = true; } +inline void UI_SetNextBorderThickness(r32 Element) { ui *UI = UI_GetState(); UI_PushBorderThickness(Element); UI->Stacks.AutoPopBorderThickness = true; } +inline void UI_SetNextLayoutAxis(axis2 Element) { ui *UI = UI_GetState(); UI_PushLayoutAxis(Element); UI->Stacks.AutoPopLayoutAxis = true; } +inline void UI_SetNextCornerRadius(r32 Element) { ui *UI = UI_GetState(); UI_PushCornerRadius(Element); UI->Stacks.AutoPopCornerRadius = true; } +inline void UI_SetNextFont(font_id Element) { ui *UI = UI_GetState(); UI_PushFont(Element); UI->Stacks.AutoPopFont = true; } +inline void UI_SetNextFontSize(r32 Element) { ui *UI = UI_GetState(); UI_PushFontSize(Element); UI->Stacks.AutoPopFontSize = true; } +inline void UI_SetNextOffsetX(r32 Element) { ui *UI = UI_GetState(); UI_PushOffsetX(Element); UI->Stacks.AutoPopOffsetX = true; } +inline void UI_SetNextOffsetY(r32 Element) { ui *UI = UI_GetState(); UI_PushOffsetY(Element); UI->Stacks.AutoPopOffsetY = true; } +inline ui_box * UI_FirstParent(void) { ui *UI = UI_GetState(); return(UI->Stacks.ParentStack[0]); } +inline ui_size UI_FirstWidth(void) { ui *UI = UI_GetState(); return(UI->Stacks.WidthStack[0]); } +inline ui_size UI_FirstHeight(void) { ui *UI = UI_GetState(); return(UI->Stacks.HeightStack[0]); } +inline r32 UI_FirstFixedX(void) { ui *UI = UI_GetState(); return(UI->Stacks.FixedXStack[0]); } +inline r32 UI_FirstFixedY(void) { ui *UI = UI_GetState(); return(UI->Stacks.FixedYStack[0]); } +inline v4 UI_FirstTextColor(void) { ui *UI = UI_GetState(); return(UI->Stacks.TextColorStack[0]); } +inline v4 UI_FirstBackgroundColor(void) { ui *UI = UI_GetState(); return(UI->Stacks.BackgroundColorStack[0]); } +inline v4 UI_FirstBorderColor(void) { ui *UI = UI_GetState(); return(UI->Stacks.BorderColorStack[0]); } +inline r32 UI_FirstBorderThickness(void) { ui *UI = UI_GetState(); return(UI->Stacks.BorderThicknessStack[0]); } +inline axis2 UI_FirstLayoutAxis(void) { ui *UI = UI_GetState(); return(UI->Stacks.LayoutAxisStack[0]); } +inline r32 UI_FirstCornerRadius(void) { ui *UI = UI_GetState(); return(UI->Stacks.CornerRadiusStack[0]); } +inline font_id UI_FirstFont(void) { ui *UI = UI_GetState(); return(UI->Stacks.FontStack[0]); } +inline r32 UI_FirstFontSize(void) { ui *UI = UI_GetState(); return(UI->Stacks.FontSizeStack[0]); } +inline r32 UI_FirstOffsetX(void) { ui *UI = UI_GetState(); return(UI->Stacks.OffsetXStack[0]); } +inline r32 UI_FirstOffsetY(void) { ui *UI = UI_GetState(); return(UI->Stacks.OffsetYStack[0]); } +inline ui_box * UI_TopParent(void) { ui *UI = UI_GetState(); return(UI->Stacks.ParentStack[UI->Stacks.ParentStackUsed - 1]); } +inline ui_size UI_TopWidth(void) { ui *UI = UI_GetState(); return(UI->Stacks.WidthStack[UI->Stacks.WidthStackUsed - 1]); } +inline ui_size UI_TopHeight(void) { ui *UI = UI_GetState(); return(UI->Stacks.HeightStack[UI->Stacks.HeightStackUsed - 1]); } +inline r32 UI_TopFixedX(void) { ui *UI = UI_GetState(); return(UI->Stacks.FixedXStack[UI->Stacks.FixedXStackUsed - 1]); } +inline r32 UI_TopFixedY(void) { ui *UI = UI_GetState(); return(UI->Stacks.FixedYStack[UI->Stacks.FixedYStackUsed - 1]); } +inline v4 UI_TopTextColor(void) { ui *UI = UI_GetState(); return(UI->Stacks.TextColorStack[UI->Stacks.TextColorStackUsed - 1]); } +inline v4 UI_TopBackgroundColor(void) { ui *UI = UI_GetState(); return(UI->Stacks.BackgroundColorStack[UI->Stacks.BackgroundColorStackUsed - 1]); } +inline v4 UI_TopBorderColor(void) { ui *UI = UI_GetState(); return(UI->Stacks.BorderColorStack[UI->Stacks.BorderColorStackUsed - 1]); } +inline r32 UI_TopBorderThickness(void) { ui *UI = UI_GetState(); return(UI->Stacks.BorderThicknessStack[UI->Stacks.BorderThicknessStackUsed - 1]); } +inline axis2 UI_TopLayoutAxis(void) { ui *UI = UI_GetState(); return(UI->Stacks.LayoutAxisStack[UI->Stacks.LayoutAxisStackUsed - 1]); } +inline r32 UI_TopCornerRadius(void) { ui *UI = UI_GetState(); return(UI->Stacks.CornerRadiusStack[UI->Stacks.CornerRadiusStackUsed - 1]); } +inline font_id UI_TopFont(void) { ui *UI = UI_GetState(); return(UI->Stacks.FontStack[UI->Stacks.FontStackUsed - 1]); } +inline r32 UI_TopFontSize(void) { ui *UI = UI_GetState(); return(UI->Stacks.FontSizeStack[UI->Stacks.FontSizeStackUsed - 1]); } +inline r32 UI_TopOffsetX(void) { ui *UI = UI_GetState(); return(UI->Stacks.OffsetXStack[UI->Stacks.OffsetXStackUsed - 1]); } +inline r32 UI_TopOffsetY(void) { ui *UI = UI_GetState(); return(UI->Stacks.OffsetYStack[UI->Stacks.OffsetYStackUsed - 1]); } +#define UI_Parent(Element) DeferLoop(UI_PushParent(Element), UI_PopParent()) +#define UI_Width(Element) DeferLoop(UI_PushWidth(Element), UI_PopWidth()) +#define UI_Height(Element) DeferLoop(UI_PushHeight(Element), UI_PopHeight()) +#define UI_FixedX(Element) DeferLoop(UI_PushFixedX(Element), UI_PopFixedX()) +#define UI_FixedY(Element) DeferLoop(UI_PushFixedY(Element), UI_PopFixedY()) +#define UI_TextColor(Element) DeferLoop(UI_PushTextColor(Element), UI_PopTextColor()) +#define UI_BackgroundColor(Element) DeferLoop(UI_PushBackgroundColor(Element), UI_PopBackgroundColor()) +#define UI_BorderColor(Element) DeferLoop(UI_PushBorderColor(Element), UI_PopBorderColor()) +#define UI_BorderThickness(Element) DeferLoop(UI_PushBorderThickness(Element), UI_PopBorderThickness()) +#define UI_LayoutAxis(Element) DeferLoop(UI_PushLayoutAxis(Element), UI_PopLayoutAxis()) +#define UI_CornerRadius(Element) DeferLoop(UI_PushCornerRadius(Element), UI_PopCornerRadius()) +#define UI_Font(Element) DeferLoop(UI_PushFont(Element), UI_PopFont()) +#define UI_FontSize(Element) DeferLoop(UI_PushFontSize(Element), UI_PopFontSize()) +#define UI_OffsetX(Element) DeferLoop(UI_PushOffsetX(Element), UI_PopOffsetX()) +#define UI_OffsetY(Element) DeferLoop(UI_PushOffsetY(Element), UI_PopOffsetY()) + +static void UI_ApplyStyles(ui_box *Box) +{ +ui *UI = UI_GetState(); +Assert(UI->Stacks.ParentStackUsed > 0); Box->Parent = UI->Stacks.ParentStack[UI->Stacks.ParentStackUsed - 1]; if(UI->Stacks.AutoPopParent) { UI_PopParent(); UI->Stacks.AutoPopParent = false; } +Assert(UI->Stacks.WidthStackUsed > 0); Box->SemanticSize[Axis2_X] = UI->Stacks.WidthStack[UI->Stacks.WidthStackUsed - 1]; if(UI->Stacks.AutoPopWidth) { UI_PopWidth(); UI->Stacks.AutoPopWidth = false; } +Assert(UI->Stacks.HeightStackUsed > 0); Box->SemanticSize[Axis2_Y] = UI->Stacks.HeightStack[UI->Stacks.HeightStackUsed - 1]; if(UI->Stacks.AutoPopHeight) { UI_PopHeight(); UI->Stacks.AutoPopHeight = false; } +Assert(UI->Stacks.FixedXStackUsed > 0); Box->FixedP.E[Axis2_X] = UI->Stacks.FixedXStack[UI->Stacks.FixedXStackUsed - 1]; if(UI->Stacks.AutoPopFixedX) { UI_PopFixedX(); UI->Stacks.AutoPopFixedX = false; } +Assert(UI->Stacks.FixedYStackUsed > 0); Box->FixedP.E[Axis2_Y] = UI->Stacks.FixedYStack[UI->Stacks.FixedYStackUsed - 1]; if(UI->Stacks.AutoPopFixedY) { UI_PopFixedY(); UI->Stacks.AutoPopFixedY = false; } +Assert(UI->Stacks.TextColorStackUsed > 0); Box->TextColor = UI->Stacks.TextColorStack[UI->Stacks.TextColorStackUsed - 1]; if(UI->Stacks.AutoPopTextColor) { UI_PopTextColor(); UI->Stacks.AutoPopTextColor = false; } +Assert(UI->Stacks.BackgroundColorStackUsed > 0); Box->BackgroundColor = UI->Stacks.BackgroundColorStack[UI->Stacks.BackgroundColorStackUsed - 1]; if(UI->Stacks.AutoPopBackgroundColor) { UI_PopBackgroundColor(); UI->Stacks.AutoPopBackgroundColor = false; } +Assert(UI->Stacks.BorderColorStackUsed > 0); Box->BorderColor = UI->Stacks.BorderColorStack[UI->Stacks.BorderColorStackUsed - 1]; if(UI->Stacks.AutoPopBorderColor) { UI_PopBorderColor(); UI->Stacks.AutoPopBorderColor = false; } +Assert(UI->Stacks.BorderThicknessStackUsed > 0); Box->BorderThickness = UI->Stacks.BorderThicknessStack[UI->Stacks.BorderThicknessStackUsed - 1]; if(UI->Stacks.AutoPopBorderThickness) { UI_PopBorderThickness(); UI->Stacks.AutoPopBorderThickness = false; } +Assert(UI->Stacks.LayoutAxisStackUsed > 0); Box->LayoutAxis = UI->Stacks.LayoutAxisStack[UI->Stacks.LayoutAxisStackUsed - 1]; if(UI->Stacks.AutoPopLayoutAxis) { UI_PopLayoutAxis(); UI->Stacks.AutoPopLayoutAxis = false; } +Assert(UI->Stacks.CornerRadiusStackUsed > 0); Box->CornerRadius = UI->Stacks.CornerRadiusStack[UI->Stacks.CornerRadiusStackUsed - 1]; if(UI->Stacks.AutoPopCornerRadius) { UI_PopCornerRadius(); UI->Stacks.AutoPopCornerRadius = false; } +Assert(UI->Stacks.FontStackUsed > 0); Box->Font = UI->Stacks.FontStack[UI->Stacks.FontStackUsed - 1]; if(UI->Stacks.AutoPopFont) { UI_PopFont(); UI->Stacks.AutoPopFont = false; } +Assert(UI->Stacks.FontSizeStackUsed > 0); Box->FontSize = UI->Stacks.FontSizeStack[UI->Stacks.FontSizeStackUsed - 1]; if(UI->Stacks.AutoPopFontSize) { UI_PopFontSize(); UI->Stacks.AutoPopFontSize = false; } +Assert(UI->Stacks.OffsetXStackUsed > 0); Box->Offset.x = UI->Stacks.OffsetXStack[UI->Stacks.OffsetXStackUsed - 1]; if(UI->Stacks.AutoPopOffsetX) { UI_PopOffsetX(); UI->Stacks.AutoPopOffsetX = false; } +Assert(UI->Stacks.OffsetYStackUsed > 0); Box->Offset.y = UI->Stacks.OffsetYStack[UI->Stacks.OffsetYStackUsed - 1]; if(UI->Stacks.AutoPopOffsetY) { UI_PopOffsetY(); UI->Stacks.AutoPopOffsetY = false; } +} + diff --git a/code/generated/vn_ui.meta.h b/code/generated/vn_ui.meta.h new file mode 100644 index 0000000..a32695f --- /dev/null +++ b/code/generated/vn_ui.meta.h @@ -0,0 +1,19 @@ +struct ui_style_stacks +{ +ui_box * ParentStack[64]; s32 ParentStackUsed; b32 AutoPopParent; +ui_size WidthStack[64]; s32 WidthStackUsed; b32 AutoPopWidth; +ui_size HeightStack[64]; s32 HeightStackUsed; b32 AutoPopHeight; +r32 FixedXStack[64]; s32 FixedXStackUsed; b32 AutoPopFixedX; +r32 FixedYStack[64]; s32 FixedYStackUsed; b32 AutoPopFixedY; +v4 TextColorStack[64]; s32 TextColorStackUsed; b32 AutoPopTextColor; +v4 BackgroundColorStack[64]; s32 BackgroundColorStackUsed; b32 AutoPopBackgroundColor; +v4 BorderColorStack[64]; s32 BorderColorStackUsed; b32 AutoPopBorderColor; +r32 BorderThicknessStack[64]; s32 BorderThicknessStackUsed; b32 AutoPopBorderThickness; +axis2 LayoutAxisStack[64]; s32 LayoutAxisStackUsed; b32 AutoPopLayoutAxis; +r32 CornerRadiusStack[64]; s32 CornerRadiusStackUsed; b32 AutoPopCornerRadius; +font_id FontStack[64]; s32 FontStackUsed; b32 AutoPopFont; +r32 FontSizeStack[64]; s32 FontSizeStackUsed; b32 AutoPopFontSize; +r32 OffsetXStack[64]; s32 OffsetXStackUsed; b32 AutoPopOffsetX; +r32 OffsetYStack[64]; s32 OffsetYStackUsed; b32 AutoPopOffsetY; +}; + diff --git a/code/opengl_render.cpp b/code/opengl_render.cpp index b650490..e1087a8 100644 --- a/code/opengl_render.cpp +++ b/code/opengl_render.cpp @@ -255,7 +255,7 @@ flat out s32 TextureIndex; SourceP = In_SourceP; - v2 ScreenP = V2(DestP.x / Uniform_Resolution.x, DestP.y / Uniform_Resolution.y); + v2 ScreenP = DestP / Uniform_Resolution;; ScreenP = ScreenP*2 - 1; ScreenP.y = -ScreenP.y; @@ -290,7 +290,7 @@ in v4 Color; R"GLSL( out v4 Out_Color; - + r32 RoundedRect(v2 P, v2 Center, v2 HalfSize, r32 r) { v2 d2 = AbsoluteValue(Center - P) - HalfSize + r; @@ -416,6 +416,11 @@ static opengl_context OpenGL_SetupContext(vn_render_commands *RenderCommands, um RenderCommands->MaxQuadIndexCount = MAX_QUAD_COUNT*6; RenderCommands->QuadIndexBase = (s32 *)OpenGL_AllocateMemory(RenderCommands->MaxQuadIndexCount*sizeof(s32)); +#if 0 + RenderCommands->MaxInstancedQuadCount = MAX_QUAD_COUNT; + RenderCommands->InstancedQuadBase = (instanced_quad *)OpenGL_AllocateMemory(RenderCommands->MaxInstancedQuadCount*sizeof(instanced_quad)); +#endif + Context.QuadProgram = OpenGL_CompileQuadProgram(); glGenBuffers(1, &Context.VertexBuffer); @@ -453,6 +458,9 @@ static void OpenGL_BeginFrame(vn_render_commands *RenderCommands, v2 RenderDim) RenderCommands->PushBufferAt = RenderCommands->PushBufferBase; RenderCommands->QuadVertexCount = 0; RenderCommands->QuadIndexCount = 0; +#if 0 + RenderCommands->InstancedQuadCount = 0; +#endif RenderCommands->RenderDim = RenderDim; } @@ -518,6 +526,27 @@ static void OpenGL_EndFrame(opengl_context *Context, vn_render_commands *RenderC glBindTexture(GL_TEXTURE_2D, 0); } break; +#if 0 + case Render_Command_render_command_instanced_quads: + { + render_command_quads *Command = (render_command_quads *)PushBufferAt; + PushBufferAt += sizeof(*Command); + + for(s32 TextureIndex = 0; + TextureIndex < Command->TexturesUsed; + ++TextureIndex) + { + opengl_texture Texture = OpenGL_GetTextureFromHandle(Command->Textures[TextureIndex]); + glActiveTexture(GL_TEXTURE0 + TextureIndex); + glBindTexture(GL_TEXTURE_2D, Texture.ID); + } + + glDrawElements(GL_TRIANGLES, Command->QuadCount*6, GL_UNSIGNED_INT, 0); + + glBindTexture(GL_TEXTURE_2D, 0); + } break; +#endif + case Render_Command_render_command_clip: { render_command_clip *Command = (render_command_clip *)PushBufferAt; diff --git a/code/opengl_render.h b/code/opengl_render.h index 344f826..0a927cd 100644 --- a/code/opengl_render.h +++ b/code/opengl_render.h @@ -4,7 +4,7 @@ #define OPENGL_RENDER_H #include "vn_opengl_defines.h" -#include "generated/vn_opengl_functions.h" +#include "generated/vn_opengl_functions.meta.h" struct opengl_texture { diff --git a/code/third_party/codegen/codegen.c b/code/third_party/codegen/codegen.c new file mode 100644 index 0000000..57f2a14 --- /dev/null +++ b/code/third_party/codegen/codegen.c @@ -0,0 +1,188 @@ +#define _CRT_SECURE_NO_WARNINGS 1 + +#include + +#include "../metadesk/md.h" +#include "../metadesk/md.c" +#include "../meow_hash_x64_aesni.h" + +#include "codegen_embed.h" +#include "codegen_table.h" + +#include "codegen.h" + +#include "codegen_embed.c" +#include "codegen_table.c" + +//////////////////////////////// +//~ rjf: Helpers + +static CG_FilePair +CG_FilePairFromNode(MD_Node *node) +{ + CG_FilePair result = {0}; + MD_CodeLoc loc = MD_CodeLocFromNode(node); + MD_String8 filename = loc.filename; + MD_b32 found = 0; + for(int i = 0; i < cg_file_pair_count; i += 1) + { + if(MD_S8Match(filename, cg_file_pairs[i].src_filename, 0)) + { + result = cg_file_pairs[i]; + found = 1; + break; + } + } + if(found == 0) + { + MD_String8 folder = MD_PathChopLastSlash(filename); + MD_String8 layer_name = MD_PathChopLastPeriod(MD_PathSkipLastSlash(loc.filename)); + MD_String8 gen_folder = MD_S8Fmt(cg_arena, "%.*s/generated", MD_S8VArg(folder)); + MD_String8 h_filename = MD_S8Fmt(cg_arena, "%.*s/%.*s.meta.h", MD_S8VArg(gen_folder), MD_S8VArg(layer_name)); + MD_String8 c_filename = MD_S8Fmt(cg_arena, "%.*s/%.*s.meta.c", MD_S8VArg(gen_folder), MD_S8VArg(layer_name)); + result.src_filename = filename; + result.h = fopen((char *)h_filename.str, "w"); + result.c = fopen((char *)c_filename.str, "w"); + cg_file_pairs[cg_file_pair_count] = result; + cg_file_pair_count += 1; + } + return result; +} + +static FILE * +CG_FileFromNodePair(MD_Node *node, CG_FilePair *pair) +{ + FILE *result = pair->h; + if(MD_NodeHasTag(node, MD_S8Lit("c"), MD_StringMatchFlag_CaseInsensitive)) + { + result = pair->c; + } + return result; +} + +static void +CG_CloseAllFiles(void) +{ + for(int i = 0; i < cg_file_pair_count; i += 1) + { + fclose(cg_file_pairs[i].h); + fclose(cg_file_pairs[i].c); + } +} + +static void +CG_GenerateMultilineStringAsCLiteral(FILE *file, MD_String8 string) +{ + fprintf(file, "\"\"\n\""); + for(MD_u64 i = 0; i < string.size; i += 1) + { + if(string.str[i] == '\n') + { + fprintf(file, "\\n\"\n\""); + } + else if(string.str[i] == '\r') + { + continue; + } + else + { + fprintf(file, "%c", string.str[i]); + } + } + fprintf(file, "\"\n"); +} + +static MD_String8 +CG_EscapedFromString(MD_Arena *arena, MD_String8 string) +{ + MD_ArenaTemp scratch = MD_GetScratch(&arena, 1); + MD_String8List strs = {0}; + MD_b32 escaped = 0; + MD_u64 start = 0; + for(MD_u64 idx = 0; idx <= string.size; idx += 1) + { + if(idx < string.size && escaped) + { + escaped = 0; + start = idx+1; + MD_u8 replace_char = 0; + switch(string.str[idx]) + { + default: break; + case 'a': replace_char = 0x07; break; + case 'b': replace_char = 0x08; break; + case 'e': replace_char = 0x1b; break; + case 'f': replace_char = 0x0c; break; + case 'n': replace_char = 0x0a; break; + case 'r': replace_char = 0x0d; break; + case 't': replace_char = 0x09; break; + case 'v': replace_char = 0x0b; break; + case '\\': replace_char = 0x5c; break; + case '\'': replace_char = 0x27; break; + case '\"': replace_char = 0x22; break; + case '\?': replace_char = 0x3f; break; + } + if(replace_char) + { + MD_String8 string = MD_S8Copy(scratch.arena, MD_S8(&replace_char, 1)); + MD_S8ListPush(scratch.arena, &strs, string); + } + } + else if(idx == string.size || string.str[idx] == '\\') + { + escaped = (string.str[idx] == '\\'); + MD_String8 part = MD_S8Substring(string, start, idx); + MD_S8ListPush(scratch.arena, &strs, part); + start = idx; + } + } + MD_String8 result = MD_S8ListJoin(arena, strs, 0); + MD_ReleaseScratch(scratch); + return result; +} + +//////////////////////////////// +//~ rjf: Entry Point + +int main(int argument_count, char **arguments) +{ + cg_arena = MD_ArenaAlloc(); + + //- rjf: parse command line + MD_String8List options = MD_StringListFromArgCV(cg_arena, argument_count, arguments); + MD_CmdLine cmdln = MD_MakeCmdLineFromOptions(cg_arena, options); + + //- rjf: parse all files + MD_Node *file_list = MD_MakeList(cg_arena); + for(MD_String8Node *n = cmdln.inputs.first; n != 0; n = n->next) + { + MD_String8 code_dir = n->string; + printf("searching %.*s for metacode...\n", MD_S8VArg(code_dir)); + MD_FileIter it = {0}; + MD_FileIterBegin(&it, code_dir); + for(MD_FileInfo info = {0};;) + { + info = MD_FileIterNext(cg_arena, &it); + if(info.filename.size == 0) + { + break; + } + if(!(info.flags & MD_FileFlag_Directory) && + MD_S8Match(MD_PathSkipLastPeriod(info.filename), MD_S8Lit("md"), MD_StringMatchFlag_CaseInsensitive)) + { + printf("parsing %.*s...\n", MD_S8VArg(info.filename)); + MD_String8 path = MD_S8Fmt(cg_arena, "%.*s/%.*s", MD_S8VArg(code_dir), MD_S8VArg(info.filename)); + MD_ParseResult parse = MD_ParseWholeFile(cg_arena, path); + MD_PushNewReference(cg_arena, file_list, parse.node); + } + } + MD_FileIterEnd(&it); + } + + //- rjf: send all parses to backends + CG_EMBED_Generate(file_list); + CG_TBL_Generate(file_list); + CG_CloseAllFiles(); + + return 0; +} diff --git a/code/third_party/codegen/codegen.h b/code/third_party/codegen/codegen.h new file mode 100644 index 0000000..569bda0 --- /dev/null +++ b/code/third_party/codegen/codegen.h @@ -0,0 +1,28 @@ +#ifndef METAPROGRAM_H +#define METAPROGRAM_H + +typedef struct CG_FilePair CG_FilePair; +struct CG_FilePair +{ + MD_String8 src_filename; + FILE *h; + FILE *c; +}; + +//////////////////////////////// +//~ rjf: Helpers + +static CG_FilePair CG_FilePairFromNode(MD_Node *node); +static FILE *CG_FileFromNodePair(MD_Node *node, CG_FilePair *pair); +static void CG_CloseAllFiles(void); +static void CG_GenerateMultilineStringAsCLiteral(FILE *file, MD_String8 string); +static MD_String8 CG_EscapedFromString(MD_Arena *arena, MD_String8 string); + +//////////////////////////////// +//~ rjf: Globals + +static MD_Arena *cg_arena = 0; +static int cg_file_pair_count = 0; +static CG_FilePair cg_file_pairs[4096] = {0}; + +#endif // METAPROGRAM_H diff --git a/code/third_party/codegen/codegen_embed.c b/code/third_party/codegen/codegen_embed.c new file mode 100644 index 0000000..ed6f4fd --- /dev/null +++ b/code/third_party/codegen/codegen_embed.c @@ -0,0 +1,40 @@ +static void +CG_EMBED_Generate(MD_Node *file_list) +{ + for(MD_EachNode(file_ref, file_list->first_child)) + { + MD_Node *file = MD_ResolveNodeFromReference(file_ref); + for(MD_EachNode(node, file->first_child)) + { + CG_FilePair f = CG_FilePairFromNode(node); + if(MD_NodeHasTag(node, MD_S8Lit("embed_string"), MD_StringMatchFlag_CaseInsensitive)) + { + FILE *file = CG_FileFromNodePair(node, &f); + fprintf(file, "read_only global String8 %.*s =\nStr8LitComp(", MD_S8VArg(node->string)); + CG_GenerateMultilineStringAsCLiteral(file, node->first_child->string); + fprintf(file, ");\n\n"); + } + if(MD_NodeHasTag(node, MD_S8Lit("embed_file"), MD_StringMatchFlag_CaseInsensitive)) + { + MD_String8 path = node->first_child->string; + MD_String8 file_data = MD_LoadEntireFile(cg_arena, path); + meow_u128 file_data_hash = MeowHash(MeowDefaultSeed, file_data.size, file_data.str); + FILE *file = CG_FileFromNodePair(node, &f); + fprintf(file, "read_only global U8 %.*s_data[] =\n{\n", MD_S8VArg(node->string)); + MD_u64 col = 0; + for(MD_u64 idx = 0; idx < file_data.size; idx += 1, col += 1) + { + fprintf(file, "%i,", (int)file_data.str[idx]); + if(col == 32) + { + fprintf(file,"\n"); + col = 0; + } + } + fprintf(file, "};\n\n"); + fprintf(file, "read_only global String8 %.*s = {%.*s_data, sizeof(%.*s_data)};\n\n", MD_S8VArg(node->string), MD_S8VArg(node->string), MD_S8VArg(node->string)); + fprintf(file, "read_only global U64 %.*s_hash[2] = {0x%" PRIx64 ", 0x%" PRIx64 "};\n\n", MD_S8VArg(node->string), MeowU64From(file_data_hash, 0), MeowU64From(file_data_hash, 1)); + } + } + } +} diff --git a/code/third_party/codegen/codegen_embed.h b/code/third_party/codegen/codegen_embed.h new file mode 100644 index 0000000..2ccd859 --- /dev/null +++ b/code/third_party/codegen/codegen_embed.h @@ -0,0 +1,6 @@ +/* date = May 5th 2023 11:04 am */ + +#ifndef CODEGEN_EMBED_H +#define CODEGEN_EMBED_H + +#endif // CODEGEN_EMBED_H diff --git a/code/third_party/codegen/codegen_table.c b/code/third_party/codegen/codegen_table.c new file mode 100644 index 0000000..7ae2f35 --- /dev/null +++ b/code/third_party/codegen/codegen_table.c @@ -0,0 +1,581 @@ +static MD_Map cg_tbl_top_level_node_grid_map = {0}; +static MD_Map cg_tbl_top_level_table_header_map = {0}; + +static MD_Map cg_tbl_layer_map_gen = {0}; +static MD_Map cg_tbl_layer_map_gen_enum = {0}; +static MD_Map cg_tbl_layer_map_gen_data = {0}; + +static MD_String8 cg_tbl_tag__table = MD_S8LitComp("table"); +static MD_String8 cg_tbl_tag__table_gen = MD_S8LitComp("table_gen"); +static MD_String8 cg_tbl_tag__table_gen_enum = MD_S8LitComp("table_gen_enum"); +static MD_String8 cg_tbl_tag__table_gen_data = MD_S8LitComp("table_gen_data"); + +static CG_NodeArray +CG_NodeArrayMake(MD_u64 count) +{ + CG_NodeArray result = {0}; + result.count = count; + result.v = MD_PushArrayZero(cg_arena, MD_Node *, result.count); + for(MD_u64 idx = 0; idx < result.count; idx += 1) + { + result.v[idx] = MD_NilNode(); + } + return result; +} + +static CG_NodeGrid +CG_GridFromNode(MD_Node *node) +{ + CG_NodeGrid grid = {0}; + + //- rjf: determine dimensions + MD_u64 row_count = 0; + MD_u64 column_count = 0; + { + for(MD_EachNode(row, node->first_child)) + { + row_count += 1; + MD_u64 cell_count_this_row = MD_ChildCountFromNode(row); + column_count = MD_Max(cell_count_this_row, column_count); + } + } + + //- rjf: allocate cells / row parents + { + grid.cells = CG_NodeArrayMake(row_count * column_count); + grid.row_parents = CG_NodeArrayMake(row_count); + } + + //- rjf: fill cells + { + MD_u64 row_idx = 0; + for(MD_EachNode(row, node->first_child)) + { + MD_u64 col_idx = 0; + grid.row_parents.v[row_idx] = row; + for(MD_EachNode(cell, row->first_child)) + { + grid.cells.v[row_idx * column_count + col_idx] = cell; + col_idx += 1; + } + row_idx += 1; + } + } + + return grid; +} + +static CG_TableHeader +CG_TableHeaderFromTag(MD_Node *tag) +{ + CG_TableHeader result = {0}; + result.column_count = MD_ChildCountFromNode(tag); + result.column_descs = MD_PushArrayZero(cg_arena, CG_ColumnDesc, result.column_count); + MD_u64 idx = 0; + for(MD_EachNode(column_node, tag->first_child)) + { + result.column_descs[idx].kind = CG_ColumnKind_Default; + result.column_descs[idx].name = column_node->string; + MD_Node *check_for_tag = MD_TagFromString(column_node, MD_S8Lit("check_for_tag"), 0); + if(!MD_NodeIsNil(check_for_tag)) + { + result.column_descs[idx].kind = CG_ColumnKind_CheckForTag; + result.column_descs[idx].tag_string = check_for_tag->first_child->string; + } + MD_Node *default_value = MD_TagFromString(column_node, MD_S8Lit("default"), 0); + if(!MD_NodeIsNil(default_value)) + { + result.column_descs[idx].default_value = default_value->first_child->string; + } + idx += 1; + } + return result; +} + +static MD_u64 +CG_RowChildIndexFromColumnName(CG_TableHeader *header, MD_String8 column_name) +{ + MD_u64 result = 0; + for(MD_u64 idx = 0; idx < header->column_count; idx += 1) + { + if(MD_S8Match(header->column_descs[idx].name, column_name, 0)) + { + break; + } + if(header->column_descs[idx].kind == CG_ColumnKind_Default) + { + result += 1; + } + } + return result; +} + +static MD_i64 +CG_TableExprEvaluate_Numeric(CG_ExpandInfo *info, MD_Expr *expr) +{ + MD_i64 result = 0; + CG_TableOp op = expr->op ? expr->op->op_id : CG_TableOp_Null; + switch(op) + { + case CG_TableOp_Equal: + case CG_TableOp_IsNotEqual: + { + MD_ArenaTemp scratch = MD_GetScratch(0, 0); + MD_String8List left_strs = {0}; + MD_String8List right_strs = {0}; + CG_TableExprEvaluate_String(info, expr->left, &left_strs); + CG_TableExprEvaluate_String(info, expr->right, &right_strs); + MD_String8 left_str = MD_S8ListJoin(scratch.arena, left_strs, 0); + MD_String8 right_str = MD_S8ListJoin(scratch.arena, right_strs, 0); + result = MD_S8Match(left_str, right_str, 0); + if(op == CG_TableOp_IsNotEqual) + { + result = !result; + } + MD_ReleaseScratch(scratch); + }break; + + case CG_TableOp_BooleanAnd: + case CG_TableOp_BooleanOr: + { + MD_i64 left = CG_TableExprEvaluate_Numeric(info, expr->left); + MD_i64 right = CG_TableExprEvaluate_Numeric(info, expr->right); + switch(op) + { + case CG_TableOp_BooleanAnd: result = left && right; break; + case CG_TableOp_BooleanOr: result = left || right; break; + } + }break; + } + return result; +} + +static void +CG_TableExprEvaluate_String(CG_ExpandInfo *info, MD_Expr *expr, MD_String8List *out) +{ + CG_TableOp op = expr->op ? expr->op->op_id : CG_TableOp_Null; + switch(op) + { + default: + case CG_TableOp_Null: + { + MD_S8ListPush(cg_arena, out, expr->md_node->string); + }break; + + case CG_TableOp_Dot: + { + MD_Expr *label_expr = expr->left; + MD_Expr *column_query_expr = expr->right; + MD_Node *label_node = label_expr->md_node; + MD_Node *column_query_node = column_query_expr->md_node; + MD_String8 label = label_node->string; + MD_String8 column_query = column_query_node->string; + MD_b32 column_query_is_by_expand_idx = MD_S8Match(column_query_node->string, MD_S8Lit("_it"), 0); + MD_b32 column_query_is_by_name = !column_query_is_by_expand_idx && column_query_node->flags & MD_NodeFlag_Identifier; + MD_b32 column_query_is_by_index = column_query_node->flags & MD_NodeFlag_Numeric; + + // rjf: find which expansion this label refers to, grab its iterator + CG_ExpandIter *iter = 0; + for(CG_ExpandIter *it = info->first_expand_iter; it != 0; it = it->next) + { + if(MD_S8Match(it->label, label, 0)) + { + iter = it; + break; + } + } + + // rjf: error on invalid label + if(iter == 0) + { + MD_PrintMessageFmt(stderr, MD_CodeLocFromNode(label_node), MD_MessageKind_Error, "Expansion label \"%S\" was not found as referring to a valid @expand tag.", label); + } + + // rjf: generate strings from iterator's table + if(iter != 0) + { + CG_NodeGrid *grid = iter->grid; + CG_TableHeader *header = iter->header; + MD_Node *row = grid->row_parents.v[iter->idx]; + + // rjf: grab the cell string given the row & column_query + MD_String8 cell_string = {0}; + { + // NOTE(rjf): expansion index counter + if(column_query_is_by_expand_idx) + { + MD_i64 index = iter->idx; + cell_string = MD_S8Fmt(cg_arena, "%" PRIu64 "", index); + } + + // NOTE(rjf): by-name index (look into table header) + else if(column_query_is_by_name && header != 0) + { + MD_u64 column_idx = 0; + CG_ColumnDesc *column = 0; + for(MD_u64 col_idx = 0; col_idx < header->column_count; col_idx += 1) + { + if(MD_S8Match(header->column_descs[col_idx].name, column_query, 0)) + { + column = &header->column_descs[col_idx]; + column_idx = col_idx; + break; + } + } + MD_u64 row_child_idx = CG_RowChildIndexFromColumnName(header, column_query); + + // rjf: error on invalid column + if(column == 0) + { + MD_PrintMessageFmt(stderr, MD_CodeLocFromNode(column_query_node), MD_MessageKind_Error, "Column query \"%S\" did not map to a valid column for expansion label \"%S\".", column_query, label); + } + + if(column != 0) + { + switch(column->kind) + { + default: + case CG_ColumnKind_Default: + { + MD_Node *cell_node = MD_ChildFromIndex(row, row_child_idx); + cell_string = cell_node->string; + if(MD_S8Match(cell_node->raw_string, MD_S8Lit("."), 0)) + { + cell_string = column->default_value; + } + }break; + + case CG_ColumnKind_CheckForTag: + { + MD_b32 has_tag = MD_NodeHasTag(row, column->tag_string, 0); + cell_string = has_tag ? MD_S8Lit("1") : MD_S8Lit("0"); + }break; + } + } + } + + // NOTE(rjf): by-index (grab nth child of row) + else if(column_query_is_by_index) + { + MD_i64 index = MD_CStyleIntFromString(column_query); + cell_string = MD_ChildFromIndex(row, index)->string; + } + } + + MD_S8ListPush(cg_arena, out, cell_string); + } + + }break; + + case CG_TableOp_Bump: + { + MD_u64 dst = MD_CStyleIntFromString(expr->unary_operand->md_node->string); + MD_u64 src = out->total_size; + MD_u64 spaces_to_print = dst - src; + if(dst > src) + { + for(MD_u64 space_idx = 0; space_idx < spaces_to_print; space_idx += 1) + { + MD_S8ListPush(cg_arena, out, MD_S8Lit(" ")); + } + } + }break; + + case CG_TableOp_CheckIfTrue: + { + MD_i64 check_val = CG_TableExprEvaluate_Numeric(info, expr->left); + if(check_val) + { + CG_TableExprEvaluate_String(info, expr->right, out); + } + }break; + + case CG_TableOp_Concat: + { + CG_TableExprEvaluate_String(info, expr->left, out); + CG_TableExprEvaluate_String(info, expr->right, out); + }break; + } +} + +static void +CG_LoopExpansionDimension(CG_ExpandIter *it, CG_ExpandInfo *info, MD_String8List *out) +{ + if(it->next) + { + for(MD_u64 idx = 0; idx < it->count; idx += 1) + { + it->idx = idx; + CG_LoopExpansionDimension(it->next, info, out); + } + } + else + { + for(MD_u64 idx = 0; idx < it->count; idx += 1) + { + it->idx = idx; + MD_String8List expansion_strs = {0}; + MD_u64 start_idx = 0; + for(MD_u64 char_idx = 0; char_idx <= info->strexpr.size; char_idx += 1) + { + MD_b32 is_expr_marker = info->strexpr.str[char_idx] == '$'; + + // rjf: push regular string contents + if(char_idx == info->strexpr.size || is_expr_marker) + { + MD_String8 normal_string_chunk = MD_S8Substring(info->strexpr, start_idx, char_idx); + MD_String8 escaped = CG_EscapedFromString(cg_arena, normal_string_chunk); + MD_S8ListPush(cg_arena, &expansion_strs, escaped); + } + + // rjf: handle expansion + if(is_expr_marker) + { + MD_String8 expr_string = MD_S8Skip(info->strexpr, char_idx+1); + { + MD_i64 paren_nest = 0; + for(MD_u64 expr_str_char_idx = 0; expr_str_char_idx < expr_string.size; expr_str_char_idx += 1) + { + if(expr_string.str[expr_str_char_idx] == '(') + { + paren_nest += 1; + } + else if(expr_string.str[expr_str_char_idx] == ')') + { + paren_nest -= 1; + if(paren_nest == 0) + { + expr_string.size = expr_str_char_idx+1; + break; + } + } + } + } + MD_ParseResult parse = MD_ParseOneNode(cg_arena, expr_string, 0); + MD_Node *node = parse.node; + MD_ExprParseResult expr_parse = MD_ExprParse(cg_arena, &info->expr_op_table, node->first_child, MD_NilNode()); + MD_Expr *expr = expr_parse.expr; + CG_TableExprEvaluate_String(info, expr, &expansion_strs); + MD_String8 parsed_string = MD_S8Substring(info->strexpr, char_idx+1, char_idx+1+parse.string_advance); + parsed_string = MD_S8ChopWhitespace(parsed_string); + start_idx = char_idx+1+parsed_string.size; + } + } + + // rjf: push expansion string to output list + MD_String8 expansion_str = MD_S8ListJoin(cg_arena, expansion_strs, 0); + MD_S8ListPush(cg_arena, out, expansion_str); + } + } +} + +static MD_String8List +CG_GenStringListFromNode(MD_ExprOprTable expr_op_table, MD_Node *gen) +{ + MD_String8List result = {0}; + MD_ArenaTemp scratch = MD_GetScratch(0, 0); + + for(MD_EachNode(strexpr, gen->first_child)) + { + //- rjf: build expansion iterator list + CG_ExpandIter *first_iter = 0; + CG_ExpandIter *last_iter = 0; + { + for(MD_EachNode(tag, strexpr->first_tag)) + { + if(MD_S8Match(tag->string, MD_S8Lit("expand"), 0)) + { + MD_Node *table_name_node = MD_ChildFromIndex(tag, 0); + MD_Node *label_node = MD_ChildFromIndex(tag, 1); + MD_String8 table_name = table_name_node->string; + MD_String8 label = label_node->string; + + // rjf: grab the table associated with table_name + CG_NodeGrid *grid = 0; + { + MD_MapSlot *slot = MD_MapLookup(&cg_tbl_top_level_node_grid_map, MD_MapKeyStr(table_name)); + if(slot != 0) + { + grid = slot->val; + } + } + + // rjf: grab the table header associated with table_name + CG_TableHeader *header = 0; + { + MD_MapSlot *slot = MD_MapLookup(&cg_tbl_top_level_table_header_map, MD_MapKeyStr(table_name)); + if(slot != 0) + { + header = slot->val; + } + } + + // rjf: make iterator node if we got a grid + if(grid != 0) + { + CG_ExpandIter *iter = MD_PushArrayZero(scratch.arena, CG_ExpandIter, 1); + MD_QueuePush(first_iter, last_iter, iter); + iter->grid = grid; + iter->header = header; + iter->label = label; + iter->count = grid->row_parents.count; + } + + // rjf: print out an error if grid is 0 + if(grid == 0) + { + MD_PrintMessageFmt(stderr, MD_CodeLocFromNode(tag), MD_MessageKind_Error, "Table \"%S\" was not found.", table_name); + } + + } + } + } + + //- rjf: generate string list for this strexpr & push to result + if(first_iter != 0) + { + CG_ExpandInfo info = {0}; + { + info.strexpr = strexpr->string; + info.first_expand_iter = first_iter; + info.expr_op_table = expr_op_table; + } + CG_LoopExpansionDimension(first_iter, &info, &result); + } + //- rjf: generate non-expansion strings + else + { + MD_String8 escaped = CG_EscapedFromString(cg_arena, strexpr->string); + MD_S8ListPush(cg_arena, &result, escaped); + } + + } + + MD_ReleaseScratch(scratch); + return result; +} + +static void +CG_TBL_Generate(MD_Node *file_list) +{ + //- rjf: initialize all maps + cg_tbl_top_level_node_grid_map = MD_MapMake(cg_arena); + cg_tbl_top_level_table_header_map = MD_MapMake(cg_arena); + cg_tbl_layer_map_gen = MD_MapMake(cg_arena); + cg_tbl_layer_map_gen_enum = MD_MapMake(cg_arena); + cg_tbl_layer_map_gen_data = MD_MapMake(cg_arena); + + //- rjf: build table expression operator table + MD_ExprOprTable table_expr_op_table = {0}; + { + MD_ExprOprList ops_list = {0}; + MD_ExprOprPush(cg_arena, &ops_list, MD_ExprOprKind_Binary, 10, MD_S8Lit("."), CG_TableOp_Dot, 0); + MD_ExprOprPush(cg_arena, &ops_list, MD_ExprOprKind_Prefix, 9, MD_S8Lit("=>"), CG_TableOp_Bump, 0); + MD_ExprOprPush(cg_arena, &ops_list, MD_ExprOprKind_Binary, 6, MD_S8Lit("??"), CG_TableOp_CheckIfTrue, 0); + MD_ExprOprPush(cg_arena, &ops_list, MD_ExprOprKind_Binary, 7, MD_S8Lit(".."), CG_TableOp_Concat, 0); + + MD_ExprOprPush(cg_arena, &ops_list, MD_ExprOprKind_Binary, 8, MD_S8Lit("=="), CG_TableOp_Equal, 0); + MD_ExprOprPush(cg_arena, &ops_list, MD_ExprOprKind_Binary, 8, MD_S8Lit("!="), CG_TableOp_IsNotEqual, 0); + MD_ExprOprPush(cg_arena, &ops_list, MD_ExprOprKind_Binary, 5, MD_S8Lit("&&"), CG_TableOp_BooleanAnd, 0); + MD_ExprOprPush(cg_arena, &ops_list, MD_ExprOprKind_Binary, 4, MD_S8Lit("||"), CG_TableOp_BooleanOr, 0); + table_expr_op_table = MD_ExprBakeOprTableFromList(cg_arena, &ops_list); + } + + //- rjf: gather phase + for(MD_EachNode(file_ref, file_list->first_child)) + { + MD_Node *file = MD_ResolveNodeFromReference(file_ref); + MD_String8 layer_name = file->string; + MD_MapKey layer_key = MD_MapKeyStr(layer_name); + for(MD_EachNode(node, file->first_child)) + { + MD_Node *table_tag = MD_TagFromString(node, cg_tbl_tag__table, 0); + if(!MD_NodeIsNil(table_tag)) + { + CG_NodeGrid *grid = MD_PushArrayZero(cg_arena, CG_NodeGrid, 1); + *grid = CG_GridFromNode(node); + MD_MapOverwrite(cg_arena, &cg_tbl_top_level_node_grid_map, MD_MapKeyStr(node->string), grid); + CG_TableHeader *header = MD_PushArrayZero(cg_arena, CG_TableHeader, 1); + *header = CG_TableHeaderFromTag(table_tag); + MD_MapOverwrite(cg_arena, &cg_tbl_top_level_table_header_map, MD_MapKeyStr(node->string), header); + } + if(MD_NodeHasTag(node, cg_tbl_tag__table_gen, 0)) + { + MD_MapInsert(cg_arena, &cg_tbl_layer_map_gen, layer_key, node); + } + if(MD_NodeHasTag(node, cg_tbl_tag__table_gen_enum, 0)) + { + MD_MapInsert(cg_arena, &cg_tbl_layer_map_gen_enum, layer_key, node); + } + if(MD_NodeHasTag(node, cg_tbl_tag__table_gen_data, 0)) + { + MD_MapInsert(cg_arena, &cg_tbl_layer_map_gen_data, layer_key, node); + } + } + } + + //- rjf: generation phase + for(MD_EachNode(file_ref, file_list->first_child)) + { + MD_Node *file = MD_ResolveNodeFromReference(file_ref); + MD_String8 layer_name = file->string; + MD_MapKey layer_key = MD_MapKeyStr(layer_name); + + //- rjf: generate all table enums + for(MD_MapSlot *slot = MD_MapLookup(&cg_tbl_layer_map_gen_enum, layer_key); + slot != 0; + slot = MD_MapScan(slot->next, layer_key)) + { + MD_Node *gen = (MD_Node *)slot->val; + CG_FilePair f = CG_FilePairFromNode(gen); + FILE *file = CG_FileFromNodePair(gen, &f); + fprintf(file, "enum %.*s\n{\n", MD_S8VArg(gen->string)); + MD_String8List gen_strings = CG_GenStringListFromNode(table_expr_op_table, gen); + MD_StringJoin join = { MD_S8Lit(""), MD_S8Lit("\n"), MD_S8Lit("") }; + MD_String8 gen_string = MD_S8ListJoin(cg_arena, gen_strings, &join); + fprintf(file, "%.*s", MD_S8VArg(gen_string)); + fprintf(file, "\n};\n\n"); + } + + //- rjf: generate all data tables + for(MD_MapSlot *slot = MD_MapLookup(&cg_tbl_layer_map_gen_data, layer_key); + slot != 0; + slot = MD_MapScan(slot->next, layer_key)) + { + MD_Node *gen = (MD_Node *)slot->val; + MD_Node *tag = MD_TagFromString(gen, cg_tbl_tag__table_gen_data, 0); + MD_Node *data_table_type_node = tag->first_child; + MD_String8 data_table_type = data_table_type_node->string; + MD_String8List gen_strings = CG_GenStringListFromNode(table_expr_op_table, gen); + + MD_String8 h_decl_specifier = MD_S8Lit("extern"); + + CG_FilePair f = CG_FilePairFromNode(gen); + fprintf(f.h, "%.*s %.*s %.*s[%" PRIu64 "];\n\n", MD_S8VArg(h_decl_specifier), MD_S8VArg(data_table_type), MD_S8VArg(gen->string), gen_strings.node_count); + + fprintf(f.c, "%.*s %.*s[%" PRIu64 "] =\n{\n", MD_S8VArg(data_table_type), MD_S8VArg(gen->string), gen_strings.node_count); + MD_StringJoin join = { MD_S8Lit(""), MD_S8Lit("\n"), MD_S8Lit("") }; + MD_String8 gen_string = MD_S8ListJoin(cg_arena, gen_strings, &join); + fprintf(f.c, "%.*s", MD_S8VArg(gen_string)); + fprintf(f.c, "\n};\n"); + fprintf(f.c, "\n"); + } + + //- rjf: generate all general generations + for(MD_MapSlot *slot = MD_MapLookup(&cg_tbl_layer_map_gen, layer_key); + slot != 0; + slot = MD_MapScan(slot->next, layer_key)) + { + MD_Node *gen = (MD_Node *)slot->val; + CG_FilePair f = CG_FilePairFromNode(gen); + FILE *file = CG_FileFromNodePair(gen, &f); + MD_String8List gen_strings = CG_GenStringListFromNode(table_expr_op_table, gen); + MD_StringJoin join = { MD_S8Lit(""), MD_S8Lit("\n"), MD_S8Lit("") }; + MD_String8 gen_string = MD_S8ListJoin(cg_arena, gen_strings, &join); + fprintf(file, "%.*s", MD_S8VArg(gen_string)); + fprintf(file, "\n\n"); + } + + } + +} diff --git a/code/third_party/codegen/codegen_table.h b/code/third_party/codegen/codegen_table.h new file mode 100644 index 0000000..bf8a6a6 --- /dev/null +++ b/code/third_party/codegen/codegen_table.h @@ -0,0 +1,93 @@ +#ifndef METAPROGRAM_TABLE_H +#define METAPROGRAM_TABLE_H + +typedef enum CG_TableOp +{ + CG_TableOp_Null, + + CG_TableOp_BeginStringOps, + CG_TableOp_Dot, + CG_TableOp_Bump, + CG_TableOp_CheckIfTrue, + CG_TableOp_Concat, + CG_TableOp_EndStringOps, + + CG_TableOp_BeginNumericOps, + CG_TableOp_Equal, + CG_TableOp_IsNotEqual, + CG_TableOp_BooleanAnd, + CG_TableOp_BooleanOr, + CG_TableOp_EndNumericOps, + + CG_TableOp_COUNT +} +CG_TableOp; + +typedef struct CG_NodeArray CG_NodeArray; +struct CG_NodeArray +{ + MD_u64 count; + MD_Node **v; +}; + +typedef struct CG_NodeGrid CG_NodeGrid; +struct CG_NodeGrid +{ + CG_NodeArray cells; + CG_NodeArray row_parents; +}; + +typedef enum CG_ColumnKind +{ + CG_ColumnKind_Default, + CG_ColumnKind_CheckForTag, + CG_ColumnKind_COUNT +} +CG_ColumnKind; + +typedef struct CG_ColumnDesc CG_ColumnDesc; +struct CG_ColumnDesc +{ + CG_ColumnKind kind; + MD_String8 name; + MD_String8 tag_string; + MD_String8 default_value; +}; + +typedef struct CG_TableHeader CG_TableHeader; +struct CG_TableHeader +{ + MD_u64 column_count; + CG_ColumnDesc *column_descs; +}; + +typedef struct CG_ExpandIter CG_ExpandIter; +struct CG_ExpandIter +{ + CG_ExpandIter *next; + CG_NodeGrid *grid; + CG_TableHeader *header; + MD_String8 label; + MD_u64 idx; + MD_u64 count; +}; + +typedef struct CG_ExpandInfo CG_ExpandInfo; +struct CG_ExpandInfo +{ + MD_String8 strexpr; + CG_ExpandIter *first_expand_iter; + MD_ExprOprTable expr_op_table; +}; + +static CG_NodeArray CG_NodeArrayMake(MD_u64 count); +static CG_NodeGrid CG_GridFromNode(MD_Node *node); +static CG_TableHeader CG_TableHeaderFromTag(MD_Node *tag); +static MD_u64 CG_RowChildIndexFromColumnName(CG_TableHeader *header, MD_String8 column_name); +static MD_i64 CG_TableExprEvaluate_Numeric(CG_ExpandInfo *info, MD_Expr *expr); +static void CG_TableExprEvaluate_String(CG_ExpandInfo *info, MD_Expr *expr, MD_String8List *out); +static void CG_LoopExpansionDimension(CG_ExpandIter *it, CG_ExpandInfo *info, MD_String8List *out); +static MD_String8List CG_GenStringListFromNode(MD_ExprOprTable expr_op_table, MD_Node *gen); +static void CG_TBL_Generate(MD_Node *file_list); + +#endif // METAPROGRAM_TABLE_H diff --git a/code/third_party/meow_hash_x64_aesni.h b/code/third_party/meow_hash_x64_aesni.h new file mode 100644 index 0000000..219bfab --- /dev/null +++ b/code/third_party/meow_hash_x64_aesni.h @@ -0,0 +1,744 @@ +/* ======================================================================== + + Meow - A Fast Non-cryptographic Hash + (C) Copyright 2018-2019 by Molly Rocket, Inc. (https://mollyrocket.com) + + See https://mollyrocket.com/meowhash for details. + + ======================================================================== + + zlib License + + (C) Copyright 2018-2019 Molly Rocket, Inc. + + 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. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + ======================================================================== + + FAQ + + Q: What is it? + + A: Meow is a 128-bit Level 3 hash taking 128 bytes of seed. It operates + at very high speeds on x64 processors, and potentially other processors + that provide accelerated AES instructions. + + Q: What is it GOOD for? + + A: Quickly hashing any amount of data for comparison purposes such as + block deduplication or change detection. It is fast on all buffer + sizes, and can generally be used anywhere you need fast Level 3 + hashing without worrying about how big or small the inputs tend to be. + + However, substantial speed improvements could be made over Meow + if you either a) know you are always hashing an exact, small number of bytes, + or b) can always supply a small number of bytes in a buffer padded to some + fixed multiple of 16. + + Q: What is it BAD for? + + A: Anything requiring Level 4 or Level 5 security guarantees (see + http://nohatcoder.dk/2019-05-19-1.html#level3). Also, note that + Meow is a new hash and has not had the extensive community + cryptanalysis necessary to ensure that it is not breakable down to + a lower level of hash, so you must do your due diligence in + deciding when and where to use Meow instead of a slower but + more extensively studied existing hash. We have tried to design + it to provide Level 3 security, but the possibility of the hash + being broken in the future always exists. + + Q: Why is it called the "Meow hash"? + + A: It is named after a character in Meow the Infinite + (https://meowtheinfinite.com) + + Q: Who wrote it? + + A: The final Meow Hash was created as a collaboration between + JACOB CHRISTIAN MUNCH-ANDERSEN (https://twitter.com/nohatcoder) and + CASEY MURATORI (https://caseymuratori.com). Casey wrote the original + implementation for use in processing large-footprint assets for the + game 1935 (https://molly1935.com). Jacob was the first to analyze + that implementation and determine the adversarial bit strength, which + was weaker than they would have liked. + + Following that, the two collaborated to figure out how the hash + could be strengthened without reducing Meow's 16 bytes/cycle + maximum theoretical throughput. Jacob created the hash candidates + and Casey did the performance validation. After a long and + exhaustive effort, Jacob found the unaligned aes/add/xor formulation + that forms the current Meow hash core. + + A number of valuable additions to Meow Hash were also contributed + by other great folks along the way: + + JEFF ROBERTS (https://radgametools.com) provided a super slick + way to handle the residual end-of-buffer bytes that dramatically + improved Meow's small hash performance. + + MARTINS MOZEIKO (https://matrins.ninja) ported Meow to ARM and + ANSI-C, and added the proper preprocessor dressing for clean + compilation on a variety of compiler configurations. + + FABIAN GIESEN (https://fgiesen.wordpress.com) analyzed many + performance oddities that came up during development, and + helped get the benchmarking working properly across a number + of platforms. + + ARAS PRANCKEVICIUS (https://aras-p.info) provided the allocation + shim for compilation on Mac OS X. + + ======================================================================== */ + +// +// IMPORTANT(casey): We are currently evaluating this hash construction as +// the final one for Meow Hash. If you find a way to produce collisions +// that should not be possible with a Level 3 hash, find significant performance +// problems, or see any bugs in this version, please be sure to report them +// to the Meow Hash GitHub as soon as possible. We would like to know as +// much as we can about the robustness and performance before committing to +// it as the final construction. +// + +#if !defined(MEOW_HASH_X64_AESNI_H) + +#define MEOW_HASH_VERSION 5 +#define MEOW_HASH_VERSION_NAME "0.5/calico" + +#if !defined(meow_u8) + +#if _MSC_VER +#if !defined(__clang__) +#define INSTRUCTION_REORDER_BARRIER _ReadWriteBarrier() +#else +#endif +#include +#else +#include +#endif + +#define meow_u8 char unsigned +#define meow_u64 long long unsigned +#define meow_u128 __m128i + +#if __x86_64__ || _M_AMD64 +#define meow_umm long long unsigned +#define MeowU64From(A, I) (_mm_extract_epi64((A), (I))) +#elif __i386__ || _M_IX86 +#define meow_umm int unsigned +#define MeowU64From(A, I) (*(meow_u64 *)&(A)) +#else +#error Cannot determine architecture to use! +#endif + +#define MeowU32From(A, I) (_mm_extract_epi32((A), (I))) +#define MeowHashesAreEqual(A, B) (_mm_movemask_epi8(_mm_cmpeq_epi8((A), (B))) == 0xFFFF) + +#if !defined INSTRUCTION_REORDER_BARRIER +#define INSTRUCTION_REORDER_BARRIER +#endif + +#if !defined MEOW_PAGESIZE +#define MEOW_PAGESIZE 4096 +#endif + +#if !defined MEOW_PREFETCH +#define MEOW_PREFETCH 4096 +#endif + +#if !defined MEOW_PREFETCH_LIMIT +#define MEOW_PREFETCH_LIMIT 0x3ff +#endif + +#endif + +#define prefetcht0(A) _mm_prefetch((char *)(A), _MM_HINT_T0) +#define movdqu(A, B) A = _mm_loadu_si128((__m128i *)(B)) +#define movdqu_mem(A, B) _mm_storeu_si128((__m128i *)(A), B) +#define movq(A, B) A = _mm_set_epi64x(0, B); +#define aesdec(A, B) A = _mm_aesdec_si128(A, B) +#define pshufb(A, B) A = _mm_shuffle_epi8(A, B) +#define pxor(A, B) A = _mm_xor_si128(A, B) +#define paddq(A, B) A = _mm_add_epi64(A, B) +#define pand(A, B) A = _mm_and_si128(A, B) +#define palignr(A, B, i) A = _mm_alignr_epi8(A, B, i) +#define pxor_clear(A, B) A = _mm_setzero_si128(); // NOTE(casey): pxor_clear is a nonsense thing that is only here because compilers don't detect xor(a, a) is clearing a :( + +#define MEOW_MIX_REG(r1, r2, r3, r4, r5, i1, i2, i3, i4) \ +aesdec(r1, r2); \ +INSTRUCTION_REORDER_BARRIER; \ +paddq(r3, i1); \ +pxor(r2, i2); \ +aesdec(r2, r4); \ +INSTRUCTION_REORDER_BARRIER; \ +paddq(r5, i3); \ +pxor(r4, i4); + +#define MEOW_MIX(r1, r2, r3, r4, r5, ptr) \ +MEOW_MIX_REG(r1, r2, r3, r4, r5, _mm_loadu_si128( (__m128i *) ((ptr) + 15) ), _mm_loadu_si128( (__m128i *) ((ptr) + 0) ), _mm_loadu_si128( (__m128i *) ((ptr) + 1) ), _mm_loadu_si128( (__m128i *) ((ptr) + 16) )) + +#define MEOW_SHUFFLE(r1, r2, r3, r4, r5, r6) \ +aesdec(r1, r4); \ +paddq(r2, r5); \ +pxor(r4, r6); \ +aesdec(r4, r2); \ +paddq(r5, r6); \ +pxor(r2, r3) + +#if MEOW_DUMP +struct meow_dump +{ + meow_u128 xmm[8]; + void *Ptr; + char const *Title; +}; +extern "C" meow_dump *MeowDumpTo; +meow_dump *MeowDumpTo; +#define MEOW_DUMP_STATE(T, xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, ptr) \ +if(MeowDumpTo) \ +{ \ +MeowDumpTo->xmm[0] = xmm0; \ +MeowDumpTo->xmm[1] = xmm1; \ +MeowDumpTo->xmm[2] = xmm2; \ +MeowDumpTo->xmm[3] = xmm3; \ +MeowDumpTo->xmm[4] = xmm4; \ +MeowDumpTo->xmm[5] = xmm5; \ +MeowDumpTo->xmm[6] = xmm6; \ +MeowDumpTo->xmm[7] = xmm7; \ +MeowDumpTo->Ptr = ptr; \ +MeowDumpTo->Title = T; \ +++MeowDumpTo; \ +} +#else +#define MEOW_DUMP_STATE(...) +#endif + +static meow_u8 MeowShiftAdjust[32] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; +static meow_u8 MeowMaskLen[32] = {255,255,255,255, 255,255,255,255, 255,255,255,255, 255,255,255,255, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0}; + +// NOTE(casey): The default seed is now a "nothing-up-our-sleeves" number for good measure. You may verify that it is just an encoding of Pi. +static meow_u8 MeowDefaultSeed[128] = +{ + 0x32, 0x43, 0xF6, 0xA8, 0x88, 0x5A, 0x30, 0x8D, + 0x31, 0x31, 0x98, 0xA2, 0xE0, 0x37, 0x07, 0x34, + 0x4A, 0x40, 0x93, 0x82, 0x22, 0x99, 0xF3, 0x1D, + 0x00, 0x82, 0xEF, 0xA9, 0x8E, 0xC4, 0xE6, 0xC8, + 0x94, 0x52, 0x82, 0x1E, 0x63, 0x8D, 0x01, 0x37, + 0x7B, 0xE5, 0x46, 0x6C, 0xF3, 0x4E, 0x90, 0xC6, + 0xCC, 0x0A, 0xC2, 0x9B, 0x7C, 0x97, 0xC5, 0x0D, + 0xD3, 0xF8, 0x4D, 0x5B, 0x5B, 0x54, 0x70, 0x91, + 0x79, 0x21, 0x6D, 0x5D, 0x98, 0x97, 0x9F, 0xB1, + 0xBD, 0x13, 0x10, 0xBA, 0x69, 0x8D, 0xFB, 0x5A, + 0xC2, 0xFF, 0xD7, 0x2D, 0xBD, 0x01, 0xAD, 0xFB, + 0x7B, 0x8E, 0x1A, 0xFE, 0xD6, 0xA2, 0x67, 0xE9, + 0x6B, 0xA7, 0xC9, 0x04, 0x5F, 0x12, 0xC7, 0xF9, + 0x92, 0x4A, 0x19, 0x94, 0x7B, 0x39, 0x16, 0xCF, + 0x70, 0x80, 0x1F, 0x2E, 0x28, 0x58, 0xEF, 0xC1, + 0x66, 0x36, 0x92, 0x0D, 0x87, 0x15, 0x74, 0xE6 +}; + +// +// NOTE(casey): Single block version +// + +static meow_u128 +MeowHash(void *Seed128Init, meow_umm Len, void *SourceInit) +{ + meow_u128 xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7; // NOTE(casey): xmm0-xmm7 are the hash accumulation lanes + meow_u128 xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15; // NOTE(casey): xmm8-xmm15 hold values to be appended (residual, length) + + meow_u8 *rax = (meow_u8 *)SourceInit; + meow_u8 *rcx = (meow_u8 *)Seed128Init; + + // + // NOTE(casey): Seed the eight hash registers + // + + movdqu(xmm0, rcx + 0x00); + movdqu(xmm1, rcx + 0x10); + movdqu(xmm2, rcx + 0x20); + movdqu(xmm3, rcx + 0x30); + + movdqu(xmm4, rcx + 0x40); + movdqu(xmm5, rcx + 0x50); + movdqu(xmm6, rcx + 0x60); + movdqu(xmm7, rcx + 0x70); + + MEOW_DUMP_STATE("Seed", xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, 0); + + // + // NOTE(casey): Hash all full 256-byte blocks + // + + meow_umm BlockCount = (Len >> 8); + if(BlockCount > MEOW_PREFETCH_LIMIT) + { + // NOTE(casey): For large input, modern Intel x64's can't hit full speed without prefetching, so we use this loop + while(BlockCount--) + { + prefetcht0(rax + MEOW_PREFETCH + 0x00); + prefetcht0(rax + MEOW_PREFETCH + 0x40); + prefetcht0(rax + MEOW_PREFETCH + 0x80); + prefetcht0(rax + MEOW_PREFETCH + 0xc0); + + MEOW_MIX(xmm0,xmm4,xmm6,xmm1,xmm2, rax + 0x00); + MEOW_MIX(xmm1,xmm5,xmm7,xmm2,xmm3, rax + 0x20); + MEOW_MIX(xmm2,xmm6,xmm0,xmm3,xmm4, rax + 0x40); + MEOW_MIX(xmm3,xmm7,xmm1,xmm4,xmm5, rax + 0x60); + MEOW_MIX(xmm4,xmm0,xmm2,xmm5,xmm6, rax + 0x80); + MEOW_MIX(xmm5,xmm1,xmm3,xmm6,xmm7, rax + 0xa0); + MEOW_MIX(xmm6,xmm2,xmm4,xmm7,xmm0, rax + 0xc0); + MEOW_MIX(xmm7,xmm3,xmm5,xmm0,xmm1, rax + 0xe0); + + rax += 0x100; + } + } + else + { + // NOTE(casey): For small input, modern Intel x64's can't hit full speed _with_ prefetching (because of port pressure), so we use this loop. + while(BlockCount--) + { + MEOW_MIX(xmm0,xmm4,xmm6,xmm1,xmm2, rax + 0x00); + MEOW_MIX(xmm1,xmm5,xmm7,xmm2,xmm3, rax + 0x20); + MEOW_MIX(xmm2,xmm6,xmm0,xmm3,xmm4, rax + 0x40); + MEOW_MIX(xmm3,xmm7,xmm1,xmm4,xmm5, rax + 0x60); + MEOW_MIX(xmm4,xmm0,xmm2,xmm5,xmm6, rax + 0x80); + MEOW_MIX(xmm5,xmm1,xmm3,xmm6,xmm7, rax + 0xa0); + MEOW_MIX(xmm6,xmm2,xmm4,xmm7,xmm0, rax + 0xc0); + MEOW_MIX(xmm7,xmm3,xmm5,xmm0,xmm1, rax + 0xe0); + + rax += 0x100; + } + } + + MEOW_DUMP_STATE("PostBlocks", xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, 0); + + // + // NOTE(casey): Load any less-than-32-byte residual + // + + pxor_clear(xmm9, xmm9); + pxor_clear(xmm11, xmm11); + + // + // TODO(casey): I need to put more thought into how the end-of-buffer stuff is actually working out here, + // because I _think_ it may be possible to remove the first branch (on Len8) and let the mask zero out the + // result, but it would take a little thought to make sure it couldn't read off the end of the buffer due + // to the & 0xf on the align computation. + // + + // NOTE(casey): First, we have to load the part that is _not_ 16-byte aligned + meow_u8 *Last = (meow_u8 *)SourceInit + (Len & ~0xf); + int unsigned Len8 = (Len & 0xf); + if(Len8) + { + // NOTE(casey): Load the mask early + movdqu(xmm8, &MeowMaskLen[0x10 - Len8]); + + meow_u8 *LastOk = (meow_u8*)((((meow_umm)(((meow_u8 *)SourceInit)+Len - 1)) | (MEOW_PAGESIZE - 1)) - 16); + int Align = (Last > LastOk) ? ((int)(meow_umm)Last) & 0xf : 0; + movdqu(xmm10, &MeowShiftAdjust[Align]); + movdqu(xmm9, Last - Align); + pshufb(xmm9, xmm10); + + // NOTE(jeffr): and off the extra bytes + pand(xmm9, xmm8); + } + + // NOTE(casey): Next, we have to load the part that _is_ 16-byte aligned + if(Len & 0x10) + { + xmm11 = xmm9; + movdqu(xmm9, Last - 0x10); + } + + // + // NOTE(casey): Construct the residual and length injests + // + + xmm8 = xmm9; + xmm10 = xmm9; + palignr(xmm8, xmm11, 15); + palignr(xmm10, xmm11, 1); + + // NOTE(casey): We have room for a 128-bit nonce and a 64-bit none here, but + // the decision was made to leave them zero'd so as not to confuse people + // about hwo to use them or what security implications they had. + pxor_clear(xmm12, xmm12); + pxor_clear(xmm13, xmm13); + pxor_clear(xmm14, xmm14); + movq(xmm15, Len); + palignr(xmm12, xmm15, 15); + palignr(xmm14, xmm15, 1); + + MEOW_DUMP_STATE("Residuals", xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15, 0); + + // NOTE(casey): To maintain the mix-down pattern, we always Meow Mix the less-than-32-byte residual, even if it was empty + MEOW_MIX_REG(xmm0, xmm4, xmm6, xmm1, xmm2, xmm8, xmm9, xmm10, xmm11); + + // NOTE(casey): Append the length, to avoid problems with our 32-byte padding + MEOW_MIX_REG(xmm1, xmm5, xmm7, xmm2, xmm3, xmm12, xmm13, xmm14, xmm15); + + MEOW_DUMP_STATE("PostAppend", xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, 0); + + // + // NOTE(casey): Hash all full 32-byte blocks + // + int unsigned LaneCount = (Len >> 5) & 0x7; + if(LaneCount == 0) goto MixDown; MEOW_MIX(xmm2,xmm6,xmm0,xmm3,xmm4, rax + 0x00); --LaneCount; + if(LaneCount == 0) goto MixDown; MEOW_MIX(xmm3,xmm7,xmm1,xmm4,xmm5, rax + 0x20); --LaneCount; + if(LaneCount == 0) goto MixDown; MEOW_MIX(xmm4,xmm0,xmm2,xmm5,xmm6, rax + 0x40); --LaneCount; + if(LaneCount == 0) goto MixDown; MEOW_MIX(xmm5,xmm1,xmm3,xmm6,xmm7, rax + 0x60); --LaneCount; + if(LaneCount == 0) goto MixDown; MEOW_MIX(xmm6,xmm2,xmm4,xmm7,xmm0, rax + 0x80); --LaneCount; + if(LaneCount == 0) goto MixDown; MEOW_MIX(xmm7,xmm3,xmm5,xmm0,xmm1, rax + 0xa0); --LaneCount; + if(LaneCount == 0) goto MixDown; MEOW_MIX(xmm0,xmm4,xmm6,xmm1,xmm2, rax + 0xc0); --LaneCount; + + // + // NOTE(casey): Mix the eight lanes down to one 128-bit hash + // + + MixDown: + + MEOW_DUMP_STATE("PostLanes", xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, 0); + + MEOW_SHUFFLE(xmm0, xmm1, xmm2, xmm4, xmm5, xmm6); + MEOW_SHUFFLE(xmm1, xmm2, xmm3, xmm5, xmm6, xmm7); + MEOW_SHUFFLE(xmm2, xmm3, xmm4, xmm6, xmm7, xmm0); + MEOW_SHUFFLE(xmm3, xmm4, xmm5, xmm7, xmm0, xmm1); + MEOW_SHUFFLE(xmm4, xmm5, xmm6, xmm0, xmm1, xmm2); + MEOW_SHUFFLE(xmm5, xmm6, xmm7, xmm1, xmm2, xmm3); + MEOW_SHUFFLE(xmm6, xmm7, xmm0, xmm2, xmm3, xmm4); + MEOW_SHUFFLE(xmm7, xmm0, xmm1, xmm3, xmm4, xmm5); + MEOW_SHUFFLE(xmm0, xmm1, xmm2, xmm4, xmm5, xmm6); + MEOW_SHUFFLE(xmm1, xmm2, xmm3, xmm5, xmm6, xmm7); + MEOW_SHUFFLE(xmm2, xmm3, xmm4, xmm6, xmm7, xmm0); + MEOW_SHUFFLE(xmm3, xmm4, xmm5, xmm7, xmm0, xmm1); + + MEOW_DUMP_STATE("PostMix", xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, 0); + + paddq(xmm0, xmm2); + paddq(xmm1, xmm3); + paddq(xmm4, xmm6); + paddq(xmm5, xmm7); + pxor(xmm0, xmm1); + pxor(xmm4, xmm5); + paddq(xmm0, xmm4); + + MEOW_DUMP_STATE("PostFold", xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, 0); + + return(xmm0); +} + +// +// NOTE(casey): Streaming construction +// + +typedef struct meow_state +{ + meow_u128 xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7; + meow_u64 TotalLengthInBytes; + + int unsigned BufferLen; + + meow_u8 Buffer[256]; + meow_u128 Pad[2]; // NOTE(casey): So we know we can over-read Buffer as necessary +} meow_state; + +static void +MeowBegin(meow_state *State, void *Seed128) +{ + meow_u8 *rcx = (meow_u8 *)Seed128; + + movdqu(State->xmm0, rcx + 0x00); + movdqu(State->xmm1, rcx + 0x10); + movdqu(State->xmm2, rcx + 0x20); + movdqu(State->xmm3, rcx + 0x30); + movdqu(State->xmm4, rcx + 0x40); + movdqu(State->xmm5, rcx + 0x50); + movdqu(State->xmm6, rcx + 0x60); + movdqu(State->xmm7, rcx + 0x70); + + MEOW_DUMP_STATE("Seed", State->xmm0, State->xmm1, State->xmm2, State->xmm3, State->xmm4, State->xmm5, State->xmm6, State->xmm7, 0); + + State->BufferLen = 0; + State->TotalLengthInBytes = 0; +} + +static void +MeowAbsorbBlocks(meow_state *State, meow_umm BlockCount, meow_u8 *rax) +{ + meow_u128 xmm0 = State->xmm0; + meow_u128 xmm1 = State->xmm1; + meow_u128 xmm2 = State->xmm2; + meow_u128 xmm3 = State->xmm3; + meow_u128 xmm4 = State->xmm4; + meow_u128 xmm5 = State->xmm5; + meow_u128 xmm6 = State->xmm6; + meow_u128 xmm7 = State->xmm7; + + if(BlockCount > MEOW_PREFETCH_LIMIT) + { + while(BlockCount--) + { + prefetcht0(rax + MEOW_PREFETCH + 0x00); + prefetcht0(rax + MEOW_PREFETCH + 0x40); + prefetcht0(rax + MEOW_PREFETCH + 0x80); + prefetcht0(rax + MEOW_PREFETCH + 0xc0); + + MEOW_MIX(xmm0,xmm4,xmm6,xmm1,xmm2, rax + 0x00); + MEOW_MIX(xmm1,xmm5,xmm7,xmm2,xmm3, rax + 0x20); + MEOW_MIX(xmm2,xmm6,xmm0,xmm3,xmm4, rax + 0x40); + MEOW_MIX(xmm3,xmm7,xmm1,xmm4,xmm5, rax + 0x60); + MEOW_MIX(xmm4,xmm0,xmm2,xmm5,xmm6, rax + 0x80); + MEOW_MIX(xmm5,xmm1,xmm3,xmm6,xmm7, rax + 0xa0); + MEOW_MIX(xmm6,xmm2,xmm4,xmm7,xmm0, rax + 0xc0); + MEOW_MIX(xmm7,xmm3,xmm5,xmm0,xmm1, rax + 0xe0); + + rax += 0x100; + } + } + else + { + while(BlockCount--) + { + MEOW_MIX(xmm0,xmm4,xmm6,xmm1,xmm2, rax + 0x00); + MEOW_MIX(xmm1,xmm5,xmm7,xmm2,xmm3, rax + 0x20); + MEOW_MIX(xmm2,xmm6,xmm0,xmm3,xmm4, rax + 0x40); + MEOW_MIX(xmm3,xmm7,xmm1,xmm4,xmm5, rax + 0x60); + MEOW_MIX(xmm4,xmm0,xmm2,xmm5,xmm6, rax + 0x80); + MEOW_MIX(xmm5,xmm1,xmm3,xmm6,xmm7, rax + 0xa0); + MEOW_MIX(xmm6,xmm2,xmm4,xmm7,xmm0, rax + 0xc0); + MEOW_MIX(xmm7,xmm3,xmm5,xmm0,xmm1, rax + 0xe0); + + rax += 0x100; + } + } + + State->xmm0 = xmm0; + State->xmm1 = xmm1; + State->xmm2 = xmm2; + State->xmm3 = xmm3; + State->xmm4 = xmm4; + State->xmm5 = xmm5; + State->xmm6 = xmm6; + State->xmm7 = xmm7; +} + +static void +MeowAbsorb(meow_state *State, meow_umm Len, void *SourceInit) +{ + State->TotalLengthInBytes += Len; + meow_u8 *Source = (meow_u8 *)SourceInit; + + // NOTE(casey): Handle any buffered residual + if(State->BufferLen) + { + int unsigned Fill = (sizeof(State->Buffer) - State->BufferLen); + if(Fill > Len) + { + Fill = (int unsigned)Len; + } + + Len -= Fill; + while(Fill--) + { + State->Buffer[State->BufferLen++] = *Source++; + } + + if(State->BufferLen == sizeof(State->Buffer)) + { + MeowAbsorbBlocks(State, 1, State->Buffer); + State->BufferLen = 0; + } + } + + // NOTE(casey): Handle any full blocks + meow_u64 BlockCount = (Len >> 8); + meow_u64 Advance = (BlockCount << 8); + MeowAbsorbBlocks(State, BlockCount, Source); + + Len -= Advance; + Source += Advance; + + // NOTE(casey): Store residual + while(Len--) + { + State->Buffer[State->BufferLen++] = *Source++; + } +} + +static meow_u128 +MeowEnd(meow_state *State, meow_u8 *Store128) +{ + meow_umm Len = State->TotalLengthInBytes; + + meow_u128 xmm0 = State->xmm0; + meow_u128 xmm1 = State->xmm1; + meow_u128 xmm2 = State->xmm2; + meow_u128 xmm3 = State->xmm3; + meow_u128 xmm4 = State->xmm4; + meow_u128 xmm5 = State->xmm5; + meow_u128 xmm6 = State->xmm6; + meow_u128 xmm7 = State->xmm7; + + meow_u128 xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15; + + meow_u8 *rax = State->Buffer; + + pxor_clear(xmm9, xmm9); + pxor_clear(xmm11, xmm11); + + meow_u8 *Last = (meow_u8 *)rax + (Len & 0xf0); + int unsigned Len8 = (Len & 0xf); + if(Len8) + { + movdqu(xmm8, &MeowMaskLen[0x10 - Len8]); + movdqu(xmm9, Last); + pand(xmm9, xmm8); + } + + if(Len & 0x10) + { + xmm11 = xmm9; + movdqu(xmm9, Last - 0x10); + } + + xmm8 = xmm9; + xmm10 = xmm9; + palignr(xmm8, xmm11, 15); + palignr(xmm10, xmm11, 1); + + pxor_clear(xmm12, xmm12); + pxor_clear(xmm13, xmm13); + pxor_clear(xmm14, xmm14); + movq(xmm15, Len); + palignr(xmm12, xmm15, 15); + palignr(xmm14, xmm15, 1); + + MEOW_DUMP_STATE("PostBlocks", xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, 0); + MEOW_DUMP_STATE("Residuals", xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15, 0); + + // NOTE(casey): To maintain the mix-down pattern, we always Meow Mix the less-than-32-byte residual, even if it was empty + MEOW_MIX_REG(xmm0, xmm4, xmm6, xmm1, xmm2, xmm8, xmm9, xmm10, xmm11); + + // NOTE(casey): Append the length, to avoid problems with our 32-byte padding + MEOW_MIX_REG(xmm1, xmm5, xmm7, xmm2, xmm3, xmm12, xmm13, xmm14, xmm15); + + MEOW_DUMP_STATE("PostAppend", xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, 0); + + // + // NOTE(casey): Hash all full 32-byte blocks + // + int unsigned LaneCount = (Len >> 5) & 0x7; + if(LaneCount == 0) goto MixDown; MEOW_MIX(xmm2,xmm6,xmm0,xmm3,xmm4, rax + 0x00); --LaneCount; + if(LaneCount == 0) goto MixDown; MEOW_MIX(xmm3,xmm7,xmm1,xmm4,xmm5, rax + 0x20); --LaneCount; + if(LaneCount == 0) goto MixDown; MEOW_MIX(xmm4,xmm0,xmm2,xmm5,xmm6, rax + 0x40); --LaneCount; + if(LaneCount == 0) goto MixDown; MEOW_MIX(xmm5,xmm1,xmm3,xmm6,xmm7, rax + 0x60); --LaneCount; + if(LaneCount == 0) goto MixDown; MEOW_MIX(xmm6,xmm2,xmm4,xmm7,xmm0, rax + 0x80); --LaneCount; + if(LaneCount == 0) goto MixDown; MEOW_MIX(xmm7,xmm3,xmm5,xmm0,xmm1, rax + 0xa0); --LaneCount; + if(LaneCount == 0) goto MixDown; MEOW_MIX(xmm0,xmm4,xmm6,xmm1,xmm2, rax + 0xc0); --LaneCount; + + // + // NOTE(casey): Mix the eight lanes down to one 128-bit hash + // + + MixDown: + + MEOW_DUMP_STATE("PostLanes", xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, 0); + + MEOW_SHUFFLE(xmm0, xmm1, xmm2, xmm4, xmm5, xmm6); + MEOW_SHUFFLE(xmm1, xmm2, xmm3, xmm5, xmm6, xmm7); + MEOW_SHUFFLE(xmm2, xmm3, xmm4, xmm6, xmm7, xmm0); + MEOW_SHUFFLE(xmm3, xmm4, xmm5, xmm7, xmm0, xmm1); + MEOW_SHUFFLE(xmm4, xmm5, xmm6, xmm0, xmm1, xmm2); + MEOW_SHUFFLE(xmm5, xmm6, xmm7, xmm1, xmm2, xmm3); + MEOW_SHUFFLE(xmm6, xmm7, xmm0, xmm2, xmm3, xmm4); + MEOW_SHUFFLE(xmm7, xmm0, xmm1, xmm3, xmm4, xmm5); + MEOW_SHUFFLE(xmm0, xmm1, xmm2, xmm4, xmm5, xmm6); + MEOW_SHUFFLE(xmm1, xmm2, xmm3, xmm5, xmm6, xmm7); + MEOW_SHUFFLE(xmm2, xmm3, xmm4, xmm6, xmm7, xmm0); + MEOW_SHUFFLE(xmm3, xmm4, xmm5, xmm7, xmm0, xmm1); + + MEOW_DUMP_STATE("PostMix", xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, 0); + + if(Store128) + { + movdqu_mem(Store128 + 0x00, xmm0); + movdqu_mem(Store128 + 0x10, xmm1); + movdqu_mem(Store128 + 0x20, xmm2); + movdqu_mem(Store128 + 0x30, xmm3); + movdqu_mem(Store128 + 0x40, xmm4); + movdqu_mem(Store128 + 0x50, xmm5); + movdqu_mem(Store128 + 0x60, xmm6); + movdqu_mem(Store128 + 0x70, xmm7); + } + + paddq(xmm0, xmm2); + paddq(xmm1, xmm3); + paddq(xmm4, xmm6); + paddq(xmm5, xmm7); + pxor(xmm0, xmm1); + pxor(xmm4, xmm5); + paddq(xmm0, xmm4); + + MEOW_DUMP_STATE("PostFold", xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, 0); + + return(xmm0); +} + +#undef INSTRUCTION_REORDER_BARRIER +#undef prefetcht0 +#undef movdqu +#undef movdqu_mem +#undef movq +#undef aesdec +#undef pshufb +#undef pxor +#undef paddq +#undef pand +#undef palignr +#undef pxor_clear +#undef MEOW_MIX +#undef MEOW_MIX_REG +#undef MEOW_SHUFFLE +#undef MEOW_DUMP_STATE + +// +// NOTE(casey): If you need to create your own seed from non-random data, you can use MeowExpandSeed +// to create a seed which you then store for repeated use. It is _expensive_ to generate the seed, +// so you do not want to do this every time you hash. You _only_ want to do it when you actually +// need to create a new seed. +// + +static void +MeowExpandSeed(meow_umm InputLen, void *Input, meow_u8 *SeedResult) +{ + meow_state State; + meow_u64 LengthTab = (meow_u64)InputLen; // NOTE(casey): We need to always injest 8-byte lengths exactly, even on 32-bit builds, to ensure identical results + meow_umm InjestCount = (256 / InputLen) + 2; + + MeowBegin(&State, MeowDefaultSeed); + MeowAbsorb(&State, sizeof(LengthTab), &LengthTab); + while(InjestCount--) + { + MeowAbsorb(&State, InputLen, Input); + } + MeowEnd(&State, SeedResult); +} + +#define MEOW_HASH_X64_AESNI_H +#endif diff --git a/code/third_party/metadesk/md.c b/code/third_party/metadesk/md.c new file mode 100644 index 0000000..0654411 --- /dev/null +++ b/code/third_party/metadesk/md.c @@ -0,0 +1,4410 @@ +// LICENSE AT END OF FILE (MIT). + +/* +** Overrides & Options Macros +** +** Overridable +** "basic types" ** REQUIRED +** #define/typedef MD_i8, MD_i16, MD_i32, MD_i64 +** #define/typedef MD_u8, MD_u16, MD_u32, MD_u64 +** #define/typedef MD_f32, MD_f64 +** +** "memset" ** REQUIRED +** #define MD_IMPL_Memset (void*, int, uint64) -> void* +** #define MD_IMPL_Memmove (void*, void*, uint64) -> void* +** +** "file iteration" ** OPTIONAL (required for the metadesk FileIter helpers to work) +** #define MD_IMPL_FileIterBegin (MD_FileIter*, MD_String8) -> Boolean +** #define MD_IMPL_FileIterNext (MD_Arena*, MD_FileIter*) -> MD_FileInfo +** #define MD_IMPL_FileIterEnd (MD_FileIter*) -> void +** +** "file load" ** OPTIONAL (required for MD_ParseWholeFile to work) +** #define MD_IMPL_LoadEntireFile (MD_Arena*, MD_String8 filename) -> MD_String8 +** +** "low level memory" ** OPTIONAL (required when relying on the default arenas) +** #define MD_IMPL_Reserve (uint64) -> void* +** #define MD_IMPL_Commit (void*, uint64) -> MD_b32 +** #define MD_IMPL_Decommit (void*, uint64) -> void +** #define MD_IMPL_Release (void*, uint64) -> void +** +** +** "arena" ** REQUIRED +** #define MD_IMPL_Arena (must set before including md.h) +** #define MD_IMPL_ArenaMinPos uint64 +** #define MD_IMPL_ArenaAlloc () -> MD_IMPL_Arena* +** #define MD_IMPL_ArenaRelease (MD_IMPL_Arena*) -> void +** #define MD_IMPL_ArenaGetPos (MD_IMPL_Arena*) -> uint64 +** #define MD_IMPL_ArenaPush (MD_IMPL_Arena*, uint64) -> void* +** #define MD_IMPL_ArenaPopTo (MD_IMPL_Arena*, uint64) -> void +** #define MD_IMPL_ArenaSetAutoAlign (MD_IMPL_Arena*, uint64) -> void +** +** "scratch" ** REQUIRED +** #define MD_IMPL_GetScratch (MD_IMPL_Arena**, uint64) -> MD_IMPL_Arena* +** "scratch constants" ** OPTIONAL (required for default scratch) +** #define MD_IMPL_ScratchCount uint64 [default 2] +** +** "sprintf" ** REQUIRED +** #define MD_IMPL_Vsnprintf (char*, uint64, char const*, va_list) -> uint64 +** +** Static Parameters to the Default Arena Implementation +** #define MD_DEFAULT_ARENA_RES_SIZE uint64 [default 64 megabytes] +** #define MD_DEFAULT_ARENA_CMT_SIZE uint64 [default 64 kilabytes] +** +** Default Implementation Controls +** These controls default to '1' i.e. 'enabled' +** #define MD_DEFAULT_BASIC_TYPES -> construct "basic types" from stdint.h header +** #define MD_DEFAULT_MEMSET -> construct "memset" from CRT +** #define MD_DEFAULT_FILE_ITER -> construct "file iteration" from OS headers +** #define MD_DEFAULT_MEMORY -> construct "low level memory" from OS headers +** #define MD_DEFAULT_ARENA -> construct "arena" from "low level memory" +** #define MD_DEFAULT_SCRATCH -> construct "scratch" from "arena" +** #define MD_DEFAULT_SPRINTF -> construct "vsnprintf" from internal implementaion +** +*/ + +#if !defined(MD_C) +#define MD_C + +//~///////////////////////////////////////////////////////////////////////////// +/////////////////////////// CRT Implementation ///////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +#if MD_DEFAULT_MEMSET + +#include +#include + +#if !defined(MD_IMPL_Memset) +# define MD_IMPL_Memset MD_CRT_Memset +#endif +#if !defined(MD_IMPL_Memmove) +# define MD_IMPL_Memmove MD_CRT_Memmove +#endif + +#define MD_CRT_Memset memset +#define MD_CRT_Memmove memmove + +#endif + +#if MD_DEFAULT_FILE_LOAD + +#include + +#if !defined(MD_IMPL_LoadEntireFile) +# define MD_IMPL_LoadEntireFile MD_CRT_LoadEntireFile +#endif + +MD_FUNCTION MD_String8 +MD_CRT_LoadEntireFile(MD_Arena *arena, MD_String8 filename) +{ + MD_ArenaTemp scratch = MD_GetScratch(&arena, 1); + MD_String8 file_contents = MD_ZERO_STRUCT; + MD_String8 filename_copy = MD_S8Copy(scratch.arena, filename); + FILE *file = fopen((char*)filename_copy.str, "rb"); + if(file != 0) + { + fseek(file, 0, SEEK_END); + MD_u64 file_size = ftell(file); + fseek(file, 0, SEEK_SET); + file_contents.str = MD_PushArray(arena, MD_u8, file_size+1); + if(file_contents.str) + { + file_contents.size = file_size; + fread(file_contents.str, 1, file_size, file); + file_contents.str[file_contents.size] = 0; + } + fclose(file); + } + MD_ReleaseScratch(scratch); + return file_contents; +} + +#endif + + +//~///////////////////////////////////////////////////////////////////////////// +/////////////////////////// Win32 Implementation /////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +//- win32 header +#if (MD_DEFAULT_FILE_ITER || MD_2DEFAULT_MEMORY) && MD_OS_WINDOWS +# include +# pragma comment(lib, "User32.lib") +#endif + +//- win32 "file iteration" +#if MD_DEFAULT_FILE_ITER && MD_OS_WINDOWS + +#if !defined(MD_IMPL_FileIterBegin) +# define MD_IMPL_FileIterBegin MD_WIN32_FileIterBegin +#endif +#if !defined(MD_IMPL_FileIterNext) +# define MD_IMPL_FileIterNext MD_WIN32_FileIterNext +#endif +#if !defined(MD_IMPL_FileIterEnd) +# define MD_IMPL_FileIterEnd MD_WIN32_FileIterEnd +#endif + +typedef struct MD_WIN32_FileIter{ + HANDLE state; + MD_u64 first; + WIN32_FIND_DATAW find_data; +} MD_WIN32_FileIter; + +MD_StaticAssert(sizeof(MD_FileIter) >= sizeof(MD_WIN32_FileIter), file_iter_size_check); + +static MD_b32 +MD_WIN32_FileIterBegin(MD_FileIter *it, MD_String8 path) +{ + //- init search + MD_ArenaTemp scratch = MD_GetScratch(0, 0); + + MD_u8 c = path.str[path.size - 1]; + MD_b32 need_star = (c == '/' || c == '\\'); + MD_String8 cpath = need_star ? MD_S8Fmt(scratch.arena, "%.*s*", MD_S8VArg(path)) : path; + MD_String16 cpath16 = MD_S16FromS8(scratch.arena, cpath); + + WIN32_FIND_DATAW find_data = MD_ZERO_STRUCT; + HANDLE state = FindFirstFileW((WCHAR*)cpath16.str, &find_data); + + MD_ReleaseScratch(scratch); + + //- fill results + MD_b32 result = !!state; + if (result) + { + MD_WIN32_FileIter *win32_it = (MD_WIN32_FileIter*)it; + win32_it->state = state; + win32_it->first = 1; + MD_MemoryCopy(&win32_it->find_data, &find_data, sizeof(find_data)); + } + return(result); +} + +static MD_FileInfo +MD_WIN32_FileIterNext(MD_Arena *arena, MD_FileIter *it) +{ + //- get low-level file info for this step + MD_b32 good = 0; + + MD_WIN32_FileIter *win32_it = (MD_WIN32_FileIter*)it; + WIN32_FIND_DATAW *find_data = &win32_it->find_data; + if (win32_it->first) + { + win32_it->first = 0; + good = 1; + } + else + { + good = FindNextFileW(win32_it->state, find_data); + } + + //- convert to MD_FileInfo + MD_FileInfo result = {0}; + if (good) + { + if (find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + result.flags |= MD_FileFlag_Directory; + } + MD_u16 *filename_base = (MD_u16*)find_data->cFileName; + MD_u16 *ptr = filename_base; + for (;*ptr != 0; ptr += 1); + MD_String16 filename16 = {filename_base, (MD_u64)(ptr - filename_base)}; + result.filename = MD_S8FromS16(arena, filename16); + result.file_size = ((((MD_u64)find_data->nFileSizeHigh) << 32) | + ((MD_u64)find_data->nFileSizeLow)); + } + return(result); +} + +static void +MD_WIN32_FileIterEnd(MD_FileIter *it) +{ + MD_WIN32_FileIter *win32_it = (MD_WIN32_FileIter*)it; + CloseHandle(win32_it->state); +} + +#endif + +//- win32 "low level memory" +#if MD_DEFAULT_MEMORY && MD_OS_WINDOWS + +#if !defined(MD_IMPL_Reserve) +# define MD_IMPL_Reserve MD_WIN32_Reserve +#endif +#if !defined(MD_IMPL_Commit) +# define MD_IMPL_Commit MD_WIN32_Commit +#endif +#if !defined(MD_IMPL_Decommit) +# define MD_IMPL_Decommit MD_WIN32_Decommit +#endif +#if !defined(MD_IMPL_Release) +# define MD_IMPL_Release MD_WIN32_Release +#endif + +static void* +MD_WIN32_Reserve(MD_u64 size) +{ + void *result = VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE); + return(result); +} + +static MD_b32 +MD_WIN32_Commit(void *ptr, MD_u64 size) +{ + MD_b32 result = (VirtualAlloc(ptr, size, MEM_COMMIT, PAGE_READWRITE) != 0); + return(result); +} + +static void +MD_WIN32_Decommit(void *ptr, MD_u64 size) +{ + VirtualFree(ptr, size, MEM_DECOMMIT); +} + +static void +MD_WIN32_Release(void *ptr, MD_u64 size) +{ + VirtualFree(ptr, 0, MEM_RELEASE); +} + +#endif + +//~///////////////////////////////////////////////////////////////////////////// +////////////////////////// Linux Implementation //////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +//- linux headers +#if (MD_DEFAULT_FILE_ITER || MD_DEFAULT_MEMORY) && MD_OS_LINUX +# include +# include +# include +# include +# include +# include +// NOTE(mal): To get these constants I need to #define _GNU_SOURCE, +// which invites non-POSIX behavior I'd rather avoid +# ifndef O_PATH +# define O_PATH 010000000 +# endif +# define AT_NO_AUTOMOUNT 0x800 +# define AT_SYMLINK_NOFOLLOW 0x100 +#endif + +//- linux "file iteration" +#if MD_DEFAULT_FILE_ITER && MD_OS_LINUX + +#if !defined(MD_IMPL_FileIterIncrement) +# define MD_IMPL_FileIterIncrement MD_LINUX_FileIterIncrement +#endif + +typedef struct MD_LINUX_FileIter MD_LINUX_FileIter; +struct MD_LINUX_FileIter +{ + int dir_fd; + DIR *dir; +}; +MD_StaticAssert(sizeof(MD_LINUX_FileIter) <= sizeof(MD_FileIter), file_iter_size_check); + +static MD_b32 +MD_LINUX_FileIterIncrement(MD_Arena *arena, MD_FileIter *opaque_it, MD_String8 path, + MD_FileInfo *out_info) +{ + MD_b32 result = 0; + + MD_LINUX_FileIter *it = (MD_LINUX_FileIter *)opaque_it; + if(it->dir == 0) + { + it->dir = opendir((char*)path.str); + it->dir_fd = open((char *)path.str, O_PATH|O_CLOEXEC); + } + + if(it->dir != 0 && it->dir_fd != -1) + { + struct dirent *dir_entry = readdir(it->dir); + if(dir_entry) + { + out_info->filename = MD_S8Fmt(arena, "%s", dir_entry->d_name); + out_info->flags = 0; + + struct stat st; + if(fstatat(it->dir_fd, dir_entry->d_name, &st, AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW) == 0) + { + if((st.st_mode & S_IFMT) == S_IFDIR) + { + out_info->flags |= MD_FileFlag_Directory; + } + out_info->file_size = st.st_size; + } + result = 1; + } + } + + if(result == 0) + { + if(it->dir != 0) + { + closedir(it->dir); + it->dir = 0; + } + if(it->dir_fd != -1) + { + close(it->dir_fd); + it->dir_fd = -1; + } + } + + return result; +} + +#endif + +//- linux "low level memory" +#if MD_DEFAULT_MEMORY && MD_OS_LINUX + +#if !defined(MD_IMPL_Reserve) +# define MD_IMPL_Reserve MD_LINUX_Reserve +#endif +#if !defined(MD_IMPL_Commit) +# define MD_IMPL_Commit MD_LINUX_Commit +#endif +#if !defined(MD_IMPL_Decommit) +# define MD_IMPL_Decommit MD_LINUX_Decommit +#endif +#if !defined(MD_IMPL_Release) +# define MD_IMPL_Release MD_LINUX_Release +#endif + +static void* +MD_LINUX_Reserve(MD_u64 size) +{ + void *result = mmap(0, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, (off_t)0); + return(result); +} + +static MD_b32 +MD_LINUX_Commit(void *ptr, MD_u64 size) +{ + MD_b32 result = (mprotect(ptr, size, PROT_READ|PROT_WRITE) == 0); + return(result); +} + +static void +MD_LINUX_Decommit(void *ptr, MD_u64 size) +{ + mprotect(ptr, size, PROT_NONE); + madvise(ptr, size, MADV_DONTNEED); +} + +static void +MD_LINUX_Release(void *ptr, MD_u64 size) +{ + munmap(ptr, size); +} + +#endif + +//~///////////////////////////////////////////////////////////////////////////// +///////////// MD Arena From Reserve/Commit/Decommit/Release //////////////////// +//////////////////////////////////////////////////////////////////////////////// + +#if MD_DEFAULT_ARENA + +#if !defined(MD_DEFAULT_ARENA_RES_SIZE) +# define MD_DEFAULT_ARENA_RES_SIZE (64 << 20) +#endif +#if !defined(MD_DEFAULT_ARENA_CMT_SIZE) +# define MD_DEFAULT_ARENA_CMT_SIZE (64 << 10) +#endif + +#define MD_DEFAULT_ARENA_VERY_BIG (MD_DEFAULT_ARENA_RES_SIZE - MD_IMPL_ArenaMinPos)/2 + +//- "low level memory" implementation check +#if !defined(MD_IMPL_Reserve) +# error Missing implementation for MD_IMPL_Reserve +#endif +#if !defined(MD_IMPL_Commit) +# error Missing implementation for MD_IMPL_Commit +#endif +#if !defined(MD_IMPL_Decommit) +# error Missing implementation for MD_IMPL_Decommit +#endif +#if !defined(MD_IMPL_Release) +# error Missing implementation for MD_IMPL_Release +#endif + +#define MD_IMPL_ArenaMinPos 64 +MD_StaticAssert(sizeof(MD_ArenaDefault) <= MD_IMPL_ArenaMinPos, arena_def_size_check); + +#define MD_IMPL_ArenaAlloc MD_ArenaDefaultAlloc +#define MD_IMPL_ArenaRelease MD_ArenaDefaultRelease +#define MD_IMPL_ArenaGetPos MD_ArenaDefaultGetPos +#define MD_IMPL_ArenaPush MD_ArenaDefaultPush +#define MD_IMPL_ArenaPopTo MD_ArenaDefaultPopTo +#define MD_IMPL_ArenaSetAutoAlign MD_ArenaDefaultSetAutoAlign + +static MD_ArenaDefault* +MD_ArenaDefaultAlloc__Size(MD_u64 cmt, MD_u64 res) +{ + MD_Assert(MD_IMPL_ArenaMinPos < cmt && cmt <= res); + MD_u64 cmt_clamped = MD_ClampTop(cmt, res); + MD_ArenaDefault *result = 0; + void *mem = MD_IMPL_Reserve(res); + if (MD_IMPL_Commit(mem, cmt_clamped)) + { + result = (MD_ArenaDefault*)mem; + result->prev = 0; + result->current = result; + result->base_pos = 0; + result->pos = MD_IMPL_ArenaMinPos; + result->cmt = cmt_clamped; + result->cap = res; + result->align = 8; + } + return(result); +} + +static MD_ArenaDefault* +MD_ArenaDefaultAlloc(void) +{ + MD_ArenaDefault *result = MD_ArenaDefaultAlloc__Size(MD_DEFAULT_ARENA_CMT_SIZE, + MD_DEFAULT_ARENA_RES_SIZE); + return(result); +} + +static void +MD_ArenaDefaultRelease(MD_ArenaDefault *arena) +{ + for (MD_ArenaDefault *node = arena->current, *prev = 0; + node != 0; + node = prev) + { + prev = node->prev; + MD_IMPL_Release(node, node->cap); + } +} + +static MD_u64 +MD_ArenaDefaultGetPos(MD_ArenaDefault *arena) +{ + MD_ArenaDefault *current = arena->current; + MD_u64 result = current->base_pos + current->pos; + return(result); +} + +static void* +MD_ArenaDefaultPush(MD_ArenaDefault *arena, MD_u64 size) +{ + // try to be fast! + MD_ArenaDefault *current = arena->current; + MD_u64 align = arena->align; + MD_u64 pos = current->pos; + MD_u64 pos_aligned = MD_AlignPow2(pos, align); + MD_u64 new_pos = pos_aligned + size; + void *result = (MD_u8*)current + pos_aligned; + current->pos = new_pos; + + // if it's not going to work do the slow path + if (new_pos > current->cmt) + { + result = 0; + current->pos = pos; + + // new chunk if necessary + if (new_pos > current->cap) + { + MD_ArenaDefault *new_arena = 0; + if (size > MD_DEFAULT_ARENA_VERY_BIG) + { + MD_u64 big_size_unrounded = size + MD_IMPL_ArenaMinPos; + MD_u64 big_size = MD_AlignPow2(big_size_unrounded, (4 << 10)); + new_arena = MD_ArenaDefaultAlloc__Size(big_size, big_size); + } + else + { + new_arena = MD_ArenaDefaultAlloc(); + } + + // link in new chunk & recompute new_pos + if (new_arena != 0) + { + new_arena->base_pos = current->base_pos + current->cap; + new_arena->prev = current; + current = new_arena; + pos_aligned = current->pos; + new_pos = pos_aligned + size; + } + } + + // move ahead if the current chunk has enough reserve + if (new_pos <= current->cap) + { + + // extend commit if necessary + if (new_pos > current->cmt) + { + MD_u64 new_cmt_unclamped = MD_AlignPow2(new_pos, MD_DEFAULT_ARENA_CMT_SIZE); + MD_u64 new_cmt = MD_ClampTop(new_cmt_unclamped, current->cap); + MD_u64 cmt_size = new_cmt - current->cmt; + if (MD_IMPL_Commit((MD_u8*)current + current->cmt, cmt_size)) + { + current->cmt = new_cmt; + } + } + + // move ahead if the current chunk has enough commit + if (new_pos <= current->cmt) + { + result = (MD_u8*)current + current->pos; + current->pos = new_pos; + } + } + } + + return(result); +} + +static void +MD_ArenaDefaultPopTo(MD_ArenaDefault *arena, MD_u64 pos) +{ + // pop chunks in the chain + MD_u64 pos_clamped = MD_ClampBot(MD_IMPL_ArenaMinPos, pos); + { + MD_ArenaDefault *node = arena->current; + for (MD_ArenaDefault *prev = 0; + node != 0 && node->base_pos >= pos; + node = prev) + { + prev = node->prev; + MD_IMPL_Release(node, node->cap); + } + arena->current = node; + } + + // reset the pos of the current + { + MD_ArenaDefault *current = arena->current; + MD_u64 local_pos_unclamped = pos - current->base_pos; + MD_u64 local_pos = MD_ClampBot(local_pos_unclamped, MD_IMPL_ArenaMinPos); + current->pos = local_pos; + } +} + +static void +MD_ArenaDefaultSetAutoAlign(MD_ArenaDefault *arena, MD_u64 align) +{ + arena->align = align; +} + +static void +MD_ArenaDefaultAbsorb(MD_ArenaDefault *arena, MD_ArenaDefault *sub_arena) +{ + MD_ArenaDefault *current = arena->current; + MD_u64 base_pos_shift = current->base_pos + current->cap; + for (MD_ArenaDefault *node = sub_arena->current; + node != 0; + node = node->prev) + { + node->base_pos += base_pos_shift; + } + sub_arena->prev = arena->current; + arena->current = sub_arena->current; +} + +#endif + +//- "arena" implementation checks +#if !defined(MD_IMPL_ArenaAlloc) +# error Missing implementation for MD_IMPL_ArenaAlloc +#endif +#if !defined(MD_IMPL_ArenaRelease) +# error Missing implementation for MD_IMPL_ArenaRelease +#endif +#if !defined(MD_IMPL_ArenaGetPos) +# error Missing implementation for MD_IMPL_ArenaGetPos +#endif +#if !defined(MD_IMPL_ArenaPush) +# error Missing implementation for MD_IMPL_ArenaPush +#endif +#if !defined(MD_IMPL_ArenaPopTo) +# error Missing implementation for MD_IMPL_ArenaPopTo +#endif +#if !defined(MD_IMPL_ArenaSetAutoAlign) +# error Missing implementation for MD_IMPL_ArenaSetAutoAlign +#endif +#if !defined(MD_IMPL_ArenaMinPos) +# error Missing implementation for MD_IMPL_ArenaMinPos +#endif + +//~///////////////////////////////////////////////////////////////////////////// +///////////////////////////// MD Scratch Pool ////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +#if MD_DEFAULT_SCRATCH + +#if !defined(MD_IMPL_ScratchCount) +# define MD_IMPL_ScratchCount 2llu +#endif + +#if !defined(MD_IMPL_GetScratch) +# define MD_IMPL_GetScratch MD_GetScratchDefault +#endif + +MD_THREAD_LOCAL MD_Arena *md_thread_scratch_pool[MD_IMPL_ScratchCount] = {0, 0}; + +static MD_Arena* +MD_GetScratchDefault(MD_Arena **conflicts, MD_u64 count) +{ + MD_Arena **scratch_pool = md_thread_scratch_pool; + if (scratch_pool[0] == 0) + { + MD_Arena **arena_ptr = scratch_pool; + for (MD_u64 i = 0; i < MD_IMPL_ScratchCount; i += 1, arena_ptr += 1) + { + *arena_ptr = MD_ArenaAlloc(); + } + } + MD_Arena *result = 0; + MD_Arena **arena_ptr = scratch_pool; + for (MD_u64 i = 0; i < MD_IMPL_ScratchCount; i += 1, arena_ptr += 1) + { + MD_Arena *arena = *arena_ptr; + MD_Arena **conflict_ptr = conflicts; + for (MD_u32 j = 0; j < count; j += 1, conflict_ptr += 1) + { + if (arena == *conflict_ptr) + { + arena = 0; + break; + } + } + if (arena != 0) + { + result = arena; + break; + } + } + return(result); +} + +#endif + +//~///////////////////////////////////////////////////////////////////////////// +//////////////////////// MD Library Implementation ///////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +#if MD_DEFAULT_SPRINTF +#define STB_SPRINTF_IMPLEMENTATION +#define STB_SPRINTF_DECORATE(name) md_stbsp_##name +#include "md_stb_sprintf.h" +#endif + + +//~ Nil Node Definition + +static MD_Node _md_nil_node = +{ + &_md_nil_node, // next + &_md_nil_node, // prev + &_md_nil_node, // parent + &_md_nil_node, // first_child + &_md_nil_node, // last_child + &_md_nil_node, // first_tag + &_md_nil_node, // last_tag + MD_NodeKind_Nil, // kind + 0, // flags + MD_ZERO_STRUCT, // string + MD_ZERO_STRUCT, // raw_string + 0, // at + &_md_nil_node, // ref_target + MD_ZERO_STRUCT, // prev_comment + MD_ZERO_STRUCT, // next_comment +}; + +//~ Arena Functions + +MD_FUNCTION MD_Arena* +MD_ArenaAlloc(void) +{ + return(MD_IMPL_ArenaAlloc()); +} + +MD_FUNCTION void +MD_ArenaRelease(MD_Arena *arena) +{ + MD_IMPL_ArenaRelease(arena); +} + +MD_FUNCTION void* +MD_ArenaPush(MD_Arena *arena, MD_u64 size) +{ + void *result = MD_IMPL_ArenaPush(arena, size); + return(result); +} + +MD_FUNCTION void +MD_ArenaPutBack(MD_Arena *arena, MD_u64 size) +{ + MD_u64 pos = MD_IMPL_ArenaGetPos(arena); + MD_u64 new_pos = pos - size; + MD_u64 new_pos_clamped = MD_ClampBot(MD_IMPL_ArenaMinPos, new_pos); + MD_IMPL_ArenaPopTo(arena, new_pos_clamped); +} + +MD_FUNCTION void +MD_ArenaSetAlign(MD_Arena *arena, MD_u64 boundary) +{ + MD_IMPL_ArenaSetAutoAlign(arena, boundary); +} + +MD_FUNCTION void +MD_ArenaPushAlign(MD_Arena *arena, MD_u64 boundary) +{ + MD_u64 pos = MD_IMPL_ArenaGetPos(arena); + MD_u64 align_m1 = boundary - 1; + MD_u64 new_pos_aligned = (pos + align_m1)&(~align_m1); + if (new_pos_aligned > pos) + { + MD_u64 amt = new_pos_aligned - pos; + MD_MemoryZero(MD_IMPL_ArenaPush(arena, amt), amt); + } +} + +MD_FUNCTION void +MD_ArenaClear(MD_Arena *arena) +{ + MD_IMPL_ArenaPopTo(arena, MD_IMPL_ArenaMinPos); +} + +MD_FUNCTION MD_ArenaTemp +MD_ArenaBeginTemp(MD_Arena *arena) +{ + MD_ArenaTemp result; + result.arena = arena; + result.pos = MD_IMPL_ArenaGetPos(arena); + return(result); +} + +MD_FUNCTION void +MD_ArenaEndTemp(MD_ArenaTemp temp) +{ + MD_IMPL_ArenaPopTo(temp.arena, temp.pos); +} + +//~ Arena Scratch Pool + +MD_FUNCTION MD_ArenaTemp +MD_GetScratch(MD_Arena **conflicts, MD_u64 count) +{ + MD_Arena *arena = MD_IMPL_GetScratch(conflicts, count); + MD_ArenaTemp result = MD_ZERO_STRUCT; + if (arena != 0) + { + result = MD_ArenaBeginTemp(arena); + } + return(result); +} + +//~ Characters + +MD_FUNCTION MD_b32 +MD_CharIsAlpha(MD_u8 c) +{ + return MD_CharIsAlphaUpper(c) || MD_CharIsAlphaLower(c); +} + +MD_FUNCTION MD_b32 +MD_CharIsAlphaUpper(MD_u8 c) +{ + return c >= 'A' && c <= 'Z'; +} + +MD_FUNCTION MD_b32 +MD_CharIsAlphaLower(MD_u8 c) +{ + return c >= 'a' && c <= 'z'; +} + +MD_FUNCTION MD_b32 +MD_CharIsDigit(MD_u8 c) +{ + return (c >= '0' && c <= '9'); +} + +MD_FUNCTION MD_b32 +MD_CharIsUnreservedSymbol(MD_u8 c) +{ + return (c == '~' || c == '!' || c == '$' || c == '%' || c == '^' || + c == '&' || c == '*' || c == '-' || c == '=' || c == '+' || + c == '<' || c == '.' || c == '>' || c == '/' || c == '?' || + c == '|'); +} + +MD_FUNCTION MD_b32 +MD_CharIsReservedSymbol(MD_u8 c) +{ + return (c == '{' || c == '}' || c == '(' || c == ')' || c == '\\' || + c == '[' || c == ']' || c == '#' || c == ',' || c == ';' || + c == ':' || c == '@'); +} + +MD_FUNCTION MD_b32 +MD_CharIsSpace(MD_u8 c) +{ + return c == ' ' || c == '\r' || c == '\t' || c == '\f' || c == '\v'; +} + +MD_FUNCTION MD_u8 +MD_CharToUpper(MD_u8 c) +{ + return (c >= 'a' && c <= 'z') ? ('A' + (c - 'a')) : c; +} + +MD_FUNCTION MD_u8 +MD_CharToLower(MD_u8 c) +{ + return (c >= 'A' && c <= 'Z') ? ('a' + (c - 'A')) : c; +} + +MD_FUNCTION MD_u8 +MD_CharToForwardSlash(MD_u8 c) +{ + return (c == '\\' ? '/' : c); +} + +//~ Strings + +MD_FUNCTION MD_u64 +MD_CalculateCStringLength(char *cstr) +{ + MD_u64 i = 0; + for(; cstr[i]; i += 1); + return i; +} + +MD_FUNCTION MD_String8 +MD_S8(MD_u8 *str, MD_u64 size) +{ + MD_String8 string; + string.str = str; + string.size = size; + return string; +} + +MD_FUNCTION MD_String8 +MD_S8Range(MD_u8 *first, MD_u8 *opl) +{ + MD_String8 string; + string.str = first; + string.size = (MD_u64)(opl - first); + return string; +} + +MD_FUNCTION MD_String8 +MD_S8Substring(MD_String8 str, MD_u64 min, MD_u64 max) +{ + if(max > str.size) + { + max = str.size; + } + if(min > str.size) + { + min = str.size; + } + if(min > max) + { + MD_u64 swap = min; + min = max; + max = swap; + } + str.size = max - min; + str.str += min; + return str; +} + +MD_FUNCTION MD_String8 +MD_S8Skip(MD_String8 str, MD_u64 min) +{ + return MD_S8Substring(str, min, str.size); +} + +MD_FUNCTION MD_String8 +MD_S8Chop(MD_String8 str, MD_u64 nmax) +{ + return MD_S8Substring(str, 0, str.size - nmax); +} + +MD_FUNCTION MD_String8 +MD_S8Prefix(MD_String8 str, MD_u64 size) +{ + return MD_S8Substring(str, 0, size); +} + +MD_FUNCTION MD_String8 +MD_S8Suffix(MD_String8 str, MD_u64 size) +{ + return MD_S8Substring(str, str.size - size, str.size); +} + +MD_FUNCTION MD_b32 +MD_S8Match(MD_String8 a, MD_String8 b, MD_MatchFlags flags) +{ + int result = 0; + if(a.size == b.size || flags & MD_StringMatchFlag_RightSideSloppy) + { + result = 1; + for(MD_u64 i = 0; i < a.size; i += 1) + { + MD_b32 match = (a.str[i] == b.str[i]); + if(flags & MD_StringMatchFlag_CaseInsensitive) + { + match |= (MD_CharToLower(a.str[i]) == MD_CharToLower(b.str[i])); + } + if(flags & MD_StringMatchFlag_SlashInsensitive) + { + match |= (MD_CharToForwardSlash(a.str[i]) == MD_CharToForwardSlash(b.str[i])); + } + if(match == 0) + { + result = 0; + break; + } + } + } + return result; +} + +MD_FUNCTION MD_u64 +MD_S8FindSubstring(MD_String8 str, MD_String8 substring, MD_u64 start_pos, MD_MatchFlags flags) +{ + MD_b32 found = 0; + MD_u64 found_idx = str.size; + for(MD_u64 i = start_pos; i < str.size; i += 1) + { + if(i + substring.size <= str.size) + { + MD_String8 substr_from_str = MD_S8Substring(str, i, i+substring.size); + if(MD_S8Match(substr_from_str, substring, flags)) + { + found_idx = i; + found = 1; + if(!(flags & MD_MatchFlag_FindLast)) + { + break; + } + } + } + } + return found_idx; +} + +MD_FUNCTION MD_String8 +MD_S8Copy(MD_Arena *arena, MD_String8 string) +{ + MD_String8 res; + res.size = string.size; + res.str = MD_PushArray(arena, MD_u8, string.size + 1); + MD_MemoryCopy(res.str, string.str, string.size); + res.str[string.size] = 0; + return(res); +} + +MD_FUNCTION MD_String8 +MD_S8FmtV(MD_Arena *arena, char *fmt, va_list args) +{ + MD_String8 result = MD_ZERO_STRUCT; + va_list args2; + va_copy(args2, args); + MD_u64 needed_bytes = MD_IMPL_Vsnprintf(0, 0, fmt, args)+1; + result.str = MD_PushArray(arena, MD_u8, needed_bytes); + result.size = needed_bytes - 1; + result.str[needed_bytes-1] = 0; + MD_IMPL_Vsnprintf((char*)result.str, needed_bytes, fmt, args2); + return result; +} + +MD_FUNCTION MD_String8 +MD_S8Fmt(MD_Arena *arena, char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + MD_String8 result = MD_S8FmtV(arena, fmt, args); + va_end(args); + return result; +} + +MD_FUNCTION void +MD_S8ListPush(MD_Arena *arena, MD_String8List *list, MD_String8 string) +{ + MD_String8Node *node = MD_PushArrayZero(arena, MD_String8Node, 1); + node->string = string; + + MD_QueuePush(list->first, list->last, node); + list->node_count += 1; + list->total_size += string.size; +} + +MD_FUNCTION void +MD_S8ListPushFmt(MD_Arena *arena, MD_String8List *list, char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + MD_String8 string = MD_S8FmtV(arena, fmt, args); + va_end(args); + MD_S8ListPush(arena, list, string); +} + +MD_FUNCTION void +MD_S8ListConcat(MD_String8List *list, MD_String8List *to_push) +{ + if(to_push->first) + { + list->node_count += to_push->node_count; + list->total_size += to_push->total_size; + + if(list->last == 0) + { + *list = *to_push; + } + else + { + list->last->next = to_push->first; + list->last = to_push->last; + } + } + MD_MemoryZeroStruct(to_push); +} + +MD_FUNCTION MD_String8List +MD_S8Split(MD_Arena *arena, MD_String8 string, int split_count, MD_String8 *splits) +{ + MD_String8List list = MD_ZERO_STRUCT; + + MD_u64 split_start = 0; + for(MD_u64 i = 0; i < string.size; i += 1) + { + MD_b32 was_split = 0; + for(int split_idx = 0; split_idx < split_count; split_idx += 1) + { + MD_b32 match = 0; + if(i + splits[split_idx].size <= string.size) + { + match = 1; + for(MD_u64 split_i = 0; split_i < splits[split_idx].size && i + split_i < string.size; split_i += 1) + { + if(splits[split_idx].str[split_i] != string.str[i + split_i]) + { + match = 0; + break; + } + } + } + if(match) + { + MD_String8 split_string = MD_S8(string.str + split_start, i - split_start); + MD_S8ListPush(arena, &list, split_string); + split_start = i + splits[split_idx].size; + i += splits[split_idx].size - 1; + was_split = 1; + break; + } + } + + if(was_split == 0 && i == string.size - 1) + { + MD_String8 split_string = MD_S8(string.str + split_start, i+1 - split_start); + MD_S8ListPush(arena, &list, split_string); + break; + } + } + + return list; +} + +MD_FUNCTION MD_String8 +MD_S8ListJoin(MD_Arena *arena, MD_String8List list, MD_StringJoin *join_ptr) +{ + // setup join parameters + MD_StringJoin join = MD_ZERO_STRUCT; + if (join_ptr != 0) + { + MD_MemoryCopy(&join, join_ptr, sizeof(join)); + } + + // calculate size & allocate + MD_u64 sep_count = 0; + if (list.node_count > 1) + { + sep_count = list.node_count - 1; + } + MD_String8 result = MD_ZERO_STRUCT; + result.size = (list.total_size + join.pre.size + + sep_count*join.mid.size + join.post.size); + result.str = MD_PushArrayZero(arena, MD_u8, result.size); + + // fill + MD_u8 *ptr = result.str; + MD_MemoryCopy(ptr, join.pre.str, join.pre.size); + ptr += join.pre.size; + for(MD_String8Node *node = list.first; node; node = node->next) + { + MD_MemoryCopy(ptr, node->string.str, node->string.size); + ptr += node->string.size; + if (node != list.last) + { + MD_MemoryCopy(ptr, join.mid.str, join.mid.size); + ptr += join.mid.size; + } + } + MD_MemoryCopy(ptr, join.pre.str, join.pre.size); + ptr += join.pre.size; + + return(result); +} + +MD_FUNCTION MD_String8 +MD_S8Stylize(MD_Arena *arena, MD_String8 string, MD_IdentifierStyle word_style, + MD_String8 separator) +{ + MD_String8 result = MD_ZERO_STRUCT; + + MD_String8List words = MD_ZERO_STRUCT; + + MD_b32 break_on_uppercase = 0; + { + break_on_uppercase = 1; + for(MD_u64 i = 0; i < string.size; i += 1) + { + if(!MD_CharIsAlpha(string.str[i]) && !MD_CharIsDigit(string.str[i])) + { + break_on_uppercase = 0; + break; + } + } + } + + MD_b32 making_word = 0; + MD_String8 word = MD_ZERO_STRUCT; + + for(MD_u64 i = 0; i < string.size;) + { + if(making_word) + { + if((break_on_uppercase && MD_CharIsAlphaUpper(string.str[i])) || + string.str[i] == '_' || MD_CharIsSpace(string.str[i]) || + i == string.size - 1) + { + if(i == string.size - 1) + { + word.size += 1; + } + making_word = 0; + MD_S8ListPush(arena, &words, word); + } + else + { + word.size += 1; + i += 1; + } + } + else + { + if(MD_CharIsAlpha(string.str[i])) + { + making_word = 1; + word.str = string.str + i; + word.size = 1; + } + i += 1; + } + } + + result.size = words.total_size; + if(words.node_count > 1) + { + result.size += separator.size*(words.node_count-1); + } + result.str = MD_PushArrayZero(arena, MD_u8, result.size); + + { + MD_u64 write_pos = 0; + for(MD_String8Node *node = words.first; node; node = node->next) + { + + // NOTE(rjf): Write word string to result. + { + MD_MemoryCopy(result.str + write_pos, node->string.str, node->string.size); + + // NOTE(rjf): Transform string based on word style. + switch(word_style) + { + case MD_IdentifierStyle_UpperCamelCase: + { + result.str[write_pos] = MD_CharToUpper(result.str[write_pos]); + for(MD_u64 i = write_pos+1; i < write_pos + node->string.size; i += 1) + { + result.str[i] = MD_CharToLower(result.str[i]); + } + }break; + + case MD_IdentifierStyle_LowerCamelCase: + { + MD_b32 is_first = (node == words.first); + result.str[write_pos] = (is_first ? + MD_CharToLower(result.str[write_pos]) : + MD_CharToUpper(result.str[write_pos])); + for(MD_u64 i = write_pos+1; i < write_pos + node->string.size; i += 1) + { + result.str[i] = MD_CharToLower(result.str[i]); + } + }break; + + case MD_IdentifierStyle_UpperCase: + { + for(MD_u64 i = write_pos; i < write_pos + node->string.size; i += 1) + { + result.str[i] = MD_CharToUpper(result.str[i]); + } + }break; + + case MD_IdentifierStyle_LowerCase: + { + for(MD_u64 i = write_pos; i < write_pos + node->string.size; i += 1) + { + result.str[i] = MD_CharToLower(result.str[i]); + } + }break; + + default: break; + } + + write_pos += node->string.size; + } + + if(node->next) + { + MD_MemoryCopy(result.str + write_pos, separator.str, separator.size); + write_pos += separator.size; + } + } + } + + return result; +} + +//~ Unicode Conversions + +MD_GLOBAL MD_u8 md_utf8_class[32] = { + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,2,2,2,2,3,3,4,5, +}; + +MD_FUNCTION MD_DecodedCodepoint +MD_DecodeCodepointFromUtf8(MD_u8 *str, MD_u64 max) +{ +#define MD_bitmask1 0x01 +#define MD_bitmask2 0x03 +#define MD_bitmask3 0x07 +#define MD_bitmask4 0x0F +#define MD_bitmask5 0x1F +#define MD_bitmask6 0x3F +#define MD_bitmask7 0x7F +#define MD_bitmask8 0xFF +#define MD_bitmask9 0x01FF +#define MD_bitmask10 0x03FF + + MD_DecodedCodepoint result = {~((MD_u32)0), 1}; + MD_u8 byte = str[0]; + MD_u8 byte_class = md_utf8_class[byte >> 3]; + switch (byte_class) + { + case 1: + { + result.codepoint = byte; + }break; + + case 2: + { + if (2 <= max) + { + MD_u8 cont_byte = str[1]; + if (md_utf8_class[cont_byte >> 3] == 0) + { + result.codepoint = (byte & MD_bitmask5) << 6; + result.codepoint |= (cont_byte & MD_bitmask6); + result.advance = 2; + } + } + }break; + + case 3: + { + if (3 <= max) + { + MD_u8 cont_byte[2] = {str[1], str[2]}; + if (md_utf8_class[cont_byte[0] >> 3] == 0 && + md_utf8_class[cont_byte[1] >> 3] == 0) + { + result.codepoint = (byte & MD_bitmask4) << 12; + result.codepoint |= ((cont_byte[0] & MD_bitmask6) << 6); + result.codepoint |= (cont_byte[1] & MD_bitmask6); + result.advance = 3; + } + } + }break; + + case 4: + { + if (4 <= max) + { + MD_u8 cont_byte[3] = {str[1], str[2], str[3]}; + if (md_utf8_class[cont_byte[0] >> 3] == 0 && + md_utf8_class[cont_byte[1] >> 3] == 0 && + md_utf8_class[cont_byte[2] >> 3] == 0) + { + result.codepoint = (byte & MD_bitmask3) << 18; + result.codepoint |= ((cont_byte[0] & MD_bitmask6) << 12); + result.codepoint |= ((cont_byte[1] & MD_bitmask6) << 6); + result.codepoint |= (cont_byte[2] & MD_bitmask6); + result.advance = 4; + } + } + }break; + } + + return(result); +} + +MD_FUNCTION MD_DecodedCodepoint +MD_DecodeCodepointFromUtf16(MD_u16 *out, MD_u64 max) +{ + MD_DecodedCodepoint result = {~((MD_u32)0), 1}; + result.codepoint = out[0]; + result.advance = 1; + if (1 < max && 0xD800 <= out[0] && out[0] < 0xDC00 && 0xDC00 <= out[1] && out[1] < 0xE000) + { + result.codepoint = ((out[0] - 0xD800) << 10) | (out[1] - 0xDC00); + result.advance = 2; + } + return(result); +} + +MD_FUNCTION MD_u32 +MD_Utf8FromCodepoint(MD_u8 *out, MD_u32 codepoint) +{ +#define MD_bit8 0x80 + MD_u32 advance = 0; + if (codepoint <= 0x7F) + { + out[0] = (MD_u8)codepoint; + advance = 1; + } + else if (codepoint <= 0x7FF) + { + out[0] = (MD_bitmask2 << 6) | ((codepoint >> 6) & MD_bitmask5); + out[1] = MD_bit8 | (codepoint & MD_bitmask6); + advance = 2; + } + else if (codepoint <= 0xFFFF) + { + out[0] = (MD_bitmask3 << 5) | ((codepoint >> 12) & MD_bitmask4); + out[1] = MD_bit8 | ((codepoint >> 6) & MD_bitmask6); + out[2] = MD_bit8 | ( codepoint & MD_bitmask6); + advance = 3; + } + else if (codepoint <= 0x10FFFF) + { + out[0] = (MD_bitmask4 << 3) | ((codepoint >> 18) & MD_bitmask3); + out[1] = MD_bit8 | ((codepoint >> 12) & MD_bitmask6); + out[2] = MD_bit8 | ((codepoint >> 6) & MD_bitmask6); + out[3] = MD_bit8 | ( codepoint & MD_bitmask6); + advance = 4; + } + else + { + out[0] = '?'; + advance = 1; + } + return(advance); +} + +MD_FUNCTION MD_u32 +MD_Utf16FromCodepoint(MD_u16 *out, MD_u32 codepoint) +{ + MD_u32 advance = 1; + if (codepoint == ~((MD_u32)0)) + { + out[0] = (MD_u16)'?'; + } + else if (codepoint < 0x10000) + { + out[0] = (MD_u16)codepoint; + } + else + { + MD_u64 v = codepoint - 0x10000; + out[0] = 0xD800 + (v >> 10); + out[1] = 0xDC00 + (v & MD_bitmask10); + advance = 2; + } + return(advance); +} + +MD_FUNCTION MD_String8 +MD_S8FromS16(MD_Arena *arena, MD_String16 in) +{ + MD_u64 cap = in.size*3; + MD_u8 *str = MD_PushArrayZero(arena, MD_u8, cap + 1); + MD_u16 *ptr = in.str; + MD_u16 *opl = ptr + in.size; + MD_u64 size = 0; + MD_DecodedCodepoint consume; + for (;ptr < opl;) + { + consume = MD_DecodeCodepointFromUtf16(ptr, opl - ptr); + ptr += consume.advance; + size += MD_Utf8FromCodepoint(str + size, consume.codepoint); + } + str[size] = 0; + MD_ArenaPutBack(arena, cap - size); // := ((cap + 1) - (size + 1)) + return(MD_S8(str, size)); +} + +MD_FUNCTION MD_String16 +MD_S16FromS8(MD_Arena *arena, MD_String8 in) +{ + MD_u64 cap = in.size*2; + MD_u16 *str = MD_PushArrayZero(arena, MD_u16, cap + 1); + MD_u8 *ptr = in.str; + MD_u8 *opl = ptr + in.size; + MD_u64 size = 0; + MD_DecodedCodepoint consume; + for (;ptr < opl;) + { + consume = MD_DecodeCodepointFromUtf8(ptr, opl - ptr); + ptr += consume.advance; + size += MD_Utf16FromCodepoint(str + size, consume.codepoint); + } + str[size] = 0; + MD_ArenaPutBack(arena, 2*(cap - size)); // := 2*((cap + 1) - (size + 1)) + MD_String16 result = {str, size}; + return(result); +} + +MD_FUNCTION MD_String8 +MD_S8FromS32(MD_Arena *arena, MD_String32 in) +{ + MD_u64 cap = in.size*4; + MD_u8 *str = MD_PushArrayZero(arena, MD_u8, cap + 1); + MD_u32 *ptr = in.str; + MD_u32 *opl = ptr + in.size; + MD_u64 size = 0; + MD_DecodedCodepoint consume; + for (;ptr < opl; ptr += 1) + { + size += MD_Utf8FromCodepoint(str + size, *ptr); + } + str[size] = 0; + MD_ArenaPutBack(arena, cap - size); // := ((cap + 1) - (size + 1)) + return(MD_S8(str, size)); +} + +MD_FUNCTION MD_String32 +MD_S32FromS8(MD_Arena *arena, MD_String8 in) +{ + MD_u64 cap = in.size; + MD_u32 *str = MD_PushArrayZero(arena, MD_u32, cap + 1); + MD_u8 *ptr = in.str; + MD_u8 *opl = ptr + in.size; + MD_u64 size = 0; + MD_DecodedCodepoint consume; + for (;ptr < opl;) + { + consume = MD_DecodeCodepointFromUtf8(ptr, opl - ptr); + ptr += consume.advance; + str[size] = consume.codepoint; + size += 1; + } + str[size] = 0; + MD_ArenaPutBack(arena, 4*(cap - size)); // := 4*((cap + 1) - (size + 1)) + MD_String32 result = {str, size}; + return(result); +} + +//~ File Name Strings + +MD_FUNCTION MD_String8 +MD_PathChopLastPeriod(MD_String8 string) +{ + MD_u64 period_pos = MD_S8FindSubstring(string, MD_S8Lit("."), 0, MD_MatchFlag_FindLast); + if(period_pos < string.size) + { + string.size = period_pos; + } + return string; +} + +MD_FUNCTION MD_String8 +MD_PathSkipLastSlash(MD_String8 string) +{ + MD_u64 slash_pos = MD_S8FindSubstring(string, MD_S8Lit("/"), 0, + MD_StringMatchFlag_SlashInsensitive| + MD_MatchFlag_FindLast); + if(slash_pos < string.size) + { + string.str += slash_pos+1; + string.size -= slash_pos+1; + } + return string; +} + +MD_FUNCTION MD_String8 +MD_PathSkipLastPeriod(MD_String8 string) +{ + MD_u64 period_pos = MD_S8FindSubstring(string, MD_S8Lit("."), 0, MD_MatchFlag_FindLast); + if(period_pos < string.size) + { + string.str += period_pos+1; + string.size -= period_pos+1; + } + return string; +} + +MD_FUNCTION MD_String8 +MD_PathChopLastSlash(MD_String8 string) +{ + MD_u64 slash_pos = MD_S8FindSubstring(string, MD_S8Lit("/"), 0, + MD_StringMatchFlag_SlashInsensitive| + MD_MatchFlag_FindLast); + if(slash_pos < string.size) + { + string.size = slash_pos; + } + return string; +} + +MD_FUNCTION MD_String8 +MD_S8SkipWhitespace(MD_String8 string) +{ + for(MD_u64 i = 0; i < string.size; i += 1) + { + if(!MD_CharIsSpace(string.str[i])) + { + string = MD_S8Skip(string, i); + break; + } + } + return string; +} + +MD_FUNCTION MD_String8 +MD_S8ChopWhitespace(MD_String8 string) +{ + for(MD_u64 i = string.size-1; i < string.size; i -= 1) + { + if(!MD_CharIsSpace(string.str[i])) + { + string = MD_S8Prefix(string, i+1); + break; + } + } + return string; +} + +//~ Numeric Strings + +MD_GLOBAL MD_u8 md_char_to_value[] = { + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +}; + +MD_GLOBAL MD_u8 md_char_is_integer[] = { + 0,0,0,0,0,0,1,1, + 1,0,0,0,1,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, +}; + +MD_FUNCTION MD_b32 +MD_StringIsU64(MD_String8 string, MD_u32 radix) +{ + MD_b32 result = 0; + if (string.size > 0) + { + result = 1; + for (MD_u8 *ptr = string.str, *opl = string.str + string.size; + ptr < opl; + ptr += 1) + { + MD_u8 c = *ptr; + if (!md_char_is_integer[c >> 3]) + { + result = 0; + break; + } + if (md_char_to_value[(c - 0x30)&0x1F] >= radix) + { + result = 0; + break; + } + } + } + return(result); +} + +MD_FUNCTION MD_b32 +MD_StringIsCStyleInt(MD_String8 string) +{ + MD_u8 *ptr = string.str; + MD_u8 *opl = string.str + string.size; + + // consume sign + for (;ptr < opl && (*ptr == '+' || *ptr == '-'); ptr += 1); + + // radix from prefix + MD_u64 radix = 10; + if (ptr < opl) + { + MD_u8 c0 = *ptr; + if (c0 == '0') + { + ptr += 1; + radix = 8; + if (ptr < opl) + { + MD_u8 c1 = *ptr; + if (c1 == 'x') + { + ptr += 1; + radix = 0x10; + } + else if (c1 == 'b') + { + ptr += 1; + radix = 2; + } + } + } + } + + // check integer "digits" + MD_String8 digits_substr = MD_S8Range(ptr, opl); + MD_b32 result = MD_StringIsU64(digits_substr, radix); + + return(result); +} + +MD_FUNCTION MD_u64 +MD_U64FromString(MD_String8 string, MD_u32 radix) +{ + MD_Assert(2 <= radix && radix <= 16); + MD_u64 value = 0; + for (MD_u64 i = 0; i < string.size; i += 1) + { + value *= radix; + MD_u8 c = string.str[i]; + value += md_char_to_value[(c - 0x30)&0x1F]; + } + return(value); +} + +MD_FUNCTION MD_i64 +MD_CStyleIntFromString(MD_String8 string) +{ + MD_u64 p = 0; + + // consume sign + MD_i64 sign = +1; + if (p < string.size) + { + MD_u8 c = string.str[p]; + if (c == '-') + { + sign = -1; + p += 1; + } + else if (c == '+') + { + p += 1; + } + } + + // radix from prefix + MD_u64 radix = 10; + if (p < string.size) + { + MD_u8 c0 = string.str[p]; + if (c0 == '0') + { + p += 1; + radix = 8; + if (p < string.size) + { + MD_u8 c1 = string.str[p]; + if (c1 == 'x') + { + p += 1; + radix = 16; + } + else if (c1 == 'b') + { + p += 1; + radix = 2; + } + } + } + } + + // consume integer "digits" + MD_String8 digits_substr = MD_S8Skip(string, p); + MD_u64 n = MD_U64FromString(digits_substr, radix); + + // combine result + MD_i64 result = sign*n; + return(result); +} + +MD_FUNCTION MD_f64 +MD_F64FromString(MD_String8 string) +{ + char str[64]; + MD_u64 str_size = string.size; + if (str_size > sizeof(str) - 1) + { + str_size = sizeof(str) - 1; + } + MD_MemoryCopy(str, string.str, str_size); + str[str_size] = 0; + return(atof(str)); +} + + +MD_FUNCTION MD_String8 +MD_CStyleHexStringFromU64(MD_Arena *arena, MD_u64 x, MD_b32 caps) +{ + static char md_int_value_to_char[] = "0123456789abcdef"; + MD_u8 buffer[10]; + MD_u8 *opl = buffer + 10; + MD_u8 *ptr = opl; + if (x == 0) + { + ptr -= 1; + *ptr = '0'; + } + else + { + for (;;) + { + MD_u32 val = x%16; + x /= 16; + MD_u8 c = (MD_u8)md_int_value_to_char[val]; + if (caps) + { + c = MD_CharToUpper(c); + } + ptr -= 1; + *ptr = c; + if (x == 0) + { + break; + } + } + } + ptr -= 1; + *ptr = 'x'; + ptr -= 1; + *ptr = '0'; + + MD_String8 result = MD_ZERO_STRUCT; + result.size = (MD_u64)(ptr - buffer); + result.str = MD_PushArray(arena, MD_u8, result.size + 1); + MD_MemoryCopy(result.str, buffer, result.size); + result.str[result.size] =0; + return(result); +} + +//~ Enum/Flag Strings + +MD_FUNCTION MD_String8 +MD_StringFromNodeKind(MD_NodeKind kind) +{ + // NOTE(rjf): @maintenance Must be kept in sync with MD_NodeKind enum. + static char *cstrs[MD_NodeKind_COUNT] = + { + "Nil", + + "File", + "ErrorMarker", + + "Main", + "Tag", + + "List", + "Reference", + }; + return MD_S8CString(cstrs[kind]); +} + +MD_FUNCTION MD_String8List +MD_StringListFromNodeFlags(MD_Arena *arena, MD_NodeFlags flags) +{ + // NOTE(rjf): @maintenance Must be kept in sync with MD_NodeFlags enum. + static char *flag_cstrs[] = + { + "HasParenLeft", + "HasParenRight", + "HasBracketLeft", + "HasBracketRight", + "HasBraceLeft", + "HasBraceRight", + + "IsBeforeSemicolon", + "IsAfterSemicolon", + + "IsBeforeComma", + "IsAfterComma", + + "StringSingleQuote", + "StringDoubleQuote", + "StringTick", + "StringTriplet", + + "Numeric", + "Identifier", + "StringLiteral", + }; + + MD_String8List list = MD_ZERO_STRUCT; + MD_u64 bits = sizeof(flags) * 8; + for(MD_u64 i = 0; i < bits && i < MD_ArrayCount(flag_cstrs); i += 1) + { + if(flags & (1ull << i)) + { + MD_S8ListPush(arena, &list, MD_S8CString(flag_cstrs[i])); + } + } + return list; +} + +//~ Map Table Data Structure + +MD_FUNCTION MD_u64 +MD_HashStr(MD_String8 string) +{ + MD_u64 result = 5381; + for(MD_u64 i = 0; i < string.size; i += 1) + { + result = ((result << 5) + result) + string.str[i]; + } + return result; +} + +// NOTE(mal): Generic 64-bit hash function (https://nullprogram.com/blog/2018/07/31/) +// Reversible, so no collisions. Assumes all bits of the pointer matter. +MD_FUNCTION MD_u64 +MD_HashPtr(void *p) +{ + MD_u64 h = (MD_u64)p; + h = (h ^ (h >> 30)) * 0xbf58476d1ce4e5b9; + h = (h ^ (h >> 27)) * 0x94d049bb133111eb; + h = h ^ (h >> 31); + return h; +} + +MD_FUNCTION MD_Map +MD_MapMakeBucketCount(MD_Arena *arena, MD_u64 bucket_count) +{ + MD_Map result = {0}; + result.bucket_count = bucket_count; + result.buckets = MD_PushArrayZero(arena, MD_MapBucket, bucket_count); + return(result); +} + +MD_FUNCTION MD_Map +MD_MapMake(MD_Arena *arena) +{ + MD_Map result = MD_MapMakeBucketCount(arena, 4093); + return(result); +} + +MD_FUNCTION MD_MapKey +MD_MapKeyStr(MD_String8 string) +{ + MD_MapKey result = {0}; + if (string.size != 0) + { + result.hash = MD_HashStr(string); + result.size = string.size; + if (string.size > 0) + { + result.ptr = string.str; + } + } + return(result); +} + +MD_FUNCTION MD_MapKey +MD_MapKeyPtr(void *ptr) +{ + MD_MapKey result = {0}; + if (ptr != 0) + { + result.hash = MD_HashPtr(ptr); + result.size = 0; + result.ptr = ptr; + } + return(result); +} + +MD_FUNCTION MD_MapSlot* +MD_MapLookup(MD_Map *map, MD_MapKey key) +{ + MD_MapSlot *result = 0; + if (map->bucket_count > 0) + { + MD_u64 index = key.hash%map->bucket_count; + result = MD_MapScan(map->buckets[index].first, key); + } + return(result); +} + +MD_FUNCTION MD_MapSlot* +MD_MapScan(MD_MapSlot *first_slot, MD_MapKey key) +{ + MD_MapSlot *result = 0; + if (first_slot != 0) + { + MD_b32 ptr_kind = (key.size == 0); + MD_String8 key_string = MD_S8((MD_u8*)key.ptr, key.size); + for (MD_MapSlot *slot = first_slot; + slot != 0; + slot = slot->next) + { + if (slot->key.hash == key.hash) + { + if (ptr_kind) + { + if (slot->key.size == 0 && slot->key.ptr == key.ptr) + { + result = slot; + break; + } + } + else + { + MD_String8 slot_string = MD_S8((MD_u8*)slot->key.ptr, slot->key.size); + if (MD_S8Match(slot_string, key_string, 0)) + { + result = slot; + break; + } + } + } + } + } + return(result); +} + +MD_FUNCTION MD_MapSlot* +MD_MapInsert(MD_Arena *arena, MD_Map *map, MD_MapKey key, void *val) +{ + MD_MapSlot *result = 0; + if (map->bucket_count > 0) + { + MD_u64 index = key.hash%map->bucket_count; + MD_MapSlot *slot = MD_PushArrayZero(arena, MD_MapSlot, 1); + MD_MapBucket *bucket = &map->buckets[index]; + MD_QueuePush(bucket->first, bucket->last, slot); + slot->key = key; + slot->val = val; + result = slot; + } + return(result); +} + +MD_FUNCTION MD_MapSlot* +MD_MapOverwrite(MD_Arena *arena, MD_Map *map, MD_MapKey key, void *val) +{ + MD_MapSlot *result = MD_MapLookup(map, key); + if (result != 0) + { + result->val = val; + } + else + { + result = MD_MapInsert(arena, map, key, val); + } + return(result); +} + +//~ Parsing + +MD_FUNCTION MD_Token +MD_TokenFromString(MD_String8 string) +{ + MD_Token token = MD_ZERO_STRUCT; + + MD_u8 *one_past_last = string.str + string.size; + MD_u8 *first = string.str; + + if(first < one_past_last) + { + MD_u8 *at = first; + MD_u32 skip_n = 0; + MD_u32 chop_n = 0; + +#define MD_TokenizerScan(cond) for (; at < one_past_last && (cond); at += 1) + + switch (*at) + { + // NOTE(allen): Whitespace parsing + case '\n': + { + token.kind = MD_TokenKind_Newline; + at += 1; + }break; + + case ' ': case '\r': case '\t': case '\f': case '\v': + { + token.kind = MD_TokenKind_Whitespace; + at += 1; + MD_TokenizerScan(*at == ' ' || *at == '\r' || *at == '\t' || *at == '\f' || *at == '\v'); + }break; + + // NOTE(allen): Comment parsing + case '/': + { + if (at + 1 < one_past_last) + { + if (at[1] == '/') + { + // trim off the first '//' + skip_n = 2; + at += 2; + token.kind = MD_TokenKind_Comment; + MD_TokenizerScan(*at != '\n' && *at != '\r'); + } + else if (at[1] == '*') + { + // trim off the first '/*' + skip_n = 2; + at += 2; + token.kind = MD_TokenKind_BrokenComment; + int counter = 1; + for (;at < one_past_last && counter > 0; at += 1) + { + if (at + 1 < one_past_last) + { + if (at[0] == '*' && at[1] == '/') + { + at += 1; + counter -= 1; + } + else if (at[0] == '/' && at[1] == '*') + { + at += 1; + counter += 1; + } + } + } + if(counter == 0) + { + token.kind = MD_TokenKind_Comment; + chop_n = 2; + } + } + } + if (token.kind == 0) goto symbol_lex; + }break; + + // NOTE(allen): Strings + case '"': + case '\'': + case '`': + { + token.kind = MD_TokenKind_BrokenStringLiteral; + + // determine delimiter setup + MD_u8 d = *at; + MD_b32 is_triplet = (at + 2 < one_past_last && at[1] == d && at[2] == d); + + // lex triple-delimiter string + if (is_triplet) + { + skip_n = 3; + at += 3; + MD_u32 consecutive_d = 0; + for (;;) + { + // fail condition + if (at >= one_past_last) + { + break; + } + + if(at[0] == d) + { + consecutive_d += 1; + at += 1; + // close condition + if (consecutive_d == 3) + { + chop_n = 3; + token.kind = MD_TokenKind_StringLiteral; + break; + } + } + else + { + consecutive_d = 0; + + // escaping rule + if(at[0] == '\\') + { + at += 1; + if(at < one_past_last && (at[0] == d || at[0] == '\\')) + { + at += 1; + } + } + else{ + at += 1; + } + } + } + } + + // lex single-delimiter string + if (!is_triplet) + { + skip_n = 1; + at += 1; + for (;at < one_past_last;) + { + // close condition + if (*at == d) + { + at += 1; + chop_n = 1; + token.kind = MD_TokenKind_StringLiteral; + break; + } + + // fail condition + if (*at == '\n') + { + break; + } + + // escaping rule + if (at[0] == '\\') + { + at += 1; + if (at < one_past_last && (at[0] == d || at[0] == '\\')) + { + at += 1; + } + } + else + { + at += 1; + } + } + } + + //- rjf: set relevant node flags on token + token.node_flags |= MD_NodeFlag_StringLiteral; + switch(d) + { + case '\'': token.node_flags |= MD_NodeFlag_StringSingleQuote; break; + case '"': token.node_flags |= MD_NodeFlag_StringDoubleQuote; break; + case '`': token.node_flags |= MD_NodeFlag_StringTick; break; + default: break; + } + if(is_triplet) + { + token.node_flags |= MD_NodeFlag_StringTriplet; + } + + }break; + + // NOTE(allen): Identifiers, Numbers, Symbols + default: + { + if (MD_CharIsAlpha(*at) || *at == '_') + { + token.node_flags |= MD_NodeFlag_Identifier; + token.kind = MD_TokenKind_Identifier; + at += 1; + MD_TokenizerScan(MD_CharIsAlpha(*at) || MD_CharIsDigit(*at) || *at == '_'); + } + + else if (MD_CharIsDigit(*at)) + { + token.node_flags |= MD_NodeFlag_Numeric; + token.kind = MD_TokenKind_Numeric; + at += 1; + + for (; at < one_past_last;) + { + MD_b32 good = 0; + if (*at == 'e' || *at == 'E') + { + good = 1; + at += 1; + if (at < one_past_last && (*at == '+' || *at == '-')) + { + at += 1; + } + } + else if (MD_CharIsAlpha(*at) || MD_CharIsDigit(*at) || *at == '.' || *at == '_') + { + good = 1; + at += 1; + } + if (!good) + { + break; + } + } + } + + else if (MD_CharIsUnreservedSymbol(*at)) + { + symbol_lex: + + token.node_flags |= MD_NodeFlag_Symbol; + token.kind = MD_TokenKind_Symbol; + at += 1; + MD_TokenizerScan(MD_CharIsUnreservedSymbol(*at)); + } + + else if (MD_CharIsReservedSymbol(*at)) + { + token.kind = MD_TokenKind_Reserved; + at += 1; + } + + else + { + token.kind = MD_TokenKind_BadCharacter; + at += 1; + } + }break; + } + + token.raw_string = MD_S8Range(first, at); + token.string = MD_S8Substring(token.raw_string, skip_n, token.raw_string.size - chop_n); + +#undef MD_TokenizerScan + + } + + return token; +} + +MD_FUNCTION MD_u64 +MD_LexAdvanceFromSkips(MD_String8 string, MD_TokenKind skip_kinds) +{ + MD_u64 result = string.size; + MD_u64 p = 0; + for (;;) + { + MD_Token token = MD_TokenFromString(MD_S8Skip(string, p)); + if ((skip_kinds & token.kind) == 0) + { + result = p; + break; + } + p += token.raw_string.size; + } + return(result); +} + +MD_FUNCTION MD_ParseResult +MD_ParseResultZero(void) +{ + MD_ParseResult result = MD_ZERO_STRUCT; + result.node = MD_NilNode(); + return result; +} + +MD_FUNCTION MD_ParseResult +MD_ParseNodeSet(MD_Arena *arena, MD_String8 string, MD_u64 offset, MD_Node *parent, + MD_ParseSetRule rule) +{ + MD_ParseResult result = MD_ParseResultZero(); + MD_u64 off = offset; + + //- rjf: fill data from set opener + MD_Token initial_token = MD_TokenFromString(MD_S8Skip(string, offset)); + MD_u8 set_opener = 0; + MD_NodeFlags set_opener_flags = 0; + MD_b32 close_with_brace = 0; + MD_b32 close_with_paren = 0; + MD_b32 close_with_separator = 0; + MD_b32 parse_all = 0; + switch(rule) + { + default: break; + + case MD_ParseSetRule_EndOnDelimiter: + { + MD_u64 opener_check_off = off; + opener_check_off += MD_LexAdvanceFromSkips(MD_S8Skip(string, opener_check_off), MD_TokenGroup_Irregular); + initial_token = MD_TokenFromString(MD_S8Skip(string, opener_check_off)); + if(initial_token.kind == MD_TokenKind_Reserved) + { + MD_u8 c = initial_token.raw_string.str[0]; + if(c == '{') + { + set_opener = '{'; + set_opener_flags |= MD_NodeFlag_HasBraceLeft; + opener_check_off += initial_token.raw_string.size; + off = opener_check_off; + close_with_brace = 1; + } + else if(c == '(') + { + set_opener = '('; + set_opener_flags |= MD_NodeFlag_HasParenLeft; + opener_check_off += initial_token.raw_string.size; + off = opener_check_off; + close_with_paren = 1; + } + else if(c == '[') + { + set_opener = '['; + set_opener_flags |= MD_NodeFlag_HasBracketLeft; + opener_check_off += initial_token.raw_string.size; + off = opener_check_off; + close_with_paren = 1; + } + else + { + close_with_separator = 1; + } + } + else + { + close_with_separator = 1; + } + }break; + + case MD_ParseSetRule_Global: + { + parse_all = 1; + }break; + } + + //- rjf: fill parent data from opener + parent->flags |= set_opener_flags; + + //- rjf: parse children + MD_b32 got_closer = 0; + MD_u64 parsed_child_count = 0; + if(set_opener != 0 || close_with_separator || parse_all) + { + MD_NodeFlags next_child_flags = 0; + for(;off < string.size;) + { + + //- rjf: check for separator closers + if(close_with_separator) + { + MD_u64 closer_check_off = off; + + //- rjf: check newlines + { + MD_Token potential_closer = MD_TokenFromString(MD_S8Skip(string, closer_check_off)); + if(potential_closer.kind == MD_TokenKind_Newline) + { + closer_check_off += potential_closer.raw_string.size; + off = closer_check_off; + + // NOTE(rjf): always terminate with a newline if we have >0 children + if(parsed_child_count > 0) + { + off = closer_check_off; + got_closer = 1; + break; + } + + // NOTE(rjf): terminate after double newline if we have 0 children + MD_Token next_closer = MD_TokenFromString(MD_S8Skip(string, closer_check_off)); + if(next_closer.kind == MD_TokenKind_Newline) + { + closer_check_off += next_closer.raw_string.size; + off = closer_check_off; + got_closer = 1; + break; + } + } + } + + //- rjf: check separators and possible braces from higher parents + { + closer_check_off += MD_LexAdvanceFromSkips(MD_S8Skip(string, off), MD_TokenGroup_Irregular); + MD_Token potential_closer = MD_TokenFromString(MD_S8Skip(string, closer_check_off)); + if(potential_closer.kind == MD_TokenKind_Reserved) + { + MD_u8 c = potential_closer.raw_string.str[0]; + if(c == ',' || c == ';') + { + off = closer_check_off; + closer_check_off += potential_closer.raw_string.size; + break; + } + else if(c == '}' || c == ']'|| c == ')') + { + goto end_parse; + } + } + } + + } + + //- rjf: check for non-separator closers + if(!close_with_separator && !parse_all) + { + MD_u64 closer_check_off = off; + closer_check_off += MD_LexAdvanceFromSkips(MD_S8Skip(string, off), MD_TokenGroup_Irregular); + MD_Token potential_closer = MD_TokenFromString(MD_S8Skip(string, closer_check_off)); + if(potential_closer.kind == MD_TokenKind_Reserved) + { + MD_u8 c = potential_closer.raw_string.str[0]; + if(close_with_brace && c == '}') + { + closer_check_off += potential_closer.raw_string.size; + off = closer_check_off; + parent->flags |= MD_NodeFlag_HasBraceRight; + got_closer = 1; + break; + } + else if(close_with_paren && c == ']') + { + closer_check_off += potential_closer.raw_string.size; + off = closer_check_off; + parent->flags |= MD_NodeFlag_HasBracketRight; + got_closer = 1; + break; + } + else if(close_with_paren && c == ')') + { + closer_check_off += potential_closer.raw_string.size; + off = closer_check_off; + parent->flags |= MD_NodeFlag_HasParenRight; + got_closer = 1; + break; + } + } + } + + //- rjf: parse next child + MD_ParseResult child_parse = MD_ParseOneNode(arena, string, off); + MD_MessageListConcat(&result.errors, &child_parse.errors); + off += child_parse.string_advance; + + //- rjf: hook child into parent + if(!MD_NodeIsNil(child_parse.node)) + { + // NOTE(rjf): @error No unnamed set children of implicitly-delimited sets + if(close_with_separator && + child_parse.node->string.size == 0 && + child_parse.node->flags & (MD_NodeFlag_HasParenLeft | + MD_NodeFlag_HasParenRight | + MD_NodeFlag_HasBracketLeft | + MD_NodeFlag_HasBracketRight | + MD_NodeFlag_HasBraceLeft | + MD_NodeFlag_HasBraceRight )) + { + MD_String8 error_str = MD_S8Lit("Unnamed set children of implicitly-delimited sets are not legal."); + MD_Message *error = MD_MakeNodeError(arena, child_parse.node, MD_MessageKind_Warning, + error_str); + MD_MessageListPush(&result.errors, error); + } + + MD_PushChild(parent, child_parse.node); + parsed_child_count += 1; + } + + //- rjf: check trailing separator + MD_NodeFlags trailing_separator_flags = 0; + if(!close_with_separator) + { + off += MD_LexAdvanceFromSkips(MD_S8Skip(string, off), MD_TokenGroup_Irregular); + MD_Token trailing_separator = MD_TokenFromString(MD_S8Skip(string, off)); + if (trailing_separator.kind == MD_TokenKind_Reserved) + { + MD_u8 c = trailing_separator.string.str[0]; + if(c == ',') + { + trailing_separator_flags |= MD_NodeFlag_IsBeforeComma; + off += trailing_separator.raw_string.size; + } + else if(c == ';') + { + trailing_separator_flags |= MD_NodeFlag_IsBeforeSemicolon; + off += trailing_separator.raw_string.size; + } + } + } + + //- rjf: fill child flags + child_parse.node->flags |= next_child_flags | trailing_separator_flags; + + //- rjf: setup next_child_flags + next_child_flags = MD_NodeFlag_AfterFromBefore(trailing_separator_flags); + } + } + end_parse:; + + //- rjf: push missing closer error, if we have one + if(set_opener != 0 && got_closer == 0) + { + // NOTE(rjf): @error We didn't get a closer for the set + MD_String8 error_str = MD_S8Fmt(arena, "Unbalanced \"%c\"", set_opener); + MD_Message *error = MD_MakeTokenError(arena, string, initial_token, + MD_MessageKind_FatalError, error_str); + MD_MessageListPush(&result.errors, error); + } + + //- rjf: push empty implicit set error, + if(close_with_separator && parsed_child_count == 0) + { + // NOTE(rjf): @error No empty implicitly-delimited sets + MD_Message *error = MD_MakeTokenError(arena, string, initial_token, MD_MessageKind_Error, + MD_S8Lit("Empty implicitly-delimited node list")); + MD_MessageListPush(&result.errors, error); + } + + //- rjf: fill result info + result.node = parent; + result.string_advance = off - offset; + + return result; +} + +MD_FUNCTION MD_ParseResult +MD_ParseOneNode(MD_Arena *arena, MD_String8 string, MD_u64 offset) +{ + MD_ParseResult result = MD_ParseResultZero(); + MD_u64 off = offset; + + //- rjf: parse pre-comment + MD_String8 prev_comment = MD_ZERO_STRUCT; + { + MD_Token comment_token = MD_ZERO_STRUCT; + for(;off < string.size;) + { + MD_Token token = MD_TokenFromString(MD_S8Skip(string, off)); + if(token.kind == MD_TokenKind_Comment) + { + off += token.raw_string.size; + comment_token = token; + } + else if(token.kind == MD_TokenKind_Newline) + { + off += token.raw_string.size; + MD_Token next_token = MD_TokenFromString(MD_S8Skip(string, off)); + if(next_token.kind == MD_TokenKind_Comment) + { + // NOTE(mal): If more than one comment, use the last comment + comment_token = next_token; + } + else if(next_token.kind == MD_TokenKind_Newline) + { + MD_MemoryZeroStruct(&comment_token); + } + } + else if((token.kind & MD_TokenGroup_Whitespace) != 0) + { + off += token.raw_string.size; + } + else + { + break; + } + prev_comment = comment_token.string; + } + } + + //- rjf: parse tag list + MD_Node *first_tag = MD_NilNode(); + MD_Node *last_tag = MD_NilNode(); + { + for(;off < string.size;) + { + //- rjf: parse @ symbol, signifying start of tag + off += MD_LexAdvanceFromSkips(MD_S8Skip(string, off), MD_TokenGroup_Irregular); + MD_Token next_token = MD_TokenFromString(MD_S8Skip(string, off)); + if(next_token.kind != MD_TokenKind_Reserved || + next_token.string.str[0] != '@') + { + break; + } + off += next_token.raw_string.size; + + //- rjf: parse string of tag node + MD_Token name = MD_TokenFromString(MD_S8Skip(string, off)); + MD_u64 name_off = off; + if((name.kind & MD_TokenGroup_Label) == 0) + { + // NOTE(rjf): @error Improper token for tag string + MD_String8 error_str = MD_S8Fmt(arena, "\"%.*s\" is not a proper tag label", + MD_S8VArg(name.raw_string)); + MD_Message *error = MD_MakeTokenError(arena, string, name, MD_MessageKind_Error, error_str); + MD_MessageListPush(&result.errors, error); + break; + } + off += name.raw_string.size; + + //- rjf: build tag + MD_Node *tag = MD_MakeNode(arena, MD_NodeKind_Tag, name.string, name.raw_string, name_off); + + //- rjf: parse tag arguments + MD_Token open_paren = MD_TokenFromString(MD_S8Skip(string, off)); + MD_ParseResult args_parse = MD_ParseResultZero(); + if(open_paren.kind == MD_TokenKind_Reserved && + open_paren.string.str[0] == '(') + { + args_parse = MD_ParseNodeSet(arena, string, off, tag, MD_ParseSetRule_EndOnDelimiter); + MD_MessageListConcat(&result.errors, &args_parse.errors); + } + off += args_parse.string_advance; + + //- rjf: push tag to result + MD_NodeDblPushBack(first_tag, last_tag, tag); + } + } + + //- rjf: parse node + MD_Node *parsed_node = MD_NilNode(); + MD_ParseResult children_parse = MD_ParseResultZero(); + retry:; + { + //- rjf: try to parse an unnamed set + off += MD_LexAdvanceFromSkips(MD_S8Skip(string, off), MD_TokenGroup_Irregular); + MD_Token unnamed_set_opener = MD_TokenFromString(MD_S8Skip(string, off)); + if(unnamed_set_opener.kind == MD_TokenKind_Reserved) + { + MD_u8 c = unnamed_set_opener.string.str[0]; + if (c == '(' || c == '{' || c == '[') + { + parsed_node = MD_MakeNode(arena, MD_NodeKind_Main, MD_S8Lit(""), MD_S8Lit(""), + unnamed_set_opener.raw_string.str - string.str); + children_parse = MD_ParseNodeSet(arena, string, off, parsed_node, + MD_ParseSetRule_EndOnDelimiter); + off += children_parse.string_advance; + MD_MessageListConcat(&result.errors, &children_parse.errors); + } + else if (c == ')' || c == '}' || c == ']') + { + // NOTE(rjf): @error Unexpected set closing symbol + MD_String8 error_str = MD_S8Fmt(arena, "Unbalanced \"%c\"", c); + MD_Message *error = MD_MakeTokenError(arena, string, unnamed_set_opener, + MD_MessageKind_FatalError, error_str); + MD_MessageListPush(&result.errors, error); + off += unnamed_set_opener.raw_string.size; + } + else + { + // NOTE(rjf): @error Unexpected reserved symbol + MD_String8 error_str = MD_S8Fmt(arena, "Unexpected reserved symbol \"%c\"", c); + MD_Message *error = MD_MakeTokenError(arena, string, unnamed_set_opener, + MD_MessageKind_Error, error_str); + MD_MessageListPush(&result.errors, error); + off += unnamed_set_opener.raw_string.size; + } + goto end_parse; + + } + + //- rjf: try to parse regular node, with/without children + off += MD_LexAdvanceFromSkips(MD_S8Skip(string, off), MD_TokenGroup_Irregular); + MD_Token label_name = MD_TokenFromString(MD_S8Skip(string, off)); + if((label_name.kind & MD_TokenGroup_Label) != 0) + { + off += label_name.raw_string.size; + parsed_node = MD_MakeNode(arena, MD_NodeKind_Main, label_name.string, label_name.raw_string, + label_name.raw_string.str - string.str); + parsed_node->flags |= label_name.node_flags; + + //- rjf: try to parse children for this node + MD_u64 colon_check_off = off; + colon_check_off += MD_LexAdvanceFromSkips(MD_S8Skip(string, colon_check_off), MD_TokenGroup_Irregular); + MD_Token colon = MD_TokenFromString(MD_S8Skip(string, colon_check_off)); + if(colon.kind == MD_TokenKind_Reserved && + colon.string.str[0] == ':') + { + colon_check_off += colon.raw_string.size; + off = colon_check_off; + + children_parse = MD_ParseNodeSet(arena, string, off, parsed_node, + MD_ParseSetRule_EndOnDelimiter); + off += children_parse.string_advance; + MD_MessageListConcat(&result.errors, &children_parse.errors); + } + goto end_parse; + } + + //- rjf: collect bad token + MD_Token bad_token = MD_TokenFromString(MD_S8Skip(string, off)); + if(bad_token.kind & MD_TokenGroup_Error) + { + off += bad_token.raw_string.size; + + switch (bad_token.kind) + { + case MD_TokenKind_BadCharacter: + { + MD_String8List bytes = {0}; + for(int i_byte = 0; i_byte < bad_token.raw_string.size; ++i_byte) + { + MD_u8 b = bad_token.raw_string.str[i_byte]; + MD_S8ListPush(arena, &bytes, MD_CStyleHexStringFromU64(arena, b, 1)); + } + + MD_StringJoin join = MD_ZERO_STRUCT; + join.mid = MD_S8Lit(" "); + MD_String8 byte_string = MD_S8ListJoin(arena, bytes, &join); + + // NOTE(rjf): @error Bad character + MD_String8 error_str = MD_S8Fmt(arena, "Non-ASCII character \"%.*s\"", + MD_S8VArg(byte_string)); + MD_Message *error = MD_MakeTokenError(arena, string, bad_token, MD_MessageKind_Error, + error_str); + MD_MessageListPush(&result.errors, error); + }break; + + case MD_TokenKind_BrokenComment: + { + // NOTE(rjf): @error Broken Comments + MD_Message *error = MD_MakeTokenError(arena, string, bad_token, MD_MessageKind_Error, + MD_S8Lit("Unterminated comment")); + MD_MessageListPush(&result.errors, error); + }break; + + case MD_TokenKind_BrokenStringLiteral: + { + // NOTE(rjf): @error Broken String Literals + MD_Message *error = MD_MakeTokenError(arena, string, bad_token, MD_MessageKind_Error, + MD_S8Lit("Unterminated string literal")); + MD_MessageListPush(&result.errors, error); + }break; + } + goto retry; + } + } + + end_parse:; + + //- rjf: parse comments after nodes. + MD_String8 next_comment = MD_ZERO_STRUCT; + { + MD_Token comment_token = MD_ZERO_STRUCT; + for(;;) + { + MD_Token token = MD_TokenFromString(MD_S8Skip(string, off)); + if(token.kind == MD_TokenKind_Comment) + { + comment_token = token; + off += token.raw_string.size; + break; + } + + else if(token.kind == MD_TokenKind_Newline) + { + break; + } + else if((token.kind & MD_TokenGroup_Whitespace) != 0) + { + off += token.raw_string.size; + } + else + { + break; + } + } + next_comment = comment_token.string; + } + + //- rjf: fill result + parsed_node->prev_comment = prev_comment; + parsed_node->next_comment = next_comment; + result.node = parsed_node; + if(!MD_NodeIsNil(result.node)) + { + result.node->first_tag = first_tag; + result.node->last_tag = last_tag; + for(MD_Node *tag = first_tag; !MD_NodeIsNil(tag); tag = tag->next) + { + tag->parent = result.node; + } + } + result.string_advance = off - offset; + + return result; +} + +MD_FUNCTION MD_ParseResult +MD_ParseWholeString(MD_Arena *arena, MD_String8 filename, MD_String8 contents) +{ + MD_Node *root = MD_MakeNode(arena, MD_NodeKind_File, filename, contents, 0); + MD_ParseResult result = MD_ParseNodeSet(arena, contents, 0, root, MD_ParseSetRule_Global); + result.node = root; + for(MD_Message *error = result.errors.first; error != 0; error = error->next) + { + if(MD_NodeIsNil(error->node->parent)) + { + error->node->parent = root; + } + } + return result; +} + +MD_FUNCTION MD_ParseResult +MD_ParseWholeFile(MD_Arena *arena, MD_String8 filename) +{ + MD_String8 file_contents = MD_LoadEntireFile(arena, filename); + MD_ParseResult parse = MD_ParseWholeString(arena, filename, file_contents); + if(file_contents.str == 0) + { + // NOTE(rjf): @error File failing to load + MD_String8 error_str = MD_S8Fmt(arena, "Could not read file \"%.*s\"", MD_S8VArg(filename)); + MD_Message *error = MD_MakeNodeError(arena, parse.node, MD_MessageKind_FatalError, + error_str); + MD_MessageListPush(&parse.errors, error); + } + return parse; +} + +//~ Messages (Errors/Warnings) + +MD_FUNCTION MD_Node* +MD_MakeErrorMarkerNode(MD_Arena *arena, MD_String8 parse_contents, MD_u64 offset) +{ + MD_Node *result = MD_MakeNode(arena, MD_NodeKind_ErrorMarker, MD_S8Lit(""), parse_contents, + offset); + return(result); +} + +MD_FUNCTION MD_Message* +MD_MakeNodeError(MD_Arena *arena, MD_Node *node, MD_MessageKind kind, MD_String8 str) +{ + MD_Message *error = MD_PushArrayZero(arena, MD_Message, 1); + error->node = node; + error->kind = kind; + error->string = str; + return error; +} + +MD_FUNCTION MD_Message * +MD_MakeTokenError(MD_Arena *arena, MD_String8 parse_contents, MD_Token token, + MD_MessageKind kind, MD_String8 str) +{ + MD_u64 offset = token.raw_string.str - parse_contents.str; + MD_Node *err_node = MD_MakeErrorMarkerNode(arena, parse_contents, offset); + return MD_MakeNodeError(arena, err_node, kind, str); +} + +MD_FUNCTION void +MD_MessageListPush(MD_MessageList *list, MD_Message *message) +{ + MD_QueuePush(list->first, list->last, message); + if(message->kind > list->max_message_kind) + { + list->max_message_kind = message->kind; + } + list->node_count += 1; +} + +MD_FUNCTION void +MD_MessageListConcat(MD_MessageList *list, MD_MessageList *to_push) +{ + if(to_push->node_count != 0) + { + if(list->last != 0) + { + list->last->next = to_push->first; + list->last = to_push->last; + list->node_count += to_push->node_count; + if(to_push->max_message_kind > list->max_message_kind) + { + list->max_message_kind = to_push->max_message_kind; + } + } + else + { + *list = *to_push; + } + MD_MemoryZeroStruct(to_push); + } +} + +//~ Location Conversions + +MD_FUNCTION MD_CodeLoc +MD_CodeLocFromFileOffset(MD_String8 filename, MD_u8 *base, MD_u64 offset) +{ + MD_CodeLoc loc; + loc.filename = filename; + loc.line = 1; + loc.column = 1; + if(base != 0) + { + MD_u8 *at = base + offset; + for(MD_u64 i = 0; base+i < at && base[i]; i += 1) + { + if(base[i] == '\n') + { + loc.line += 1; + loc.column = 1; + } + else + { + loc.column += 1; + } + } + } + return loc; +} + +MD_FUNCTION MD_CodeLoc +MD_CodeLocFromNode(MD_Node *node) +{ + MD_Node *file_root = MD_NilNode(); + for(MD_Node *parent = node->parent; !MD_NodeIsNil(parent); parent = parent->parent) + { + if(parent->kind == MD_NodeKind_File) + { + file_root = parent; + break; + } + } + MD_Node *first_tag = file_root->first_tag; + MD_CodeLoc loc = {0}; + if(MD_NodeIsNil(first_tag)) + { + loc = MD_CodeLocFromFileOffset(file_root->string, file_root->raw_string.str, node->offset); + } + else + { + loc = MD_CodeLocFromFileOffset(file_root->string, first_tag->raw_string.str, node->offset); + } + return loc; +} + +//~ Tree/List Building + +MD_FUNCTION MD_b32 +MD_NodeIsNil(MD_Node *node) +{ + return(node == 0 || node == &_md_nil_node || node->kind == MD_NodeKind_Nil); +} + +MD_FUNCTION MD_Node * +MD_NilNode(void) { return &_md_nil_node; } + +MD_FUNCTION MD_Node * +MD_MakeNode(MD_Arena *arena, MD_NodeKind kind, MD_String8 string, MD_String8 raw_string, + MD_u64 offset) +{ + MD_Node *node = MD_PushArrayZero(arena, MD_Node, 1); + node->kind = kind; + node->string = string; + node->raw_string = raw_string; + node->next = node->prev = node->parent = + node->first_child = node->last_child = + node->first_tag = node->last_tag = node->ref_target = MD_NilNode(); + node->offset = offset; + return node; +} + +MD_FUNCTION void +MD_PushChild(MD_Node *parent, MD_Node *new_child) +{ + if (!MD_NodeIsNil(new_child)) + { + MD_NodeDblPushBack(parent->first_child, parent->last_child, new_child); + new_child->parent = parent; + } +} + +MD_FUNCTION void +MD_PushTag(MD_Node *node, MD_Node *tag) +{ + if (!MD_NodeIsNil(tag)) + { + MD_NodeDblPushBack(node->first_tag, node->last_tag, tag); + tag->parent = node; + } +} + +MD_FUNCTION MD_Node* +MD_MakeList(MD_Arena *arena) +{ + MD_String8 empty = {0}; + MD_Node *result = MD_MakeNode(arena, MD_NodeKind_List, empty, empty, 0); + return(result); +} + +MD_FUNCTION void +MD_ListConcatInPlace(MD_Node *list, MD_Node *to_push) +{ + if (!MD_NodeIsNil(to_push->first_child)) + { + if (!MD_NodeIsNil(list->first_child)) + { + list->last_child->next = to_push->first_child; + list->last_child = to_push->last_child; + } + else + { + list->first_child = to_push->first_child; + list->last_child = to_push->last_child; + } + to_push->first_child = to_push->last_child = MD_NilNode(); + } +} + +MD_FUNCTION MD_Node* +MD_PushNewReference(MD_Arena *arena, MD_Node *list, MD_Node *target) +{ + MD_Node *n = MD_MakeNode(arena, MD_NodeKind_Reference, target->string, target->raw_string, + target->offset); + n->ref_target = target; + MD_PushChild(list, n); + return(n); +} + +//~ Introspection Helpers + +MD_FUNCTION MD_Node * +MD_FirstNodeWithString(MD_Node *first, MD_String8 string, MD_MatchFlags flags) +{ + MD_Node *result = MD_NilNode(); + for(MD_Node *node = first; !MD_NodeIsNil(node); node = node->next) + { + if(MD_S8Match(string, node->string, flags)) + { + result = node; + break; + } + } + return result; +} + +MD_FUNCTION MD_Node * +MD_NodeAtIndex(MD_Node *first, int n) +{ + MD_Node *result = MD_NilNode(); + if(n >= 0) + { + int idx = 0; + for(MD_Node *node = first; !MD_NodeIsNil(node); node = node->next, idx += 1) + { + if(idx == n) + { + result = node; + break; + } + } + } + return result; +} + +MD_FUNCTION MD_Node * +MD_FirstNodeWithFlags(MD_Node *first, MD_NodeFlags flags) +{ + MD_Node *result = MD_NilNode(); + for(MD_Node *n = first; !MD_NodeIsNil(n); n = n->next) + { + if(n->flags & flags) + { + result = n; + break; + } + } + return result; +} + +MD_FUNCTION int +MD_IndexFromNode(MD_Node *node) +{ + int idx = 0; + for(MD_Node *last = node->prev; !MD_NodeIsNil(last); last = last->prev, idx += 1); + return idx; +} + +MD_FUNCTION MD_Node * +MD_RootFromNode(MD_Node *node) +{ + MD_Node *parent = node; + for(MD_Node *p = parent; !MD_NodeIsNil(p); p = p->parent) + { + parent = p; + } + return parent; +} + +MD_FUNCTION MD_Node * +MD_ChildFromString(MD_Node *node, MD_String8 child_string, MD_MatchFlags flags) +{ + return MD_FirstNodeWithString(node->first_child, child_string, flags); +} + +MD_FUNCTION MD_Node * +MD_TagFromString(MD_Node *node, MD_String8 tag_string, MD_MatchFlags flags) +{ + return MD_FirstNodeWithString(node->first_tag, tag_string, flags); +} + +MD_FUNCTION MD_Node * +MD_ChildFromIndex(MD_Node *node, int n) +{ + return MD_NodeAtIndex(node->first_child, n); +} + +MD_FUNCTION MD_Node * +MD_TagFromIndex(MD_Node *node, int n) +{ + return MD_NodeAtIndex(node->first_tag, n); +} + +MD_FUNCTION MD_Node * +MD_TagArgFromIndex(MD_Node *node, MD_String8 tag_string, MD_MatchFlags flags, int n) +{ + MD_Node *tag = MD_TagFromString(node, tag_string, flags); + return MD_ChildFromIndex(tag, n); +} + +MD_FUNCTION MD_Node * +MD_TagArgFromString(MD_Node *node, MD_String8 tag_string, MD_MatchFlags tag_str_flags, MD_String8 arg_string, MD_MatchFlags arg_str_flags) +{ + MD_Node *tag = MD_TagFromString(node, tag_string, tag_str_flags); + MD_Node *arg = MD_ChildFromString(tag, arg_string, arg_str_flags); + return arg; +} + +MD_FUNCTION MD_b32 +MD_NodeHasChild(MD_Node *node, MD_String8 string, MD_MatchFlags flags) +{ + return !MD_NodeIsNil(MD_ChildFromString(node, string, flags)); +} + +MD_FUNCTION MD_b32 +MD_NodeHasTag(MD_Node *node, MD_String8 string, MD_MatchFlags flags) +{ + return !MD_NodeIsNil(MD_TagFromString(node, string, flags)); +} + +MD_FUNCTION MD_i64 +MD_ChildCountFromNode(MD_Node *node) +{ + MD_i64 result = 0; + for(MD_EachNode(child, node->first_child)) + { + result += 1; + } + return result; +} + +MD_FUNCTION MD_i64 +MD_TagCountFromNode(MD_Node *node) +{ + MD_i64 result = 0; + for(MD_EachNode(tag, node->first_tag)) + { + result += 1; + } + return result; +} + +MD_FUNCTION MD_Node * +MD_ResolveNodeFromReference(MD_Node *node) +{ + MD_u64 safety = 100; + for(; safety > 0 && node->kind == MD_NodeKind_Reference; + safety -= 1, node = node->ref_target); + MD_Node *result = node; + return(result); +} + +MD_FUNCTION MD_Node* +MD_NodeNextWithLimit(MD_Node *node, MD_Node *opl) +{ + node = node->next; + if (node == opl) + { + node = MD_NilNode(); + } + return(node); +} + +MD_FUNCTION MD_String8 +MD_PrevCommentFromNode(MD_Node *node) +{ + return(node->prev_comment); +} + +MD_FUNCTION MD_String8 +MD_NextCommentFromNode(MD_Node *node) +{ + return(node->next_comment); +} + +//~ Error/Warning Helpers + +MD_FUNCTION MD_String8 +MD_StringFromMessageKind(MD_MessageKind kind) +{ + MD_String8 result = MD_ZERO_STRUCT; + switch (kind) + { + default: break; + case MD_MessageKind_Note: result = MD_S8Lit("note"); break; + case MD_MessageKind_Warning: result = MD_S8Lit("warning"); break; + case MD_MessageKind_Error: result = MD_S8Lit("error"); break; + case MD_MessageKind_FatalError: result = MD_S8Lit("fatal error"); break; + } + return(result); +} + +MD_FUNCTION MD_String8 +MD_FormatMessage(MD_Arena *arena, MD_CodeLoc loc, MD_MessageKind kind, MD_String8 string) +{ + MD_String8 kind_string = MD_StringFromMessageKind(kind); + MD_String8 result = MD_S8Fmt(arena, "" MD_FmtCodeLoc " %.*s: %.*s\n", + MD_CodeLocVArg(loc), MD_S8VArg(kind_string), MD_S8VArg(string)); + return(result); +} + +#if !MD_DISABLE_PRINT_HELPERS + +MD_FUNCTION void +MD_PrintMessage(FILE *file, MD_CodeLoc code_loc, MD_MessageKind kind, MD_String8 string) +{ + MD_ArenaTemp scratch = MD_GetScratch(0, 0); + MD_String8 message = MD_FormatMessage(scratch.arena, code_loc, kind, string); + fwrite(message.str, message.size, 1, file); + MD_ReleaseScratch(scratch); +} + +MD_FUNCTION void +MD_PrintMessageFmt(FILE *file, MD_CodeLoc code_loc, MD_MessageKind kind, char *fmt, ...) +{ + MD_ArenaTemp scratch = MD_GetScratch(0, 0); + va_list args; + va_start(args, fmt); + MD_String8 string = MD_S8FmtV(scratch.arena, fmt, args); + va_end(args); + MD_String8 message = MD_FormatMessage(scratch.arena, code_loc, kind, string); + fwrite(message.str, message.size, 1, file); + MD_ReleaseScratch(scratch); +} + +#endif + +//~ Tree Comparison/Verification + +MD_FUNCTION MD_b32 +MD_NodeMatch(MD_Node *a, MD_Node *b, MD_MatchFlags flags) +{ + MD_b32 result = 0; + if(a->kind == b->kind && MD_S8Match(a->string, b->string, flags)) + { + result = 1; + if(result && flags & MD_NodeMatchFlag_NodeFlags) + { + result = result && a->flags == b->flags; + } + if(result && a->kind != MD_NodeKind_Tag && (flags & MD_NodeMatchFlag_Tags)) + { + for(MD_Node *a_tag = a->first_tag, *b_tag = b->first_tag; + !MD_NodeIsNil(a_tag) || !MD_NodeIsNil(b_tag); + a_tag = a_tag->next, b_tag = b_tag->next) + { + if(MD_NodeMatch(a_tag, b_tag, flags)) + { + if(flags & MD_NodeMatchFlag_TagArguments) + { + for(MD_Node *a_tag_arg = a_tag->first_child, *b_tag_arg = b_tag->first_child; + !MD_NodeIsNil(a_tag_arg) || !MD_NodeIsNil(b_tag_arg); + a_tag_arg = a_tag_arg->next, b_tag_arg = b_tag_arg->next) + { + if(!MD_NodeDeepMatch(a_tag_arg, b_tag_arg, flags)) + { + result = 0; + goto end; + } + } + } + } + else + { + result = 0; + goto end; + } + } + } + } + end:; + return result; +} + +MD_FUNCTION MD_b32 +MD_NodeDeepMatch(MD_Node *a, MD_Node *b, MD_MatchFlags flags) +{ + MD_b32 result = MD_NodeMatch(a, b, flags); + if(result) + { + for(MD_Node *a_child = a->first_child, *b_child = b->first_child; + !MD_NodeIsNil(a_child) || !MD_NodeIsNil(b_child); + a_child = a_child->next, b_child = b_child->next) + { + if(!MD_NodeDeepMatch(a_child, b_child, flags)) + { + result = 0; + goto end; + } + } + } + end:; + return result; +} + +//~ Expression Parsing + +MD_FUNCTION void +MD_ExprOprPush(MD_Arena *arena, MD_ExprOprList *list, + MD_ExprOprKind kind, MD_u64 precedence, MD_String8 string, + MD_u32 op_id, void *op_ptr) +{ + MD_ExprOpr *op = MD_PushArrayZero(arena, MD_ExprOpr, 1); + MD_QueuePush(list->first, list->last, op); + list->count += 1; + op->op_id = op_id; + op->kind = kind; + op->precedence = precedence; + op->string = string; + op->op_ptr = op_ptr; +} + +MD_GLOBAL MD_BakeOperatorErrorHandler md_bake_operator_error_handler = 0; + +MD_FUNCTION MD_BakeOperatorErrorHandler +MD_ExprSetBakeOperatorErrorHandler(MD_BakeOperatorErrorHandler handler){ + MD_BakeOperatorErrorHandler old_handler = md_bake_operator_error_handler; + md_bake_operator_error_handler = handler; + return old_handler; +} + +MD_FUNCTION MD_ExprOprTable +MD_ExprBakeOprTableFromList(MD_Arena *arena, MD_ExprOprList *list) +{ + MD_ExprOprTable result = MD_ZERO_STRUCT; + + // TODO(allen): @upgrade_potential(minor) + + for(MD_ExprOpr *op = list->first; + op != 0; + op = op->next) + { + MD_ExprOprKind op_kind = op->kind; + MD_String8 op_s = op->string; + + // error checking + MD_String8 error_str = MD_ZERO_STRUCT; + + MD_Token op_token = MD_TokenFromString(op_s); + MD_b32 is_setlike_op = + (op_s.size == 2 && + (MD_S8Match(op_s, MD_S8Lit("[]"), 0) || MD_S8Match(op_s, MD_S8Lit("()"), 0) || + MD_S8Match(op_s, MD_S8Lit("[)"), 0) || MD_S8Match(op_s, MD_S8Lit("(]"), 0) || + MD_S8Match(op_s, MD_S8Lit("{}"), 0))); + + if(op_kind != MD_ExprOprKind_Prefix && op_kind != MD_ExprOprKind_Postfix && + op_kind != MD_ExprOprKind_Binary && op_kind != MD_ExprOprKind_BinaryRightAssociative) + { + error_str = MD_S8Fmt(arena, "Ignored operator \"%.*s\" because its kind value (%d) does not match " + "any valid operator kind", MD_S8VArg(op_s), op_kind); + } + else if(is_setlike_op && op_kind != MD_ExprOprKind_Postfix) + { + error_str = + MD_S8Fmt(arena, "Ignored operator \"%.*s\". \"%.*s\" is only allowed as unary postfix", + MD_S8VArg(op_s), MD_S8VArg(op_s)); + } + else if(!is_setlike_op && + (op_token.kind != MD_TokenKind_Identifier && op_token.kind != MD_TokenKind_Symbol)) + { + error_str = MD_S8Fmt(arena, "Ignored operator \"%.*s\" because it is neither a symbol " + "nor an identifier token", MD_S8VArg(op_s)); + } + else if(!is_setlike_op && op_token.string.size < op_s.size) + { + error_str = MD_S8Fmt(arena, "Ignored operator \"%.*s\" because its prefix \"%.*s\" " + "constitutes a standalone operator", + MD_S8VArg(op_s), MD_S8VArg(op_token.string)); + } + else + { + for(MD_ExprOpr *op2 = list->first; + op2 != op; + op2 = op2->next) + { // NOTE(mal): O(n^2) + MD_ExprOprKind op2_kind = op2->kind; + MD_String8 op2_s = op2->string; + if(op->precedence == op2->precedence && + ((op_kind == MD_ExprOprKind_Binary && + op2_kind == MD_ExprOprKind_BinaryRightAssociative) || + (op_kind == MD_ExprOprKind_BinaryRightAssociative && + op2_kind == MD_ExprOprKind_Binary))) + { + error_str = + MD_S8Fmt(arena, "Ignored binary operator \"%.*s\" because another binary operator" + "has the same precedence and different associativity", MD_S8VArg(op_s)); + } + else if(MD_S8Match(op_s, op2_s, 0)) + { + if(op_kind == op2_kind) + { + error_str = MD_S8Fmt(arena, "Ignored repeat operator \"%.*s\"", MD_S8VArg(op_s)); + } + else if(op_kind != MD_ExprOprKind_Prefix && op2_kind != MD_ExprOprKind_Prefix) + { + error_str = + MD_S8Fmt(arena, "Ignored conflicting repeat operator \"%.*s\". There can't be" + "more than one posfix/binary operator associated to the same token", + MD_S8VArg(op_s)); + } + } + } + } + + // save error + if(error_str.size != 0 && md_bake_operator_error_handler) + { + md_bake_operator_error_handler(MD_MessageKind_Warning, error_str); + } + + // save list + else + { + MD_ExprOprList *list = result.table + op_kind; + MD_ExprOpr *op_node_copy = MD_PushArray(arena, MD_ExprOpr, 1); + *op_node_copy = *op; + MD_QueuePush(list->first, list->last, op_node_copy); + list->count += 1; + } + } + + return(result); +} + +MD_FUNCTION MD_ExprOpr* +MD_ExprOprFromKindString(MD_ExprOprTable *table, MD_ExprOprKind kind, MD_String8 s) +{ + // TODO(allen): @upgrade_potential + + // NOTE(mal): Look for operator on one or all (kind == MD_ExprOprKind_Null) tables + MD_ExprOpr *result = 0; + for(MD_ExprOprKind cur_kind = (MD_ExprOprKind)(MD_ExprOprKind_Null + 1); + cur_kind < MD_ExprOprKind_COUNT; + cur_kind = (MD_ExprOprKind)(cur_kind + 1)) + { + if(kind == MD_ExprOprKind_Null || kind == cur_kind) + { + MD_ExprOprList *op_list = table->table+cur_kind; + for(MD_ExprOpr *op = op_list->first; + op != 0; + op = op->next) + { + if(MD_S8Match(op->string, s, 0)) + { + result = op; + goto dbl_break; + } + } + } + } + dbl_break:; + return result; +} + +MD_FUNCTION MD_ExprParseResult +MD_ExprParse(MD_Arena *arena, MD_ExprOprTable *op_table, MD_Node *first, MD_Node *opl) +{ + // setup a context + MD_ExprParseCtx ctx = MD_ExprParse_MakeContext(op_table); + + // parse the top level + MD_Expr *expr = MD_ExprParse_TopLevel(arena, &ctx, first, opl); + + // fill result + MD_ExprParseResult result = {0}; + result.expr = expr; + result.errors = ctx.errors; + return(result); +} + +MD_FUNCTION MD_Expr* +MD_Expr_NewLeaf(MD_Arena *arena, MD_Node *node) +{ + MD_Expr *result = MD_PushArrayZero(arena, MD_Expr, 1); + result->md_node = node; + return(result); +} + +MD_FUNCTION MD_Expr* +MD_Expr_NewOpr(MD_Arena *arena, MD_ExprOpr *op, MD_Node *op_node, MD_Expr *l, MD_Expr *r) +{ + MD_Expr *result = MD_PushArrayZero(arena, MD_Expr, 1); + result->op = op; + result->md_node = op_node; + result->parent = 0; + result->left = l; + result->right = r; + if (l != 0) + { + MD_Assert(l->parent == 0); + l->parent = result; + } + if(r != 0) + { + MD_Assert(r->parent == 0); + r->parent = result; + } + return(result); +} + +MD_FUNCTION MD_ExprParseCtx +MD_ExprParse_MakeContext(MD_ExprOprTable *op_table) +{ + MD_ExprParseCtx result = MD_ZERO_STRUCT; + result.op_table = op_table; + + result.accel.postfix_set_ops[0] = MD_ExprOprFromKindString(op_table, MD_ExprOprKind_Postfix, MD_S8Lit("()")); + result.accel.postfix_set_flags[0] = MD_NodeFlag_HasParenLeft | MD_NodeFlag_HasParenRight; + + result.accel.postfix_set_ops[1] = MD_ExprOprFromKindString(op_table, MD_ExprOprKind_Postfix, MD_S8Lit("[]")); + result.accel.postfix_set_flags[1] = MD_NodeFlag_HasBracketLeft | MD_NodeFlag_HasBracketRight; + + result.accel.postfix_set_ops[2] = MD_ExprOprFromKindString(op_table, MD_ExprOprKind_Postfix, MD_S8Lit("{}")); + result.accel.postfix_set_flags[2] = MD_NodeFlag_HasBraceLeft | MD_NodeFlag_HasBraceRight; + + result.accel.postfix_set_ops[3] = MD_ExprOprFromKindString(op_table, MD_ExprOprKind_Postfix, MD_S8Lit("[)")); + result.accel.postfix_set_flags[3] = MD_NodeFlag_HasBracketLeft | MD_NodeFlag_HasParenRight; + + result.accel.postfix_set_ops[4] = MD_ExprOprFromKindString(op_table, MD_ExprOprKind_Postfix, MD_S8Lit("(]")); + result.accel.postfix_set_flags[4] = MD_NodeFlag_HasParenLeft | MD_NodeFlag_HasBracketRight; + + return(result); +} + +MD_FUNCTION MD_Expr* +MD_ExprParse_TopLevel(MD_Arena *arena, MD_ExprParseCtx *ctx, MD_Node *first, MD_Node *opl) +{ + // parse the node range + MD_Node *iter = first; + MD_Expr *expr = MD_ExprParse_MinPrecedence(arena, ctx, &iter, first, opl, 0); + + // check for failed-to-reach-end error + if(ctx->errors.max_message_kind == MD_MessageKind_Null) + { + MD_Node *stop_node = iter; + if(!MD_NodeIsNil(stop_node)) + { + MD_String8 error_str = MD_S8Lit("Expected binary or unary postfix operator."); + MD_Message *error = MD_MakeNodeError(arena,stop_node,MD_MessageKind_FatalError,error_str); + MD_MessageListPush(&ctx->errors, error); + } + } + + return(expr); +} + +MD_FUNCTION MD_b32 +MD_ExprParse_OprConsume(MD_ExprParseCtx *ctx, MD_Node **iter, MD_Node *opl, + MD_ExprOprKind kind, MD_u32 min_precedence, MD_ExprOpr **op_out) +{ + MD_b32 result = 0; + MD_Node *node = *iter; + if(!MD_NodeIsNil(node)) + { + MD_ExprOpr *op = MD_ExprOprFromKindString(ctx->op_table, kind, node->string); + if(op != 0 && op->precedence >= min_precedence) + { + result = 1; + *op_out = op; + *iter= MD_NodeNextWithLimit(*iter, opl); + } + } + return result; +} + +MD_FUNCTION MD_Expr* +MD_ExprParse_Atom(MD_Arena *arena, MD_ExprParseCtx *ctx, MD_Node **iter, + MD_Node *first, MD_Node *opl) +{ + // TODO(allen): nil + MD_Expr* result = 0; + + MD_Node *node = *iter; + MD_ExprOpr *op = 0; + + if(MD_NodeIsNil(node)) + { + MD_Node *last = first; + for (;last->next != opl; last = last->next); + + MD_Node *error_node = last->next; + if (MD_NodeIsNil(error_node)) + { + MD_Node *root = MD_RootFromNode(node); + MD_String8 parse_contents = root->raw_string; + MD_u64 offset = last->offset + last->raw_string.size; + error_node = MD_MakeErrorMarkerNode(arena, parse_contents, offset); + } + + MD_String8 error_str = MD_S8Lit("Unexpected end of expression."); + MD_Message *error = MD_MakeNodeError(arena, error_node, MD_MessageKind_FatalError, + error_str); + MD_MessageListPush(&ctx->errors, error); + } + else if((node->flags & MD_NodeFlag_HasParenLeft) && + (node->flags & MD_NodeFlag_HasParenRight)) + { // NOTE(mal): Parens + *iter = MD_NodeNextWithLimit(*iter, opl); + result = MD_ExprParse_TopLevel(arena, ctx, node->first_child, MD_NilNode()); + } + else if(((node->flags & MD_NodeFlag_HasBraceLeft) && (node->flags & MD_NodeFlag_HasBraceRight)) || + ((node->flags & MD_NodeFlag_HasBracketLeft) && (node->flags & MD_NodeFlag_HasBracketRight)) || + ((node->flags & MD_NodeFlag_HasBracketLeft) && (node->flags & MD_NodeFlag_HasParenRight)) || + ((node->flags & MD_NodeFlag_HasParenLeft) && (node->flags & MD_NodeFlag_HasBracketRight))) + { // NOTE(mal): Unparsed leaf sets ({...}, [...], [...), (...]) + *iter = MD_NodeNextWithLimit(*iter, opl); + result = MD_Expr_NewLeaf(arena, node); + } + else if(MD_ExprParse_OprConsume(ctx, iter, opl, MD_ExprOprKind_Prefix, 1, &op)) + { + MD_u32 min_precedence = op->precedence + 1; + MD_Expr *sub_expr = + MD_ExprParse_MinPrecedence(arena, ctx, iter, first, opl, min_precedence); + if(ctx->errors.max_message_kind == MD_MessageKind_Null) + { + result = MD_Expr_NewOpr(arena, op, node, sub_expr, 0); + } + } + else if(MD_ExprParse_OprConsume(ctx, iter, opl, MD_ExprOprKind_Null, 1, &op)) + { + MD_String8 error_str = MD_S8Fmt(arena, "Expected leaf. Got operator \"%.*s\".", MD_S8VArg(node->string)); + + MD_Message *error = MD_MakeNodeError(arena, node, MD_MessageKind_FatalError, error_str); + MD_MessageListPush(&ctx->errors, error); + } + else if(node->flags & + (MD_NodeFlag_HasParenLeft|MD_NodeFlag_HasParenRight|MD_NodeFlag_HasBracketLeft| + MD_NodeFlag_HasBracketRight|MD_NodeFlag_HasBraceLeft|MD_NodeFlag_HasBraceRight)) + { + MD_String8 error_str = MD_S8Fmt(arena, "Unexpected set.", MD_S8VArg(node->string)); + MD_Message *error = MD_MakeNodeError(arena, node, MD_MessageKind_FatalError, error_str); + MD_MessageListPush(&ctx->errors, error); + } + else{ // NOTE(mal): leaf + *iter = MD_NodeNextWithLimit(*iter, opl); + result = MD_Expr_NewLeaf(arena, node); + } + + return(result); +} + +MD_FUNCTION MD_Expr* +MD_ExprParse_MinPrecedence(MD_Arena *arena, MD_ExprParseCtx *ctx, + MD_Node **iter, MD_Node *first, MD_Node *opl, + MD_u32 min_precedence) +{ + // TODO(allen): nil + MD_Expr* result = 0; + + result = MD_ExprParse_Atom(arena, ctx, iter, first, opl); + if(ctx->errors.max_message_kind == MD_MessageKind_Null) + { + for (;!MD_NodeIsNil(*iter);) + { + MD_Node *node = *iter; + MD_ExprOpr *op = 0; + + if(MD_ExprParse_OprConsume(ctx, iter, opl, MD_ExprOprKind_Binary, + min_precedence, &op) || + MD_ExprParse_OprConsume(ctx, iter, opl, MD_ExprOprKind_BinaryRightAssociative, + min_precedence, &op)) + { + MD_u32 next_min_precedence = op->precedence + (op->kind == MD_ExprOprKind_Binary); + MD_Expr *sub_expr = + MD_ExprParse_MinPrecedence(arena, ctx, iter, first, opl, next_min_precedence); + if(ctx->errors.max_message_kind == MD_MessageKind_Null) + { + result = MD_Expr_NewOpr(arena, op, node, result, sub_expr); + } + else{ + break; + } + } + + else + { + MD_b32 found_postfix_setlike_operator = 0; + for(MD_u32 i_op = 0; + i_op < MD_ArrayCount(ctx->accel.postfix_set_ops); + ++i_op) + { + MD_ExprOpr *op = ctx->accel.postfix_set_ops[i_op]; + if(op && op->precedence >= min_precedence && + node->flags == ctx->accel.postfix_set_flags[i_op]) + { + *iter = MD_NodeNextWithLimit(*iter, opl); + result = MD_Expr_NewOpr(arena, op, node, result, 0); + found_postfix_setlike_operator = 1; + break; + } + } + + if(!found_postfix_setlike_operator) + { + if(MD_ExprParse_OprConsume(ctx, iter, opl, MD_ExprOprKind_Postfix, + min_precedence, &op)) + { + result = MD_Expr_NewOpr(arena, op, node, result, 0); + } + else + { + break; // NOTE: Due to lack of progress + } + } + + } + + } + } + + return(result); +} + + + + +//~ String Generation + +MD_FUNCTION void +MD_DebugDumpFromNode(MD_Arena *arena, MD_String8List *out, MD_Node *node, + int indent, MD_String8 indent_string, MD_GenerateFlags flags) +{ +#define MD_PrintIndent(_indent_level) do\ +{\ +for(int i = 0; i < (_indent_level); i += 1)\ +{\ +MD_S8ListPush(arena, out, indent_string);\ +}\ +}while(0) + + //- rjf: prev-comment + if(flags & MD_GenerateFlag_Comments && node->prev_comment.size != 0) + { + MD_PrintIndent(indent); + MD_S8ListPush(arena, out, MD_S8Lit("/*\n")); + MD_PrintIndent(indent); + MD_S8ListPush(arena, out, node->prev_comment); + MD_PrintIndent(indent); + MD_S8ListPush(arena, out, MD_S8Lit("\n")); + MD_PrintIndent(indent); + MD_S8ListPush(arena, out, MD_S8Lit("*/\n")); + } + + //- rjf: tags of node + if(flags & MD_GenerateFlag_Tags) + { + for(MD_EachNode(tag, node->first_tag)) + { + MD_PrintIndent(indent); + MD_S8ListPush(arena, out, MD_S8Lit("@")); + MD_S8ListPush(arena, out, tag->string); + if(flags & MD_GenerateFlag_TagArguments && !MD_NodeIsNil(tag->first_child)) + { + int tag_arg_indent = indent + 1 + tag->string.size + 1; + MD_S8ListPush(arena, out, MD_S8Lit("(")); + for(MD_EachNode(child, tag->first_child)) + { + int child_indent = tag_arg_indent; + if(MD_NodeIsNil(child->prev)) + { + child_indent = 0; + } + MD_DebugDumpFromNode(arena, out, child, child_indent, MD_S8Lit(" "), flags); + if(!MD_NodeIsNil(child->next)) + { + MD_S8ListPush(arena, out, MD_S8Lit(",\n")); + } + } + MD_S8ListPush(arena, out, MD_S8Lit(")\n")); + } + else + { + MD_S8ListPush(arena, out, MD_S8Lit("\n")); + } + } + } + + //- rjf: node kind + if(flags & MD_GenerateFlag_NodeKind) + { + MD_PrintIndent(indent); + MD_S8ListPush(arena, out, MD_S8Lit("// kind: \"")); + MD_S8ListPush(arena, out, MD_StringFromNodeKind(node->kind)); + MD_S8ListPush(arena, out, MD_S8Lit("\"\n")); + } + + //- rjf: node flags + if(flags & MD_GenerateFlag_NodeFlags) + { + MD_PrintIndent(indent); + MD_ArenaTemp scratch = MD_GetScratch(&arena, 1); + MD_String8List flag_strs = MD_StringListFromNodeFlags(scratch.arena, node->flags); + MD_StringJoin join = { MD_S8Lit(""), MD_S8Lit("|"), MD_S8Lit("") }; + MD_String8 flag_str = MD_S8ListJoin(arena, flag_strs, &join); + MD_S8ListPush(arena, out, MD_S8Lit("// flags: \"")); + MD_S8ListPush(arena, out, flag_str); + MD_S8ListPush(arena, out, MD_S8Lit("\"\n")); + MD_ReleaseScratch(scratch); + } + + //- rjf: location + if(flags & MD_GenerateFlag_Location) + { + MD_PrintIndent(indent); + MD_CodeLoc loc = MD_CodeLocFromNode(node); + MD_String8 string = MD_S8Fmt(arena, "// location: %.*s:%i:%i\n", MD_S8VArg(loc.filename), (int)loc.line, (int)loc.column); + MD_S8ListPush(arena, out, string); + } + + //- rjf: name of node + if(node->string.size != 0) + { + MD_PrintIndent(indent); + if(node->kind == MD_NodeKind_File) + { + MD_S8ListPush(arena, out, MD_S8Lit("`")); + MD_S8ListPush(arena, out, node->string); + MD_S8ListPush(arena, out, MD_S8Lit("`")); + } + else + { + MD_S8ListPush(arena, out, node->raw_string); + } + } + + //- rjf: children list + if(flags & MD_GenerateFlag_Children && !MD_NodeIsNil(node->first_child)) + { + if(node->string.size != 0) + { + MD_S8ListPush(arena, out, MD_S8Lit(":\n")); + } + MD_PrintIndent(indent); + MD_S8ListPush(arena, out, MD_S8Lit("{\n")); + for(MD_EachNode(child, node->first_child)) + { + MD_DebugDumpFromNode(arena, out, child, indent+1, indent_string, flags); + MD_S8ListPush(arena, out, MD_S8Lit(",\n")); + } + MD_PrintIndent(indent); + MD_S8ListPush(arena, out, MD_S8Lit("}")); + } + + //- rjf: next-comment + if(flags & MD_GenerateFlag_Comments && node->next_comment.size != 0) + { + MD_PrintIndent(indent); + MD_S8ListPush(arena, out, MD_S8Lit("\n/*\n")); + MD_PrintIndent(indent); + MD_S8ListPush(arena, out, node->next_comment); + MD_PrintIndent(indent); + MD_S8ListPush(arena, out, MD_S8Lit("\n")); + MD_PrintIndent(indent); + MD_S8ListPush(arena, out, MD_S8Lit("*/\n")); + } + +#undef MD_PrintIndent +} + +MD_FUNCTION void +MD_ReconstructionFromNode(MD_Arena *arena, MD_String8List *out, MD_Node *node, + int indent, MD_String8 indent_string) +{ + MD_CodeLoc code_loc = MD_CodeLocFromNode(node); + +#define MD_PrintIndent(_indent_level) do\ +{\ +for(int i = 0; i < (_indent_level); i += 1)\ +{\ +MD_S8ListPush(arena, out, indent_string);\ +}\ +}while(0) + + //- rjf: prev-comment + if(node->prev_comment.size != 0) + { + MD_String8 comment = MD_S8SkipWhitespace(MD_S8ChopWhitespace(node->prev_comment)); + MD_b32 requires_multiline = MD_S8FindSubstring(comment, MD_S8Lit("\n"), 0, 0) < comment.size; + MD_PrintIndent(indent); + if(requires_multiline) + { + MD_S8ListPush(arena, out, MD_S8Lit("/*\n")); + } + else + { + MD_S8ListPush(arena, out, MD_S8Lit("// ")); + } + MD_S8ListPush(arena, out, comment); + if(requires_multiline) + { + MD_S8ListPush(arena, out, MD_S8Lit("\n*/\n")); + } + else + { + MD_S8ListPush(arena, out, MD_S8Lit("\n")); + } + } + + //- rjf: tags of node + MD_u32 tag_first_line = MD_CodeLocFromNode(node->first_tag).line; + MD_u32 tag_last_line = tag_first_line; + { + for(MD_EachNode(tag, node->first_tag)) + { + MD_u32 tag_line = MD_CodeLocFromNode(tag).line; + if(tag_line != tag_last_line) + { + MD_S8ListPush(arena, out, MD_S8Lit("\n")); + tag_last_line = tag_line; + } + else if(!MD_NodeIsNil(tag->prev)) + { + MD_S8ListPush(arena, out, MD_S8Lit(" ")); + } + + MD_PrintIndent(indent); + MD_S8ListPush(arena, out, MD_S8Lit("@")); + MD_S8ListPush(arena, out, tag->string); + if(!MD_NodeIsNil(tag->first_child)) + { + int tag_arg_indent = indent + 1 + tag->string.size + 1; + MD_S8ListPush(arena, out, MD_S8Lit("(")); + MD_u32 last_line = MD_CodeLocFromNode(tag).line; + for(MD_EachNode(child, tag->first_child)) + { + MD_CodeLoc child_loc = MD_CodeLocFromNode(child); + if(child_loc.line != last_line) + { + MD_S8ListPush(arena, out, MD_S8Lit("\n")); + MD_PrintIndent(indent); + } + last_line = child_loc.line; + + int child_indent = tag_arg_indent; + if(MD_NodeIsNil(child->prev)) + { + child_indent = 0; + } + MD_ReconstructionFromNode(arena, out, child, child_indent, MD_S8Lit(" ")); + if(!MD_NodeIsNil(child->next)) + { + MD_S8ListPush(arena, out, MD_S8Lit(",\n")); + } + } + MD_S8ListPush(arena, out, MD_S8Lit(")")); + } + } + } + + //- rjf: name of node + if(node->string.size != 0) + { + if(tag_first_line != tag_last_line) + { + MD_S8ListPush(arena, out, MD_S8Lit("\n")); + MD_PrintIndent(indent); + } + else if(!MD_NodeIsNil(node->first_tag) || !MD_NodeIsNil(node->prev)) + { + MD_S8ListPush(arena, out, MD_S8Lit(" ")); + } + if(node->kind == MD_NodeKind_File) + { + MD_S8ListPush(arena, out, MD_S8Lit("`")); + MD_S8ListPush(arena, out, node->string); + MD_S8ListPush(arena, out, MD_S8Lit("`")); + } + else + { + MD_S8ListPush(arena, out, node->raw_string); + } + } + + //- rjf: children list + if(!MD_NodeIsNil(node->first_child)) + { + if(node->string.size != 0) + { + MD_S8ListPush(arena, out, MD_S8Lit(":")); + } + + // rjf: figure out opener/closer symbols + MD_u8 opener_char = 0; + MD_u8 closer_char = 0; + if(node->flags & MD_NodeFlag_HasParenLeft) { opener_char = '('; } + else if(node->flags & MD_NodeFlag_HasBracketLeft) { opener_char = '['; } + else if(node->flags & MD_NodeFlag_HasBraceLeft) { opener_char = '{'; } + if(node->flags & MD_NodeFlag_HasParenRight) { closer_char = ')'; } + else if(node->flags & MD_NodeFlag_HasBracketRight){ closer_char = ']'; } + else if(node->flags & MD_NodeFlag_HasBraceRight) { closer_char = '}'; } + + MD_b32 multiline = 0; + for(MD_EachNode(child, node->first_child)) + { + MD_CodeLoc child_loc = MD_CodeLocFromNode(child); + if(child_loc.line != code_loc.line) + { + multiline = 1; + break; + } + } + + if(opener_char != 0) + { + if(multiline) + { + MD_S8ListPush(arena, out, MD_S8Lit("\n")); + MD_PrintIndent(indent); + } + else + { + MD_S8ListPush(arena, out, MD_S8Lit(" ")); + } + MD_S8ListPush(arena, out, MD_S8(&opener_char, 1)); + if(multiline) + { + MD_S8ListPush(arena, out, MD_S8Lit("\n")); + MD_PrintIndent(indent+1); + } + } + MD_u32 last_line = MD_CodeLocFromNode(node->first_child).line; + for(MD_EachNode(child, node->first_child)) + { + int child_indent = 0; + MD_CodeLoc child_loc = MD_CodeLocFromNode(child); + if(child_loc.line != last_line) + { + MD_S8ListPush(arena, out, MD_S8Lit("\n")); + MD_PrintIndent(indent); + child_indent = indent+1; + } + last_line = child_loc.line; + MD_ReconstructionFromNode(arena, out, child, child_indent, indent_string); + } + MD_PrintIndent(indent); + if(closer_char != 0) + { + if(last_line != code_loc.line) + { + MD_S8ListPush(arena, out, MD_S8Lit("\n")); + MD_PrintIndent(indent); + } + else + { + MD_S8ListPush(arena, out, MD_S8Lit(" ")); + } + MD_S8ListPush(arena, out, MD_S8(&closer_char, 1)); + } + } + + //- rjf: trailing separator symbols + if(node->flags & MD_NodeFlag_IsBeforeSemicolon) + { + MD_S8ListPush(arena, out, MD_S8Lit(";")); + } + else if(node->flags & MD_NodeFlag_IsBeforeComma) + { + MD_S8ListPush(arena, out, MD_S8Lit(",")); + } + + //- rjf: next-comment + // TODO(rjf): @node_comments + if(node->next_comment.size != 0) + { + MD_String8 comment = MD_S8SkipWhitespace(MD_S8ChopWhitespace(node->next_comment)); + MD_b32 requires_multiline = MD_S8FindSubstring(comment, MD_S8Lit("\n"), 0, 0) < comment.size; + MD_PrintIndent(indent); + if(requires_multiline) + { + MD_S8ListPush(arena, out, MD_S8Lit("/*\n")); + } + else + { + MD_S8ListPush(arena, out, MD_S8Lit("// ")); + } + MD_S8ListPush(arena, out, comment); + if(requires_multiline) + { + MD_S8ListPush(arena, out, MD_S8Lit("\n*/\n")); + } + else + { + MD_S8ListPush(arena, out, MD_S8Lit("\n")); + } + } + +#undef MD_PrintIndent +} + + +#if !MD_DISABLE_PRINT_HELPERS +MD_FUNCTION void +MD_PrintDebugDumpFromNode(FILE *file, MD_Node *node, MD_GenerateFlags flags) +{ + MD_ArenaTemp scratch = MD_GetScratch(0, 0); + MD_String8List list = {0}; + MD_DebugDumpFromNode(scratch.arena, &list, node, + 0, MD_S8Lit(" "), flags); + MD_String8 string = MD_S8ListJoin(scratch.arena, list, 0); + fwrite(string.str, string.size, 1, file); + MD_ReleaseScratch(scratch); +} +#endif + + +//~ Command Line Argument Helper + +MD_FUNCTION MD_String8List +MD_StringListFromArgCV(MD_Arena *arena, int argument_count, char **arguments) +{ + MD_String8List options = MD_ZERO_STRUCT; + for(int i = 1; i < argument_count; i += 1) + { + MD_S8ListPush(arena, &options, MD_S8CString(arguments[i])); + } + return options; +} + +MD_FUNCTION MD_CmdLine +MD_MakeCmdLineFromOptions(MD_Arena *arena, MD_String8List options) +{ + MD_CmdLine cmdln = MD_ZERO_STRUCT; + MD_b32 parsing_only_inputs = 0; + + for(MD_String8Node *n = options.first, *next = 0; + n; n = next) + { + next = n->next; + + //- rjf: figure out whether or not this is an option by checking for `-` or `--` + // from the beginning of the string + MD_String8 option_name = MD_ZERO_STRUCT; + if(MD_S8Match(n->string, MD_S8Lit("--"), 0)) + { + parsing_only_inputs = 1; + } + else if(MD_S8Match(MD_S8Prefix(n->string, 2), MD_S8Lit("--"), 0)) + { + option_name = MD_S8Skip(n->string, 2); + } + else if(MD_S8Match(MD_S8Prefix(n->string, 1), MD_S8Lit("-"), 0)) + { + option_name = MD_S8Skip(n->string, 1); + } + + //- rjf: trim off anything after a `:` or `=`, use that as the first value string + MD_String8 first_value = MD_ZERO_STRUCT; + MD_b32 has_many_values = 0; + if(option_name.size != 0) + { + MD_u64 colon_signifier_pos = MD_S8FindSubstring(option_name, MD_S8Lit(":"), 0, 0); + MD_u64 equal_signifier_pos = MD_S8FindSubstring(option_name, MD_S8Lit("="), 0, 0); + MD_u64 signifier_pos = MD_Min(colon_signifier_pos, equal_signifier_pos); + if(signifier_pos < option_name.size) + { + first_value = MD_S8Skip(option_name, signifier_pos+1); + option_name = MD_S8Prefix(option_name, signifier_pos); + if(MD_S8Match(MD_S8Suffix(first_value, 1), MD_S8Lit(","), 0)) + { + has_many_values = 1; + } + } + } + + //- rjf: gather arguments + if(option_name.size != 0 && !parsing_only_inputs) + { + MD_String8List option_values = MD_ZERO_STRUCT; + + //- rjf: push first value + if(first_value.size != 0) + { + MD_S8ListPush(arena, &option_values, first_value); + } + + //- rjf: scan next string values, add them to option values until we hit a lack + // of a ',' between values + if(has_many_values) + { + for(MD_String8Node *v = next; v; v = v->next, next = v) + { + MD_String8 value_str = v->string; + MD_b32 next_has_arguments = MD_S8Match(MD_S8Suffix(value_str, 1), MD_S8Lit(","), 0); + MD_b32 in_quotes = 0; + MD_u64 start = 0; + for(MD_u64 i = 0; i <= value_str.size; i += 1) + { + if(i == value_str.size || (value_str.str[i] == ',' && in_quotes == 0)) + { + if(start != i) + { + MD_S8ListPush(arena, &option_values, MD_S8Substring(value_str, start, i)); + } + start = i+1; + } + else if(value_str.str[i] == '"') + { + in_quotes = !in_quotes; + } + } + if(next_has_arguments == 0) + { + break; + } + } + } + + //- rjf: insert the fully parsed option + { + MD_CmdLineOption *opt = MD_PushArrayZero(arena, MD_CmdLineOption, 1); + opt->name = option_name; + opt->values = option_values; + if(cmdln.last_option == 0) + { + cmdln.first_option = cmdln.last_option = opt; + } + else + { + cmdln.last_option->next = opt; + cmdln.last_option = cmdln.last_option->next; + } + } + } + + //- rjf: this argument is not an option, push it to regular inputs list. + else + { + MD_S8ListPush(arena, &cmdln.inputs, n->string); + } + } + + return cmdln; +} + +MD_FUNCTION MD_String8List +MD_CmdLineValuesFromString(MD_CmdLine cmdln, MD_String8 name) +{ + MD_String8List values = MD_ZERO_STRUCT; + for(MD_CmdLineOption *opt = cmdln.first_option; opt; opt = opt->next) + { + if(MD_S8Match(opt->name, name, 0)) + { + values = opt->values; + break; + } + } + return values; +} + +MD_FUNCTION MD_b32 +MD_CmdLineB32FromString(MD_CmdLine cmdln, MD_String8 name) +{ + MD_b32 result = 0; + for(MD_CmdLineOption *opt = cmdln.first_option; opt; opt = opt->next) + { + if(MD_S8Match(opt->name, name, 0)) + { + result = 1; + break; + } + } + return result; +} + +MD_FUNCTION MD_i64 +MD_CmdLineI64FromString(MD_CmdLine cmdln, MD_String8 name) +{ + MD_String8List values = MD_CmdLineValuesFromString(cmdln, name); + MD_ArenaTemp scratch = MD_GetScratch(0, 0); + MD_String8 value_str = MD_S8ListJoin(scratch.arena, values, 0); + MD_i64 result = MD_CStyleIntFromString(value_str); + MD_ReleaseScratch(scratch); + return(result); +} + +//~ File System + +MD_FUNCTION MD_String8 +MD_LoadEntireFile(MD_Arena *arena, MD_String8 filename) +{ + MD_String8 result = MD_ZERO_STRUCT; +#if defined(MD_IMPL_LoadEntireFile) + result = MD_IMPL_LoadEntireFile(arena, filename); +#endif + return(result); +} + +MD_FUNCTION MD_b32 +MD_FileIterBegin(MD_FileIter *it, MD_String8 path) +{ +#if !defined(MD_IMPL_FileIterBegin) + return(0); +#else + return(MD_IMPL_FileIterBegin(it, path)); +#endif +} + +MD_FUNCTION MD_FileInfo +MD_FileIterNext(MD_Arena *arena, MD_FileIter *it) +{ +#if !defined(MD_IMPL_FileIterNext) + MD_FileInfo result = {0}; + return(result); +#else + return(MD_IMPL_FileIterNext(arena, it)); +#endif +} + +MD_FUNCTION void +MD_FileIterEnd(MD_FileIter *it) +{ +#if defined(MD_IMPL_FileIterEnd) + MD_IMPL_FileIterEnd(it); +#endif +} + +#endif // MD_C + +/* +Copyright 2021 Dion Systems LLC + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ diff --git a/code/third_party/metadesk/md.h b/code/third_party/metadesk/md.h new file mode 100644 index 0000000..a7da6f5 --- /dev/null +++ b/code/third_party/metadesk/md.h @@ -0,0 +1,1248 @@ +// LICENSE AT END OF FILE (MIT). + +/* +** Weclome to Metadesk! +** +** Metadesk is a data description language designed to look like a programming +** language, and this is the accompanying parser library. While you are free to +** use it however you see fit, here are a couple of the uses we have intended +** to support: +** + quickly writing a C or C++ metaprogram from scratch +** + building "low budget" domain specific languages, such as marked-up +** webpage content, or asset metadata +** + creating robust and flexible config systems for applications +** +** If it's your first time with Metadesk, check out the "How to Build" section +** below, and consider looking at the examples included with the library. The +** examples_directory.txt will help you find your way from the intro examples +** through all the more advanced aspects of the library you might like to +** learn about. +** +** Direct issues, questions, suggestions, requests, etc to: +** https://github.com/Dion-Systems/metadesk +** +** +** How To Build: +** +** The library is set up as a direct source-include library, so if you have a +** single unit build you can just #include "md.h" and "md.c". If you have a +** multiple unit build you can #include "md.h" where necessary and add "md.c" +** as a separate compilation unit (extra care has to be taken if you intend to +** use overrides in a multiple unit build). +** +** See `bin/compile_flags.txt` for the flags to build with. +** +** The tests and examples can be built with the bash scripts in bin. There are +** a few things to know to use these scripts: +** 1. First you should run `bld_init.sh` which will initialize your copy of +** Metadesk's build system. +** 2. On Linux the shell scripts should work as written. On Windows you will +** need to use a bash interpreter specifically. Generally the `bash.exe` +** that comes with an install of git on Windows works well for this. +** Add it to your path or setup a batch script that calls it and then +** pass the bash scripts to the interpreter to build. +** 3. You should be able to run the scripts: +** `build_tests.sh` +** `build_examples.sh` +** `run_tests.sh` +** `run_examples.sh` +** `type_metadata_example.sh` +*/ + +#ifndef MD_H +#define MD_H + +#define MD_VERSION_MAJ 1 +#define MD_VERSION_MIN 0 + +//~ Set default values for controls +#if !defined(MD_DEFAULT_BASIC_TYPES) +# define MD_DEFAULT_BASIC_TYPES 1 +#endif +#if !defined(MD_DEFAULT_MEMSET) +# define MD_DEFAULT_MEMSET 1 +#endif +#if !defined(MD_DEFAULT_FILE_LOAD) +# define MD_DEFAULT_FILE_LOAD 1 +#endif +#if !defined(MD_DEFAULT_FILE_ITER) +# define MD_DEFAULT_FILE_ITER 1 +#endif +#if !defined(MD_DEFAULT_MEMORY) +# define MD_DEFAULT_MEMORY 1 +#endif +#if !defined(MD_DEFAULT_ARENA) +# define MD_DEFAULT_ARENA 1 +#endif +#if !defined(MD_DEFAULT_SCRATCH) +# define MD_DEFAULT_SCRATCH 1 +#endif +#if !defined(MD_DEFAULT_SPRINTF) +# define MD_DEFAULT_SPRINTF 1 +#endif + +#if !defined(MD_DISABLE_PRINT_HELPERS) +# define MD_DISABLE_PRINT_HELPERS 0 +#endif + + +//~///////////////////////////////////////////////////////////////////////////// +////////////////////////////// Context Cracking //////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +#if defined(__clang__) + +# define MD_COMPILER_CLANG 1 + +# if defined(__APPLE__) && defined(__MACH__) +# define MD_OS_MAC 1 +# elif defined(__gnu_linux__) +# define MD_OS_LINUX 1 +# elif defined(_WIN32) +# define MD_OS_WINDOWS 1 +# else +# error This compiler/platform combo is not supported yet +# endif + +# if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) +# define MD_ARCH_X64 1 +# elif defined(i386) || defined(__i386) || defined(__i386__) +# define MD_ARCH_X86 1 +# elif defined(__aarch64__) +# define MD_ARCH_ARM64 1 +# elif defined(__arm__) +# define MD_ARCH_ARM32 1 +# else +# error architecture not supported yet +# endif + +#elif defined(_MSC_VER) + +# define MD_COMPILER_CL 1 + +# if defined(_WIN32) +# define MD_OS_WINDOWS 1 +# else +# error This compiler/platform combo is not supported yet +# endif + +# if defined(_M_AMD64) +# define MD_ARCH_X64 1 +# elif defined(_M_IX86) +# define MD_ARCH_X86 1 +# elif defined(_M_ARM64) +# define MD_ARCH_ARM64 1 +# elif defined(_M_ARM) +# define MD_ARCH_ARM32 1 +# else +# error architecture not supported yet +# endif + +# if _MSC_VER >= 1920 +# define MD_COMPILER_CL_YEAR 2019 +# elif _MSC_VER >= 1910 +# define MD_COMPILER_CL_YEAR 2017 +# elif _MSC_VER >= 1900 +# define MD_COMPILER_CL_YEAR 2015 +# elif _MSC_VER >= 1800 +# define MD_COMPILER_CL_YEAR 2013 +# elif _MSC_VER >= 1700 +# define MD_COMPILER_CL_YEAR 2012 +# elif _MSC_VER >= 1600 +# define MD_COMPILER_CL_YEAR 2010 +# elif _MSC_VER >= 1500 +# define MD_COMPILER_CL_YEAR 2008 +# elif _MSC_VER >= 1400 +# define MD_COMPILER_CL_YEAR 2005 +# else +# define MD_COMPILER_CL_YEAR 0 +# endif + +#elif defined(__GNUC__) || defined(__GNUG__) + +# define MD_COMPILER_GCC 1 + +# if defined(__gnu_linux__) +# define MD_OS_LINUX 1 +# else +# error This compiler/platform combo is not supported yet +# endif + +# if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) +# define MD_ARCH_X64 1 +# elif defined(i386) || defined(__i386) || defined(__i386__) +# define MD_ARCH_X86 1 +# elif defined(__aarch64__) +# define MD_ARCH_ARM64 1 +# elif defined(__arm__) +# define MD_ARCH_ARM32 1 +# else +# error architecture not supported yet +# endif + +#else +# error This compiler is not supported yet +#endif + +#if defined(MD_ARCH_X64) +# define MD_ARCH_64BIT 1 +#elif defined(MD_ARCH_X86) +# define MD_ARCH_32BIT 1 +#endif + +#if defined(__cplusplus) +# define MD_LANG_CPP 1 + +// We can't get this 100% correct thanks to Microsoft's compiler. +// So this check lets us pre-define MD_CPP_VERSION if we have to. +# if !defined(MD_CPP_VERSION) +# if defined(MD_COMPILER_CL) +// CL is annoying and didn't update __cplusplus over time +// If it is available _MSVC_LANG serves the same role +# if defined(_MSVC_LANG) +# if _MSVC_LANG <= 199711L +# define MD_CPP_VERSION 98 +# elif _MSVC_LANG <= 201103L +# define MD_CPP_VERSION 11 +# elif _MSVC_LANG <= 201402L +# define MD_CPP_VERSION 14 +# elif _MSVC_LANG <= 201703L +# define MD_CPP_VERSION 17 +# elif _MSVC_LANG <= 202002L +# define MD_CPP_VERSION 20 +# else +# define MD_CPP_VERSION 23 +# endif +// If we don't have _MSVC_LANG we can guess from the compiler version +# else +# if MD_COMPILER_CL_YEAR <= 2010 +# define MD_CPP_VERSION 98 +# elif MD_COMPILER_CL_YEAR <= 2015 +# define MD_CPP_VERSION 11 +# else +# define MD_CPP_VERSION 17 +# endif +# endif +# else +// Other compilers use __cplusplus correctly +# if __cplusplus <= 199711L +# define MD_CPP_VERSION 98 +# elif __cplusplus <= 201103L +# define MD_CPP_VERSION 11 +# elif __cplusplus <= 201402L +# define MD_CPP_VERSION 14 +# elif __cplusplus <= 201703L +# define MD_CPP_VERSION 17 +# elif __cplusplus <= 202002L +# define MD_CPP_VERSION 20 +# else +# define MD_CPP_VERSION 23 +# endif +# endif +# endif + +#else +# define MD_LANG_C 1 +#endif + +// zeroify + +#if !defined(MD_ARCH_32BIT) +# define MD_ARCH_32BIT 0 +#endif +#if !defined(MD_ARCH_64BIT) +# define MD_ARCH_64BIT 0 +#endif +#if !defined(MD_ARCH_X64) +# define MD_ARCH_X64 0 +#endif +#if !defined(MD_ARCH_X86) +# define MD_ARCH_X86 0 +#endif +#if !defined(MD_ARCH_ARM64) +# define MD_ARCH_ARM64 0 +#endif +#if !defined(MD_ARCH_ARM32) +# define MD_ARCH_ARM32 0 +#endif +#if !defined(MD_COMPILER_CL) +# define MD_COMPILER_CL 0 +#endif +#if !defined(MD_COMPILER_GCC) +# define MD_COMPILER_GCC 0 +#endif +#if !defined(MD_COMPILER_CLANG) +# define MD_COMPILER_CLANG 0 +#endif +#if !defined(MD_OS_WINDOWS) +# define MD_OS_WINDOWS 0 +#endif +#if !defined(MD_OS_LINUX) +# define MD_OS_LINUX 0 +#endif +#if !defined(MD_OS_MAC) +# define MD_OS_MAC 0 +#endif +#if !defined(MD_LANG_C) +# define MD_LANG_C 0 +#endif +#if !defined(MD_LANG_CPP) +# define MD_LANG_CPP 0 +#endif +#if !defined(MD_CPP_VERSION) +# define MD_CPP_VERSION 0 +#endif + +#if MD_LANG_CPP +# define MD_ZERO_STRUCT {} +#else +# define MD_ZERO_STRUCT {0} +#endif + +#if MD_LANG_C +# define MD_C_LINKAGE_BEGIN +# define MD_C_LINKAGE_END +#else +# define MD_C_LINKAGE_BEGIN extern "C"{ +# define MD_C_LINKAGE_END } +#endif + +#if MD_COMPILER_CL +# define MD_THREAD_LOCAL __declspec(thread) +#elif MD_COMPILER_GCC || MD_COMPILER_CLANG +# define MD_THREAD_LOCAL __thread +#endif + +//~///////////////////////////////////////////////////////////////////////////// +///////////////////////////// Helpers, Macros, Etc ///////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +//~ Linkage Wrappers + +#if !defined(MD_FUNCTION) +# define MD_FUNCTION +#endif + +#if !defined(MD_GLOBAL) +# define MD_GLOBAL static +#endif + +//~ Basic Utilities + +#define MD_Assert(c) if (!(c)) { *(volatile MD_u64 *)0 = 0; } +#define MD_StaticAssert(c,label) MD_u8 MD_static_assert_##label[(c)?(1):(-1)] +#define MD_ArrayCount(a) (sizeof(a) / sizeof((a)[0])) + +#define MD_Min(a,b) (((a)<(b))?(a):(b)) +#define MD_Max(a,b) (((a)>(b))?(a):(b)) +#define MD_ClampBot(a,b) MD_Max(a,b) +#define MD_ClampTop(a,b) MD_Min(a,b) + +#define MD_AlignPow2(x,b) (((x)+((b)-1))&(~((b)-1))) + +//~ Linked List Macros + +// terminator modes +#define MD_CheckNull(p) ((p)==0) +#define MD_SetNull(p) ((p)=0) +#define MD_CheckNil(p) (MD_NodeIsNil(p)) +#define MD_SetNil(p) ((p)=MD_NilNode()) + +// implementations +#define MD_QueuePush_NZ(f,l,n,next,zchk,zset) (zchk(f)?\ +(f)=(l)=(n):\ +((l)->next=(n),(l)=(n),zset((n)->next))) +#define MD_QueuePop_NZ(f,l,next,zset) ((f)==(l)?\ +(zset(f),zset(l)):\ +((f)=(f)->next)) +#define MD_StackPush_N(f,n,next) ((n)->next=(f),(f)=(n)) +#define MD_StackPop_NZ(f,next,zchk) (zchk(f)?0:(f)=(f)->next) + +#define MD_DblPushBack_NPZ(f,l,n,next,prev,zchk,zset) \ +(zchk(f)?\ +((f)=(l)=(n),zset((n)->next),zset((n)->prev)):\ +((n)->prev=(l),(l)->next=(n),(l)=(n),zset((n)->next))) +#define MD_DblRemove_NPZ(f,l,n,next,prev,zset) (((f)==(n)?\ +((f)=(f)->next,zset((f)->prev)):\ +(l)==(n)?\ +((l)=(l)->prev,zset((l)->next)):\ +((n)->next->prev=(n)->prev,\ +(n)->prev->next=(n)->next))) + +// compositions +#define MD_QueuePush(f,l,n) MD_QueuePush_NZ(f,l,n,next,MD_CheckNull,MD_SetNull) +#define MD_QueuePop(f,l) MD_QueuePop_NZ(f,l,next,MD_SetNull) +#define MD_StackPush(f,n) MD_StackPush_N(f,n,next) +#define MD_StackPop(f) MD_StackPop_NZ(f,next,MD_CheckNull) +#define MD_DblPushBack(f,l,n) MD_DblPushBack_NPZ(f,l,n,next,prev,MD_CheckNull,MD_SetNull) +#define MD_DblPushFront(f,l,n) MD_DblPushBack_NPZ(l,f,n,prev,next,MD_CheckNull,MD_SetNull) +#define MD_DblRemove(f,l,n) MD_DblRemove_NPZ(f,l,n,next,prev,MD_SetNull) + +#define MD_NodeDblPushBack(f,l,n) MD_DblPushBack_NPZ(f,l,n,next,prev,MD_CheckNil,MD_SetNil) +#define MD_NodeDblPushFront(f,l,n) MD_DblPushBack_NPZ(l,f,n,prev,next,MD_CheckNil,MD_SetNil) +#define MD_NodeDblRemove(f,l,n) MD_DblRemove_NPZ(f,l,n,next,prev,MD_SetNil) + + +//~ Memory Operations + +#define MD_MemorySet(p,v,z) (MD_IMPL_Memset(p,v,z)) +#define MD_MemoryZero(p,z) (MD_IMPL_Memset(p,0,z)) +#define MD_MemoryZeroStruct(p) (MD_IMPL_Memset(p,0,sizeof(*(p)))) +#define MD_MemoryCopy(d,s,z) (MD_IMPL_Memmove(d,s,z)) + +//~ sprintf +#if MD_DEFAULT_SPRINTF +#define STB_SPRINTF_DECORATE(name) md_stbsp_##name +#define MD_IMPL_Vsnprintf md_stbsp_vsnprintf +#include "md_stb_sprintf.h" +#endif + +//~///////////////////////////////////////////////////////////////////////////// +//////////////////////////////////// Types ///////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +//~ Basic Types + +#include + +#if defined(MD_DEFAULT_BASIC_TYPES) + +#include +typedef int8_t MD_i8; +typedef int16_t MD_i16; +typedef int32_t MD_i32; +typedef int64_t MD_i64; +typedef uint8_t MD_u8; +typedef uint16_t MD_u16; +typedef uint32_t MD_u32; +typedef uint64_t MD_u64; +typedef float MD_f32; +typedef double MD_f64; + +#endif + +typedef MD_i8 MD_b8; +typedef MD_i16 MD_b16; +typedef MD_i32 MD_b32; +typedef MD_i64 MD_b64; + +//~ Default Arena + +#if MD_DEFAULT_ARENA + +typedef struct MD_ArenaDefault MD_ArenaDefault; +struct MD_ArenaDefault +{ + MD_ArenaDefault *prev; + MD_ArenaDefault *current; + MD_u64 base_pos; + MD_u64 pos; + MD_u64 cmt; + MD_u64 cap; + MD_u64 align; +}; +#define MD_IMPL_Arena MD_ArenaDefault + +#endif + +//~ Abstract Arena + +#if !defined(MD_IMPL_Arena) +# error Missing implementation for MD_IMPL_Arena +#endif + +typedef MD_IMPL_Arena MD_Arena; + +//~ Arena Helpers + +typedef struct MD_ArenaTemp MD_ArenaTemp; +struct MD_ArenaTemp +{ + MD_Arena *arena; + MD_u64 pos; +}; + +//~ Basic Unicode string types. + +typedef struct MD_String8 MD_String8; +struct MD_String8 +{ + MD_u8 *str; + MD_u64 size; +}; + +typedef struct MD_String16 MD_String16; +struct MD_String16 +{ + MD_u16 *str; + MD_u64 size; +}; + +typedef struct MD_String32 MD_String32; +struct MD_String32 +{ + MD_u32 *str; + MD_u64 size; +}; + +typedef struct MD_String8Node MD_String8Node; +struct MD_String8Node +{ + MD_String8Node *next; + MD_String8 string; +}; + +typedef struct MD_String8List MD_String8List; +struct MD_String8List +{ + MD_u64 node_count; + MD_u64 total_size; + MD_String8Node *first; + MD_String8Node *last; +}; + +typedef struct MD_StringJoin MD_StringJoin; +struct MD_StringJoin +{ + MD_String8 pre; + MD_String8 mid; + MD_String8 post; +}; + +// NOTE(rjf): @maintenance These three flag types must not overlap. +typedef MD_u32 MD_MatchFlags; +typedef MD_u32 MD_StringMatchFlags; +typedef MD_u32 MD_NodeMatchFlags; +enum +{ + MD_MatchFlag_FindLast = (1<<0), +}; +enum +{ + MD_StringMatchFlag_CaseInsensitive = (1<<4), + MD_StringMatchFlag_RightSideSloppy = (1<<5), + MD_StringMatchFlag_SlashInsensitive = (1<<6), +}; +enum +{ + MD_NodeMatchFlag_Tags = (1<<16), + MD_NodeMatchFlag_TagArguments = (1<<17), + MD_NodeMatchFlag_NodeFlags = (1<<18), +}; + +typedef struct MD_DecodedCodepoint MD_DecodedCodepoint; +struct MD_DecodedCodepoint +{ + MD_u32 codepoint; + MD_u32 advance; +}; + +typedef enum MD_IdentifierStyle +{ + MD_IdentifierStyle_UpperCamelCase, + MD_IdentifierStyle_LowerCamelCase, + MD_IdentifierStyle_UpperCase, + MD_IdentifierStyle_LowerCase, +} +MD_IdentifierStyle; + +//~ Node types that are used to build all ASTs. + +typedef enum MD_NodeKind +{ + // NOTE(rjf): @maintenance Must be kept in sync with MD_StringFromNodeKind. + + MD_NodeKind_Nil, + + // NOTE(rjf): Generated by parser + MD_NodeKind_File, + MD_NodeKind_ErrorMarker, + + // NOTE(rjf): Parsed from user Metadesk code + MD_NodeKind_Main, + MD_NodeKind_Tag, + + // NOTE(rjf): User-created data structures + MD_NodeKind_List, + MD_NodeKind_Reference, + + MD_NodeKind_COUNT, +} +MD_NodeKind; + +typedef MD_u64 MD_NodeFlags; +#define MD_NodeFlag_AfterFromBefore(f) ((f) << 1) +enum +{ + // NOTE(rjf): @maintenance Must be kept in sync with MD_StringListFromNodeFlags. + + // NOTE(rjf): @maintenance Because of MD_NodeFlag_AfterFromBefore, it is + // *required* that every single pair of "Before*" or "After*" flags be in + // the correct order which is that the Before* flag comes first, and the + // After* flag comes immediately after (After* being the more significant + // bit). + + MD_NodeFlag_HasParenLeft = (1<<0), + MD_NodeFlag_HasParenRight = (1<<1), + MD_NodeFlag_HasBracketLeft = (1<<2), + MD_NodeFlag_HasBracketRight = (1<<3), + MD_NodeFlag_HasBraceLeft = (1<<4), + MD_NodeFlag_HasBraceRight = (1<<5), + + MD_NodeFlag_MaskSetDelimiters = (0x3F<<0), + + MD_NodeFlag_IsBeforeSemicolon = (1<<6), + MD_NodeFlag_IsAfterSemicolon = (1<<7), + MD_NodeFlag_IsBeforeComma = (1<<8), + MD_NodeFlag_IsAfterComma = (1<<9), + + MD_NodeFlag_MaskSeperators = (0xF<<6), + + MD_NodeFlag_StringSingleQuote = (1<<10), + MD_NodeFlag_StringDoubleQuote = (1<<11), + MD_NodeFlag_StringTick = (1<<12), + MD_NodeFlag_StringTriplet = (1<<13), + + MD_NodeFlag_MaskStringDelimiters = (0xF<<10), + + MD_NodeFlag_Numeric = (1<<14), + MD_NodeFlag_Identifier = (1<<15), + MD_NodeFlag_StringLiteral = (1<<16), + MD_NodeFlag_Symbol = (1<<17), + + MD_NodeFlag_MaskLabelKind = (0xF<<14), +}; + +typedef struct MD_Node MD_Node; +struct MD_Node +{ + // Tree relationship data. + MD_Node *next; + MD_Node *prev; + MD_Node *parent; + MD_Node *first_child; + MD_Node *last_child; + + // Tag list. + MD_Node *first_tag; + MD_Node *last_tag; + + // Node info. + MD_NodeKind kind; + MD_NodeFlags flags; + MD_String8 string; + MD_String8 raw_string; + + // Source code location information. + MD_u64 offset; + + // Reference. + MD_Node *ref_target; + + // Comments. + // @usage prev_comment/next_comment should be considered "hidden". Rely on + // the functions MD_PrevCommentFromNode/MD_NextCommentFromNode to access + // these. Directly access to these is likely to break in a future version. + MD_String8 prev_comment; + MD_String8 next_comment; +}; + +//~ Code Location Info. + +typedef struct MD_CodeLoc MD_CodeLoc; +struct MD_CodeLoc +{ + MD_String8 filename; + MD_u32 line; + MD_u32 column; +}; + +//~ String-To-Ptr and Ptr-To-Ptr tables + +typedef struct MD_MapKey MD_MapKey; +struct MD_MapKey +{ + MD_u64 hash; + MD_u64 size; + void *ptr; +}; + +typedef struct MD_MapSlot MD_MapSlot; +struct MD_MapSlot +{ + MD_MapSlot *next; + MD_MapKey key; + void *val; +}; + +typedef struct MD_MapBucket MD_MapBucket; +struct MD_MapBucket +{ + MD_MapSlot *first; + MD_MapSlot *last; +}; + +typedef struct MD_Map MD_Map; +struct MD_Map +{ + MD_MapBucket *buckets; + MD_u64 bucket_count; +}; + +//~ Tokens + +typedef MD_u32 MD_TokenKind; +enum +{ + MD_TokenKind_Identifier = (1<<0), + MD_TokenKind_Numeric = (1<<1), + MD_TokenKind_StringLiteral = (1<<2), + MD_TokenKind_Symbol = (1<<3), + MD_TokenKind_Reserved = (1<<4), + MD_TokenKind_Comment = (1<<5), + MD_TokenKind_Whitespace = (1<<6), + MD_TokenKind_Newline = (1<<7), + MD_TokenKind_BrokenComment = (1<<8), + MD_TokenKind_BrokenStringLiteral = (1<<9), + MD_TokenKind_BadCharacter = (1<<10), +}; + +typedef MD_u32 MD_TokenGroups; +enum +{ + MD_TokenGroup_Comment = MD_TokenKind_Comment, + MD_TokenGroup_Whitespace = (MD_TokenKind_Whitespace| + MD_TokenKind_Newline), + MD_TokenGroup_Irregular = (MD_TokenGroup_Comment| + MD_TokenGroup_Whitespace), + MD_TokenGroup_Regular = ~MD_TokenGroup_Irregular, + MD_TokenGroup_Label = (MD_TokenKind_Identifier| + MD_TokenKind_Numeric| + MD_TokenKind_StringLiteral| + MD_TokenKind_Symbol), + MD_TokenGroup_Error = (MD_TokenKind_BrokenComment| + MD_TokenKind_BrokenStringLiteral| + MD_TokenKind_BadCharacter), +}; + +typedef struct MD_Token MD_Token; +struct MD_Token +{ + MD_TokenKind kind; + MD_NodeFlags node_flags; + MD_String8 string; + MD_String8 raw_string; +}; + +//~ Parsing State + +typedef enum MD_MessageKind +{ + // NOTE(rjf): @maintenance This enum needs to be sorted in order of + // severity. + MD_MessageKind_Null, + MD_MessageKind_Note, + MD_MessageKind_Warning, + MD_MessageKind_Error, + MD_MessageKind_FatalError, +} +MD_MessageKind; + +typedef struct MD_Message MD_Message; +struct MD_Message +{ + MD_Message *next; + MD_Node *node; + MD_MessageKind kind; + MD_String8 string; + void *user_ptr; +}; + +typedef struct MD_MessageList MD_MessageList; +struct MD_MessageList +{ + MD_MessageKind max_message_kind; + // TODO(allen): rename + MD_u64 node_count; + MD_Message *first; + MD_Message *last; +}; + +typedef enum MD_ParseSetRule +{ + MD_ParseSetRule_EndOnDelimiter, + MD_ParseSetRule_Global, +} MD_ParseSetRule; + +typedef struct MD_ParseResult MD_ParseResult; +struct MD_ParseResult +{ + MD_Node *node; + MD_u64 string_advance; + MD_MessageList errors; +}; + +//~ Expression Parsing + +typedef enum MD_ExprOprKind +{ + MD_ExprOprKind_Null, + MD_ExprOprKind_Prefix, + MD_ExprOprKind_Postfix, + MD_ExprOprKind_Binary, + MD_ExprOprKind_BinaryRightAssociative, + MD_ExprOprKind_COUNT, +} MD_ExprOprKind; + +typedef struct MD_ExprOpr MD_ExprOpr; +struct MD_ExprOpr +{ + struct MD_ExprOpr *next; + MD_u32 op_id; + MD_ExprOprKind kind; + MD_u32 precedence; + MD_String8 string; + void *op_ptr; +}; + +typedef struct MD_ExprOprList MD_ExprOprList; +struct MD_ExprOprList +{ + MD_ExprOpr *first; + MD_ExprOpr *last; + MD_u64 count; +}; + +typedef struct MD_ExprOprTable MD_ExprOprTable; +struct MD_ExprOprTable +{ + // TODO(mal): @upgrade_potential Hash? + MD_ExprOprList table[MD_ExprOprKind_COUNT]; +}; + +typedef struct MD_Expr MD_Expr; +struct MD_Expr +{ + struct MD_Expr *parent; + union + { + struct MD_Expr *left; + struct MD_Expr *unary_operand; + }; + struct MD_Expr *right; + MD_ExprOpr *op; + MD_Node *md_node; +}; + +typedef struct MD_ExprParseResult MD_ExprParseResult; +struct MD_ExprParseResult +{ + MD_Expr *expr; + MD_MessageList errors; +}; + +// TODO(allen): nil MD_Expr + +typedef struct MD_ExprParseCtx MD_ExprParseCtx; +struct MD_ExprParseCtx +{ + MD_ExprOprTable *op_table; + +#define MD_POSTFIX_SETLIKE_OP_COUNT 5 // (), [], {}, [), (] + struct + { + MD_ExprOpr *postfix_set_ops[MD_POSTFIX_SETLIKE_OP_COUNT]; + MD_NodeFlags postfix_set_flags[MD_POSTFIX_SETLIKE_OP_COUNT]; + } accel; +#undef MD_POSTFIX_SETLIKE_OP_COUNT + + MD_MessageList errors; +}; + +typedef void (*MD_BakeOperatorErrorHandler)(MD_MessageKind kind, MD_String8 s); + +//~ String Generation Types + +typedef MD_u32 MD_GenerateFlags; +enum +{ + MD_GenerateFlag_Tags = (1<<0), + MD_GenerateFlag_TagArguments = (1<<1), + MD_GenerateFlag_Children = (1<<2), + MD_GenerateFlag_Comments = (1<<3), + MD_GenerateFlag_NodeKind = (1<<4), + MD_GenerateFlag_NodeFlags = (1<<5), + MD_GenerateFlag_Location = (1<<6), + + MD_GenerateFlags_Tree = (MD_GenerateFlag_Tags | + MD_GenerateFlag_TagArguments | + MD_GenerateFlag_Children), + MD_GenerateFlags_All = 0xffffffff, +}; + +//~ Command line parsing helper types. + +typedef struct MD_CmdLineOption MD_CmdLineOption; +struct MD_CmdLineOption +{ + MD_CmdLineOption *next; + MD_String8 name; + MD_String8List values; +}; + +typedef struct MD_CmdLine MD_CmdLine; +struct MD_CmdLine +{ + MD_String8List inputs; + MD_CmdLineOption *first_option; + MD_CmdLineOption *last_option; +}; + +//~ File system access types. + +typedef MD_u32 MD_FileFlags; +enum +{ + MD_FileFlag_Directory = (1<<0), +}; + +typedef struct MD_FileInfo MD_FileInfo; +struct MD_FileInfo +{ + MD_FileFlags flags; + MD_String8 filename; + MD_u64 file_size; +}; + +typedef struct MD_FileIter MD_FileIter; +struct MD_FileIter +{ + // This is opaque state to store OS-specific file-system iteration data. + MD_u8 opaque[640]; +}; + +//~///////////////////////////////////////////////////////////////////////////// +////////////////////////////////// Functions /////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +//~ Arena + +MD_FUNCTION MD_Arena* MD_ArenaAlloc(void); +MD_FUNCTION void MD_ArenaRelease(MD_Arena *arena); + +MD_FUNCTION void* MD_ArenaPush(MD_Arena *arena, MD_u64 size); +MD_FUNCTION void MD_ArenaPutBack(MD_Arena *arena, MD_u64 size); +MD_FUNCTION void MD_ArenaSetAlign(MD_Arena *arena, MD_u64 boundary); +MD_FUNCTION void MD_ArenaPushAlign(MD_Arena *arena, MD_u64 boundary); +MD_FUNCTION void MD_ArenaClear(MD_Arena *arena); + +#define MD_PushArray(a,T,c) (T*)(MD_ArenaPush((a), sizeof(T)*(c))) +#define MD_PushArrayZero(a,T,c) (T*)(MD_MemoryZero(MD_PushArray(a,T,c), sizeof(T)*(c))) + +MD_FUNCTION MD_ArenaTemp MD_ArenaBeginTemp(MD_Arena *arena); +MD_FUNCTION void MD_ArenaEndTemp(MD_ArenaTemp temp); + +//~ Arena Scratch Pool + +MD_FUNCTION MD_ArenaTemp MD_GetScratch(MD_Arena **conflicts, MD_u64 count); + +#define MD_ReleaseScratch(scratch) MD_ArenaEndTemp(scratch) + +//~ Characters + +MD_FUNCTION MD_b32 MD_CharIsAlpha(MD_u8 c); +MD_FUNCTION MD_b32 MD_CharIsAlphaUpper(MD_u8 c); +MD_FUNCTION MD_b32 MD_CharIsAlphaLower(MD_u8 c); +MD_FUNCTION MD_b32 MD_CharIsDigit(MD_u8 c); +MD_FUNCTION MD_b32 MD_CharIsUnreservedSymbol(MD_u8 c); +MD_FUNCTION MD_b32 MD_CharIsReservedSymbol(MD_u8 c); +MD_FUNCTION MD_b32 MD_CharIsSpace(MD_u8 c); +MD_FUNCTION MD_u8 MD_CharToUpper(MD_u8 c); +MD_FUNCTION MD_u8 MD_CharToLower(MD_u8 c); +MD_FUNCTION MD_u8 MD_CharToForwardSlash(MD_u8 c); + +//~ Strings + +MD_FUNCTION MD_u64 MD_CalculateCStringLength(char *cstr); + +MD_FUNCTION MD_String8 MD_S8(MD_u8 *str, MD_u64 size); +#define MD_S8CString(s) MD_S8((MD_u8 *)(s), MD_CalculateCStringLength(s)) + +#if MD_LANG_C +# define MD_S8Lit(s) (MD_String8){(MD_u8 *)(s), sizeof(s)-1} +#elif MD_LANG_CPP +# define MD_S8Lit(s) MD_S8((MD_u8*)(s), sizeof(s) - 1) +#endif +#define MD_S8LitComp(s) {(MD_u8 *)(s), sizeof(s)-1} + +#if MD_CPP_VERSION >= 11 +static inline MD_String8 +operator "" _md(const char *s, size_t size) +{ + MD_String8 str = MD_S8((MD_u8 *)s, (MD_u64)size); + return str; +} +#endif + +MD_FUNCTION MD_String8 MD_S8Range(MD_u8 *first, MD_u8 *opl); + +MD_FUNCTION MD_String8 MD_S8Substring(MD_String8 str, MD_u64 min, MD_u64 max); +MD_FUNCTION MD_String8 MD_S8Skip(MD_String8 str, MD_u64 min); +MD_FUNCTION MD_String8 MD_S8Chop(MD_String8 str, MD_u64 nmax); +MD_FUNCTION MD_String8 MD_S8Prefix(MD_String8 str, MD_u64 size); +MD_FUNCTION MD_String8 MD_S8Suffix(MD_String8 str, MD_u64 size); + +MD_FUNCTION MD_b32 MD_S8Match(MD_String8 a, MD_String8 b, MD_MatchFlags flags); +MD_FUNCTION MD_u64 MD_S8FindSubstring(MD_String8 str, MD_String8 substring, + MD_u64 start_pos, MD_MatchFlags flags); + +MD_FUNCTION MD_String8 MD_S8Copy(MD_Arena *arena, MD_String8 string); +MD_FUNCTION MD_String8 MD_S8FmtV(MD_Arena *arena, char *fmt, va_list args); + +MD_FUNCTION MD_String8 MD_S8Fmt(MD_Arena *arena, char *fmt, ...); + +#define MD_S8VArg(s) (int)(s).size, (s).str + +MD_FUNCTION void MD_S8ListPush(MD_Arena *arena, MD_String8List *list, + MD_String8 string); +MD_FUNCTION void MD_S8ListPushFmt(MD_Arena *arena, MD_String8List *list, + char *fmt, ...); + +MD_FUNCTION void MD_S8ListConcat(MD_String8List *list, MD_String8List *to_push); +MD_FUNCTION MD_String8List MD_S8Split(MD_Arena *arena, MD_String8 string, int split_count, + MD_String8 *splits); +MD_FUNCTION MD_String8 MD_S8ListJoin(MD_Arena *arena, MD_String8List list, + MD_StringJoin *join); + +MD_FUNCTION MD_String8 MD_S8Stylize(MD_Arena *arena, MD_String8 string, + MD_IdentifierStyle style, MD_String8 separator); + +//~ Unicode Conversions + +MD_FUNCTION MD_DecodedCodepoint MD_DecodeCodepointFromUtf8(MD_u8 *str, MD_u64 max); +MD_FUNCTION MD_DecodedCodepoint MD_DecodeCodepointFromUtf16(MD_u16 *str, MD_u64 max); +MD_FUNCTION MD_u32 MD_Utf8FromCodepoint(MD_u8 *out, MD_u32 codepoint); +MD_FUNCTION MD_u32 MD_Utf16FromCodepoint(MD_u16 *out, MD_u32 codepoint); +MD_FUNCTION MD_String8 MD_S8FromS16(MD_Arena *arena, MD_String16 str); +MD_FUNCTION MD_String16 MD_S16FromS8(MD_Arena *arena, MD_String8 str); +MD_FUNCTION MD_String8 MD_S8FromS32(MD_Arena *arena, MD_String32 str); +MD_FUNCTION MD_String32 MD_S32FromS8(MD_Arena *arena, MD_String8 str); + +//~ String Skipping/Chopping Helpers + +// This is intended for removing extensions. +MD_FUNCTION MD_String8 MD_PathChopLastPeriod(MD_String8 string); + +// This is intended for removing everything but the filename. +MD_FUNCTION MD_String8 MD_PathSkipLastSlash(MD_String8 string); + +// This is intended for getting an extension from a filename. +MD_FUNCTION MD_String8 MD_PathSkipLastPeriod(MD_String8 string); + +// This is intended for getting the folder string from a full path. +MD_FUNCTION MD_String8 MD_PathChopLastSlash(MD_String8 string); + +MD_FUNCTION MD_String8 MD_S8SkipWhitespace(MD_String8 string); +MD_FUNCTION MD_String8 MD_S8ChopWhitespace(MD_String8 string); + +//~ Numeric Strings + +MD_FUNCTION MD_b32 MD_StringIsU64(MD_String8 string, MD_u32 radix); +MD_FUNCTION MD_b32 MD_StringIsCStyleInt(MD_String8 string); + +MD_FUNCTION MD_u64 MD_U64FromString(MD_String8 string, MD_u32 radix); +MD_FUNCTION MD_i64 MD_CStyleIntFromString(MD_String8 string); +MD_FUNCTION MD_f64 MD_F64FromString(MD_String8 string); + +MD_FUNCTION MD_String8 MD_CStyleHexStringFromU64(MD_Arena *arena, MD_u64 x, MD_b32 caps); + +//~ Enum/Flag Strings + +MD_FUNCTION MD_String8 MD_StringFromNodeKind(MD_NodeKind kind); +MD_FUNCTION MD_String8List MD_StringListFromNodeFlags(MD_Arena *arena, MD_NodeFlags flags); + +//~ Map Table Data Structure + +MD_FUNCTION MD_u64 MD_HashStr(MD_String8 string); +MD_FUNCTION MD_u64 MD_HashPtr(void *p); + +MD_FUNCTION MD_Map MD_MapMakeBucketCount(MD_Arena *arena, MD_u64 bucket_count); +MD_FUNCTION MD_Map MD_MapMake(MD_Arena *arena); +MD_FUNCTION MD_MapKey MD_MapKeyStr(MD_String8 string); +MD_FUNCTION MD_MapKey MD_MapKeyPtr(void *ptr); +MD_FUNCTION MD_MapSlot* MD_MapLookup(MD_Map *map, MD_MapKey key); +MD_FUNCTION MD_MapSlot* MD_MapScan(MD_MapSlot *first_slot, MD_MapKey key); +MD_FUNCTION MD_MapSlot* MD_MapInsert(MD_Arena *arena, MD_Map *map, MD_MapKey key, void *val); +MD_FUNCTION MD_MapSlot* MD_MapOverwrite(MD_Arena *arena, MD_Map *map, MD_MapKey key, + void *val); + +//~ Parsing + +MD_FUNCTION MD_Token MD_TokenFromString(MD_String8 string); +MD_FUNCTION MD_u64 MD_LexAdvanceFromSkips(MD_String8 string, MD_TokenKind skip_kinds); +MD_FUNCTION MD_ParseResult MD_ParseResultZero(void); +MD_FUNCTION MD_ParseResult MD_ParseNodeSet(MD_Arena *arena, MD_String8 string, MD_u64 offset, MD_Node *parent, + MD_ParseSetRule rule); +MD_FUNCTION MD_ParseResult MD_ParseOneNode(MD_Arena *arena, MD_String8 string, MD_u64 offset); +MD_FUNCTION MD_ParseResult MD_ParseWholeString(MD_Arena *arena, MD_String8 filename, MD_String8 contents); + +MD_FUNCTION MD_ParseResult MD_ParseWholeFile(MD_Arena *arena, MD_String8 filename); + +//~ Messages (Errors/Warnings) + +MD_FUNCTION MD_Node* MD_MakeErrorMarkerNode(MD_Arena *arena, MD_String8 parse_contents, + MD_u64 offset); + +MD_FUNCTION MD_Message*MD_MakeNodeError(MD_Arena *arena, MD_Node *node, + MD_MessageKind kind, MD_String8 str); +MD_FUNCTION MD_Message*MD_MakeDetachedError(MD_Arena *arena, MD_MessageKind kind, + MD_String8 str, void *ptr); +MD_FUNCTION MD_Message*MD_MakeTokenError(MD_Arena *arena, MD_String8 parse_contents, + MD_Token token, MD_MessageKind kind, + MD_String8 str); + +MD_FUNCTION void MD_MessageListPush(MD_MessageList *list, MD_Message *message); +MD_FUNCTION void MD_MessageListConcat(MD_MessageList *list, MD_MessageList *to_push); + +//~ Location Conversion + +MD_FUNCTION MD_CodeLoc MD_CodeLocFromFileOffset(MD_String8 filename, MD_u8 *base, MD_u64 offset); +MD_FUNCTION MD_CodeLoc MD_CodeLocFromNode(MD_Node *node); + +//~ Tree/List Building + +MD_FUNCTION MD_b32 MD_NodeIsNil(MD_Node *node); +MD_FUNCTION MD_Node *MD_NilNode(void); +MD_FUNCTION MD_Node *MD_MakeNode(MD_Arena *arena, MD_NodeKind kind, MD_String8 string, + MD_String8 raw_string, MD_u64 offset); +MD_FUNCTION void MD_PushChild(MD_Node *parent, MD_Node *new_child); +MD_FUNCTION void MD_PushTag(MD_Node *node, MD_Node *tag); + +MD_FUNCTION MD_Node *MD_MakeList(MD_Arena *arena); +MD_FUNCTION void MD_ListConcatInPlace(MD_Node *list, MD_Node *to_push); +MD_FUNCTION MD_Node *MD_PushNewReference(MD_Arena *arena, MD_Node *list, MD_Node *target); + +//~ Introspection Helpers + +// These calls are for getting info from nodes, and introspecting +// on trees that are returned to you by the parser. + +MD_FUNCTION MD_Node * MD_FirstNodeWithString(MD_Node *first, MD_String8 string, MD_MatchFlags flags); +MD_FUNCTION MD_Node * MD_NodeAtIndex(MD_Node *first, int n); +MD_FUNCTION MD_Node * MD_FirstNodeWithFlags(MD_Node *first, MD_NodeFlags flags); +MD_FUNCTION int MD_IndexFromNode(MD_Node *node); +MD_FUNCTION MD_Node * MD_RootFromNode(MD_Node *node); +MD_FUNCTION MD_Node * MD_ChildFromString(MD_Node *node, MD_String8 child_string, MD_MatchFlags flags); +MD_FUNCTION MD_Node * MD_TagFromString(MD_Node *node, MD_String8 tag_string, MD_MatchFlags flags); +MD_FUNCTION MD_Node * MD_ChildFromIndex(MD_Node *node, int n); +MD_FUNCTION MD_Node * MD_TagFromIndex(MD_Node *node, int n); +MD_FUNCTION MD_Node * MD_TagArgFromIndex(MD_Node *node, MD_String8 tag_string, MD_MatchFlags flags, int n); +MD_FUNCTION MD_Node * MD_TagArgFromString(MD_Node *node, MD_String8 tag_string, MD_MatchFlags tag_str_flags, MD_String8 arg_string, MD_MatchFlags arg_str_flags); +MD_FUNCTION MD_b32 MD_NodeHasChild(MD_Node *node, MD_String8 string, MD_MatchFlags flags); +MD_FUNCTION MD_b32 MD_NodeHasTag(MD_Node *node, MD_String8 string, MD_MatchFlags flags); +MD_FUNCTION MD_i64 MD_ChildCountFromNode(MD_Node *node); +MD_FUNCTION MD_i64 MD_TagCountFromNode(MD_Node *node); +MD_FUNCTION MD_Node * MD_ResolveNodeFromReference(MD_Node *node); +MD_FUNCTION MD_Node* MD_NodeNextWithLimit(MD_Node *node, MD_Node *opl); + +MD_FUNCTION MD_String8 MD_PrevCommentFromNode(MD_Node *node); +MD_FUNCTION MD_String8 MD_NextCommentFromNode(MD_Node *node); + +// NOTE(rjf): For-Loop Helpers +#define MD_EachNode(it, first) MD_Node *it = (first); !MD_NodeIsNil(it); it = it->next + +//~ Error/Warning Helpers + +MD_FUNCTION MD_String8 MD_StringFromMessageKind(MD_MessageKind kind); + +#define MD_FmtCodeLoc "%.*s:%i:%i:" +#define MD_CodeLocVArg(loc) MD_S8VArg((loc).filename), (loc).line, (loc).column + +MD_FUNCTION MD_String8 MD_FormatMessage(MD_Arena *arena, MD_CodeLoc loc, MD_MessageKind kind, + MD_String8 string); + +#if !MD_DISABLE_PRINT_HELPERS +#include +MD_FUNCTION void MD_PrintMessage(FILE *file, MD_CodeLoc loc, MD_MessageKind kind, + MD_String8 string); +MD_FUNCTION void MD_PrintMessageFmt(FILE *file, MD_CodeLoc code_loc, MD_MessageKind kind, + char *fmt, ...); + +#define MD_PrintGenNoteCComment(f) fprintf((f), "// generated by %s:%d\n", __FILE__, __LINE__) +#endif + +//~ Tree Comparison/Verification + +MD_FUNCTION MD_b32 MD_NodeMatch(MD_Node *a, MD_Node *b, MD_MatchFlags flags); +MD_FUNCTION MD_b32 MD_NodeDeepMatch(MD_Node *a, MD_Node *b, MD_MatchFlags flags); + +//~ Expression Parsing + +MD_FUNCTION void MD_ExprOprPush(MD_Arena *arena, MD_ExprOprList *list, + MD_ExprOprKind kind, MD_u64 precedence, + MD_String8 op_string, + MD_u32 op_id, void *op_ptr); + +MD_FUNCTION MD_ExprOprTable MD_ExprBakeOprTableFromList(MD_Arena *arena, + MD_ExprOprList *list); +MD_FUNCTION MD_ExprOpr* MD_ExprOprFromKindString(MD_ExprOprTable *table, + MD_ExprOprKind kind, MD_String8 s); + +MD_FUNCTION MD_ExprParseResult MD_ExprParse(MD_Arena *arena, MD_ExprOprTable *op_table, + MD_Node *first, MD_Node *one_past_last); + +MD_FUNCTION MD_Expr* MD_Expr_NewLeaf(MD_Arena *arena, MD_Node *node); +MD_FUNCTION MD_Expr* MD_Expr_NewOpr(MD_Arena *arena, MD_ExprOpr *op, MD_Node *op_node, + MD_Expr *left, MD_Expr *right); + +MD_FUNCTION MD_ExprParseCtx MD_ExprParse_MakeContext(MD_ExprOprTable *table); + +MD_FUNCTION MD_Expr* MD_ExprParse_TopLevel(MD_Arena *arena, MD_ExprParseCtx *ctx, + MD_Node *first, MD_Node *opl); +MD_FUNCTION MD_b32 MD_ExprParse_OprConsume(MD_ExprParseCtx *ctx, + MD_Node **iter, MD_Node *opl, + MD_ExprOprKind kind, + MD_u32 min_precedence, + MD_ExprOpr **op_out); +MD_FUNCTION MD_Expr* MD_ExprParse_Atom(MD_Arena *arena, MD_ExprParseCtx *ctx, + MD_Node **iter, MD_Node *first, MD_Node *opl); +MD_FUNCTION MD_Expr* MD_ExprParse_MinPrecedence(MD_Arena *arena, MD_ExprParseCtx *ctx, + MD_Node **iter, MD_Node *first, MD_Node *opl, + MD_u32 min_precedence); + + +//~ String Generation + +MD_FUNCTION void MD_DebugDumpFromNode(MD_Arena *arena, MD_String8List *out, MD_Node *node, + int indent, MD_String8 indent_string, + MD_GenerateFlags flags); +MD_FUNCTION void MD_ReconstructionFromNode(MD_Arena *arena, MD_String8List *out, MD_Node *node, + int indent, MD_String8 indent_string); + +//~ Command Line Argument Helper + +MD_FUNCTION MD_String8List MD_StringListFromArgCV(MD_Arena *arena, int argument_count, + char **arguments); +MD_FUNCTION MD_CmdLine MD_MakeCmdLineFromOptions(MD_Arena *arena, MD_String8List options); +MD_FUNCTION MD_String8List MD_CmdLineValuesFromString(MD_CmdLine cmdln, MD_String8 name); +MD_FUNCTION MD_b32 MD_CmdLineB32FromString(MD_CmdLine cmdln, MD_String8 name); +MD_FUNCTION MD_i64 MD_CmdLineI64FromString(MD_CmdLine cmdln, MD_String8 name); + +//~ File System + +MD_FUNCTION MD_String8 MD_LoadEntireFile(MD_Arena *arena, MD_String8 filename); +MD_FUNCTION MD_b32 MD_FileIterBegin(MD_FileIter *it, MD_String8 path); +MD_FUNCTION MD_FileInfo MD_FileIterNext(MD_Arena *arena, MD_FileIter *it); +MD_FUNCTION void MD_FileIterEnd(MD_FileIter *it); + +#endif // MD_H + +/* +Copyright 2021 Dion Systems LLC + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ diff --git a/code/third_party/metadesk/md_stb_sprintf.h b/code/third_party/metadesk/md_stb_sprintf.h new file mode 100644 index 0000000..c5b5fcc --- /dev/null +++ b/code/third_party/metadesk/md_stb_sprintf.h @@ -0,0 +1,1905 @@ +// NOTE(rjf): This library has been modified for Metadesk. + +// stb_sprintf - v1.09 - public domain snprintf() implementation +// originally by Jeff Roberts / RAD Game Tools, 2015/10/20 +// http://github.com/nothings/stb +// +// allowed types: sc uidBboXx p AaGgEef n +// lengths : hh h ll j z t I64 I32 I +// +// Contributors: +// Fabian "ryg" Giesen (reformatting) +// +// Contributors (bugfixes): +// github:d26435 +// github:trex78 +// github:account-login +// Jari Komppa (SI suffixes) +// Rohit Nirmal +// Marcin Wojdyr +// Leonard Ritter +// Stefano Zanotti +// Adam Allison +// Arvid Gerstmann +// Markus Kolb +// +// LICENSE: +// +// See end of file for license information. + +#ifndef STB_SPRINTF_H_INCLUDE +#define STB_SPRINTF_H_INCLUDE + +/* +Single file sprintf replacement. + +Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20. +Hereby placed in public domain. + +This is a full sprintf replacement that supports everything that +the C runtime sprintfs support, including float/double, 64-bit integers, +hex floats, field parameters (%*.*d stuff), length reads backs, etc. + +Why would you need this if sprintf already exists? Well, first off, +it's *much* faster (see below). It's also much smaller than the CRT +versions code-space-wise. We've also added some simple improvements +that are super handy (commas in thousands, callbacks at buffer full, +for example). Finally, the format strings for MSVC and GCC differ +for 64-bit integers (among other small things), so this lets you use +the same format strings in cross platform code. + +It uses the standard single file trick of being both the header file +and the source itself. If you just include it normally, you just get +the header file function definitions. To get the code, you include +it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first. + +It only uses va_args macros from the C runtime to do it's work. It +does cast doubles to S64s and shifts and divides U64s, which does +drag in CRT code on most platforms. + +It compiles to roughly 8K with float support, and 4K without. +As a comparison, when using MSVC static libs, calling sprintf drags +in 16K. + +API: +==== +int stbsp_sprintf( char * buf, char const * fmt, ... ) +int stbsp_snprintf( char * buf, int count, char const * fmt, ... ) + Convert an arg list into a buffer. stbsp_snprintf always returns + a zero-terminated string (unlike regular snprintf). + +int stbsp_vsprintf( char * buf, char const * fmt, va_list va ) +int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va ) + Convert a va_list arg list into a buffer. stbsp_vsnprintf always returns + a zero-terminated string (unlike regular snprintf). + +int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va ) + typedef char * STBSP_SPRINTFCB( char const * buf, void * user, int len ); + Convert into a buffer, calling back every STB_SPRINTF_MIN chars. + Your callback can then copy the chars out, print them or whatever. + This function is actually the workhorse for everything else. + The buffer you pass in must hold at least STB_SPRINTF_MIN characters. + // you return the next buffer to use or 0 to stop converting + +void stbsp_set_separators( char comma, char period ) + Set the comma and period characters to use. + +FLOATS/DOUBLES: +=============== +This code uses a internal float->ascii conversion method that uses +doubles with error correction (double-doubles, for ~105 bits of +precision). This conversion is round-trip perfect - that is, an atof +of the values output here will give you the bit-exact double back. + +One difference is that our insignificant digits will be different than +with MSVC or GCC (but they don't match each other either). We also +don't attempt to find the minimum length matching float (pre-MSVC15 +doesn't either). + +If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT +and you'll save 4K of code space. + +64-BIT INTS: +============ +This library also supports 64-bit integers and you can use MSVC style or +GCC style indicators (%I64d or %lld). It supports the C99 specifiers +for size_t and ptr_diff_t (%jd %zd) as well. + +EXTRAS: +======= +Like some GCCs, for integers and floats, you can use a ' (single quote) +specifier and commas will be inserted on the thousands: "%'d" on 12345 +would print 12,345. + +For integers and floats, you can use a "$" specifier and the number +will be converted to float and then divided to get kilo, mega, giga or +tera and then printed, so "%$d" 1000 is "1.0 k", "%$.2d" 2536000 is +"2.53 M", etc. For byte values, use two $:s, like "%$$d" to turn +2536000 to "2.42 Mi". If you prefer JEDEC suffixes to SI ones, use three +$:s: "%$$$d" -> "2.42 M". To remove the space between the number and the +suffix, add "_" specifier: "%_$d" -> "2.53M". + +In addition to octal and hexadecimal conversions, you can print +integers in binary: "%b" for 256 would print 100. + +PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC): +=================================================================== +"%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC) +"%24d" across all 32-bit ints (4.5x/4.2x faster) +"%x" across all 32-bit ints (4.5x/3.8x faster) +"%08x" across all 32-bit ints (4.3x/3.8x faster) +"%f" across e-10 to e+10 floats (7.3x/6.0x faster) +"%e" across e-10 to e+10 floats (8.1x/6.0x faster) +"%g" across e-10 to e+10 floats (10.0x/7.1x faster) +"%f" for values near e-300 (7.9x/6.5x faster) +"%f" for values near e+300 (10.0x/9.1x faster) +"%e" for values near e-300 (10.1x/7.0x faster) +"%e" for values near e+300 (9.2x/6.0x faster) +"%.320f" for values near e-300 (12.6x/11.2x faster) +"%a" for random values (8.6x/4.3x faster) +"%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster) +"%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster) +"%s%s%s" for 64 char strings (7.1x/7.3x faster) +"...512 char string..." ( 35.0x/32.5x faster!) +*/ + +#if defined(__clang__) +#if defined(__has_feature) && defined(__has_attribute) +#if __has_feature(address_sanitizer) +#if __has_attribute(__no_sanitize__) +#define STBSP__ASAN __attribute__((__no_sanitize__("address"))) +#elif __has_attribute(__no_sanitize_address__) +#define STBSP__ASAN __attribute__((__no_sanitize_address__)) +#elif __has_attribute(__no_address_safety_analysis__) +#define STBSP__ASAN __attribute__((__no_address_safety_analysis__)) +#endif +#endif +#endif +#elif __GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) +#if __SANITIZE_ADDRESS__ +#define STBSP__ASAN __attribute__((__no_sanitize_address__)) +#endif +#endif + +#ifndef STBSP__ASAN +#define STBSP__ASAN +#endif + +#ifdef STB_SPRINTF_STATIC +#define STBSP__PUBLICDEC static +#define STBSP__PUBLICDEF static STBSP__ASAN +#else +#ifdef __cplusplus +#define STBSP__PUBLICDEC extern "C" +#define STBSP__PUBLICDEF extern "C" STBSP__ASAN +#else +#define STBSP__PUBLICDEC extern +#define STBSP__PUBLICDEF STBSP__ASAN +#endif +#endif + +#include // for va_list() +#include // size_t, ptrdiff_t + +#ifndef STB_SPRINTF_MIN +#define STB_SPRINTF_MIN 512 // how many characters per callback +#endif +typedef char *STBSP_SPRINTFCB(const char *buf, void *user, int len); + +#ifndef STB_SPRINTF_DECORATE +#define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names +#endif + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va); +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va); +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...); +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...); + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va); +STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char comma, char period); + +#endif // STB_SPRINTF_H_INCLUDE + +#ifdef STB_SPRINTF_IMPLEMENTATION + +#include // for va_arg() + +#define stbsp__uint32 unsigned int +#define stbsp__int32 signed int + +#ifdef _MSC_VER +#define stbsp__uint64 unsigned __int64 +#define stbsp__int64 signed __int64 +#else +#define stbsp__uint64 unsigned long long +#define stbsp__int64 signed long long +#endif +#define stbsp__uint16 unsigned short + +#ifndef stbsp__uintptr +#if defined(__ppc64__) || defined(__powerpc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) +#define stbsp__uintptr stbsp__uint64 +#else +#define stbsp__uintptr stbsp__uint32 +#endif +#endif + +#ifndef STB_SPRINTF_MSVC_MODE // used for MSVC2013 and earlier (MSVC2015 matches GCC) +#if defined(_MSC_VER) && (_MSC_VER < 1900) +#define STB_SPRINTF_MSVC_MODE +#endif +#endif + +#ifdef STB_SPRINTF_NOUNALIGNED // define this before inclusion to force stbsp_sprintf to always use aligned accesses +#define STBSP__UNALIGNED(code) +#else +#define STBSP__UNALIGNED(code) code +#endif + +#ifndef STB_SPRINTF_NOFLOAT +// internal float utility functions +static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits); +static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value); +#define STBSP__SPECIAL 0x7000 +#endif + +static char stbsp__period = '.'; +static char stbsp__comma = ','; +static struct +{ + short temp; // force next field to be 2-byte aligned + char pair[201]; +} stbsp__digitpair = +{ + 0, + "00010203040506070809101112131415161718192021222324" + "25262728293031323334353637383940414243444546474849" + "50515253545556575859606162636465666768697071727374" + "75767778798081828384858687888990919293949596979899" +}; + +STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char pcomma, char pperiod) +{ + stbsp__period = pperiod; + stbsp__comma = pcomma; +} + +#define STBSP__LEFTJUST 1 +#define STBSP__LEADINGPLUS 2 +#define STBSP__LEADINGSPACE 4 +#define STBSP__LEADING_0X 8 +#define STBSP__LEADINGZERO 16 +#define STBSP__INTMAX 32 +#define STBSP__TRIPLET_COMMA 64 +#define STBSP__NEGATIVE 128 +#define STBSP__METRIC_SUFFIX 256 +#define STBSP__HALFWIDTH 512 +#define STBSP__METRIC_NOSPACE 1024 +#define STBSP__METRIC_1024 2048 +#define STBSP__METRIC_JEDEC 4096 + +static void stbsp__lead_sign(stbsp__uint32 fl, char *sign) +{ + sign[0] = 0; + if (fl & STBSP__NEGATIVE) { + sign[0] = 1; + sign[1] = '-'; + } else if (fl & STBSP__LEADINGSPACE) { + sign[0] = 1; + sign[1] = ' '; + } else if (fl & STBSP__LEADINGPLUS) { + sign[0] = 1; + sign[1] = '+'; + } +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va) +{ + static char hex[] = "0123456789abcdefxp"; + static char hexu[] = "0123456789ABCDEFXP"; + char *bf; + char const *f; + int tlen = 0; + + bf = buf; + f = fmt; + for (;;) { + stbsp__int32 fw, pr, tz; + stbsp__uint32 fl; + + // macros for the callback buffer stuff +#define stbsp__chk_cb_bufL(bytes) \ +{ \ +int len = (int)(bf - buf); \ +if ((len + (bytes)) >= STB_SPRINTF_MIN) { \ +tlen += len; \ +if (0 == (bf = buf = callback(buf, user, len))) \ +goto done; \ +} \ +} +#define stbsp__chk_cb_buf(bytes) \ +{ \ +if (callback) { \ +stbsp__chk_cb_bufL(bytes); \ +} \ +} +#define stbsp__flush_cb() \ +{ \ +stbsp__chk_cb_bufL(STB_SPRINTF_MIN - 1); \ +} // flush if there is even one byte in the buffer +#define stbsp__cb_buf_clamp(cl, v) \ +cl = v; \ +if (callback) { \ +int lg = STB_SPRINTF_MIN - (int)(bf - buf); \ +if (cl > lg) \ +cl = lg; \ +} + + // fast copy everything up to the next % (or end of string) + for (;;) { + while (((stbsp__uintptr)f) & 3) { + schk1: + if (f[0] == '%') + goto scandd; + schk2: + if (f[0] == 0) + goto endfmt; + stbsp__chk_cb_buf(1); + *bf++ = f[0]; + ++f; + } + for (;;) { + // Check if the next 4 bytes contain %(0x25) or end of string. + // Using the 'hasless' trick: + // https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord + stbsp__uint32 v, c; + v = *(stbsp__uint32 *)f; + c = (~v) & 0x80808080; + if (((v ^ 0x25252525) - 0x01010101) & c) + goto schk1; + if ((v - 0x01010101) & c) + goto schk2; + if (callback) + if ((STB_SPRINTF_MIN - (int)(bf - buf)) < 4) + goto schk1; +#ifdef STB_SPRINTF_NOUNALIGNED + if(((stbsp__uintptr)bf) & 3) { + bf[0] = f[0]; + bf[1] = f[1]; + bf[2] = f[2]; + bf[3] = f[3]; + } else +#endif + { + *(stbsp__uint32 *)bf = v; + } + bf += 4; + f += 4; + } + } + scandd: + + ++f; + + // ok, we have a percent, read the modifiers first + fw = 0; + pr = -1; + fl = 0; + tz = 0; + + // flags + for (;;) { + switch (f[0]) { + // if we have left justify + case '-': + fl |= STBSP__LEFTJUST; + ++f; + continue; + // if we have leading plus + case '+': + fl |= STBSP__LEADINGPLUS; + ++f; + continue; + // if we have leading space + case ' ': + fl |= STBSP__LEADINGSPACE; + ++f; + continue; + // if we have leading 0x + case '#': + fl |= STBSP__LEADING_0X; + ++f; + continue; + // if we have thousand commas + case '\'': + fl |= STBSP__TRIPLET_COMMA; + ++f; + continue; + // if we have kilo marker (none->kilo->kibi->jedec) + case '$': + if (fl & STBSP__METRIC_SUFFIX) { + if (fl & STBSP__METRIC_1024) { + fl |= STBSP__METRIC_JEDEC; + } else { + fl |= STBSP__METRIC_1024; + } + } else { + fl |= STBSP__METRIC_SUFFIX; + } + ++f; + continue; + // if we don't want space between metric suffix and number + case '_': + fl |= STBSP__METRIC_NOSPACE; + ++f; + continue; + // if we have leading zero + case '0': + fl |= STBSP__LEADINGZERO; + ++f; + goto flags_done; + default: goto flags_done; + } + } + flags_done: + + // get the field width + if (f[0] == '*') { + fw = va_arg(va, stbsp__uint32); + ++f; + } else { + while ((f[0] >= '0') && (f[0] <= '9')) { + fw = fw * 10 + f[0] - '0'; + f++; + } + } + // get the precision + if (f[0] == '.') { + ++f; + if (f[0] == '*') { + pr = va_arg(va, stbsp__uint32); + ++f; + } else { + pr = 0; + while ((f[0] >= '0') && (f[0] <= '9')) { + pr = pr * 10 + f[0] - '0'; + f++; + } + } + } + + // handle integer size overrides + switch (f[0]) { + // are we halfwidth? + case 'h': + fl |= STBSP__HALFWIDTH; + ++f; + if (f[0] == 'h') + ++f; // QUARTERWIDTH + break; + // are we 64-bit (unix style) + case 'l': + fl |= ((sizeof(long) == 8) ? STBSP__INTMAX : 0); + ++f; + if (f[0] == 'l') { + fl |= STBSP__INTMAX; + ++f; + } + break; + // are we 64-bit on intmax? (c99) + case 'j': + fl |= (sizeof(size_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + // are we 64-bit on size_t or ptrdiff_t? (c99) + case 'z': + fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + case 't': + fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + // are we 64-bit (msft style) + case 'I': + if ((f[1] == '6') && (f[2] == '4')) { + fl |= STBSP__INTMAX; + f += 3; + } else if ((f[1] == '3') && (f[2] == '2')) { + f += 3; + } else { + fl |= ((sizeof(void *) == 8) ? STBSP__INTMAX : 0); + ++f; + } + break; + default: break; + } + + // handle each replacement + switch (f[0]) { +#define STBSP__NUMSZ 512 // big enough for e308 (with commas) or e-307 + char num[STBSP__NUMSZ]; + char lead[8]; + char tail[8]; + char *s; + char const *h; + stbsp__uint32 l, n, cs; + stbsp__uint64 n64; +#ifndef STB_SPRINTF_NOFLOAT + double fv; +#endif + stbsp__int32 dp; + char const *sn; + + case 's': + // get the string + s = va_arg(va, char *); + if (s == 0) + s = (char *)"null"; + // get the length + sn = s; + for (;;) { + if ((((stbsp__uintptr)sn) & 3) == 0) + break; + lchk: + if (sn[0] == 0) + goto ld; + ++sn; + } + n = 0xffffffff; + if (pr >= 0) { + n = (stbsp__uint32)(sn - s); + if (n >= (stbsp__uint32)pr) + goto ld; + n = ((stbsp__uint32)(pr - n)) >> 2; + } + while (n) { + stbsp__uint32 v = *(stbsp__uint32 *)sn; + if ((v - 0x01010101) & (~v) & 0x80808080UL) + goto lchk; + sn += 4; + --n; + } + goto lchk; + ld: + + l = (stbsp__uint32)(sn - s); + // clamp to precision + if (l > (stbsp__uint32)pr) + l = pr; + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + // copy the string in + goto scopy; + + //~ rjf: METADESK ADDITION: %S for MD_String8's + + case 'S': // MD_String8 + { + //- rjf: pull out string + MD_String8 str = va_arg(va, MD_String8); + + //- rjf: get string length + s = (char *)str.str; + sn = (const char *)(str.str + str.size); + l = str.size; + + //- rjf: clamp to precision + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + + goto scopy; + }break; + + //~ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + case 'c': // char + // get the character + s = num + STBSP__NUMSZ - 1; + *s = (char)va_arg(va, int); + l = 1; + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + goto scopy; + + case 'n': // weird write-bytes specifier + { + int *d = va_arg(va, int *); + *d = tlen + (int)(bf - buf); + } break; + +#ifdef STB_SPRINTF_NOFLOAT + case 'A': // float + case 'a': // hex float + case 'G': // float + case 'g': // float + case 'E': // float + case 'e': // float + case 'f': // float + va_arg(va, double); // eat it + s = (char *)"No float"; + l = 8; + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + goto scopy; +#else + case 'A': // hex float + case 'a': // hex float + h = (f[0] == 'A') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_parts((stbsp__int64 *)&n64, &dp, fv)) + fl |= STBSP__NEGATIVE; + + s = num + 64; + + stbsp__lead_sign(fl, lead); + + if (dp == -1023) + dp = (n64) ? -1022 : 0; + else + n64 |= (((stbsp__uint64)1) << 52); + n64 <<= (64 - 56); + if (pr < 15) + n64 += ((((stbsp__uint64)8) << 56) >> (pr * 4)); + // add leading chars + +#ifdef STB_SPRINTF_MSVC_MODE + *s++ = '0'; + *s++ = 'x'; +#else + lead[1 + lead[0]] = '0'; + lead[2 + lead[0]] = 'x'; + lead[0] += 2; +#endif + *s++ = h[(n64 >> 60) & 15]; + n64 <<= 4; + if (pr) + *s++ = stbsp__period; + sn = s; + + // print the bits + n = pr; + if (n > 13) + n = 13; + if (pr > (stbsp__int32)n) + tz = pr - n; + pr = 0; + while (n--) { + *s++ = h[(n64 >> 60) & 15]; + n64 <<= 4; + } + + // print the expo + tail[1] = h[17]; + if (dp < 0) { + tail[2] = '-'; + dp = -dp; + } else + tail[2] = '+'; + n = (dp >= 1000) ? 6 : ((dp >= 100) ? 5 : ((dp >= 10) ? 4 : 3)); + tail[0] = (char)n; + for (;;) { + tail[n] = '0' + dp % 10; + if (n <= 3) + break; + --n; + dp /= 10; + } + + dp = (int)(s - sn); + l = (int)(s - (num + 64)); + s = num + 64; + cs = 1 + (3 << 24); + goto scopy; + + case 'G': // float + case 'g': // float + h = (f[0] == 'G') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; + else if (pr == 0) + pr = 1; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, (pr - 1) | 0x80000000)) + fl |= STBSP__NEGATIVE; + + // clamp the precision and delete extra zeros after clamp + n = pr; + if (l > (stbsp__uint32)pr) + l = pr; + while ((l > 1) && (pr) && (sn[l - 1] == '0')) { + --pr; + --l; + } + + // should we use %e + if ((dp <= -4) || (dp > (stbsp__int32)n)) { + if (pr > (stbsp__int32)l) + pr = l - 1; + else if (pr) + --pr; // when using %e, there is one digit before the decimal + goto doexpfromg; + } + // this is the insane action to get the pr to match %g semantics for %f + if (dp > 0) { + pr = (dp < (stbsp__int32)l) ? l - dp : 0; + } else { + pr = -dp + ((pr > (stbsp__int32)l) ? (stbsp__int32) l : pr); + } + goto dofloatfromg; + + case 'E': // float + case 'e': // float + h = (f[0] == 'E') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr | 0x80000000)) + fl |= STBSP__NEGATIVE; + doexpfromg: + tail[0] = 0; + stbsp__lead_sign(fl, lead); + if (dp == STBSP__SPECIAL) { + s = (char *)sn; + cs = 0; + pr = 0; + goto scopy; + } + s = num + 64; + // handle leading chars + *s++ = sn[0]; + + if (pr) + *s++ = stbsp__period; + + // handle after decimal + if ((l - 1) > (stbsp__uint32)pr) + l = pr + 1; + for (n = 1; n < l; n++) + *s++ = sn[n]; + // trailing zeros + tz = pr - (l - 1); + pr = 0; + // dump expo + tail[1] = h[0xe]; + dp -= 1; + if (dp < 0) { + tail[2] = '-'; + dp = -dp; + } else + tail[2] = '+'; +#ifdef STB_SPRINTF_MSVC_MODE + n = 5; +#else + n = (dp >= 100) ? 5 : 4; +#endif + tail[0] = (char)n; + for (;;) { + tail[n] = '0' + dp % 10; + if (n <= 3) + break; + --n; + dp /= 10; + } + cs = 1 + (3 << 24); // how many tens + goto flt_lead; + + case 'f': // float + fv = va_arg(va, double); + doafloat: + // do kilos + if (fl & STBSP__METRIC_SUFFIX) { + double divisor; + divisor = 1000.0f; + if (fl & STBSP__METRIC_1024) + divisor = 1024.0; + while (fl < 0x4000000) { + if ((fv < divisor) && (fv > -divisor)) + break; + fv /= divisor; + fl += 0x1000000; + } + } + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr)) + fl |= STBSP__NEGATIVE; + dofloatfromg: + tail[0] = 0; + stbsp__lead_sign(fl, lead); + if (dp == STBSP__SPECIAL) { + s = (char *)sn; + cs = 0; + pr = 0; + goto scopy; + } + s = num + 64; + + // handle the three decimal varieties + if (dp <= 0) { + stbsp__int32 i; + // handle 0.000*000xxxx + *s++ = '0'; + if (pr) + *s++ = stbsp__period; + n = -dp; + if ((stbsp__int32)n > pr) + n = pr; + i = n; + while (i) { + if ((((stbsp__uintptr)s) & 3) == 0) + break; + *s++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)s = 0x30303030; + s += 4; + i -= 4; + } + while (i) { + *s++ = '0'; + --i; + } + if ((stbsp__int32)(l + n) > pr) + l = pr - n; + i = l; + while (i) { + *s++ = *sn++; + --i; + } + tz = pr - (n + l); + cs = 1 + (3 << 24); // how many tens did we write (for commas below) + } else { + cs = (fl & STBSP__TRIPLET_COMMA) ? ((600 - (stbsp__uint32)dp) % 3) : 0; + if ((stbsp__uint32)dp >= l) { + // handle xxxx000*000.0 + n = 0; + for (;;) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } else { + *s++ = sn[n]; + ++n; + if (n >= l) + break; + } + } + if (n < (stbsp__uint32)dp) { + n = dp - n; + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + while (n) { + if ((((stbsp__uintptr)s) & 3) == 0) + break; + *s++ = '0'; + --n; + } + while (n >= 4) { + *(stbsp__uint32 *)s = 0x30303030; + s += 4; + n -= 4; + } + } + while (n) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } else { + *s++ = '0'; + --n; + } + } + } + cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens + if (pr) { + *s++ = stbsp__period; + tz = pr; + } + } else { + // handle xxxxx.xxxx000*000 + n = 0; + for (;;) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } else { + *s++ = sn[n]; + ++n; + if (n >= (stbsp__uint32)dp) + break; + } + } + cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens + if (pr) + *s++ = stbsp__period; + if ((l - dp) > (stbsp__uint32)pr) + l = pr + dp; + while (n < l) { + *s++ = sn[n]; + ++n; + } + tz = pr - (l - dp); + } + } + pr = 0; + + // handle k,m,g,t + if (fl & STBSP__METRIC_SUFFIX) { + char idx; + idx = 1; + if (fl & STBSP__METRIC_NOSPACE) + idx = 0; + tail[0] = idx; + tail[1] = ' '; + { + if (fl >> 24) { // SI kilo is 'k', JEDEC and SI kibits are 'K'. + if (fl & STBSP__METRIC_1024) + tail[idx + 1] = "_KMGT"[fl >> 24]; + else + tail[idx + 1] = "_kMGT"[fl >> 24]; + idx++; + // If printing kibits and not in jedec, add the 'i'. + if (fl & STBSP__METRIC_1024 && !(fl & STBSP__METRIC_JEDEC)) { + tail[idx + 1] = 'i'; + idx++; + } + tail[0] = idx; + } + } + }; + + flt_lead: + // get the length that we copied + l = (stbsp__uint32)(s - (num + 64)); + s = num + 64; + goto scopy; +#endif + + case 'B': // upper binary + case 'b': // lower binary + h = (f[0] == 'B') ? hexu : hex; + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 2; + lead[1] = '0'; + lead[2] = h[0xb]; + } + l = (8 << 4) | (1 << 8); + goto radixnum; + + case 'o': // octal + h = hexu; + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 1; + lead[1] = '0'; + } + l = (3 << 4) | (3 << 8); + goto radixnum; + + case 'p': // pointer + fl |= (sizeof(void *) == 8) ? STBSP__INTMAX : 0; + pr = sizeof(void *) * 2; + fl &= ~STBSP__LEADINGZERO; // 'p' only prints the pointer with zeros + // fall through - to X + + case 'X': // upper hex + case 'x': // lower hex + h = (f[0] == 'X') ? hexu : hex; + l = (4 << 4) | (4 << 8); + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 2; + lead[1] = '0'; + lead[2] = h[16]; + } + radixnum: + // get the number + if (fl & STBSP__INTMAX) + n64 = va_arg(va, stbsp__uint64); + else + n64 = va_arg(va, stbsp__uint32); + + s = num + STBSP__NUMSZ; + dp = 0; + // clear tail, and clear leading if value is zero + tail[0] = 0; + if (n64 == 0) { + lead[0] = 0; + if (pr == 0) { + l = 0; + cs = (((l >> 4) & 15)) << 24; + goto scopy; + } + } + // convert to string + for (;;) { + *--s = h[n64 & ((1 << (l >> 8)) - 1)]; + n64 >>= (l >> 8); + if (!((n64) || ((stbsp__int32)((num + STBSP__NUMSZ) - s) < pr))) + break; + if (fl & STBSP__TRIPLET_COMMA) { + ++l; + if ((l & 15) == ((l >> 4) & 15)) { + l &= ~15; + *--s = stbsp__comma; + } + } + }; + // get the tens and the comma pos + cs = (stbsp__uint32)((num + STBSP__NUMSZ) - s) + ((((l >> 4) & 15)) << 24); + // get the length that we copied + l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); + // copy it + goto scopy; + + case 'u': // unsigned + case 'i': + case 'd': // integer + // get the integer and abs it + if (fl & STBSP__INTMAX) { + stbsp__int64 i64 = va_arg(va, stbsp__int64); + n64 = (stbsp__uint64)i64; + if ((f[0] != 'u') && (i64 < 0)) { + n64 = (stbsp__uint64)-i64; + fl |= STBSP__NEGATIVE; + } + } else { + stbsp__int32 i = va_arg(va, stbsp__int32); + n64 = (stbsp__uint32)i; + if ((f[0] != 'u') && (i < 0)) { + n64 = (stbsp__uint32)-i; + fl |= STBSP__NEGATIVE; + } + } + +#ifndef STB_SPRINTF_NOFLOAT + if (fl & STBSP__METRIC_SUFFIX) { + if (n64 < 1024) + pr = 0; + else if (pr == -1) + pr = 1; + fv = (double)(stbsp__int64)n64; + goto doafloat; + } +#endif + + // convert to string + s = num + STBSP__NUMSZ; + l = 0; + + for (;;) { + // do in 32-bit chunks (avoid lots of 64-bit divides even with constant denominators) + char *o = s - 8; + if (n64 >= 100000000) { + n = (stbsp__uint32)(n64 % 100000000); + n64 /= 100000000; + } else { + n = (stbsp__uint32)n64; + n64 = 0; + } + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + do { + s -= 2; + *(stbsp__uint16 *)s = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; + n /= 100; + } while (n); + } + while (n) { + if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { + l = 0; + *--s = stbsp__comma; + --o; + } else { + *--s = (char)(n % 10) + '0'; + n /= 10; + } + } + if (n64 == 0) { + if ((s[0] == '0') && (s != (num + STBSP__NUMSZ))) + ++s; + break; + } + while (s != o) + if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { + l = 0; + *--s = stbsp__comma; + --o; + } else { + *--s = '0'; + } + } + + tail[0] = 0; + stbsp__lead_sign(fl, lead); + + // get the length that we copied + l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); + if (l == 0) { + *--s = '0'; + l = 1; + } + cs = l + (3 << 24); + if (pr < 0) + pr = 0; + + scopy: + // get fw=leading/trailing space, pr=leading zeros + if (pr < (stbsp__int32)l) + pr = l; + n = pr + lead[0] + tail[0] + tz; + if (fw < (stbsp__int32)n) + fw = n; + fw -= n; + pr -= l; + + // handle right justify and leading zeros + if ((fl & STBSP__LEFTJUST) == 0) { + if (fl & STBSP__LEADINGZERO) // if leading zeros, everything is in pr + { + pr = (fw > pr) ? fw : pr; + fw = 0; + } else { + fl &= ~STBSP__TRIPLET_COMMA; // if no leading zeros, then no commas + } + } + + // copy the spaces and/or zeros + if (fw + pr) { + stbsp__int32 i; + stbsp__uint32 c; + + // copy leading spaces (or when doing %8.4d stuff) + if ((fl & STBSP__LEFTJUST) == 0) + while (fw > 0) { + stbsp__cb_buf_clamp(i, fw); + fw -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = ' '; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x20202020; + bf += 4; + i -= 4; + } + while (i) { + *bf++ = ' '; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy leader + sn = lead + 1; + while (lead[0]) { + stbsp__cb_buf_clamp(i, lead[0]); + lead[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy leading zeros + c = cs >> 24; + cs &= 0xffffff; + cs = (fl & STBSP__TRIPLET_COMMA) ? ((stbsp__uint32)(c - ((pr + cs) % (c + 1)))) : 0; + while (pr > 0) { + stbsp__cb_buf_clamp(i, pr); + pr -= i; + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x30303030; + bf += 4; + i -= 4; + } + } + while (i) { + if ((fl & STBSP__TRIPLET_COMMA) && (cs++ == c)) { + cs = 0; + *bf++ = stbsp__comma; + } else + *bf++ = '0'; + --i; + } + stbsp__chk_cb_buf(1); + } + } + + // copy leader if there is still one + sn = lead + 1; + while (lead[0]) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, lead[0]); + lead[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy the string + n = l; + while (n) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, n); + n -= i; + STBSP__UNALIGNED(while (i >= 4) { + *(stbsp__uint32 volatile *)bf = *(stbsp__uint32 volatile *)s; + bf += 4; + s += 4; + i -= 4; + }) + while (i) { + *bf++ = *s++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy trailing zeros + while (tz) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, tz); + tz -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x30303030; + bf += 4; + i -= 4; + } + while (i) { + *bf++ = '0'; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy tail if there is one + sn = tail + 1; + while (tail[0]) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, tail[0]); + tail[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // handle the left justify + if (fl & STBSP__LEFTJUST) + if (fw > 0) { + while (fw) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, fw); + fw -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = ' '; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x20202020; + bf += 4; + i -= 4; + } + while (i--) + *bf++ = ' '; + stbsp__chk_cb_buf(1); + } + } + break; + + default: // unknown, just copy code + s = num + STBSP__NUMSZ - 1; + *s = f[0]; + l = 1; + fw = fl = 0; + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + goto scopy; + } + ++f; + } + endfmt: + + if (!callback) + *bf = 0; + else + stbsp__flush_cb(); + + done: + return tlen + (int)(bf - buf); +} + +// cleanup +#undef STBSP__LEFTJUST +#undef STBSP__LEADINGPLUS +#undef STBSP__LEADINGSPACE +#undef STBSP__LEADING_0X +#undef STBSP__LEADINGZERO +#undef STBSP__INTMAX +#undef STBSP__TRIPLET_COMMA +#undef STBSP__NEGATIVE +#undef STBSP__METRIC_SUFFIX +#undef STBSP__NUMSZ +#undef stbsp__chk_cb_bufL +#undef stbsp__chk_cb_buf +#undef stbsp__flush_cb +#undef stbsp__cb_buf_clamp + +// ============================================================================ +// wrapper functions + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) +{ + int result; + va_list va; + va_start(va, fmt); + result = STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); + va_end(va); + return result; +} + +typedef struct stbsp__context { + char *buf; + int count; + int length; + char tmp[STB_SPRINTF_MIN]; +} stbsp__context; + +static char *stbsp__clamp_callback(const char *buf, void *user, int len) +{ + stbsp__context *c = (stbsp__context *)user; + c->length += len; + + if (len > c->count) + len = c->count; + + if (len) { + if (buf != c->buf) { + const char *s, *se; + char *d; + d = c->buf; + s = buf; + se = buf + len; + do { + *d++ = *s++; + } while (s < se); + } + c->buf += len; + c->count -= len; + } + + if (c->count <= 0) + return c->tmp; + return (c->count >= STB_SPRINTF_MIN) ? c->buf : c->tmp; // go direct into buffer if you can +} + +static char * stbsp__count_clamp_callback( const char * buf, void * user, int len ) +{ + stbsp__context * c = (stbsp__context*)user; + (void) sizeof(buf); + + c->length += len; + return c->tmp; // go direct into buffer if you can +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, char const * fmt, va_list va ) +{ + stbsp__context c; + + if ( (count == 0) && !buf ) + { + c.length = 0; + + STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__count_clamp_callback, &c, c.tmp, fmt, va ); + } + else + { + int l; + + c.buf = buf; + c.count = count; + c.length = 0; + + STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__clamp_callback, &c, stbsp__clamp_callback(0,&c,0), fmt, va ); + + // zero-terminate + l = (int)( c.buf - buf ); + if ( l >= count ) // should never be greater, only equal (or less) than count + l = count - 1; + buf[l] = 0; + } + + return c.length; +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) +{ + int result; + va_list va; + va_start(va, fmt); + + result = STB_SPRINTF_DECORATE(vsnprintf)(buf, count, fmt, va); + va_end(va); + + return result; +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va) +{ + return STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); +} + +// ======================================================================= +// low level float utility functions + +#ifndef STB_SPRINTF_NOFLOAT + +// copies d to bits w/ strict aliasing (this compiles to nothing on /Ox) +#define STBSP__COPYFP(dest, src) \ +{ \ +int cn; \ +for (cn = 0; cn < 8; cn++) \ +((char *)&dest)[cn] = ((char *)&src)[cn]; \ +} + +// get float info +static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value) +{ + double d; + stbsp__int64 b = 0; + + // load value and round at the frac_digits + d = value; + + STBSP__COPYFP(b, d); + + *bits = b & ((((stbsp__uint64)1) << 52) - 1); + *expo = (stbsp__int32)(((b >> 52) & 2047) - 1023); + + return (stbsp__int32)((stbsp__uint64) b >> 63); +} + +static double const stbsp__bot[23] = { + 1e+000, 1e+001, 1e+002, 1e+003, 1e+004, 1e+005, 1e+006, 1e+007, 1e+008, 1e+009, 1e+010, 1e+011, + 1e+012, 1e+013, 1e+014, 1e+015, 1e+016, 1e+017, 1e+018, 1e+019, 1e+020, 1e+021, 1e+022 +}; +static double const stbsp__negbot[22] = { + 1e-001, 1e-002, 1e-003, 1e-004, 1e-005, 1e-006, 1e-007, 1e-008, 1e-009, 1e-010, 1e-011, + 1e-012, 1e-013, 1e-014, 1e-015, 1e-016, 1e-017, 1e-018, 1e-019, 1e-020, 1e-021, 1e-022 +}; +static double const stbsp__negboterr[22] = { + -5.551115123125783e-018, -2.0816681711721684e-019, -2.0816681711721686e-020, -4.7921736023859299e-021, -8.1803053914031305e-022, 4.5251888174113741e-023, + 4.5251888174113739e-024, -2.0922560830128471e-025, -6.2281591457779853e-026, -3.6432197315497743e-027, 6.0503030718060191e-028, 2.0113352370744385e-029, + -3.0373745563400371e-030, 1.1806906454401013e-032, -7.7705399876661076e-032, 2.0902213275965398e-033, -7.1542424054621921e-034, -7.1542424054621926e-035, + 2.4754073164739869e-036, 5.4846728545790429e-037, 9.2462547772103625e-038, -4.8596774326570872e-039 +}; +static double const stbsp__top[13] = { + 1e+023, 1e+046, 1e+069, 1e+092, 1e+115, 1e+138, 1e+161, 1e+184, 1e+207, 1e+230, 1e+253, 1e+276, 1e+299 +}; +static double const stbsp__negtop[13] = { + 1e-023, 1e-046, 1e-069, 1e-092, 1e-115, 1e-138, 1e-161, 1e-184, 1e-207, 1e-230, 1e-253, 1e-276, 1e-299 +}; +static double const stbsp__toperr[13] = { + 8388608, + 6.8601809640529717e+028, + -7.253143638152921e+052, + -4.3377296974619174e+075, + -1.5559416129466825e+098, + -3.2841562489204913e+121, + -3.7745893248228135e+144, + -1.7356668416969134e+167, + -3.8893577551088374e+190, + -9.9566444326005119e+213, + 6.3641293062232429e+236, + -5.2069140800249813e+259, + -5.2504760255204387e+282 +}; +static double const stbsp__negtoperr[13] = { + 3.9565301985100693e-040, -2.299904345391321e-063, 3.6506201437945798e-086, 1.1875228833981544e-109, + -5.0644902316928607e-132, -6.7156837247865426e-155, -2.812077463003139e-178, -5.7778912386589953e-201, + 7.4997100559334532e-224, -4.6439668915134491e-247, -6.3691100762962136e-270, -9.436808465446358e-293, + 8.0970921678014997e-317 +}; + +#if defined(_MSC_VER) && (_MSC_VER <= 1200) +static stbsp__uint64 const stbsp__powten[20] = { + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000, + 100000000000, + 1000000000000, + 10000000000000, + 100000000000000, + 1000000000000000, + 10000000000000000, + 100000000000000000, + 1000000000000000000, + 10000000000000000000U +}; +#define stbsp__tento19th ((stbsp__uint64)1000000000000000000) +#else +static stbsp__uint64 const stbsp__powten[20] = { + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000ULL, + 100000000000ULL, + 1000000000000ULL, + 10000000000000ULL, + 100000000000000ULL, + 1000000000000000ULL, + 10000000000000000ULL, + 100000000000000000ULL, + 1000000000000000000ULL, + 10000000000000000000ULL +}; +#define stbsp__tento19th (1000000000000000000ULL) +#endif + +#define stbsp__ddmulthi(oh, ol, xh, yh) \ +{ \ +double ahi = 0, alo, bhi = 0, blo; \ +stbsp__int64 bt; \ +oh = xh * yh; \ +STBSP__COPYFP(bt, xh); \ +bt &= ((~(stbsp__uint64)0) << 27); \ +STBSP__COPYFP(ahi, bt); \ +alo = xh - ahi; \ +STBSP__COPYFP(bt, yh); \ +bt &= ((~(stbsp__uint64)0) << 27); \ +STBSP__COPYFP(bhi, bt); \ +blo = yh - bhi; \ +ol = ((ahi * bhi - oh) + ahi * blo + alo * bhi) + alo * blo; \ +} + +#define stbsp__ddtoS64(ob, xh, xl) \ +{ \ +double ahi = 0, alo, vh, t; \ +ob = (stbsp__int64)ph; \ +vh = (double)ob; \ +ahi = (xh - vh); \ +t = (ahi - xh); \ +alo = (xh - (ahi - t)) - (vh + t); \ +ob += (stbsp__int64)(ahi + alo + xl); \ +} + +#define stbsp__ddrenorm(oh, ol) \ +{ \ +double s; \ +s = oh + ol; \ +ol = ol - (s - oh); \ +oh = s; \ +} + +#define stbsp__ddmultlo(oh, ol, xh, xl, yh, yl) ol = ol + (xh * yl + xl * yh); + +#define stbsp__ddmultlos(oh, ol, xh, yl) ol = ol + (xh * yl); + +static void stbsp__raise_to_power10(double *ohi, double *olo, double d, stbsp__int32 power) // power can be -323 to +350 +{ + double ph, pl; + if ((power >= 0) && (power <= 22)) { + stbsp__ddmulthi(ph, pl, d, stbsp__bot[power]); + } else { + stbsp__int32 e, et, eb; + double p2h, p2l; + + e = power; + if (power < 0) + e = -e; + et = (e * 0x2c9) >> 14; /* %23 */ + if (et > 13) + et = 13; + eb = e - (et * 23); + + ph = d; + pl = 0.0; + if (power < 0) { + if (eb) { + --eb; + stbsp__ddmulthi(ph, pl, d, stbsp__negbot[eb]); + stbsp__ddmultlos(ph, pl, d, stbsp__negboterr[eb]); + } + if (et) { + stbsp__ddrenorm(ph, pl); + --et; + stbsp__ddmulthi(p2h, p2l, ph, stbsp__negtop[et]); + stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__negtop[et], stbsp__negtoperr[et]); + ph = p2h; + pl = p2l; + } + } else { + if (eb) { + e = eb; + if (eb > 22) + eb = 22; + e -= eb; + stbsp__ddmulthi(ph, pl, d, stbsp__bot[eb]); + if (e) { + stbsp__ddrenorm(ph, pl); + stbsp__ddmulthi(p2h, p2l, ph, stbsp__bot[e]); + stbsp__ddmultlos(p2h, p2l, stbsp__bot[e], pl); + ph = p2h; + pl = p2l; + } + } + if (et) { + stbsp__ddrenorm(ph, pl); + --et; + stbsp__ddmulthi(p2h, p2l, ph, stbsp__top[et]); + stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__top[et], stbsp__toperr[et]); + ph = p2h; + pl = p2l; + } + } + } + stbsp__ddrenorm(ph, pl); + *ohi = ph; + *olo = pl; +} + +// given a float value, returns the significant bits in bits, and the position of the +// decimal point in decimal_pos. +/-INF and NAN are specified by special values +// returned in the decimal_pos parameter. +// frac_digits is absolute normally, but if you want from first significant digits (got %g and %e), or in 0x80000000 +static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits) +{ + double d; + stbsp__int64 bits = 0; + stbsp__int32 expo, e, ng, tens; + + d = value; + STBSP__COPYFP(bits, d); + expo = (stbsp__int32)((bits >> 52) & 2047); + ng = (stbsp__int32)((stbsp__uint64) bits >> 63); + if (ng) + d = -d; + + if (expo == 2047) // is nan or inf? + { + *start = (bits & ((((stbsp__uint64)1) << 52) - 1)) ? "NaN" : "Inf"; + *decimal_pos = STBSP__SPECIAL; + *len = 3; + return ng; + } + + if (expo == 0) // is zero or denormal + { + if (((stbsp__uint64) bits << 1) == 0) // do zero + { + *decimal_pos = 1; + *start = out; + out[0] = '0'; + *len = 1; + return ng; + } + // find the right expo for denormals + { + stbsp__int64 v = ((stbsp__uint64)1) << 51; + while ((bits & v) == 0) { + --expo; + v >>= 1; + } + } + } + + // find the decimal exponent as well as the decimal bits of the value + { + double ph, pl; + + // log10 estimate - very specifically tweaked to hit or undershoot by no more than 1 of log10 of all expos 1..2046 + tens = expo - 1023; + tens = (tens < 0) ? ((tens * 617) / 2048) : (((tens * 1233) / 4096) + 1); + + // move the significant bits into position and stick them into an int + stbsp__raise_to_power10(&ph, &pl, d, 18 - tens); + + // get full as much precision from double-double as possible + stbsp__ddtoS64(bits, ph, pl); + + // check if we undershot + if (((stbsp__uint64)bits) >= stbsp__tento19th) + ++tens; + } + + // now do the rounding in integer land + frac_digits = (frac_digits & 0x80000000) ? ((frac_digits & 0x7ffffff) + 1) : (tens + frac_digits); + if ((frac_digits < 24)) { + stbsp__uint32 dg = 1; + if ((stbsp__uint64)bits >= stbsp__powten[9]) + dg = 10; + while ((stbsp__uint64)bits >= stbsp__powten[dg]) { + ++dg; + if (dg == 20) + goto noround; + } + if (frac_digits < dg) { + stbsp__uint64 r; + // add 0.5 at the right position and round + e = dg - frac_digits; + if ((stbsp__uint32)e >= 24) + goto noround; + r = stbsp__powten[e]; + bits = bits + (r / 2); + if ((stbsp__uint64)bits >= stbsp__powten[dg]) + ++tens; + bits /= r; + } + noround:; + } + + // kill long trailing runs of zeros + if (bits) { + stbsp__uint32 n; + for (;;) { + if (bits <= 0xffffffff) + break; + if (bits % 1000) + goto donez; + bits /= 1000; + } + n = (stbsp__uint32)bits; + while ((n % 1000) == 0) + n /= 1000; + bits = n; + donez:; + } + + // convert to string + out += 64; + e = 0; + for (;;) { + stbsp__uint32 n; + char *o = out - 8; + // do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, constant denomiators be damned) + if (bits >= 100000000) { + n = (stbsp__uint32)(bits % 100000000); + bits /= 100000000; + } else { + n = (stbsp__uint32)bits; + bits = 0; + } + while (n) { + out -= 2; + *(stbsp__uint16 *)out = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; + n /= 100; + e += 2; + } + if (bits == 0) { + if ((e) && (out[0] == '0')) { + ++out; + --e; + } + break; + } + while (out != o) { + *--out = '0'; + ++e; + } + } + + *decimal_pos = tens; + *start = out; + *len = e; + return ng; +} + +#undef stbsp__ddmulthi +#undef stbsp__ddrenorm +#undef stbsp__ddmultlo +#undef stbsp__ddmultlos +#undef STBSP__SPECIAL +#undef STBSP__COPYFP + +#endif // STB_SPRINTF_NOFLOAT + +// clean up +#undef stbsp__uint16 +#undef stbsp__uint32 +#undef stbsp__int32 +#undef stbsp__uint64 +#undef stbsp__int64 +#undef STBSP__UNALIGNED + +#endif // STB_SPRINTF_IMPLEMENTATION + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/code/vn.cpp b/code/vn.cpp index fd45005..f92c0aa 100644 --- a/code/vn.cpp +++ b/code/vn.cpp @@ -1,21 +1,23 @@ #include "vn_platform.h" #include "vn_platform.cpp" -#if VN_INTERNAL +#define STB_IMAGE_IMPLEMENTATION +#include "third_party/stb_image.h" +#if VN_INTERNAL struct debug_settings { b32 RenderUIDebugRects; b32 RenderFPSCounter; b32 ListHotAndActive; + b32 ShowWelcomeMessage; }; per_thread debug_settings *DEBUG_DebugSettings = 0; - #endif -#include "vn_config.h" #include "vn_tokenizer.h" +#include "vn_config.h" #include "vn_font.h" #include "vn_text_op.h" #include "vn_ui.h" @@ -23,7 +25,9 @@ per_thread debug_settings *DEBUG_DebugSettings = 0; #include "vn_workspace.h" #include "vn_theme_dark.h" #include "vn_animation_curve.h" +#include "vn_scene.h" +#include "vn_tokenizer.cpp" #include "vn_config.cpp" #include "vn_render.cpp" #include "vn_font.cpp" @@ -31,10 +35,11 @@ per_thread debug_settings *DEBUG_DebugSettings = 0; #include "vn_ui_utils.cpp" #include "vn_animation_curve.cpp" #include "vn_workspace.cpp" +#include "vn_scene.cpp" struct vn_state { - memory_arena Arena; + memory_arena *Arena; glyph_atlas *GlyphAtlas; config *Config; @@ -43,11 +48,47 @@ struct vn_state workspace Workspace; animation_curve_state AnimationCurveState; + render_handle BackgroundTexture; + #if VN_INTERNAL debug_settings DebugSettings; #endif }; +static render_handle CreateTextureFromPath(vn_render_commands *Commands, string Path) +{ + render_handle Result = {}; + + temporary_memory Scratch = GetScratch(); + platform_file_handle File = Platform.OpenFile(Path, PlatformAccess_Read); + if(File.IsValid) + { + u64 DataCount = Platform.GetFileSize(File); + u8 *Data = PushArray(Scratch.Arena, u8, DataCount); + Platform.ReadFile(File, Data, 0, DataCount); + + s32 Width, Height, BPP; + u8 *TextureData = stbi_load_from_memory(Data, DataCount, &Width, &Height, &BPP, 0); + + render_texture_format TextureFormat = Render_TextureFormat_Invalid; + switch(BPP) + { + InvalidDefaultCase; + case 1: { TextureFormat = Render_TextureFormat_R8; } break; + case 3: { TextureFormat = Render_TextureFormat_RGB8; } break; + case 4: { TextureFormat = Render_TextureFormat_RGBA8; } break; + } + + Result = Commands->AllocateTexture(V2S32(Width, Height), TextureFormat, TextureData); + + stbi_image_free(TextureData); + + Platform.CloseFile(File); + } + ReleaseScratch(Scratch); + return(Result); +} + VN_UPDATE_AND_RENDER(VN_UpdateAndRender) { SetThreadContext(ThreadContext); @@ -55,35 +96,47 @@ VN_UPDATE_AND_RENDER(VN_UpdateAndRender) vn_state *State = Memory->State; + //- sixten: initialize application state if(!Memory->State) { - State = Memory->State = BootstrapPushStruct(vn_state, Arena); + memory_arena *Arena = ArenaAllocate(Gigabytes(1)); + State = Memory->State = PushStruct(Arena, vn_state); + State->Arena = Arena; State->GlyphAtlas = CreateGlyphAtlas(RenderCommands); - State->Config = BootstrapPushStruct(config, Arena); + State->Config = CreateConfig(); - // sixten: Setup config binds and load current config. + //- sixten: load assets + State->BackgroundTexture = CreateTextureFromPath(RenderCommands, StrLit("data/backgrounds/Futon_Room.png")); + + //- sixten: setup config binds and load current config { Config_BindS32(State->Config, StrLit("Platform/RefreshRate"), &Input->RefreshRate, 60); #if VN_INTERNAL - Config_BindB32(State->Config, StrLit("Dev/RenderUIDebugRects"), &State->DebugSettings.RenderUIDebugRects, 0); - Config_BindB32(State->Config, StrLit("Dev/RenderFPSCounter"), &State->DebugSettings.RenderFPSCounter, 0); - Config_BindB32(State->Config, StrLit("Dev/ListHotAndActive"), &State->DebugSettings.ListHotAndActive, 0); + DEBUG_DebugSettings = &State->DebugSettings; + Config_BindB32(State->Config, StrLit("Dev/RenderUIDebugRects"), &DEBUG_DebugSettings->RenderUIDebugRects, 0); + Config_BindB32(State->Config, StrLit("Dev/RenderFPSCounter"), &DEBUG_DebugSettings->RenderFPSCounter, 0); + Config_BindB32(State->Config, StrLit("Dev/ListHotAndActive"), &DEBUG_DebugSettings->ListHotAndActive, 0); + Config_BindB32(State->Config, StrLit("Dev/ShowWelcomeMessage"), &DEBUG_DebugSettings->ShowWelcomeMessage, 1); #endif Config_ReadFile(State->Config, StrLit("config.vn")); } + UI_Init(&State->UI); Workspace_Init(&State->Workspace); + AnimationCurve_Init(&State->AnimationCurveState); } #if VN_INTERNAL DEBUG_DebugSettings = &State->DebugSettings; #endif + //- sixten: begin new frame AnimationCurve_NewFrame(&State->AnimationCurveState, Input->dtForFrame); UI_NewFrame(&State->UI, Input->EventList, Input->MouseP, State->GlyphAtlas); + //- sixten: build the ui UI_BeginBuild(RenderCommands->RenderDim); { Workspace_Update(&State->Workspace, RenderCommands, Input, State->GlyphAtlas); @@ -91,6 +144,7 @@ VN_UPDATE_AND_RENDER(VN_UpdateAndRender) UI_EndBuild(); + //- sixten: consume all remaining evetns for(platform_event *Event = Input->EventList->First; Event != 0; Event = Event->Next) @@ -104,16 +158,24 @@ VN_UPDATE_AND_RENDER(VN_UpdateAndRender) Platform_ConsumeEvent(Input->EventList, Event); } - render_group Group = BeginRenderGroup(RenderCommands); - PushClear(&Group, V3(0.1, 0.1, 0.1)); - - UI_RenderFrame(&Group, State->GlyphAtlas); - - if(DEBUG_DebugSettings->ListHotAndActive) + //- sixten: render the frame { - PushText(&Group, State->GlyphAtlas, Font_Regular, V2(5, RenderCommands->RenderDim.y - 20), 15, Color_Grey, - PushFormat(&State->UI.FrameArena, "Hot: %S:%llu", UI_GetBoxNameByKey(UI_GetHot()), UI_GetHot())); - PushText(&Group, State->GlyphAtlas, Font_Regular, V2(5, RenderCommands->RenderDim.y - 40), 15, Color_Grey, - PushFormat(&State->UI.FrameArena, "Active: %S:%llu", UI_GetBoxNameByKey(UI_GetActive()), UI_GetActive())); + render_group Group = BeginRenderGroup(RenderCommands); + PushClear(&Group, V3(0.1, 0.1, 0.1)); + + PushTexturedQuad(&Group, + Range2R32(V2R32(0, 0), RenderCommands->RenderDim), + Range2R32(V2R32(0, 0), ConvertV2ToR32(DimFromTexture(State->BackgroundTexture))), + Color_White, Color_White, Color_White, Color_White, 0, 0, 0, State->BackgroundTexture); + + UI_RenderFrame(&Group, State->GlyphAtlas); + + if(DEBUG_DebugSettings->ListHotAndActive) + { + PushText(&Group, State->GlyphAtlas, Font_Regular, V2(5, RenderCommands->RenderDim.y - 20), 15, Color_Grey, + PushFormat(State->UI.FrameArena, "Hot: %S:%llu", UI_GetBoxNameByKey(UI_GetHot()), UI_GetHot())); + PushText(&Group, State->GlyphAtlas, Font_Regular, V2(5, RenderCommands->RenderDim.y - 40), 15, Color_Grey, + PushFormat(State->UI.FrameArena, "Active: %S:%llu", UI_GetBoxNameByKey(UI_GetActive()), UI_GetActive())); + } } } \ No newline at end of file diff --git a/code/vn_animation_curve.cpp b/code/vn_animation_curve.cpp index 53a9fbf..45ff5f0 100644 --- a/code/vn_animation_curve.cpp +++ b/code/vn_animation_curve.cpp @@ -10,6 +10,11 @@ inline void AnimationCurve_SetState(animation_curve_state *State) Global_AnimationCurveState = State; } +static void AnimationCurve_Init(animation_curve_state *State) +{ + State->Arena = ArenaAllocate(Gigabytes(1)); +} + inline animation_curve_key AnimationCurve_GenerateKeyFromString(string String) { animation_curve_key Key; @@ -43,7 +48,7 @@ static animation_curve_entry *AnimationCurve_GetEntryByKey(animation_curve_key K { if(DLLIsEmpty(State->FirstFreeEntry)) { - Result = PushStruct(&State->Arena, animation_curve_entry); + Result = PushStruct(State->Arena, animation_curve_entry); } else { diff --git a/code/vn_animation_curve.h b/code/vn_animation_curve.h index 794f066..5cf9d34 100644 --- a/code/vn_animation_curve.h +++ b/code/vn_animation_curve.h @@ -33,7 +33,7 @@ struct animation_curve_bucket struct animation_curve_state { - memory_arena Arena; + memory_arena *Arena; u32 CurrentFrame; r32 dtForFrame; @@ -48,6 +48,7 @@ struct animation_curve_state inline void AnimationCurve_SetState(animation_curve_state *State); inline animation_curve_key AnimationCurve_GenerateKeyFromString(string String); +static void AnimationCurve_Init(animation_curve_state *State); static animation_curve_entry *AnimationCurve_GetEntryByKey(animation_curve_key Key, r32 Initial = 0); inline r32 AnimationCurve_GetValue(string Name, r32 Initial); inline void AnimationCurve_SetValue(string Name, r32 Value); diff --git a/code/vn_config.cpp b/code/vn_config.cpp index 1c3bd5d..a297a8e 100644 --- a/code/vn_config.cpp +++ b/code/vn_config.cpp @@ -1,3 +1,11 @@ +static config *CreateConfig(void) +{ + memory_arena *Arena = ArenaAllocate(Gigabytes(1)); + config *Config = PushStruct(Arena, config); + Config->Arena = Arena; + return(Config); +} + static config_entry *Config_FindEntryByName(config *Config, string Name) { config_entry *Result = 0; @@ -24,8 +32,8 @@ static void Config_BindEntry(config *Config, string Name, config_entry_type Type config_entry *Entry = Config_FindEntryByName(Config, Name); if(!Entry) { - Entry = PushStruct(&Config->Arena, config_entry); - Entry->Name = PushString(&Config->Arena, Name); + Entry = PushStruct(Config->Arena, config_entry); + Entry->Name = PushString(Config->Arena, Name); Entry->Type = Type; u32 BucketSlot = HashString(Name) % ArrayCount(Config->EntryBuckets); @@ -66,9 +74,10 @@ inline void Config_BindB32(config *Config, string Name, b32 *Target, b32 Default Config_BindEntry(Config, Name, Config_Entry_B32, Target); } -static void Config_ParseError(char *Message, memory_arena *Arena) +static void Config_ParseError(string Message, string FileText, s64 Offset, memory_arena *Arena) { - string String = PushFormat(Arena, "An error occured during config parsing:\n\"%s\"", Message); + text_point Point = TextPointFromOffset(FileText, Offset); + string String = PushFormat(Arena, "Config: At %i:%i - %S", Point.Line, Point.Column, Message); Platform.ShowMessage(String, Platform_Message_Warning); } @@ -76,137 +85,168 @@ static void Config_ReadFile(config *Config, string Path) { temporary_memory Scratch = GetScratch(); - tokenizer Tokenizer = Tokenizer_BeginTokenization(Platform_ReadEntireFile(Scratch.Arena, Path)); - if(Tokenizer.Input.Data) + //- sixten: read & tokenize input file + string Text = Platform_ReadEntireFile(Scratch.Arena, Path); + tokenize_result TokenizeResult = T_TokenizeFromText(Scratch.Arena, Path, Text, TokenGroup_Whitespace|TokenGroup_Comment); + token_array Tokens = TokenizeResult.Tokens; + + // sixten: parse context + config_parse_list FullPath = {}; + config_parse_mode ParseMode = ConfigParseMode_Main; + + //- sixten: parse tokens + token *TokensStart = Tokens.Tokens; + token *TokensEnd = Tokens.Tokens + Tokens.Count; + token *Token = TokensStart; + for(;Token < TokensEnd;) { - token Token; - for(;;) + string TokenString = Substring(Text, Token->Range); + + //- sixten: get next name + if(ParseMode == ConfigParseMode_Main && Token->Flags & TokenFlag_Identifier) { - Token = Tokenizer_GetNextToken(&Tokenizer); - - if(Token.Type == Token_Identifier) + Config_ParseListPush(Scratch.Arena, &FullPath, TokenString); + ParseMode = ConfigParseMode_ScanForCurlyOpenOrEquals; + Token += 1; + goto TokenConsumed; + } + + //- sixten: scan for curly close + if(ParseMode == ConfigParseMode_Main && Token->Flags & TokenFlag_Reserved && AreEqual(TokenString, StrLit("}"))) + { + Config_ParseListPop(&FullPath); + Token += 1; + goto TokenConsumed; + } + + //- sixten: scan for curly open + if(ParseMode == ConfigParseMode_ScanForCurlyOpenOrEquals && Token->Flags & TokenFlag_Reserved && AreEqual(TokenString, StrLit("{"))) + { + ParseMode = ConfigParseMode_Main; + Token += 1; + goto TokenConsumed; + } + + //- sixten: scan for equals + if(ParseMode == ConfigParseMode_ScanForCurlyOpenOrEquals && Token->Flags & TokenFlag_Symbol && AreEqual(TokenString, StrLit("="))) + { + ParseMode = ConfigParseMode_ScanForValue; + Token += 1; + goto TokenConsumed; + } + + //- sixten: scan for semicolon + if(ParseMode == ConfigParseMode_ScanForSemicolon && Token->Flags & TokenFlag_Reserved && AreEqual(TokenString, StrLit(";"))) + { + ParseMode = ConfigParseMode_Main; + Token += 1; + goto TokenConsumed; + } + + //- sixten: scan for boolean value + if(ParseMode == ConfigParseMode_ScanForValue && Token->Flags & TokenFlag_Identifier && (AreEqual(TokenString, StrLit("true")) || AreEqual(TokenString, StrLit("false")))) + { + string FullName = Config_ParseListJoin(Scratch.Arena, &FullPath); + config_entry *Entry = Config_FindEntryByName(Config, FullName); + if(Entry) { - string Dir = Token.String; - - if(Tokenizer_RequireToken(&Tokenizer, Token_CurlyOpen)) + b32 Value = AreEqual(TokenString, StrLit("true")); + Assert(Entry->Type == Config_Entry_B32); + *(b32 *)Entry->Target = Value; + } + Config_ParseListPop(&FullPath); + ParseMode = ConfigParseMode_ScanForSemicolon; + Token += 1; + goto TokenConsumed; + } + + //- sixten: scan for integer value + if(ParseMode == ConfigParseMode_ScanForValue && Token->Flags & TokenFlag_Numeric) + { + string FullName = Config_ParseListJoin(Scratch.Arena, &FullPath); + config_entry *Entry = Config_FindEntryByName(Config, FullName); + if(Entry) + { + s64 Value = ConvertStringToS64(TokenString); + if(Entry->Type == Config_Entry_S32) { - for(;;) - { - Token = Tokenizer_GetNextToken(&Tokenizer); - - if(Token.Type == Token_Identifier) - { - string Name = Token.String; - - if(Tokenizer_RequireToken(&Tokenizer, Token_Equals)) - { - Token = Tokenizer_GetNextToken(&Tokenizer); - - if(Token.Type == Token_IntegerValue) - { - s64 Value = ConvertStringToS64(Token.String); - - string FullName = PushFormat(Scratch.Arena, "%S/%S", Dir, Name); - config_entry *Entry = Config_FindEntryByName(Config, FullName); - if(Entry) - { - if(Entry->Type == Config_Entry_S32) - { - *(s32 *)Entry->Target = (s32)Value; - } - else if(Entry->Type == Config_Entry_S64) - { - *(s64 *)Entry->Target = Value; - } - else - { - Config_ParseError("Entry has wrong data type.", Scratch.Arena); - goto End; - } - } - else - { - Config_ParseError("Cannot find entry.", Scratch.Arena); - goto End; - } - } - else if(Token.Type == Token_Identifier) - { - string FullName = PushFormat(Scratch.Arena, "%S/%S", Dir, Name); - config_entry *Entry = Config_FindEntryByName(Config, FullName); - if(Entry) - { - if(AreEqual(Token.String, StrLit("true"))) - { - *(b32 *)Entry->Target = true; - } - else if(AreEqual(Token.String, StrLit("false"))) - { - *(b32 *)Entry->Target = false; - } - else - { - Config_ParseError("Entry has wrong data type.", Scratch.Arena); - goto End; - } - } - else - { - Config_ParseError("Cannot find entry.", Scratch.Arena); - goto End; - } - } - else - { - Config_ParseError("Expected a value.", Scratch.Arena); - goto End; - } - - if(!Tokenizer_RequireToken(&Tokenizer, Token_Semicolon)) - { - Config_ParseError("Expected a ';'.", Scratch.Arena); - goto End; - } - } - else - { - Config_ParseError("Expected '='.", Scratch.Arena); - goto End; - } - } - else if(Token.Type == Token_CurlyClose) - { - break; - } - else - { - Config_ParseError("Expected '}' or identifier.", Scratch.Arena); - goto End; - } - } + *(s32 *)Entry->Target = Value; + } + else if(Entry->Type == Config_Entry_S64) + { + *(s64 *)Entry->Target = Value; } else { - Config_ParseError("Expected '{'.", Scratch.Arena); - goto End; + InvalidCodepath; } } - else if(Token.Type == Token_EndOfFile) - { - goto End; - } - else - { - Config_ParseError("Unexpected token.", Scratch.Arena); - goto End; - } + Config_ParseListPop(&FullPath); + ParseMode = ConfigParseMode_ScanForSemicolon; + Token += 1; + goto TokenConsumed; } + + //- sixten: if the token has not been consumed, something's gone wrong + { + string ErrorMessage = StrLit("Unknown parse error"); + //- sixten: determine error message + switch(ParseMode) + { + case ConfigParseMode_Main: { ErrorMessage = StrLit("Expected identifier or '}'"); } break; + case ConfigParseMode_ScanForCurlyOpenOrEquals: { ErrorMessage = StrLit("Expected '{' or '='") ; } break; + case ConfigParseMode_ScanForValue: { ErrorMessage = StrLit("Expected value"); } break; + case ConfigParseMode_ScanForSemicolon: { ErrorMessage = StrLit("Expected ';'"); } break; + } + + Config_ParseError(ErrorMessage, Text, Token->Range.Min, Scratch.Arena); + Token += 1; + } + + TokenConsumed:; } - End: ReleaseScratch(Scratch); } +//////////////////////////////// +//~ sixten: Config Parse Type Functions +static void Config_ParseListPush(memory_arena *Arena, config_parse_list *List, string Name) +{ + config_parse_node *Node = PushStruct(Arena, config_parse_node); + Node->Name = Name; + List->TotalCountPlusOne += Name.Count + 1; + DLLInsertLast(List->First, List->Last, Node); +} + +static void Config_ParseListPop(config_parse_list *List) +{ + config_parse_node *Node = List->Last; + if(Node) + { + List->TotalCountPlusOne -= Node->Name.Count + 1; + DLLRemove(List->First, List->Last, Node); + } +} + +static string Config_ParseListJoin(memory_arena *Arena, config_parse_list *List) +{ + s64 TotalCount = List->TotalCountPlusOne - 1; + string Result = MakeString(PushArray(Arena, u8, List->TotalCountPlusOne), TotalCount); + s64 Index = 0; + for(config_parse_node *Node = List->First; Node != 0; Node = Node->Next) + { + Copy(Result.Data + Index, Node->Name.Data, Node->Name.Count); + Index += Node->Name.Count; + if(Node->Next) + { + Result.Data[Index] = '/'; + Index += 1; + } + } + return(Result); +} + static void Config_WriteFile(config *Config, string Path) { string_list Out = {}; diff --git a/code/vn_config.h b/code/vn_config.h index 2c972f4..97bc7fa 100644 --- a/code/vn_config.h +++ b/code/vn_config.h @@ -3,6 +3,8 @@ #ifndef VN_CONFIG_H #define VN_CONFIG_H +//////////////////////////////// +//~ sixten: Config Types enum config_entry_type { Config_Entry_S32, @@ -30,7 +32,7 @@ struct config_entry_bucket struct config { - memory_arena Arena; + memory_arena *Arena; config_entry_bucket EntryBuckets[32]; // sixten(NOTE): Keeps track of the order in which the entries were declared. (Used during saving) @@ -38,6 +40,35 @@ struct config config_entry *LastInternal; }; +//////////////////////////////// +//~ sixten: Config Parse Types +enum config_parse_mode +{ + ConfigParseMode_Main, + ConfigParseMode_ScanForCurlyOpenOrEquals, + ConfigParseMode_ScanForValue, + ConfigParseMode_ScanForSemicolon, +}; + +struct config_parse_node +{ + config_parse_node *Next; + config_parse_node *Prev; + string Name; +}; + +struct config_parse_list +{ + config_parse_node *First; + config_parse_node *Last; + s64 TotalCountPlusOne; +}; + +//////////////////////////////// +//~ sixten: Config Type Functions + +static config *CreateConfig(void); + static config_entry *Config_FindEntryByName(config *Config, string Name); static void Config_BindEntry(config *Config, string Name, config_entry_type Type, void *Target); @@ -48,4 +79,10 @@ inline void Config_BindB32(config *Config, string Name, b32 *Target, b32 Default static void Config_ReadFile(config *Config, string Path); static void Config_WriteFile(config *Config); +//////////////////////////////// +//~ sixten: Config Parse Type Functions +static void Config_ParseListPush(memory_arena *Arena, config_parse_list *List, string Name); +static void Config_ParseListPop(config_parse_list *List); +static string Config_ParseListJoin(memory_arena *Arena, config_parse_list *List); + #endif //VN_CONFIG_H diff --git a/code/vn_font.cpp b/code/vn_font.cpp index eb8368e..2863936 100644 --- a/code/vn_font.cpp +++ b/code/vn_font.cpp @@ -1,5 +1,7 @@ #define GLYPH_SUBPIXEL_SEGMENTS 3 +global read_only s32 Font_Oversample = 2; + inline s32 GetSubpixelSegmentAtP(r32 Value) { s32 Result = (s32)(Value - Floor(Value))*GLYPH_SUBPIXEL_SEGMENTS; @@ -102,22 +104,24 @@ static glyph_atlas *CreateGlyphAtlas(vn_render_commands *RenderCommands, s32 BitmapSize = DEFAULT_GLYPH_ATLAS_DIM, s32 GlyphSize = MAX_GLYPH_SIZE) { - glyph_atlas *Atlas = BootstrapPushStruct(glyph_atlas, Arena); + memory_arena *Arena = ArenaAllocate(Megabytes(128)); + glyph_atlas *Atlas = PushStruct(Arena, glyph_atlas); + Atlas->Arena = Arena; Atlas->BitmapSize = BitmapSize; Atlas->GlyphSize = GlyphSize; Atlas->MaxGlyphCount = (DEFAULT_GLYPH_ATLAS_DIM / MAX_GLYPH_SIZE)*(DEFAULT_GLYPH_ATLAS_DIM / MAX_GLYPH_SIZE); - Atlas->Glyphs = PushArray(&Atlas->Arena, glyph, Atlas->MaxGlyphCount); + Atlas->Glyphs = PushArray(Atlas->Arena, glyph, Atlas->MaxGlyphCount); Atlas->RenderCommands = RenderCommands; Atlas->Texture = RenderCommands->AllocateTexture(V2S32(BitmapSize, BitmapSize), Render_TextureFormat_R8, 0); - Atlas->Fonts[Font_Regular].Data = Platform_ReadEntireFile(&Atlas->Arena, StrLit("fonts/Roboto-Regular.ttf")); - Atlas->Fonts[Font_Bold].Data = Platform_ReadEntireFile(&Atlas->Arena, StrLit("fonts/Roboto-Bold.ttf")); - Atlas->Fonts[Font_Monospace].Data = Platform_ReadEntireFile(&Atlas->Arena, StrLit("fonts/Liberation-Mono.ttf")); - Atlas->Fonts[Font_Hand].Data = Platform_ReadEntireFile(&Atlas->Arena, StrLit("fonts/PatrickHand-Regular.ttf")); - Atlas->Fonts[Font_Icons].Data = Platform_ReadEntireFile(&Atlas->Arena, StrLit("fonts/icons.ttf")); + Atlas->Fonts[Font_Regular].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("fonts/Roboto-Regular.ttf")); + Atlas->Fonts[Font_Bold].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("fonts/Roboto-Bold.ttf")); + Atlas->Fonts[Font_Monospace].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("fonts/DejaVuSansMono.ttf")); + Atlas->Fonts[Font_Hand].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("fonts/PatrickHand-Regular.ttf")); + Atlas->Fonts[Font_Icons].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("fonts/icons.ttf")); for(s32 FontIndex = 0; FontIndex < Font_Count; @@ -131,40 +135,38 @@ static glyph_atlas *CreateGlyphAtlas(vn_render_commands *RenderCommands, stbtt_GetFontVMetrics(&Font->Info, &Font->Ascent, &Font->Descent, &Font->LineGap); } - Atlas->BitmapBuffer = PushArray(&Atlas->Arena, u8, GlyphSize*GlyphSize); + Atlas->BitmapBuffer = PushArray(Atlas->Arena, u8, GlyphSize*GlyphSize); return(Atlas); } -static void PushText(render_group *Group, glyph_atlas *Atlas, font_id Font, - v2 P, r32 Size, v4 Color, - string Text) +static r32 PushText(render_group *Group, glyph_atlas *Atlas, font_id Font, + v2 P, r32 Size, v4 Color, + string Text) { - r32 Oversample = 2; - + r32 OffsetX = 0; for(utf8_iterator Iter = IterateUTF8String(Text); IsValid(&Iter); Advance(&Iter)) { u32 Codepoint = Iter.Codepoint; - glyph *Glyph = GetGlyph(Atlas, Font, Codepoint, Size*Oversample, GetSubpixelSegmentAtP(P.x*Oversample)); + glyph *Glyph = GetGlyph(Atlas, Font, Codepoint, Size*Font_Oversample, GetSubpixelSegmentAtP(P.x*Font_Oversample)); Assert(Glyph); - v2 GlyphP = P; - GlyphP.x += Glyph->Offset.x/Oversample; - GlyphP.y += Glyph->Offset.y/Oversample; + v2 GlyphP = P + Glyph->Offset*(1.0 / Font_Oversample) + V2(OffsetX, 1); v2 RenderDim = ConvertV2ToR32(Glyph->P1 - Glyph->P0); - v2 Dim = RenderDim; - Dim.x /= Oversample; - Dim.y /= Oversample; + v2 Dim = RenderDim*(1.0 / Font_Oversample); - PushTexturedQuad(Group, GlyphP, Dim, ConvertV2ToR32(Glyph->P0), RenderDim, + PushTexturedQuad(Group, + Range2R32(GlyphP, GlyphP+Dim), + Range2R32(ConvertV2ToR32(Glyph->P0), ConvertV2ToR32(Glyph->P1)), Color, Color, Color, Color, 0, 0, 0, Atlas->Texture); - P.x += Glyph->Advance/Oversample; + OffsetX += Glyph->Advance/Font_Oversample; } + return(OffsetX); } static void PushTextF(render_group *Group, glyph_atlas *Atlas, font_id Font, @@ -185,8 +187,6 @@ static void PushTextF(render_group *Group, glyph_atlas *Atlas, font_id Font, inline r32 CalculateRasterizedTextWidth(glyph_atlas *Atlas, font_id Font, r32 Size, string Text) { - r32 Oversample = 2; - r32 X = 0; for(utf8_iterator Iter = IterateUTF8String(Text); @@ -195,10 +195,10 @@ inline r32 CalculateRasterizedTextWidth(glyph_atlas *Atlas, font_id Font, r32 Si { u32 Codepoint = Iter.Codepoint; - glyph *Glyph = GetGlyph(Atlas, Font, Codepoint, Size*Oversample, GetSubpixelSegmentAtP(X*Oversample)); + glyph *Glyph = GetGlyph(Atlas, Font, Codepoint, Size*Font_Oversample, GetSubpixelSegmentAtP(X*Font_Oversample)); Assert(Glyph); - X += Glyph->Advance/Oversample; + X += Glyph->Advance/Font_Oversample; } return(X); diff --git a/code/vn_font.h b/code/vn_font.h index 8d33dbb..85a69ca 100644 --- a/code/vn_font.h +++ b/code/vn_font.h @@ -89,7 +89,7 @@ struct loaded_font struct glyph_atlas { - memory_arena Arena; + memory_arena *Arena; s32 MaxGlyphCount; s32 GlyphsUsed; diff --git a/code/vn_opengl_functions.md b/code/vn_opengl_functions.md new file mode 100644 index 0000000..bf66828 --- /dev/null +++ b/code/vn_opengl_functions.md @@ -0,0 +1,113 @@ +@table(Name, Type, Arguments) +OpenGLFunctions: +{ + {BindTexture `void` `GLenum target, GLuint texture`} + {BlendFunc `void` `GLenum sfactor, GLenum dfactor`} + {Clear `void` `GLbitfield mask`} + {ClearAccum `void` `GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha`} + {ClearColor `void` `GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha`} + {ClearDepth `void` `GLclampd depth`} + {ClearIndex `void` `GLfloat c`} + {ClearStencil `void` `GLint s`} + {ClipPlane `void` `GLenum plane, const GLdouble *equation`} + {CullFace `void` `GLenum mode`} + {DeleteTextures `void` `GLsizei n, const GLuint *textures`} + {Disable `void` `GLenum cap`} + {DrawArrays `void` `GLenum mode, GLint first, GLsizei count`} + {DrawBuffer `void` `GLenum mode`} + {DrawElements `void` `GLenum mode, GLsizei count, GLenum type, const GLvoid *indices`} + {DrawArraysInstanced `void` `GLenum mode, GLint first, GLsizei count, GLsizei instancecount`} + {Enable `void` `GLenum cap`} + {GenTextures `void` `GLsizei n, GLuint *textures`} + {GetClipPlane `void` `GLenum plane, GLdouble *equation`} + {GetDoublev `void` `GLenum pname, GLdouble *params`} + {GetError `GLenum` `void`} + {GetFloatv `void` `GLenum pname, GLfloat *params`} + {GetIntegerv `void` `GLenum pname, GLint *params`} + {GetPointerv `void` `GLenum pname, GLvoid* *params`} + {GetString `const GLubyte *` `GLenum name`} + {GetTexEnvfv `void` `GLenum target, GLenum pname, GLfloat *params`} + {GetTexEnviv `void` `GLenum target, GLenum pname, GLint *params`} + {GetTexGendv `void` `GLenum coord, GLenum pname, GLdouble *params`} + {GetTexGenfv `void` `GLenum coord, GLenum pname, GLfloat *params`} + {GetTexGeniv `void` `GLenum coord, GLenum pname, GLint *params`} + {GetTexImage `void` `GLenum target, GLint level, GLenum format, GLenum type, GLvoid *pixels`} + {GetTexLevelParameterfv `void` `GLenum target, GLint level, GLenum pname, GLfloat *params`} + {GetTexLevelParameteriv `void` `GLenum target, GLint level, GLenum pname, GLint *params`} + {GetTexParameterfv `void` `GLenum target, GLenum pname, GLfloat *params`} + {GetTexParameteriv `void` `GLenum target, GLenum pname, GLint *params`} + {Hint `void` `GLenum target, GLenum mode`} + {IsTexture `GLboolean` `GLuint texture`} + {LineWidth `void` `GLfloat width`} + {ListBase `void` `GLuint base`} + {LoadName `void` `GLuint name`} + {LogicOp `void` `GLenum opcode`} + {PointSize `void` `GLfloat size`} + {PolygonMode `void` `GLenum face, GLenum mode`} + {Scissor `void` `GLint x, GLint y, GLsizei width, GLsizei height`} + {TexImage1D `void` `GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels`} + {TexImage2D `void` `GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels`} + {TexParameterf `void` `GLenum target, GLenum pname, GLfloat param`} + {TexParameterfv `void` `GLenum target, GLenum pname, const GLfloat *params`} + {TexParameteri `void` `GLenum target, GLenum pname, GLint param`} + {TexParameteriv `void` `GLenum target, GLenum pname, const GLint *params`} + {TexSubImage1D `void` `GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels`} + {TexSubImage2D `void` `GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels`} + {CompressedTexImage2D `void` `GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *pixels`} + {ActiveTexture `void` `GLenum texture`} + {Viewport `void` `GLint x, GLint y, GLsizei width, GLsizei height`} + {GenBuffers `void` `GLsizei n, GLuint *buffers`} + {BindBuffer `void` `GLenum target, GLuint buffer`} + {BufferData `void` `GLenum target, GLsizeiptr size, const void *data, GLenum usage`} + {BufferSubData `void` `GLenum target, GLintptr offset, GLsizeiptr size, const void *data`} + {GenVertexArrays `void` `GLsizei n, GLuint *arrays`} + {BindVertexArray `void` `GLenum array`} + {GetAttribLocation `GLint` `GLuint program, const GLchar *name`} + {EnableVertexAttribArray `void` `GLuint index`} + {DisableVertexAttribArray `void` `GLuint index`} + {VertexAttribPointer `void` `GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer`} + {VertexAttribIPointer `void` `GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer`} + {VertexAttribLPointer `void` `GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer`} + {VertexAttribDivisor `void` `GLuint index, GLuint divisor`} + {CreateShader `GLuint` `GLenum type`} + {ShaderSource `void` `GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length`} + {CompileShader `void` `GLuint shader`} + {DeleteShader `void` `GLuint shader`} + {GetShaderiv `void` `GLuint shader, GLenum pname, GLint *params`} + {GetShaderInfoLog `void` `GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog`} + {CreateProgram `GLuint` `void`} + {UseProgram `void` `GLuint program`} + {AttachShader `void` `GLuint program, GLuint shader`} + {DeleteProgram `void` `GLuint program`} + {LinkProgram `void` `GLuint program`} + {GetProgramiv `void` `GLuint program, GLenum pname, GLint *params`} + {GetProgramInfoLog `void` `GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog`} + {GetUniformLocation `GLint` `GLuint program, const GLchar *name`} + {Uniform1i `void` `GLint location, GLint v0`} + {Uniform2f `void` `GLint location, GLfloat v0, GLfloat v1`} + {Uniform3f `void` `GLint location, GLfloat v0, GLfloat v1, GLfloat v2`} + {UniformMatrix4fv `void` `GLint location, GLsizei count, GLboolean transpose, const GLfloat *value`} + {DebugMessageCallback `void` `void (*)(GLenum, GLenum, GLuint, GLenum, GLsizei, const GLchar *, const void *), const void *userParam`} +} + +// sixten: Generate the typedefs. +@table_gen +{ + @expand(OpenGLFunctions s) `typedef $(s.Type) opengl_$(s.Name)($(s.Arguments));`; +} + +// sixten: Generate the global pointers. +@table_gen +{ + @expand(OpenGLFunctions s) `global opengl_$(s.Name) *gl$(s.Name) = 0;`; +} + +// sixten: Generate OpenGL_LoadAllFunctions +@table_gen +{ + `extern void *OpenGL_LoadFunction(char *);`; + `static void OpenGL_LoadAllFunctions(void)`; + `{`; + @expand(OpenGLFunctions s) `gl$(s.Name) = (opengl_$(s.Name) *)OpenGL_LoadFunction("gl$(s.Name)");`; + `}`; +} \ No newline at end of file diff --git a/code/vn_platform.h b/code/vn_platform.h index 9c43e36..bac4608 100644 --- a/code/vn_platform.h +++ b/code/vn_platform.h @@ -11,21 +11,6 @@ // sixten: Services the platform provides to the application -struct platform_memory_block -{ - u8 *Base; - u64 Size; - - u64 Used; - platform_memory_block *ArenaPrev; -}; - -#define PLATFORM_ALLOCATE_MEMORY(name) platform_memory_block *name(u64 Size) -typedef PLATFORM_ALLOCATE_MEMORY(platform_allocate_memory); - -#define PLATFORM_DEALLOCATE_MEMORY(name) b32 name(platform_memory_block *Block) -typedef PLATFORM_DEALLOCATE_MEMORY(platform_deallocate_memory); - enum { PlatformAccess_Read = 0x1, @@ -39,21 +24,6 @@ struct platform_file_handle b32 IsValid; }; -#define PLATFORM_OPEN_FILE(name) platform_file_handle name(string Path, platform_access_flags FileAccess) -typedef PLATFORM_OPEN_FILE(platform_open_file); - -#define PLATFORM_CLOSE_FILE(name) void name(platform_file_handle Handle) -typedef PLATFORM_CLOSE_FILE(platform_close_file); - -#define PLATFORM_READ_FILE(name) void name(platform_file_handle Handle, void *Dest, u64 Offset, u64 Size) -typedef PLATFORM_READ_FILE(platform_read_file); - -#define PLATFORM_WRITE_FILE(name) void name(platform_file_handle Handle, void *Source, u64 Offset, u64 Size) -typedef PLATFORM_WRITE_FILE(platform_write_file); - -#define PLATFORM_GET_FILE_SIZE(name) u64 name(platform_file_handle Handle) -typedef PLATFORM_GET_FILE_SIZE(platform_get_file_size); - enum platform_cursor { PlatformCursor_Arrow, @@ -72,12 +42,6 @@ enum platform_cursor PlatformCursor_Count }; -#define PLATFORM_SET_CURSOR(name) void name(platform_cursor Cursor) -typedef PLATFORM_SET_CURSOR(platform_set_cursor); - -#define PLATFORM_TOGGLE_FULLSCREEN(name) void name(void) -typedef PLATFORM_TOGGLE_FULLSCREEN(platform_toggle_fullscreen); - enum platform_message_type { Platform_Message_Info, @@ -86,25 +50,6 @@ enum platform_message_type Platform_Message_Fatal, }; -#define PLATFORM_SHOW_MESSAGE(name) void name(string Message, platform_message_type Type) -typedef PLATFORM_SHOW_MESSAGE(platform_show_message); - -struct platform_api -{ - platform_allocate_memory *AllocateMemory; - platform_deallocate_memory *DeallocateMemory; - - platform_open_file *OpenFile; - platform_close_file *CloseFile; - platform_read_file *ReadFile; - platform_write_file *WriteFile; - platform_get_file_size *GetFileSize; - - platform_set_cursor *SetCursor; - platform_toggle_fullscreen *ToggleFullscreen; - platform_show_message *ShowMessage; -}; - enum platform_event_type { PlatformEvent_Press, @@ -170,6 +115,8 @@ struct platform_event_list platform_event *Last; }; +#include "generated/vn_platform.meta.h" + static platform_api Platform; // sixten: Services that the render backend provides to the application diff --git a/code/vn_platform.md b/code/vn_platform.md new file mode 100644 index 0000000..47d743d --- /dev/null +++ b/code/vn_platform.md @@ -0,0 +1,39 @@ +@table(Name, NameLower, NameCaps, Type, Arguments) platform_functions: +{ + { Reserve reserve RESERVE `void *` `u64 Size` } + { Release release RELEASE `void` `void *Pointer` } + { Commit commit COMMIT `void` `void *Pointer, u64 Size` } + { Decommit decommit DECOMMIT `void` `void *Pointer, u64 Size` } + { OpenFile open_file OPEN_FILE `platform_file_handle` `string Path, platform_access_flags FileAccess` } + { CloseFile close_file CLOSE_FILE `void` `platform_file_handle Handle` } + { ReadFile read_file READ_FILE `void` `platform_file_handle Handle, void *Dest, u64 Offset, u64 Size` } + { WriteFile write_file WRITE_FILE `void` `platform_file_handle Handle, void *Source, u64 Offset, u64 Size` } + { GetFileSize get_file_size GET_FILE_SIZE `u64` `platform_file_handle Handle` } + { SetCursor set_cursor SET_CURSOR `void` `platform_cursor Cursor` } + { ToggleFullscreen toggle_fullscreen TOGGLE_FULLSCREEN `void` `void` } + { ShowMessage show_message SHOW_MESSAGE `void` `string Message, platform_message_type Type` } +} + +@table_gen +{ + @expand(platform_functions s) `#define PLATFORM_$(s.NameCaps)(name) $(s.Type) name($(s.Arguments))` +} + +@table_gen +{ + @expand(platform_functions s) `typedef PLATFORM_$(s.NameCaps)(platform_$(s.NameLower));` +} + +@table_gen +{ + `struct platform_api` + `{` + @expand(platform_functions s) `platform_$(s.NameLower) *$(s.Name); ` + `};` +} + +@table_gen +{ + `#define RegisterPlatformFunctions(PlatformName)\\` + @expand(platform_functions s) `Platform.$(s.Name) = PlatformName##_$(s.Name);\\` +} \ No newline at end of file diff --git a/code/vn_render.cpp b/code/vn_render.cpp index 09ab620..fd2c62d 100644 --- a/code/vn_render.cpp +++ b/code/vn_render.cpp @@ -9,7 +9,7 @@ inline b32 AreEqual(render_handle A, render_handle B) return(Result); } -inline v2_s32 GetTextureDim(render_handle Handle) +inline v2_s32 DimFromTexture(render_handle Handle) { v2_s32 Result = V2S32(Handle.U32[2], Handle.U32[3]); return(Result); @@ -102,9 +102,49 @@ inline u32 PackV4ToU32(v4 V) return(Result); } -inline void PushTexturedQuad(render_group *Group, - v2 P, v2 Dim, - v2 SourceP, v2 SourceDim, +#if 0 +static void PushTexturedQuadAsInstances(render_group *Group, + range2_r32 Dest, + range2_r32 Source, + v4 Color00, v4 Color10, v4 Color01, v4 Color11, + r32 CornerRadius, r32 EdgeSoftness, r32 BorderThickness, + render_handle Texture) +{ + vn_render_commands *Commands = Group->Commands; + + render_command_instanced_quads *Command = (render_command_instanced_quads *)(Group->CurrentCommand + 1); + s32 TextureIndex; + if(!(Group->CurrentCommand && Group->CurrentCommand->Type == Render_Command_render_command_instanced_quads && + (TextureIndex = GetTextureIndexForCommand((render_command_quads *)Command, Texture)) != -1)) + { + Command = PushCommand(Group, render_command_instanced_quads); + TextureIndex = GetTextureIndexForCommand((render_command_quads *)Command, Texture); + Command->QuadBufferIndex = Commands->QuadIndexCount; + } + + range2_r32 Clip = Group->ClipStack[Group->ClipStackUsed]; + range2_r32 FinalDest = Intersection(Dest, Clip); + + if(InRange(Clip, Dest.Min) || InRange(Clip, Dest.Max)) + { + instanced_quad *Quad = Commands->InstancedQuadBase + Commands->InstancedQuadCount++; + Quad->Dest = Dest; + Quad->Source = Source; + Quad->TextureIndex = TextureIndex; + Quad->Colors[0] = PackV4ToU32(Color00); + Quad->Colors[1] = PackV4ToU32(Color10); + Quad->Colors[2] = PackV4ToU32(Color01); + Quad->Colors[3] = PackV4ToU32(Color11); + Quad->CornerRadius = CornerRadius; + Quad->EdgeSoftness = EdgeSoftness; + Quad->BorderThickness = BorderThickness; + } +} +#endif + +static void PushTexturedQuad(render_group *Group, + range2_r32 Dest, + range2_r32 Source, v4 Color00, v4 Color10, v4 Color01, v4 Color11, r32 CornerRadius, r32 EdgeSoftness, r32 BorderThickness, render_handle Texture) @@ -122,33 +162,30 @@ inline void PushTexturedQuad(render_group *Group, } range2_r32 Clip = Group->ClipStack[Group->ClipStackUsed]; - range2_r32 Dest = Range2R32(P, P + Dim); v2 DestMin = Max(Dest.Min, Clip.Min); v2 DestMax = Min(Dest.Max, Clip.Max); - if(InRange(Clip, P) || InRange(Clip, P + Dim)) + // sixten(TODO): Proper aabb overlap testing here! + if(InRange(Clip, Dest.Min) || InRange(Clip, Dest.Max)) { - v2 HalfSize = Dim*0.5; + v2 HalfSize = DimOfRange(Dest)*0.5; v2 P00 = V2(DestMin.x, DestMin.y); v2 P01 = V2(DestMin.x, DestMax.y); v2 P10 = V2(DestMax.x, DestMin.y); v2 P11 = V2(DestMax.x, DestMax.y); - v2 Center = P + HalfSize; + v2 Center = (DestMin + DestMax)*0.5; - u32 Width = Texture.U32[2]; - u32 Height = Texture.U32[3]; + v2 TextureSize = V2(Texture.U32[2], Texture.U32[3]); - SourceP.x /= Width; - SourceP.y /= Height; - SourceDim.x /= Width; - SourceDim.y /= Height; + Source.Min /= TextureSize; + Source.Max /= TextureSize; - v2 Source00 = SourceP; - v2 Source01 = SourceP + V2(0, SourceDim.y); - v2 Source10 = SourceP + V2(SourceDim.x, 0); - v2 Source11 = SourceP + SourceDim; + v2 Source00 = Source.Min; + v2 Source01 = V2(Source.Min.x, Source.Max.y); + v2 Source10 = V2(Source.Max.x, Source.Min.y); + v2 Source11 = Source.Max;; s32 BaseVertex = Commands->QuadVertexCount; @@ -213,19 +250,19 @@ inline void PushTexturedQuad(render_group *Group, #endif } -inline void PushQuad(render_group *Group, v2 P, v2 Dim, +inline void PushQuad(render_group *Group, range2_r32 Dest, v4 Color00, v4 Color01, v4 Color10, v4 Color11, r32 CornerRadius, r32 EdgeSoftness, r32 BorderThickness) { - PushTexturedQuad(Group, P, Dim, V2(0, 0), V2(0, 0), Color00, Color01, Color10, Color11, + PushTexturedQuad(Group, Dest, Range2R32(V2(0, 0), V2(0, 0)), Color00, Color01, Color10, Color11, CornerRadius, EdgeSoftness, BorderThickness, Group->Commands->WhiteTexture); } -inline void PushQuad(render_group *Group, v2 P, v2 Dim, +inline void PushQuad(render_group *Group, range2_r32 Dest, v4 Color, r32 CornerRadius, r32 EdgeSoftness, r32 BorderThickness) { - PushTexturedQuad(Group, P, Dim, V2(0, 0), V2(0, 0), Color, Color, Color, Color, + PushTexturedQuad(Group, Dest, Range2R32(V2(0, 0), V2(0, 0)), Color, Color, Color, Color, CornerRadius, EdgeSoftness, BorderThickness, Group->Commands->WhiteTexture); } diff --git a/code/vn_render.h b/code/vn_render.h index 6375b57..aff2041 100644 --- a/code/vn_render.h +++ b/code/vn_render.h @@ -4,7 +4,7 @@ #define VN_RENDER_H #define MAX_BOUND_TEXTURES 16 -#define MAX_QUAD_COUNT 16*1024 +#define MAX_QUAD_COUNT 128*1024 #define ColorFromHex(Value) V4((((Value) >> 24) & 0xFF) / 255.0, (((Value) >> 16) & 0xFF) / 255.0, (((Value) >> 8) & 0xFF) / 255.0, (((Value) >> 0) & 0xFF) / 255.0) @@ -21,8 +21,11 @@ read_only v4 Color_Yellow = V4(1, 1, 0, 1); read_only v4 Color_Magenta = V4(1, 0, 1, 1); read_only v4 Color_Cyan = V4(0, 1, 1, 1); +#define SetAlpha(Color, Value) V4(Color.x, Color.y, Color.z, Value) + enum render_texture_format { + Render_TextureFormat_Invalid, Render_TextureFormat_R8, Render_TextureFormat_RGB8, Render_TextureFormat_RGBA8, @@ -41,6 +44,7 @@ enum render_command_type { Render_Command_render_command_clear, Render_Command_render_command_quads, + //Render_Command_render_command_instanced_quads, Render_Command_render_command_clip, }; @@ -78,6 +82,19 @@ struct render_group s32 ClipStackUsed; }; +#if 0 +struct instanced_quad +{ + range2_r32 Dest; + range2_r32 Source; + u32 TextureIndex; + u32 Colors[4]; + r32 CornerRadius; + r32 EdgeSoftness; + r32 BorderThickness; +}; +#endif + struct quad_vertex { v2 P; diff --git a/code/vn_scene.cpp b/code/vn_scene.cpp new file mode 100644 index 0000000..08611a8 --- /dev/null +++ b/code/vn_scene.cpp @@ -0,0 +1,63 @@ +//////////////////////////////// +//~ sixten: Scene Message Functions +static void S_PushMessage(memory_arena *Arena, scene_message_list *Messages, scene_node *Node, scene_message_type Type, string String) +{ + scene_message *Message = PushStruct(Arena, scene_message); + Message->Type = Type; + Message->Node = Node; + Message->String = String; + QueuePush(Messages->First, Messages->Last, Message); + Messages->Count += 1; +} + +//////////////////////////////// +//~ sixten: Scene Node Functions +static scene_node *S_SceneFromText(memory_arena *Arena, string Filename, string Text) +{ + tokenize_result TokenizeResult = T_TokenizeFromText(Arena, Filename, Text, TokenGroup_Comment|TokenGroup_Whitespace); + scene_parse_result ParseResult = S_ParseFromTokens(Arena, Filename, Text, TokenizeResult.Tokens); + scene_node *Result = ParseResult.Root; + return(Result); +} +//////////////////////////////// +//~ sixten: Tokens -> Syntax Tree +static scene_parse_result S_ParseFromTokens(memory_arena *Arena, string Filename, string Text, token_array Tokens) +{ + temporary_memory Scratch = GetScratch(&Arena, 1); + + //scene_node *Root = {}; + + //- sixten: setup parse context + scene_parse_context Context = {}; + { + Context.TokensStart = Tokens.Tokens; + Context.TokensEnd = Context.TokensStart + Tokens.Count; + Context.Token = Context.TokensStart; + } + + //- sixten: parse top-level + for(;Context.Token < Context.TokensEnd;) + { + string TokenString = PushString(Scratch.Arena, T_StringFromToken(Text, *Context.Token)); + + if(T_TokenMatches(*Context.Token, TokenFlag_Identifier, Text, StrLit("proc"))) + { + Context.Token += 1; + + goto TokenConsumed; + } + //- sixten: token was not consumed, something has gone wrong + { + Context.Token += 1; + } + TokenConsumed:; + } + + ReleaseScratch(Scratch); + + scene_parse_result Result = + { + 0 + }; + return(Result); +} \ No newline at end of file diff --git a/code/vn_scene.h b/code/vn_scene.h new file mode 100644 index 0000000..8d6dd6a --- /dev/null +++ b/code/vn_scene.h @@ -0,0 +1,97 @@ +/* date = July 9th 2023 11:08 am */ + +#ifndef VN_SCENE_H +#define VN_SCENE_H + +//////////////////////////////// +//~ sixten: Scene Node Types +enum scene_node_kind +{ + S_NodeKind_Invalid, + S_NodeKind_File, + S_NodeKind_Proc, + S_NodeKind_Text, + S_NodeKind_Branch, + S_NodeKind_Count, +}; + +struct scene_node +{ + scene_node *Next; + scene_node *Prev; + scene_node *First; + scene_node *Last; + scene_node *Parent; + + scene_node *FirstTag; + scene_node *LastTag; + + scene_node_kind Kind; + string String; + + u64 SourceOffset; +}; + +//////////////////////////////// +//~ sixten: Scene Messages +enum scene_message_type +{ + S_MessageType_Invalid, + S_MessageType_Note, + S_MessageType_Warning, + S_MessageType_Error, +}; + +struct scene_message +{ + scene_message_type Type; + scene_message *Next; + scene_node *Node; + string String; +}; + +struct scene_message_list +{ + scene_message *First; + scene_message *Last; + s64 Count; +}; + +//////////////////////////////// +//~ sixten: Text -> Tokens +struct scene_tokenize_result +{ + token_array Tokens; + scene_message_list Messages; +}; + +struct scene_parse_context +{ + token *TokensStart; + token *TokensEnd; + token *Token; +}; + +//////////////////////////////// +//~ sixten: Tokens -> Syntax Tree +struct scene_parse_result +{ + scene_node *Root; + scene_message_list Messages; +}; + +//////////////////////////////// +//~ sixten: Scene Message Functions +static void S_PushMessage(memory_arena *Arena, scene_message_list *Messages, scene_node *Node, scene_message_type Type, string String);; + +//////////////////////////////// +//~ sixten: Scene Node Functions +static scene_node *S_PushNode(memory_arena *Arena, scene_node_kind Kind, string String); +static scene_node *S_SceneFromText(memory_arena *Arena, string Text); + +//////////////////////////////// +//~ sixten: Tokens -> Syntax Tree +static scene_node *S_ParseProc(scene_parse_context *Context, string ProcName); +static scene_parse_result S_ParseFromTokens(memory_arena *Arena, string Filename, string Text, token_array Tokens); + +#endif //VN_SCENE_H diff --git a/code/vn_scene.md b/code/vn_scene.md new file mode 100644 index 0000000..681c947 --- /dev/null +++ b/code/vn_scene.md @@ -0,0 +1,34 @@ +@table(Name, NameLower, Contents) scene_ast_node_types: +{ + { Invalid invalid `` } + { BlockStatement block_statement `scene_ast_node *First; scene_ast_node *Last;` } + { SceneDecl scene_declaration `` } +} + +@table_gen_enum scene_ast_node_type: +{ + @expand(scene_ast_node_types s) `S_AstNode_$(s.Name),`; + `S_AstNode_Count,`: +} + +@table_gen +{ + `struct scene_ast_node;` +} + +@table_gen +{ + @expand(scene_ast_node_types s) `struct scene_ast_node_$(s.NameLower) {$(s.Contents)};` +} + +@table_gen +{ + `struct scene_ast_node`; + `{`; + `scene_ast_node_type Type;`; + `union`; + `{`; + @expand(scene_ast_node_types s) `scene_ast_node_$(s.NameLower) $(s.Name)Data;`; + `};`; + `};`; +} \ No newline at end of file diff --git a/code/vn_text_op.h b/code/vn_text_op.h index 8575e4a..3fe5fb1 100644 --- a/code/vn_text_op.h +++ b/code/vn_text_op.h @@ -26,6 +26,10 @@ enum TextActionFlag_Delete = (1<<2), TextActionFlag_ZeroDeltaWithSelection = (1<<3), TextActionFlag_DeltaPicksSelectionSide = (1<<4), + TextActionFlag_OperateOnLine = (1<<5), + TextActionFlag_StopOnNewline = (1<<6), + + TextActionFlag_EmptyLineScan = TextActionFlag_WordScan, }; struct text_action @@ -98,7 +102,82 @@ static text_action SingleLineTextActionFromEvent(platform_event *Event) return(Action); } -inline s64 CodepointScan(string String, s64 Index, s64 Delta) +static text_action MultiLineTextActionFromEvent(platform_event *Event) +{ + text_action Action = {}; + + if(Event->Type == PlatformEvent_Text) + { + Action.Codepoint = Event->Codepoint; + } + else if(Event->Type == PlatformEvent_Press) + { + if(Event->Modifiers & PlatformModifier_Ctrl) + { + Action.Flags |= TextActionFlag_WordScan; + } + if(Event->Modifiers & PlatformModifier_Shift) + { + Action.Flags |= TextActionFlag_KeepMark; + } + + switch(Event->Key) + { + case Key_Right: + { + Action.Delta = +1; + Action.Flags |= TextActionFlag_DeltaPicksSelectionSide; + } break; + + case Key_Left: + { + Action.Delta = -1; + Action.Flags |= TextActionFlag_DeltaPicksSelectionSide; + } break; + + case Key_Down: + { + Action.Delta = +1; + Action.Flags |= TextActionFlag_OperateOnLine; + } break; + + case Key_Up: + { + Action.Delta = -1; + Action.Flags |= TextActionFlag_OperateOnLine; + } break; + + case Key_Home: + { + Action.Delta = S64_Min; + Action.Flags |= TextActionFlag_StopOnNewline; + } break; + + case Key_End: + { + Action.Delta = S64_Max; + Action.Flags |= TextActionFlag_StopOnNewline; + } break; + + case Key_Backspace: + { + Action.Delta = -1; + Action.Flags |= TextActionFlag_Delete|TextActionFlag_ZeroDeltaWithSelection; + } break; + + case Key_Delete: + { + Action.Delta = +1; + Action.Flags |= TextActionFlag_Delete|TextActionFlag_ZeroDeltaWithSelection; + } break; + + default: {} break; + } + } + return(Action); +} + +inline s64 CodepointScan(string String, s64 Index, s64 Delta, b32 StopOnNewline) { s64 Result = 0; if(Delta > 0) @@ -108,6 +187,11 @@ inline s64 CodepointScan(string String, s64 Index, s64 Delta) u8 Base = String.Data[Index]; s64 ToMove = 0; + if(StopOnNewline && Base == '\n') + { + break; + } + if((Base & 0x80) == 0x00) { ToMove = 1; @@ -138,6 +222,13 @@ inline s64 CodepointScan(string String, s64 Index, s64 Delta) while(Index >= 0 && (Delta != 0)) { u8 Base = String.Data[Index]; + + if(StopOnNewline && Base == '\n') + { + break; + } + + if(((Base & 0x80) == 0) || !((Base & 0xC0) == 0x80)) { ++Delta; @@ -218,7 +309,8 @@ static s64 WordScan(string String, s64 Index, s64 Delta) } static text_op TextOpFromAction(memory_arena *Arena, string String, - text_edit_state *State, text_action *Action) + text_edit_state *State, text_action *Action, + range1_s64_array *Lines = 0, s64 LastColumnIndex = 0) { text_op Op = {}; @@ -228,15 +320,57 @@ static text_op TextOpFromAction(memory_arena *Arena, string String, Op.ReplaceString = StrLit(""); s64 Delta = 0; - if(Action->Flags & TextActionFlag_WordScan) + if(Action->Flags & TextActionFlag_OperateOnLine) { - Delta = WordScan(String, State->Cursor, Action->Delta); + if(Action->Flags & TextActionFlag_EmptyLineScan) + { + } + else + { + //- sixten: determine what line we are on. + s64 LineIndex = 0; + s64 ColumnIndex = 0; + u8 *TextBegin = String.Data; + u8 *TextEnd = TextBegin + State->Cursor; + u8 *Char = TextBegin; + for(;Char < TextEnd; Char += 1) + { + ColumnIndex += 1; + if(*Char == '\n') + { + LineIndex += 1; + ColumnIndex = 0; + } + } + u64 ColumnOffset = Max(LastColumnIndex - ColumnIndex, 0LLU); + + //- sixten: check that the line we are trying to access is inbouse, else just go to the start or end of the text. + if(InRange(Range1S64(0, Lines->Count), LineIndex + Action->Delta)) + { + Delta = Lines->Ranges[LineIndex+Action->Delta].Min - Lines->Ranges[LineIndex].Min + ColumnOffset; + if(!InRange(Lines->Ranges[LineIndex+Action->Delta], State->Cursor + Delta)) + { + Delta = Lines->Ranges[LineIndex+Action->Delta].Max - State->Cursor - 1; + } + } + else + { + s64 TempDelta = (Action->Delta > 0)?S64_Max:S64_Min; + Delta = CodepointScan(String, State->Cursor, TempDelta, Action->Flags & TextActionFlag_StopOnNewline); + } + } } else { - Delta = CodepointScan(String, State->Cursor, Action->Delta); + if(Action->Flags & TextActionFlag_WordScan) + { + Delta = WordScan(String, State->Cursor, Action->Delta); + } + else + { + Delta = CodepointScan(String, State->Cursor, Action->Delta, Action->Flags & TextActionFlag_StopOnNewline); + } } - if(State->Cursor != State->Mark && Action->Flags & TextActionFlag_ZeroDeltaWithSelection) { diff --git a/code/vn_tokenizer.cpp b/code/vn_tokenizer.cpp new file mode 100644 index 0000000..ee5aa45 --- /dev/null +++ b/code/vn_tokenizer.cpp @@ -0,0 +1,288 @@ + +//////////////////////////////// +//~ sixten: Token Type Functions +static string T_StringFromToken(string Text, token Token) +{ + string Result = Substring(Text, Token.Range); + return(Result); +} + +static b32 T_TokenMatches(token Token, token_flags Flags, string Text, string String) +{ + b32 Result = (Token.Flags & Flags && AreEqual(T_StringFromToken(Text, Token), String)); + return(Result); +} + +static void T_TokenChunkListPush(memory_arena *Arena, token_chunk_list *List, token Token, s64 MaxTokenCountPerNode) +{ + token_chunk_node *Node = List->Last; + if(!Node || Node->Count >= Node->MaxCount) + { + Node = PushStruct(Arena, token_chunk_node); + Node->Count = 0; + Node->MaxCount = MaxTokenCountPerNode; + Node->Tokens = PushArrayNoClear(Arena, token, Node->MaxCount); + QueuePush(List->First, List->Last, Node); + } + Node->Tokens[Node->Count] = Token; + Node->Count += 1; + List->Count += 1; +} + +static token_array T_TokenArrayFromList(memory_arena *Arena, token_chunk_list *List) +{ + token_array Result = {}; + Result.Tokens = PushArrayNoClear(Arena, token, List->Count); + Result.Count = List->Count; + s64 Index = 0; + for(token_chunk_node *Node = List->First; Node != 0; Node = Node->Next) + { + Copy(Result.Tokens + Index, Node->Tokens, sizeof(token)*Node->Count); + Index += Node->Count; + } + return(Result); +} + +//////////////////////////////// +//~ sixten: Tokenizer Message Functions + +static void T_MessageListPush(memory_arena *Arena, tokenizer_message_list *List, tokenizer_message_kind Kind, s64 Offset, string String) +{ + tokenizer_message *Message = PushStructNoClear(Arena, tokenizer_message); + Message->Kind = Kind; + Message->Offset = Offset; + Message->String = String; + QueuePush(List->First, List->Last, Message); +} + +//////////////////////////////// +//~ sixten: Text -> Token Functions + +static tokenize_result T_TokenizeFromText(memory_arena *Arena, string Filename, string Text, token_flags ExcludeFilter) +{ + temporary_memory Scratch = GetScratch(&Arena, 1); + token_chunk_list Tokens = {}; + tokenizer_message_list Messages = {}; + u8 *TextStart = Text.Data; + u8 *TextEnd = TextStart + Text.Count; + u8 *Byte = TextStart; + + //- sixten: scan string & produce tokens + for(;Byte < TextEnd;) + { + token_flags TokenFlags = 0; + u8 *TokenStart = 0; + u8 *TokenEnd = 0; + + //- sixten: whitespace + if(TokenFlags == 0 && (*Byte == ' ' || *Byte == '\t' || *Byte == '\v' || *Byte == '\r')) + { + TokenFlags = TokenFlag_Whitespace; + TokenStart = Byte; + TokenEnd = Byte; + Byte += 1; + for(;Byte <= TextEnd; Byte += 1) + { + TokenEnd += 1; + if(Byte == TextEnd || !(*Byte == ' ' || *Byte == '\t' || *Byte == '\v' || *Byte == '\r')) + { + break; + } + } + } + + //- sixten: newlines + if(TokenFlags == 0 && *Byte == '\n') + { + TokenFlags = TokenFlag_Newline; + TokenStart = Byte; + TokenEnd = Byte + 1; + Byte += 1; + } + + //- sixten: single-line comments + if(TokenFlags == 0 && (Byte[0] == '/' && Byte[1] == '/')) + { + TokenFlags = TokenFlag_Comment; + TokenStart = Byte; + TokenEnd = Byte + 2; + Byte += 2; + for(;Byte <= TextEnd; Byte += 1) + { + if(Byte == TextEnd || *Byte == '\n' || *Byte == '\r') + { + break; + } + TokenEnd += 1; + } + } + + //- sixten: multi-line comments + if(TokenFlags == 0 && (Byte+1 < TextEnd && Byte[0] == '/' && Byte[1] == '*')) + { + TokenFlags = TokenFlag_Comment; + TokenStart = Byte; + TokenEnd = Byte + 2; + Byte += 2; + for(;Byte <= TextEnd; Byte += 1) + { + // sixten(NOTE): This could potentially be wrong. The TokenEnd += 1 statement could currently make the token include the EOF. + if(Byte == TextEnd) + { + TokenFlags |= TokenFlag_BrokenComment; + break; + } + TokenEnd += 1; + if(Byte+1 < TextEnd && Byte[0] == '*' && Byte[1] == '/') + { + TokenEnd += 1; + Byte += 2; + break; + } + } + } + + //- sixten: identifiers + if(TokenFlags == 0 && (('A' <= *Byte && *Byte <= 'Z') || + ('a' <= *Byte && *Byte <= 'z') || + (UTF8Lengths[*Byte>>3] > 1) || + *Byte == '_')) + { + TokenFlags = TokenFlag_Identifier; + TokenStart = Byte; + TokenEnd = Byte; + Byte += 1; + for(;Byte <= TextEnd; Byte += 1) + { + TokenEnd += 1; + if(Byte == TextEnd || !(('A' <= *Byte && *Byte <= 'Z') || + ('a' <= *Byte && *Byte <= 'z') || + ('0' <= *Byte && *Byte <= '9') || + (UTF8Lengths[*Byte>>3] > 1) || + *Byte == '_')) + { + break; + } + } + } + + //- sixten: numerics + if(TokenFlags == 0 && (('0' <= *Byte && *Byte <= '9') || + *Byte == '.' || + ((*Byte == '+' || *Byte == '-') && Byte + 1 < TextEnd && '0' <= Byte[1] && Byte[1] <= '9'))) + { + TokenFlags = TokenFlag_Numeric; + TokenStart = Byte; + TokenEnd = Byte; + Byte += 1; + for(;Byte <= TextEnd; Byte += 1) + { + TokenEnd += 1; + if(Byte == TextEnd || + !(('A' <= *Byte && *Byte <= 'Z') || + ('a' <= *Byte && *Byte <= 'z') || + ('0' <= *Byte && *Byte <= '9') || + *Byte == '_' || *Byte == '.')) + { + break; + } + } + } + + //- sixten: string literals + if(TokenFlags == 0 && *Byte == '"') + { + TokenFlags = TokenFlag_StringLiteral; + TokenStart = Byte; + TokenEnd = Byte; + Byte += 1; + for(;Byte <= TextEnd; Byte += 1) + { + TokenEnd += 1; + if(Byte == TextEnd || *Byte == '\n') + { + TokenFlags |= TokenFlag_BrokenStringLiteral; + break; + } + if(*Byte == '"') + { + Byte += 1; + TokenEnd += 1; + break; + } + } + } + + //- sixten: multi-char symbols + if(TokenFlags == 0 && (*Byte == '!' || *Byte == '%' || *Byte == '&' || *Byte == '|' || + *Byte == '/' || *Byte == '=' || *Byte == '?' || *Byte == '^' || + *Byte == '*' || *Byte == '+' || *Byte == '-' || *Byte == '$' || + *Byte == '<' || *Byte == '>' || *Byte == '~' || *Byte == '\'')) + { + TokenFlags = TokenFlag_Symbol; + TokenStart = Byte; + TokenEnd = Byte; + Byte += 1; + for(;Byte <= TextEnd; Byte += 1) + { + TokenEnd += 1; + if(Byte == TextEnd || + !(*Byte == '!' || *Byte == '%' || *Byte == '&' || *Byte == '|' || + *Byte == '/' || *Byte == '=' || *Byte == '?' || *Byte == '^' || + *Byte == '*' || *Byte == '+' || *Byte == '-' || *Byte == '$' || + *Byte == '<' || *Byte == '>' || *Byte == '~' || *Byte == '\'')) + { + break; + } + } + } + + //- sixten: single-char symbols + if(TokenFlags == 0 && (*Byte == '{' || *Byte == '}' || *Byte == '(' || *Byte == ')' || + *Byte == '[' || *Byte == ']' || *Byte == ',' || *Byte == ';' || + *Byte == ':' || *Byte == '@' || *Byte == '#')) + { + TokenFlags = TokenFlag_Reserved; + TokenStart = Byte; + TokenEnd = Byte + 1; + Byte += 1; + } + + //- sixten: bad character + if(TokenFlags == 0) + { + TokenFlags = TokenFlag_BadCharacter; + TokenStart = Byte; + TokenStart = Byte + 1; + Byte += 1; + } + + //- sixten: push token + if(TokenFlags != 0 && !(TokenFlags & ExcludeFilter) && TokenStart != 0 && TokenEnd > TokenStart) + { + token Token = {TokenFlags, {TokenStart - TextStart, TokenEnd - TextStart}}; + T_TokenChunkListPush(Scratch.Arena, &Tokens, Token, 4096); + } + + if(TokenFlags & TokenFlag_BrokenComment) + { + string Message = StrLit("broken comment"); + T_MessageListPush(Arena, &Messages, T_MessageKind_Error, TokenStart - TextStart, Message); + } + + if(TokenFlags & TokenFlag_BrokenStringLiteral) + { + string Message = StrLit("broken string literal"); + T_MessageListPush(Arena, &Messages, T_MessageKind_Error, TokenStart - TextStart, Message); + } + } + + tokenize_result Result = + { + T_TokenArrayFromList(Arena, &Tokens), + Messages + }; + ReleaseScratch(Scratch); + return(Result); +} + diff --git a/code/vn_tokenizer.h b/code/vn_tokenizer.h index 37a9e3c..190e254 100644 --- a/code/vn_tokenizer.h +++ b/code/vn_tokenizer.h @@ -1,122 +1,118 @@ -/* date = June 18th 2023 5:12 pm */ +/* date = July 9th 2023 8:04 pm */ #ifndef VN_TOKENIZER_H #define VN_TOKENIZER_H -enum token_type +//////////////////////////////// +//~ sixten: Token Types +typedef u32 token_flags; +enum { - Token_EndOfFile, - Token_Identifier, - Token_IntegerValue, - //Token_RealValue, - Token_CurlyOpen, - Token_CurlyClose, - Token_Semicolon, - Token_Equals, + TokenFlag_Identifier = (1<<0), + TokenFlag_Numeric = (1<<1), + TokenFlag_StringLiteral = (1<<2), + TokenFlag_Symbol = (1<<3), + TokenFlag_Reserved = (1<<4), + TokenFlag_Comment = (1<<5), + TokenFlag_Whitespace = (1<<6), + TokenFlag_Newline = (1<<7), + + TokenFlag_BrokenComment = (1<<8), + TokenFlag_BrokenStringLiteral = (1<<9), + TokenFlag_BadCharacter = (1<<10), }; -struct tokenizer +typedef u32 token_group; +enum { - string Input; - s64 At; - s64 Index; + TokenGroup_Comment = TokenFlag_Comment, + TokenGroup_Whitespace = (TokenFlag_Whitespace | + TokenFlag_Newline), + TokenGroup_Irregular = (TokenGroup_Comment | + TokenGroup_Whitespace), + TokenGroup_Regular = ~TokenGroup_Irregular, + TokenGroup_Label = (TokenFlag_Identifier | + TokenFlag_Numeric | + TokenFlag_StringLiteral | + TokenFlag_Symbol), + TokenGroup_Error = (TokenFlag_BrokenComment | + TokenFlag_BrokenStringLiteral | + TokenFlag_BadCharacter), }; struct token { - token_type Type; + token_flags Flags; + range1_s64 Range; +}; + +struct token_chunk_node +{ + token *Tokens; + s64 MaxCount; + s64 Count; + token_chunk_node *Next; +}; + +struct token_chunk_list +{ + token_chunk_node *First; + token_chunk_node *Last; + s64 Count; +}; + +struct token_array +{ + token *Tokens; + s64 Count; +}; + +//////////////////////////////// +//~ sixten: Tokenizer Message Types +enum tokenizer_message_kind +{ + T_MessageKind_Invalid, + T_MessageKind_Note, + T_MessageKind_Warning, + T_MessageKind_Error, +}; + +struct tokenizer_message +{ + tokenizer_message *Next; + tokenizer_message_kind Kind; + s64 Offset; string String; }; -inline tokenizer Tokenizer_BeginTokenization(string Input) +struct tokenizer_message_list { - tokenizer Result = {}; - Result.Input = Input; - - return(Result); -} + tokenizer_message *First; + tokenizer_message *Last; + s64 Count; +}; -static token Tokenizer_GetNextToken(tokenizer *Tokenizer) +//////////////////////////////// +//~ sixten: Text -> Token Types +struct tokenize_result { - token Token = {}; - - string Input = Tokenizer->Input; - u8 *Base = Input.Data; - - // sixten: Consume whitespace - while(IsWhitespace(Base[Tokenizer->Index])) - { - ++Tokenizer->Index; - } - - // sixten(NOTE): Assume single char token. - Token.String.Data = Base + Tokenizer->Index; - Token.String.Count = 1; - - if(Tokenizer->Index < Input.Count) - { - switch(Base[Tokenizer->Index]) - { - case '{': { Token.Type = Token_CurlyOpen; } break; - case '}': { Token.Type = Token_CurlyClose; } break; - case ';': { Token.Type = Token_Semicolon; } break; - case '=': { Token.Type = Token_Equals; } break; - - default: - { - if(IsDigit(Base[Tokenizer->Index]) || Base[Tokenizer->Index] == '-') - { - // sixten: Parse integer number - Token.Type = Token_IntegerValue; - - Token.String.Data = Base + Tokenizer->Index; - Token.String.Count = 0; - - while(IsDigit(Token.String.Data[Token.String.Count]) || Token.String.Data[Token.String.Count] == '-') - { - ++Token.String.Count; - } - } - else - { - // sixten: Parse tokenizer - Token.Type = Token_Identifier; - - Token.String.Data = Base + Tokenizer->Index; - Token.String.Count = 0; - - while(IsDigit(Token.String.Data[Token.String.Count]) || - IsLetter(Token.String.Data[Token.String.Count])) - { - ++Token.String.Count; - } - } - } break; - } - } - else - { - Token.Type = Token_EndOfFile; - } - - Tokenizer->Index += Token.String.Count; - - return(Token); -} + token_array Tokens; + tokenizer_message_list Messages; +}; -inline token Tokenizer_PeekNextToken(tokenizer Tokenizer) -{ - // sixten(NOTE): Yup, we just make a copy of the tokenizer and read the next token. - token Result = Tokenizer_GetNextToken(&Tokenizer); - return(Result); -} +//////////////////////////////// +//~ sixten: Token Type Functions +static string T_StringFromToken(string Text, token Token); +static b32 T_TokenMatches(token Token, token_flags Flags, string Text, string String); +static void T_TokenChunkListPush(memory_arena *Arena, token_chunk_list *List, token Token, s64 MaxTokenCountPerNode); +static token_array T_TokenArrayFromChunkList(memory_arena *Arena, token_chunk_list *List); -inline b32 Tokenizer_RequireToken(tokenizer *Tokenizer, token_type Type) -{ - token Token = Tokenizer_GetNextToken(Tokenizer); - - b32 Result = (Token.Type == Type); - return(Result); -} +//////////////////////////////// +//~ sixten: Tokenizer Message Functions +static void T_MessageListPush(memory_arena *Arena, tokenizer_message_list *List, tokenizer_message_kind Kind, s64 Offset, string String); + +//////////////////////////////// +//~ sixten: Text -> Token Functions +static tokenize_result T_TokenizeFromText(memory_arena *Arena, string Filename, string Text, token_flags ExcludeFilter = 0); #endif //VN_TOKENIZER_H diff --git a/code/vn_ui.cpp b/code/vn_ui.cpp index 391bf84..d16b4de 100644 --- a/code/vn_ui.cpp +++ b/code/vn_ui.cpp @@ -1,4 +1,4 @@ -#include "generated/vn_generated_ui.cpp" +#include "generated/vn_ui.meta.c" inline ui_size UI_Pixels(r32 Value, r32 Strictness) { @@ -54,6 +54,25 @@ inline ui_key UI_GetActive(void) return(UI->Active); } +inline platform_event_list *UI_EventList(void) +{ + ui *UI = UI_GetState(); + return(UI->EventList); +} + +inline v2 UI_MouseP(void) +{ + ui *UI = UI_GetState(); + return(UI->MouseP); +} + +inline glyph_atlas *UI_GlyphAtlas(void) +{ + ui *UI = UI_GetState(); + return(UI->GlyphAtlas); +} + + inline void UI_SetDragStartP(v2 P) { ui *UI = UI_GetState(); @@ -207,7 +226,7 @@ inline ui_box *UI_GetBoxByKey(ui *UI, ui_key Key) if(DLLIsEmpty(UI->FirstFreeBox)) { // sixten: If not, simply allocate one - Result = PushStruct(&UI->Arena, ui_box); + Result = PushStruct(UI->Arena, ui_box); } else { @@ -259,7 +278,7 @@ inline ui_box *UI_MakeBox(ui_box_flags Flags, string String) Box->LastFrameTouched = UI->CurrentFrame; Box->Flags = Flags; - Box->String = PushString(&UI->FrameArena, String); + Box->String = PushString(UI->FrameArena, String); s64 HashIndex = LastIndexOf(Box->String, '#'); if(HashIndex != -1) @@ -286,7 +305,7 @@ inline ui_box *UI_MakeBoxF(ui_box_flags Flags, char *Format, ...) va_list Arguments; va_start(Arguments, Format); - string String = PushFormatVariadic(&UI->FrameArena, Format, Arguments); + string String = PushFormatVariadic(UI->FrameArena, Format, Arguments); va_end(Arguments); ui_box *Box = UI_MakeBox(Flags, String); @@ -335,6 +354,11 @@ static b32 UI_ChildrenContainsP(ui_box *Parent, v2 P, range2_r32 Clip) break; } } + + if(Child == Parent->Last) + { + break; + } } return(Result); @@ -358,7 +382,21 @@ static ui_signal UI_SignalFromBox(ui_box *Box) } } - Signal.Hovering = InRange(ClippedRect, UI->MouseP) && !UI_ChildrenContainsP(Box, UI->MouseP, ClippedRect); + Signal.Hovering = InRange(ClippedRect, UI->MouseP); + + // sixten: Make sure no children boxes overlap. + Signal.Hovering &= !UI_ChildrenContainsP(Box, UI->MouseP, ClippedRect); + + // sixten: Make sure no previous boxes overlap. + if(Box->Prev) + { + ui_box PrevBoxParent = {}; + PrevBoxParent.Rect = Box->Rect; + PrevBoxParent.First = Box->Parent->First; + PrevBoxParent.Last = Box->Prev; + + Signal.Hovering &= !UI_ChildrenContainsP(&PrevBoxParent, UI->MouseP, ClippedRect); + } // sixten: Make sure the tooltip is not overlapping. { @@ -557,23 +595,23 @@ static void UI_DrawBox(ui_box *Box, render_group *Group, glyph_atlas *GlyphAtlas if(Box->Flags & UI_BoxFlag_DrawBackground) { - PushQuad(Group, Box->Rect.Min, Box->ComputedDim, Box->BackgroundColor, Box->CornerRadius, 0, 0); + PushQuad(Group, Box->Rect, Box->BackgroundColor, Box->CornerRadius, 0, 0); } if(Box->Flags & UI_BoxFlag_HotAnimation) { - v4 Top = V4(1, 1, 1, 0.06F*Box->HotTransition); + v4 Top = V4(1, 1, 1, 0.08F*Box->HotTransition); v4 Bottom = V4(1, 1, 1, 0.0); - PushQuad(Group, Box->Rect.Min, Box->ComputedDim, Top, Top, Bottom, Bottom, Box->CornerRadius, 0, 0); + PushQuad(Group, Box->Rect, Top, Top, Bottom, Bottom, Box->CornerRadius, 0, 0); } if(Box->Flags & UI_BoxFlag_ActiveAnimation) { - v4 Top = V4(0, 0, 0, 0.5F*Box->ActiveTransition); + v4 Top = V4(0, 0, 0, 0.7F*Box->ActiveTransition); v4 Bottom = V4(0, 0, 0, 0.1F*Box->ActiveTransition); - PushQuad(Group, Box->Rect.Min, Box->ComputedDim, Top, Top, Bottom, Bottom, Box->CornerRadius, 0, 0); + PushQuad(Group, Box->Rect, Top, Top, Bottom, Bottom, Box->CornerRadius, 0, 0); } if(Box->Flags & UI_BoxFlag_DrawText) @@ -599,7 +637,7 @@ static void UI_DrawBox(ui_box *Box, render_group *Group, glyph_atlas *GlyphAtlas r32 G = (((Box->Key.Value >> 21) & ((1 << 22) - 1)) / (r32)((1 << 22) - 1)); r32 B = (((Box->Key.Value >> 42) & ((1 << 22) - 1)) / (r32)((1 << 22) - 1)); v4 Red = V4R32(R, G, B, 1); - PushQuad(Group, Box->Rect.Min, Box->ComputedDim, Red, Red, Red, Red, 0, 1.8, 1.8); + PushQuad(Group, Box->Rect, Red, Red, Red, Red, 0, 1.8, 1.8); } #endif @@ -619,9 +657,12 @@ static void UI_DrawBox(ui_box *Box, render_group *Group, glyph_atlas *GlyphAtlas v2 P = Child->Rect.Min - V2(ShadowRadius, ShadowRadius); v2 Dim = Child->ComputedDim + V2(ShadowRadius, ShadowRadius)*2; + range2_r32 Rect = Range2R32(Box->Rect.Min - V2R32(ShadowRadius, ShadowRadius), + Box->Rect.Min + V2R32(ShadowRadius, ShadowRadius)); + v4 ShadowColor = V4(0, 0, 0, 0.7); - PushQuad(Group, P, Dim, ShadowColor, 0, ShadowRadius, 0); + PushQuad(Group, Rect, ShadowColor, 0, ShadowRadius, 0); } } @@ -639,7 +680,7 @@ static void UI_DrawBox(ui_box *Box, render_group *Group, glyph_atlas *GlyphAtlas if(Box->Flags & UI_BoxFlag_DrawBorder) { - PushQuad(Group, Box->Rect.Min, Box->ComputedDim, Box->BorderColor, Box->CornerRadius, 0.8, Box->BorderThickness); + PushQuad(Group, Box->Rect, Box->BorderColor, Box->CornerRadius, 0.8, Box->BorderThickness); } } @@ -673,7 +714,13 @@ static r32 UI_CalculateBoxSize(ui_box *Box, axis2 Axis) { if(Parent && Parent->SemanticSize[Axis].Type != UI_SizeType_ChildrenSum) { - Result = Parent->ComputedDim.E[Axis]*Box->SemanticSize[Axis].Value; + r32 Size = Parent->ComputedDim.E[Axis]; + // sixten: if the size is zero, try to find it. + if(Size == 0.0) + { + Size = DimOfRange(Parent->Rect).E[Axis]; + } + Result = Size*Box->SemanticSize[Axis].Value; } else { @@ -743,6 +790,12 @@ static void UI_LayoutBox(ui_box *Box) } } +static void UI_Init(ui *UI) +{ + UI->Arena = ArenaAllocate(Gigabytes(1)); + UI->FrameArena = ArenaAllocate(Gigabytes(1)); +} + static void UI_BeginBuild(v2 ScreenDim) { ui *UI = UI_GetState(); @@ -837,12 +890,7 @@ static void UI_NewFrame(ui *UI, platform_event_list *EventList, v2 MouseP, glyph { UI_SetState(UI); - if(UI->FrameMemory.Arena) - { - EndTemporaryMemory(UI->FrameMemory); - } - - UI->FrameMemory = BeginTemporaryMemory(&UI->FrameArena); + ArenaClear(UI->FrameArena); UI->EventList = EventList; UI->MouseP = MouseP; diff --git a/code/vn_ui.h b/code/vn_ui.h index f029bae..ba74f74 100644 --- a/code/vn_ui.h +++ b/code/vn_ui.h @@ -52,10 +52,12 @@ enum UI_BoxFlag_OverflowY = (1 << 9), UI_BoxFlag_FloatingX = (1 << 10), UI_BoxFlag_FloatingY = (1 << 11), + UI_BoxFlag_Scrollable = (1 << 12), }; typedef u32 ui_box_flags; -typedef void ui_draw_callback(render_group *Group, glyph_atlas *Atlas, struct ui_box *Box, void *Data); +#define UI_CUSTOM_DRAW_CALLBACK(name) void name(render_group *Group, glyph_atlas *Atlas, struct ui_box *Box, void *Data) +typedef UI_CUSTOM_DRAW_CALLBACK(ui_custom_draw_callback); struct ui_box { @@ -86,7 +88,7 @@ struct ui_box r32 FontSize; v2 Offset; - ui_draw_callback *DrawCallback; + ui_custom_draw_callback *DrawCallback; void *DrawCallbackData; v2 ComputedRelativeP; @@ -109,6 +111,7 @@ struct ui_signal ui_box *Box; v2 MouseP; v2 DragDelta; + v2 Scroll; b8 Clicked; b8 Pressed; b8 PressedRight; @@ -117,13 +120,12 @@ struct ui_signal b8 Dragging; }; -#include "generated/vn_generated_ui.h" +#include "generated/vn_ui.meta.h" struct ui { - memory_arena Arena; - memory_arena FrameArena; - temporary_memory FrameMemory; + memory_arena *Arena; + memory_arena *FrameArena; ui_box *FirstFreeBox; ui_box *LastFreeBox; @@ -158,6 +160,10 @@ inline ui *UI_GetState(void); inline ui_key UI_GetHot(void); inline ui_key UI_GetActive(void); +inline platform_event_list *UI_EventList(void); +inline v2 UI_MouseP(void); +inline glyph_atlas *UI_GlyphAtlas(void); + //- sixten: Drag helpers inline void UI_SetDragStartP(v2 P); inline void UI_UpdateDragStartP(void); @@ -186,6 +192,7 @@ static ui_box *UI_MakeBoxF(ui_box_flags Flags, char *Format, ...); static ui_signal UI_SignalFromBox(ui_box *Box); //- sixten: Building and rendering +static void UI_Init(ui *UI); static void UI_BeginBuild(ui *UI, v2 ScreenDim); static void UI_EndBuild(void); static void UI_RenderFrame(render_group *RenderGroup); diff --git a/code/vn_ui.md b/code/vn_ui.md new file mode 100644 index 0000000..ed324af --- /dev/null +++ b/code/vn_ui.md @@ -0,0 +1,46 @@ +@table(Name, Type, BoxName) +UIStyleStacks: +{ + { Parent `ui_box *` `Parent` } + { Width `ui_size` `SemanticSize[Axis2_X]` } + { Height `ui_size` `SemanticSize[Axis2_Y]` } + { FixedX `r32` `FixedP.E[Axis2_X]` } + { FixedY `r32` `FixedP.E[Axis2_Y]` } + { TextColor `v4` `TextColor` } + { BackgroundColor `v4` `BackgroundColor` } + { BorderColor `v4` `BorderColor` } + { BorderThickness `r32` `BorderThickness` } + { LayoutAxis `axis2` `LayoutAxis` } + { CornerRadius `r32` `CornerRadius` } + { Font `font_id` `Font` } + { FontSize `r32` `FontSize` } + { OffsetX `r32` `Offset.x` } + { OffsetY `r32` `Offset.y` } +} + +@table_gen +{ + `struct ui_style_stacks`; + `{`; + @expand(UIStyleStacks s) `$(s.Type) $(s.Name)Stack[64]; s32 $(s.Name)StackUsed; b32 AutoPop$(s.Name);`; + `};`; +} + +@c @table_gen +{ + @expand(UIStyleStacks s) `inline void UI_Push$(s.Name)($(s.Type) Element) $(=>50) { ui *UI = UI_GetState(); Assert(UI->Stacks.$(s.Name)StackUsed $(=>125) + 1 < ArrayCount(UI->Stacks.$(s.Name)Stack)); $(=>180) UI->Stacks.$(s.Name)Stack[UI->Stacks.$(s.Name)StackUsed++] $(=>260) = Element; }`; + @expand(UIStyleStacks s) `inline void UI_Pop$(s.Name)(void) $(=>50) { ui *UI = UI_GetState(); Assert(UI->Stacks.$(s.Name)StackUsed $(=>125) > 0); $(=>180) --UI->Stacks.$(s.Name)StackUsed; $(=>271) }`; + @expand(UIStyleStacks s) `inline void UI_SetNext$(s.Name)($(s.Type) Element) { ui *UI = UI_GetState(); UI_Push$(s.Name)(Element); UI->Stacks.AutoPop$(s.Name) = true; }`; + @expand(UIStyleStacks s) `inline $(s.Type) UI_First$(s.Name)(void) { ui *UI = UI_GetState(); return(UI->Stacks.$(s.Name)Stack[0]); }`; + @expand(UIStyleStacks s) `inline $(s.Type) UI_Top$(s.Name)(void) { ui *UI = UI_GetState(); return(UI->Stacks.$(s.Name)Stack[UI->Stacks.$(s.Name)StackUsed - 1]); }`; + @expand(UIStyleStacks s) `#define UI_$(s.Name)(Element) DeferLoop(UI_Push$(s.Name)(Element), UI_Pop$(s.Name)())` +} + +@c @table_gen +{ + `static void UI_ApplyStyles(ui_box *Box)`; + `{`; + `ui *UI = UI_GetState();`; + @expand(UIStyleStacks s) `Assert(UI->Stacks.$(s.Name)StackUsed > 0); $(=>50) Box->$(s.BoxName) $(=>75) = UI->Stacks.$(s.Name)Stack[UI->Stacks.$(s.Name)StackUsed - 1]; $(=>160) if(UI->Stacks.AutoPop$(s.Name)) $(=>200) { UI_Pop$(s.Name)(); $(=>230) UI->Stacks.AutoPop$(s.Name) = false; $(=>260) }` + `}`; +} \ No newline at end of file diff --git a/code/vn_ui_utils.cpp b/code/vn_ui_utils.cpp index fdd88d3..bc1e714 100644 --- a/code/vn_ui_utils.cpp +++ b/code/vn_ui_utils.cpp @@ -223,13 +223,6 @@ static ui_signal UI_Scrollbar(axis2 Axis, string Name, r32 Size, r32 Offset) return(Signal); } -/* - -Goal for next week: Be able to make basic dialog trees. -Goal for tomorrow: Scrollable regions and basic text editing (with line wrapping). - -*/ - static void UI_ScrollBegin(r32 *X, r32 *Y, string Name) { u32 Flags = 0; @@ -270,6 +263,7 @@ static void UI_ScrollEnd(r32 *X, r32 *Y) b32 AllowOnY = (Y != 0); ui_box *ScrollableBox = UI_TopParent(); + ui_signal ScrollableBoxSignal = UI_SignalFromBox(ScrollableBox); { { UI_PopParent(); diff --git a/code/vn_workspace.cpp b/code/vn_workspace.cpp index 18327b4..14138dd 100644 --- a/code/vn_workspace.cpp +++ b/code/vn_workspace.cpp @@ -1,12 +1,28 @@ #include "vn_workspace_view.cpp" #include "vn_workspace_editor.cpp" +#include "vn_workspace_text_editor.cpp" #include "vn_workspace_commands.cpp" +//- sixten: State management +per_thread workspace *ThreadLocal_Workspace; + +static void Workspace_SetState(workspace *Workspace) +{ + ThreadLocal_Workspace = Workspace; +} + +static workspace *Workspace_GetState(void) +{ + return(ThreadLocal_Workspace); +} + //- sixten: Commands -static void Workspace_IssueCommand(workspace *Workspace, workspace_command_sig *Sig, u64 Argument = 0) +static void Workspace_IssueCommand(workspace_command_sig *Sig, u64 Argument = 0) { workspace_command *Result = 0; + workspace *Workspace = Workspace_GetState(); + if(Workspace->FirstFreeCommand) { Result = Workspace->FirstFreeCommand; @@ -15,7 +31,7 @@ static void Workspace_IssueCommand(workspace *Workspace, workspace_command_sig * if(!Result) { - Result = PushStruct(&Workspace->CommandArena, workspace_command); + Result = PushStruct(Workspace->CommandArena, workspace_command); } Result->Command = Sig; @@ -23,12 +39,14 @@ static void Workspace_IssueCommand(workspace *Workspace, workspace_command_sig * DLLInsertLast(Workspace->FirstCommand, Workspace->LastCommand, Result); } -static void Workspace_ProcessCommands(workspace *Workspace) +static void Workspace_ProcessCommands(void) { + workspace *Workspace = Workspace_GetState(); + workspace_command *Command = Workspace->FirstCommand; while(Command != 0) { - Command->Command(Workspace, Command->Argument); + Command->Command(Command->Argument); workspace_command *ToRemove = Command; Command = Command->Next; @@ -39,8 +57,10 @@ static void Workspace_ProcessCommands(workspace *Workspace) } } -static void Workspace_ProcessKeyBinds(workspace *Workspace) +static void Workspace_ProcessKeyBinds() { + workspace *Workspace = Workspace_GetState(); + platform_event_list *EventList = Workspace->EventList; for(platform_event *Event = EventList->First; @@ -56,7 +76,7 @@ static void Workspace_ProcessKeyBinds(workspace *Workspace) workspace_keybind *Keybind = Workspace->Keybinds + KeybindIndex; if((Event->Key == Keybind->Key) && (Event->Modifiers == Keybind->Modifiers)) { - Workspace_IssueCommand(Workspace, Keybind->Command, Keybind->Argument); + Workspace_IssueCommand(Keybind->Command, Keybind->Argument); } } } @@ -65,8 +85,10 @@ static void Workspace_ProcessKeyBinds(workspace *Workspace) //- sixten: Builder code -static ui_signal Workspace_BuildToolbarButton(workspace *Workspace, char *Text, toolbar_menu Menu) +static ui_signal Workspace_BuildToolbarButton(char *Text, toolbar_menu Menu) { + workspace *Workspace = Workspace_GetState(); + UI_SetNextWidth(UI_TextContent(20, 1)); UI_SetNextHeight(UI_Pixels(30, 1)); UI_SetNextCornerRadius(4); @@ -136,8 +158,10 @@ static ui_signal Workspace_BuildMenuItem(u32 Icon, char *Text, char *Shortcut) return(Signal); } -static void Workspace_BuildToolbar(workspace *Workspace, r32 dtForFrame) +static void Workspace_BuildToolbar() { + workspace *Workspace = Workspace_GetState(); + UI_SetNextLayoutAxis(Axis2_X); UI_SetNextHeight(UI_Pixels(30, 1)); UI_SetNextBackgroundColor(Theme_BackgroundColor); @@ -147,9 +171,9 @@ static void Workspace_BuildToolbar(workspace *Workspace, r32 dtForFrame) UI_Parent(ToolbarBox) { - Workspace_BuildToolbarButton(Workspace, "Panel", ToolbarMenu_Panel); - Workspace_BuildToolbarButton(Workspace, "View", ToolbarMenu_View); - Workspace_BuildToolbarButton(Workspace, "Window", ToolbarMenu_Window); + Workspace_BuildToolbarButton("Panel", ToolbarMenu_Panel); + Workspace_BuildToolbarButton("View", ToolbarMenu_View); + Workspace_BuildToolbarButton("Window", ToolbarMenu_Window); } if(Workspace->Menu != ToolbarMenu_None) @@ -179,13 +203,12 @@ static void Workspace_BuildToolbar(workspace *Workspace, r32 dtForFrame) { if(Workspace_BuildMenuItem(FontIcon_ResizeHorizontal, "Split Horizontal", "Ctrl + P").Clicked) { - Workspace_IssueCommand(Workspace, Workspace_Command_SplitPanelHorizontal); + Workspace_IssueCommand(Workspace_Command_SplitPanelHorizontal); Workspace->Menu = ToolbarMenu_None; } - if(Workspace_BuildMenuItem(FontIcon_ResizeVertical, "Split Vertical", "Ctrl + L").Clicked) { - Workspace_IssueCommand(Workspace, Workspace_Command_SplitPanelVertical); + Workspace_IssueCommand(Workspace_Command_SplitPanelVertical); Workspace->Menu = ToolbarMenu_None; } } @@ -193,28 +216,28 @@ static void Workspace_BuildToolbar(workspace *Workspace, r32 dtForFrame) { workspace_panel *CurrentPanel = Workspace->CurrentPanel; + // sixten(TODO): The views should be created at the end of the frame i.e. this should be done via + // commmands instead. (since we want all things that deals with major state changes to occur at a fixed + // point in our programs execution) + if(Workspace_BuildMenuItem(FontIcon_None, "Welcome", "").Clicked) { Workspace_CreateNewView(Workspace_View_Startup, CurrentPanel); - Workspace->Menu = ToolbarMenu_None; } if(Workspace_BuildMenuItem(FontIcon_None, "Editor", "").Clicked) { Workspace_CreateNewView(Workspace_View_Editor, CurrentPanel); - Workspace->Menu = ToolbarMenu_None; } if(Workspace_BuildMenuItem(FontIcon_None, "Command Palette", "").Clicked) { Workspace_CreateNewView(Workspace_View_CommandPalette, CurrentPanel); - Workspace->Menu = ToolbarMenu_None; } if(Workspace_BuildMenuItem(FontIcon_Wrench, "Settings", "").Clicked) { Workspace_CreateNewView(Workspace_View_Settings, CurrentPanel); - Workspace->Menu = ToolbarMenu_None; } } @@ -239,13 +262,15 @@ static void Workspace_BuildToolbar(workspace *Workspace, r32 dtForFrame) } //- sixten: Panels -static workspace_panel *Workspace_CreateNewPanel(workspace *Workspace, workspace_panel *Parent) +static workspace_panel *Workspace_CreateNewPanel(workspace_panel *Parent) { workspace_panel *Result = 0; + workspace *Workspace = Workspace_GetState(); + if(DLLIsEmpty(Workspace->FirstFreePanel)) { - Result = PushStruct(&Workspace->PanelArena, workspace_panel); + Result = PushStruct(Workspace->PanelArena, workspace_panel); } else { @@ -260,8 +285,10 @@ static workspace_panel *Workspace_CreateNewPanel(workspace *Workspace, workspace return(Result); } -static void Workspace_DeletePanel(workspace *Workspace, workspace_panel *Panel) +static void Workspace_DeletePanel(workspace_panel *Panel) { + workspace *Workspace = Workspace_GetState(); + if(Workspace->CurrentPanel == Panel) { Workspace->CurrentPanel = 0; @@ -271,22 +298,24 @@ static void Workspace_DeletePanel(workspace *Workspace, workspace_panel *Panel) DLLInsertLast(Workspace->FirstFreePanel, Workspace->LastFreePanel, Panel); } -static void Workspace_SplitPanel(workspace *Workspace, workspace_panel *Panel, axis2 Axis) +static void Workspace_SplitPanel(workspace_panel *Panel, axis2 Axis) { + workspace *Workspace = Workspace_GetState(); + if(Panel) { workspace_panel *Parent = Panel->Parent; if(Parent && (Parent->SplitAxis == Axis)) { - workspace_panel *NewPanel = Workspace_CreateNewPanel(Workspace, Parent); + workspace_panel *NewPanel = Workspace_CreateNewPanel(Parent); NewPanel->PercentOfParent = Panel->PercentOfParent = Panel->PercentOfParent * 0.5; DLLInsert_NP(Parent->First, Parent->Last, Panel, NewPanel, Next, Prev); } else { - workspace_panel *NewPanel = Workspace_CreateNewPanel(Workspace, Panel); + workspace_panel *NewPanel = Workspace_CreateNewPanel(Panel); NewPanel->FirstView = Panel->FirstView; NewPanel->LastView = Panel->LastView; @@ -302,7 +331,7 @@ static void Workspace_SplitPanel(workspace *Workspace, workspace_panel *Panel, a NewPanel->PercentOfParent = 0.5; DLLInsertLast(Panel->First, Panel->Last, NewPanel); - NewPanel = Workspace_CreateNewPanel(Workspace, Panel); + NewPanel = Workspace_CreateNewPanel(Panel); NewPanel->PercentOfParent = 0.5; DLLInsertLast(Panel->First, Panel->Last, NewPanel); @@ -319,8 +348,10 @@ static void Workspace_SplitPanel(workspace *Workspace, workspace_panel *Panel, a } } -inline void Workspace_BeginDrag(workspace *Workspace, workspace_drag_payload *Payload) +inline void Workspace_BeginDrag(workspace_drag_payload *Payload) { + workspace *Workspace = Workspace_GetState(); + // sixten(TODO): Right now, if you spam-click a draggable item, you can trigger this // assertion. I don't know what I want to do about this at the moment, but I'm sure // future me will have a soulution at hand. ^.^ @@ -330,15 +361,19 @@ inline void Workspace_BeginDrag(workspace *Workspace, workspace_drag_payload *Pa Workspace->DragPayloadState = Workspace_DragPayload_Active; } -inline b32 Workspace_GetDragPayload(workspace *Workspace, workspace_drag_payload *Dest) +inline b32 Workspace_GetDragPayload(workspace_drag_payload *Dest) { + workspace *Workspace = Workspace_GetState(); + b32 Result = (Workspace->DragPayloadState != Workspace_DragPayload_Inactive); *Dest = Workspace->DragPayload; return(Result); } -static void Workspace_BuildTabItem(workspace *Workspace, workspace_panel *Panel, workspace_view *View) +static void Workspace_BuildTabItem(workspace_panel *Panel, workspace_view *View) { + workspace *Workspace = Workspace_GetState(); + b32 ViewIsCurrent = (Panel->CurrentView == View); b32 PanelIsCurrent = (Workspace->CurrentPanel == Panel); @@ -396,6 +431,7 @@ static void Workspace_BuildTabItem(workspace *Workspace, workspace_panel *Panel, Panel->CurrentView = View; } + // sixten: Handle dragging the view. if(Signal.Dragging) { if(Signal.Pressed) @@ -404,13 +440,15 @@ static void Workspace_BuildTabItem(workspace *Workspace, workspace_panel *Panel, Payload.View = View; Payload.Key = TabBox->Key; - Workspace_BeginDrag(Workspace, &Payload); + Workspace_BeginDrag(&Payload); } } } -static void Workspace_BuildPanelHeader(workspace *Workspace, workspace_panel *Panel) +static void Workspace_BuildPanelHeader(workspace_panel *Panel) { + workspace *Workspace = Workspace_GetState(); + UI_SetNextLayoutAxis(Axis2_X); UI_SetNextBackgroundColor(ColorFromHex(0x252728FF)); UI_SetNextCornerRadius(0); @@ -422,14 +460,14 @@ static void Workspace_BuildPanelHeader(workspace *Workspace, workspace_panel *Pa View != 0; View = View->Next) { - Workspace_BuildTabItem(Workspace, Panel, View); + Workspace_BuildTabItem(Panel, View); } - UI_Spacer(UI_Percent(1, 0)); - // sixten: Panel Close Button if(Panel != Workspace->RootPanel) { + UI_Spacer(UI_Percent(1, 0)); + UI_SetNextSize(UI_Pixels(30, 1), UI_Pixels(30, 1)); UI_SetNextFont(Font_Icons); UI_SetNextBorderColor(ColorFromHex(0xA6514288)); @@ -447,7 +485,7 @@ static void Workspace_BuildPanelHeader(workspace *Workspace, workspace_panel *Pa ui_signal Signal = UI_SignalFromBox(CloseBox); if(Signal.Clicked) { - Workspace_IssueCommand(Workspace, Workspace_Command_ClosePanel, PointerToU64(Panel)); + Workspace_IssueCommand(Workspace_Command_ClosePanel, PointerToU64(Panel)); } } @@ -455,8 +493,10 @@ static void Workspace_BuildPanelHeader(workspace *Workspace, workspace_panel *Pa } } -static void Workspace_BuildPanel(workspace *Workspace, workspace_panel *Panel) +static void Workspace_BuildPanel(workspace_panel *Panel) { + workspace *Workspace = Workspace_GetState(); + // sixten: Fill remaining percent of parent. workspace_panel *Parent = Panel->Parent; if(Parent && Panel != Parent->First) @@ -480,7 +520,7 @@ static void Workspace_BuildPanel(workspace *Workspace, workspace_panel *Panel) { if(DLLIsEmpty(Panel->First)) { - Workspace_BuildPanelHeader(Workspace, Panel); + Workspace_BuildPanelHeader(Panel); // sixten: Main body { @@ -522,7 +562,7 @@ static void Workspace_BuildPanel(workspace *Workspace, workspace_panel *Panel) { ui_key CurrentActive = UI_GetActive(); - Workspace_BuildView(Workspace, Panel->CurrentView); + Workspace_BuildView(Panel->CurrentView); if(!AreEqual(CurrentActive, UI_GetActive())) { @@ -533,7 +573,7 @@ static void Workspace_BuildPanel(workspace *Workspace, workspace_panel *Panel) // sixten: Draw dragged view overlay. { workspace_drag_payload Payload; - b32 DragActive = Workspace_GetDragPayload(Workspace, &Payload); + b32 DragActive = Workspace_GetDragPayload(&Payload); b32 OverlayActive = (DragActive && (Payload.View->Parent != Panel) && InRange(BodyBox->Rect, UI_GetState()->MouseP)); @@ -630,8 +670,9 @@ static void Workspace_BuildPanel(workspace *Workspace, workspace_panel *Panel) { UI_SetNextAxisSize(Panel->SplitAxis, UI_Percent(Child->PercentOfParent*SizeScalar, 0)); UI_SetNextAxisSize(Opposite(Panel->SplitAxis), UI_Percent(1, 0)); - Workspace_BuildPanel(Workspace, Child); + Workspace_BuildPanel(Child); + // sixten: Create drag area if(Child->Next) { UI_SetNextAxisSize(Panel->SplitAxis, UI_Pixels(PaddingSize, 1)); @@ -667,10 +708,13 @@ static void Workspace_BuildPanel(workspace *Workspace, workspace_panel *Panel) } } -static void Workspace_BuildDragPayload(workspace *Workspace, vn_input *Input) +static void Workspace_BuildDragPayload() { + workspace *Workspace = Workspace_GetState(); + vn_input *Input = Workspace->Input; + workspace_drag_payload Payload; - if(Workspace_GetDragPayload(Workspace, &Payload)) + if(Workspace_GetDragPayload(&Payload)) { if(Workspace->DragPayloadState == Workspace_DragPayload_Released) { @@ -709,15 +753,28 @@ static void Workspace_BuildDragPayload(workspace *Workspace, vn_input *Input) //- sixten: Workspace static void Workspace_Init(workspace *Workspace) { - Workspace->RootPanel = Workspace->CurrentPanel = Workspace_CreateNewPanel(Workspace, 0); + Workspace_SetState(Workspace); - Workspace_CreateNewView(Workspace_View_Startup, Workspace->RootPanel); + Workspace->CommandArena = ArenaAllocate(Gigabytes(1)); + Workspace->PanelArena = ArenaAllocate(Gigabytes(1)); + + Workspace->RootPanel = Workspace->CurrentPanel = Workspace_CreateNewPanel(0); + + if(DEBUG_DebugSettings->ShowWelcomeMessage) + { + Workspace_CreateNewView(Workspace_View_Startup, Workspace->RootPanel); + } + Workspace_CreateNewView(Workspace_View_TextEditor, Workspace->RootPanel); // sixten: Setup keybinds { #define BIND_COMMAND(...) Workspace->Keybinds[Workspace->KeybindCount++] = {__VA_ARGS__} BIND_COMMAND(Key_P, PlatformModifier_Ctrl, Workspace_Command_SplitPanelHorizontal); BIND_COMMAND(Key_L, PlatformModifier_Ctrl, Workspace_Command_SplitPanelVertical); + +#if VN_INTERNAL + BIND_COMMAND(Key_U, PlatformModifier_Ctrl|PlatformModifier_Shift, Workspace_Command_ToggleRenderUIDebugRects); +#endif #undef BIND_COMMAND } } @@ -725,13 +782,12 @@ static void Workspace_Init(workspace *Workspace) static void Workspace_Update(workspace *Workspace, vn_render_commands *RenderCommands, vn_input *Input, glyph_atlas *GlyphAtlas) { + Workspace_SetState(Workspace); + Workspace->Input = Input; Workspace->EventList = Input->EventList; - // sixten: Process last frame's commands. - Workspace_ProcessKeyBinds(Workspace); - Workspace_ProcessCommands(Workspace); - + // sixten: Make sure that a view/panel is always selected. if(!Workspace->CurrentPanel) { Workspace->CurrentPanel = Workspace->RootPanel; @@ -743,12 +799,16 @@ static void Workspace_Update(workspace *Workspace, vn_render_commands *RenderCom // sixten: Build the UI. { - Workspace_BuildToolbar(Workspace, Input->dtForFrame); + Workspace_BuildToolbar(); UI_SetNextSize(UI_Percent(1, 1), UI_Percent(1, 0)); - Workspace_BuildPanel(Workspace, Workspace->RootPanel); + Workspace_BuildPanel(Workspace->RootPanel); - Workspace_BuildDragPayload(Workspace, Input); + Workspace_BuildDragPayload(); } + // sixten: Process the keybinds and commands. + Workspace_ProcessKeyBinds(); + Workspace_ProcessCommands(); + } \ No newline at end of file diff --git a/code/vn_workspace.h b/code/vn_workspace.h index ed39f47..57c8890 100644 --- a/code/vn_workspace.h +++ b/code/vn_workspace.h @@ -4,6 +4,7 @@ #define VN_WORKSPACE_H #include "vn_workspace_editor.h" +#include "vn_workspace_text_editor.h" // sixten(TODO): Remove this type entirely. enum toolbar_menu @@ -33,7 +34,7 @@ struct workspace_panel #include "vn_workspace_view.h" -#define WORKSPACE_COMMAND(name, ...) void name(workspace *Workspace, u64 Argument) +#define WORKSPACE_COMMAND(name, ...) void name(u64 Argument) typedef WORKSPACE_COMMAND(workspace_command_sig); struct workspace_command @@ -72,7 +73,7 @@ struct workspace platform_event_list *EventList; // sixten: Command Allocation - memory_arena CommandArena; + memory_arena *CommandArena; workspace_command *FirstFreeCommand; workspace_command *LastFreeCommand; @@ -85,7 +86,7 @@ struct workspace s32 KeybindCount; // sixten: Panels - memory_arena PanelArena; + memory_arena *PanelArena; workspace_panel *FirstFreePanel; workspace_panel *LastFreePanel; @@ -100,19 +101,23 @@ struct workspace workspace_panel *CurrentPanel; }; +//- sixten: State management +static void Workspace_SetState(workspace *Workspace); +static workspace *Workspace_GetState(void); + //- sixten: Commands -static void Workspace_IssueCommand(workspace *Workspace, workspace_command_sig *Sig, u64 Argument); -static void Workspace_ProcessCommands(workspace *Workspace); +static void Workspace_IssueCommand(workspace_command_sig *Sig, u64 Argument); +static void Workspace_ProcessCommands(void); //- sixten: Panels -static workspace_panel *Workspace_CreateNewPanel(workspace *Workspace, workspace_panel *Parent); -static void Workspace_DeletePanel(workspace *Workspace, workspace_panel *Panel); -static void Workspace_SplitPanel(workspace *Workspace, workspace_panel *Panel, axis2 Axis); +static workspace_panel *Workspace_CreateNewPanel(workspace_panel *Parent); +static void Workspace_DeletePanel(workspace_panel *Panel); +static void Workspace_SplitPanel(workspace_panel *Panel, axis2 Axis); //- sixten: Builder code -static ui_signal Workspace_BuildToolbarButton(workspace *Workspace, char *Text, toolbar_menu Menu); +static ui_signal Workspace_BuildToolbarButton(char *Text, toolbar_menu Menu); static ui_signal Workspace_BuildMenuItem(u32 Icon, char *Text, char *Shortcut); -static void Workspace_BuildToolbar(workspace *Workspace, r32 dtForFrame); +static void Workspace_BuildToolbar(r32 dtForFrame); //- sixten: Workspace static void Workspace_Init(workspace *Workspace); diff --git a/code/vn_workspace_commands.cpp b/code/vn_workspace_commands.cpp index 0f2bf7b..b552e85 100644 --- a/code/vn_workspace_commands.cpp +++ b/code/vn_workspace_commands.cpp @@ -1,15 +1,19 @@ WORKSPACE_COMMAND(Workspace_Command_SplitPanelHorizontal) { - Workspace_SplitPanel(Workspace, Workspace->CurrentPanel, Axis2_X); + workspace *Workspace = Workspace_GetState(); + Workspace_SplitPanel(Workspace->CurrentPanel, Axis2_X); } WORKSPACE_COMMAND(Workspace_Command_SplitPanelVertical) { - Workspace_SplitPanel(Workspace, Workspace->CurrentPanel, Axis2_Y); + workspace *Workspace = Workspace_GetState(); + Workspace_SplitPanel(Workspace->CurrentPanel, Axis2_Y); } WORKSPACE_COMMAND(Workspace_Command_ClosePanel) { + workspace *Workspace = Workspace_GetState(); + workspace_panel *Panel = (workspace_panel *)Argument; if(!Panel) { @@ -50,7 +54,7 @@ WORKSPACE_COMMAND(Workspace_Command_ClosePanel) } DLLRemove(Parent->First, Parent->Last, Child); - Workspace_DeletePanel(Workspace, Child); + Workspace_DeletePanel(Child); } else { @@ -79,5 +83,12 @@ WORKSPACE_COMMAND(Workspace_Command_ClosePanel) //Workspace_DeleteView(Child); } - Workspace_DeletePanel(Workspace, Panel); -} \ No newline at end of file + Workspace_DeletePanel(Panel); +} + +#if VN_INTERNAL +WORKSPACE_COMMAND(Workspace_Command_ToggleRenderUIDebugRects) +{ + DEBUG_DebugSettings->RenderUIDebugRects = !DEBUG_DebugSettings->RenderUIDebugRects; +} +#endif \ No newline at end of file diff --git a/code/vn_workspace_editor.cpp b/code/vn_workspace_editor.cpp index 1d11466..2b97073 100644 --- a/code/vn_workspace_editor.cpp +++ b/code/vn_workspace_editor.cpp @@ -9,7 +9,7 @@ static workspace_editor_node *Workspace_GetNewEditorNode(workspace_view *View) if(DLLIsEmpty(Editor->FirstFreeNode)) { - Result = PushStruct(&View->Arena, workspace_editor_node); + Result = PushStruct(View->Arena, workspace_editor_node); } else { @@ -63,6 +63,126 @@ inline r32 Workspace_CalculateScaleFromZoomLevel(r32 ZoomLevel) //- sixten: Builder code +static void Workspace_BuildEditorListerDropdown(workspace_editor_lister_dropdown *ListerDropdown) +{ + b32 ActiveInDropdown = false; + + UI_PushBackgroundColor(SetAlpha(Color_Black, 0.3)); + UI_PushBorderColor(SetAlpha(Color_Black, 0.7)); + + r32 HeightTransition = AnimationCurve_AnimateValueF(1, 0, 0.3, "Editor Lister Dropdown %p", ListerDropdown); + + UI_SetNextSize(UI_Em(15, 1), UI_Em(25*HeightTransition, 1)); + UI_SetNextCornerRadius(4.0); + UI_SetNextFixedP(ListerDropdown->P); + UI_SetNextLayoutAxis(Axis2_Y); + + ui_box *Box = UI_MakeBox(UI_BoxFlag_DrawBackground | + UI_BoxFlag_FloatingX | + UI_BoxFlag_FloatingY | + UI_BoxFlag_Clickable | + UI_BoxFlag_Clip, + StrLit("Editor Lister Dropdown")); + + UI_Parent(Box) + { + + UI_BorderColor(SetAlpha(Color_Grey, 0.7)) + UI_Row() UI_Column() + { + UI_Size(UI_TextContent(15, 1), UI_TextContent(12, 1)) UI_LabelF("Create new node"); + + // sixten: Build search bar + UI_SetNextWidth(UI_Percent(1, 1)); + UI_SetNextHeight(UI_Em(2, 1)); + UI_SetNextCornerRadius(4.0); + UI_SetNextLayoutAxis(Axis2_X); + UI_Parent(UI_MakeBox(UI_BoxFlag_DrawBorder|UI_BoxFlag_DrawBackground, StrLit("Editor Lister Field"))) + { + UI_Spacer(UI_Em(0.8, 1)); + UI_Width(UI_TextContent(1, 1)) UI_Font(Font_Icons) UI_LabelF("%U", FontIcon_Filter); + UI_Width(UI_Percent(1, 0)) + { + UI_MakeBox(0, StrLit("")); + } + } + + struct lister_option + { + char *Text; + u32 Icons; + }; + + lister_option Options[] = + { + {"Text", FontIcon_Pencil}, + {"Branch", FontIcon_Menu}, + }; + + for(s32 OptionIndex = 0; + OptionIndex < ArrayCount(Options); + ++OptionIndex) + { + lister_option *Option = Options + OptionIndex; + + UI_SetNextSize(UI_Percent(1, 1), UI_Em(2, 1)); + UI_SetNextLayoutAxis(Axis2_X); + ui_box *ClickableBox = UI_MakeBoxF(UI_BoxFlag_Clickable, "Editor Lister %s", Option->Text); + + ui_signal Signal = UI_SignalFromBox(ClickableBox); + + if(AreEqual(UI_GetActive(), ClickableBox->Key)) + { + ActiveInDropdown = true; + } + + UI_Parent(ClickableBox) + UI_Height(UI_Percent(1, 1)) + { + b32 IsHot = AreEqual(UI_GetHot(), ClickableBox->Key); + r32 TargetHot = IsHot*0.8; + + UI_SetNextCornerRadius(3.0); + UI_SetNextSize(UI_Percent(1, 1), UI_Percent(1, 1)); + UI_SetNextBackgroundColor(SetAlpha(Theme_HighlightBorderColor, + AnimationCurve_AnimateValueF(TargetHot, TargetHot, 0.1, "Editor Lister %s%p", + Option->Text, ListerDropdown))); + UI_SetNextLayoutAxis(Axis2_X); + ui_box *HighlightBox = UI_MakeBox(UI_BoxFlag_DrawBackground, StrLit("")); + + UI_Parent(HighlightBox) + UI_Padding(UI_Em(0.8, 1)) + { + UI_Width(UI_TextContent(0, 1)) + UI_Font(Font_Icons) UI_MakeBoxF(UI_BoxFlag_DrawText, "%U", Option->Icons); + UI_Spacer(UI_Em(0.6, 1)); + UI_Width(UI_TextContent(0, 1)) UI_MakeBoxF(UI_BoxFlag_DrawText, "%s", Option->Text); + + if(Signal.Clicked) + { + // sixten(TODO): Issue the requested command. + ListerDropdown->Open = false; + } + } + } + } + } + + ui_signal Signal = UI_SignalFromBox(Box); + if(AreEqual(UI_GetActive(), Box->Key)) + { + ActiveInDropdown = true; + } + } + UI_PopBackgroundColor(); + UI_PopBorderColor(); + + if(!ActiveInDropdown && !AreEqual(UI_GetActive(), UI_EmptyKey())) + { + ListerDropdown->Open = false; + } +} + static void Workspace_EditorDrawCallback(render_group *Group, glyph_atlas *Atlas, ui_box *Box, void *Data) { workspace_view_editor *Editor = (workspace_view_editor *)Data; @@ -79,7 +199,9 @@ static void Workspace_EditorDrawCallback(render_group *Group, glyph_atlas *Atlas ++LineIndex) { r32 OffsetX = Workspace_WorldToView(Editor->Offset.x, Scale, Dim.x, LineIndex - (s32)Editor->Offset.x); - PushQuad(Group, Box->Rect.Min + V2(OffsetX, 0), V2(1.5, Dim.y), LineColor, 0, 1.2, 0); + v2 Min = Box->Rect.Min + V2(OffsetX, 0); + v2 Max = Min + V2(1.5, Dim.y); + PushQuad(Group, Range2R32(Min, Max), LineColor, 0, 1.2, 0); } s32 HorizontalLineCount = Dim.y / Scale + 4; @@ -88,18 +210,21 @@ static void Workspace_EditorDrawCallback(render_group *Group, glyph_atlas *Atlas ++LineIndex) { r32 OffsetY = Workspace_WorldToView(Editor->Offset.y, Scale, Dim.y, LineIndex - (s32)Editor->Offset.y); + v2 Min = Box->Rect.Min + V2(0, OffsetY); + v2 Max = Min + V2(Dim.x, 1.5); - PushQuad(Group, Box->Rect.Min + V2(0, OffsetY), V2(Dim.x, 1.5), LineColor, 0, 1.2, 0); + PushQuad(Group, Range2R32(Min, Max), LineColor, 0, 1.2, 0); } } -static void Workspace_BuildEditor(workspace *Workspace, workspace_view *View) +static void Workspace_BuildEditor(workspace_view *View) { + workspace *Workspace = Workspace_GetState(); + workspace_view_editor *Editor = (workspace_view_editor *)View->Data; - // sixten(TODO): These should be able to have a strictness of 1, but they can't. Fix that. - UI_SetNextWidth(UI_Percent(1, 0)); - UI_SetNextHeight(UI_Percent(1, 0)); + UI_SetNextWidth(UI_Percent(1, 1)); + UI_SetNextHeight(UI_Percent(1, 1)); ui_box *EditorBox = UI_MakeBoxF(UI_BoxFlag_Clip|UI_BoxFlag_Clickable, "Workspace Editor %p", View); EditorBox->DrawCallback = Workspace_EditorDrawCallback; @@ -110,40 +235,55 @@ static void Workspace_BuildEditor(workspace *Workspace, workspace_view *View) v2 EditorDim = DimOfRange(EditorBox->Rect); - // sixten: Build the node boxes. - for(workspace_editor_node *Node = Editor->FirstNode; - Node != 0; - Node = Node->Next) + UI_Parent(EditorBox) { - v2 ViewDim = V2(2, 1.5)*Editor->Scale; - v2 ViewP = Workspace_WorldToView(Editor->Offset, Editor->Scale, EditorDim, Node->P) - ViewDim*0.5; - - UI_SetNextSize(UI_Pixels(ViewDim.x, 1), UI_Pixels(ViewDim.y, 1)); - UI_SetNextFixedP(ViewP); - UI_SetNextLayoutAxis(Axis2_Y); - - Node->Box = UI_MakeBoxF(UI_BoxFlag_DrawBackground | - UI_BoxFlag_DrawBorder | - UI_BoxFlag_FloatingX | - UI_BoxFlag_FloatingY | - UI_BoxFlag_DrawDropShadow, - "Workspace Editor Node %p", Node); - - UI_Parent(Node->Box) + // sixten: Build the node boxes. + for(workspace_editor_node *Node = Editor->FirstNode; + Node != 0; + Node = Node->Next) { - UI_SetNextBackgroundColor(LinearBlend(Theme_BackgroundColor, Color_Black, 0.3)); + v2 ViewDim = V2(2, 1.5)*Editor->Scale; + v2 ViewP = Workspace_WorldToView(Editor->Offset, Editor->Scale, EditorDim, Node->P) - ViewDim*0.5; - UI_SetNextSize(UI_Percent(1, 1), UI_Pixels(20, 1)); - Node->TitleBox = UI_MakeBoxF(UI_BoxFlag_DrawBackground | - UI_BoxFlag_DrawBorder | - UI_BoxFlag_Clickable, - "Workspace Editor Node Title"); + UI_SetNextSize(UI_Pixels(ViewDim.x, 1), UI_Pixels(ViewDim.y, 1)); + UI_SetNextFixedP(ViewP); + UI_SetNextLayoutAxis(Axis2_Y); - UI_Parent(Node->TitleBox) + Node->Box = UI_MakeBoxF(UI_BoxFlag_DrawBackground | + UI_BoxFlag_DrawBorder | + UI_BoxFlag_FloatingX | + UI_BoxFlag_FloatingY | + UI_BoxFlag_DrawDropShadow, + "Workspace Editor Node %p", Node); + + UI_Parent(Node->Box) { - UI_Width(UI_TextContent(7, 0)) UI_Font(Font_Bold) UI_LabelF("Node"); + UI_SetNextBackgroundColor(LinearBlend(Theme_BackgroundColor, Color_Black, 0.3)); + UI_SetNextLayoutAxis(Axis2_X); + UI_SetNextSize(UI_Percent(1, 1), UI_Em(1.8, 1)); + Node->TitleBox = UI_MakeBoxF(UI_BoxFlag_DrawBackground | + UI_BoxFlag_DrawBorder | + UI_BoxFlag_Clickable, + "Workspace Editor Node Title"); + + UI_Parent(Node->TitleBox) + { + UI_Spacer(UI_Em(0.5, 1)); + + UI_Width(UI_TextContent(0, 1)) UI_Font(Font_Bold) UI_LabelF("Node"); + UI_Spacer(UI_Percent(1, 0)); + + UI_SetNextSize(UI_Em(1.8, 1), UI_Percent(1, 1)); + UI_SetNextFont(Font_Icons); + Node->CloseBox = UI_MakeBoxF(UI_BoxFlag_DrawText, "%U", FontIcon_Cancel); + } } } + + if(Editor->ListerDropdown.Open) + { + Workspace_BuildEditorListerDropdown(&Editor->ListerDropdown); + } } // sixten: Get input from boxes. @@ -176,10 +316,6 @@ static void Workspace_BuildEditor(workspace *Workspace, workspace_view *View) } } - // sixten: Process the movement of the node. - { - } - // sixten: Process panning and zooming of the editor. ui_signal Signal = UI_SignalFromBox(EditorBox); { @@ -227,6 +363,16 @@ static void Workspace_BuildEditor(workspace *Workspace, workspace_view *View) // sixten: Process shortcuts. if(Platform_KeyPress(Workspace->EventList, Key_Space, PlatformModifier_Ctrl)) { - Workspace_GetNewEditorNode(View); + workspace_editor_lister_dropdown *ListerDropdown = &Editor->ListerDropdown; + + if(ListerDropdown->Open) + { + ListerDropdown->Open = false; + } + else if(InRange(EditorBox->Rect, UI_GetState()->MouseP)) + { + ListerDropdown->Open = true; + ListerDropdown->P = UI_GetState()->MouseP - EditorBox->Rect.Min; + } } } diff --git a/code/vn_workspace_editor.h b/code/vn_workspace_editor.h index a1a8507..3cc15ab 100644 --- a/code/vn_workspace_editor.h +++ b/code/vn_workspace_editor.h @@ -6,15 +6,16 @@ /* sixten(NOTE): Node types * * Text(String, opt. Character) -> Node -* Menu(Strings[0..n]) -> Nodes[0..n] +* Branch(Strings[0..n]) -> Nodes[0..n] * */ enum workspace_editor_node_type { Workspace_EditorNode_None, + Workspace_EditorNode_Start, Workspace_EditorNode_Text, - Workspace_EditorNode_Menu, + Workspace_EditorNode_Branch, }; struct workspace_editor_node @@ -26,10 +27,23 @@ struct workspace_editor_node ui_box *Box; ui_box *TitleBox; + ui_box *CloseBox; v2 P; }; +struct workspace_editor_lister_dropdown +{ + // sixten: Input field + char InputField[128]; + s32 InputFieldUsed; + b32 InputFieldSelected; + + // sixten: Properties + b32 Open; + v2 P; +}; + //- sixten: Managing nodes static workspace_editor_node *Workspace_GetNewEditorNode(struct workspace_view *View); @@ -38,8 +52,12 @@ inline v2 Workspace_ViewToWorld(v2 Offset, r32 Scale, v2 Dim, v2 P); inline v2 Workspace_WorldToView(v2 Offset, r32 Scale, v2 Dim, v2 P); inline r32 Workspace_CalculateScaleFromZoomLevel(r32 ZoomLevel); +//- sixten: Lister dropdown +static void Workspace_OpenListerDropdown(v2 MouseP); + //- sixten: Builder code +static void Workspace_BuildEditorListerDropdown(workspace_editor_lister_dropdown *ListerDropdown); static void Workspace_EditorDrawCallback(render_group *Group, glyph_atlas *Atlas, ui_box *Box, void *Data); -static void Workspace_BuildEditor(struct workspace *Workspace, struct workspace_view *View); +static void Workspace_BuildEditor(struct workspace_view *View); #endif //VN_WORKSPACE_EDITOR_H diff --git a/code/vn_workspace_text_editor.cpp b/code/vn_workspace_text_editor.cpp new file mode 100644 index 0000000..c127068 --- /dev/null +++ b/code/vn_workspace_text_editor.cpp @@ -0,0 +1,362 @@ +//////////////////////////////// +//~ sixten: Mutable String Functions + +static mutable_string MutableStringAllocate(u64 Size) +{ + mutable_string Result = {}; + Result.Arena = ArenaAllocate(Size); + ArenaSetAlign(Result.Arena, 1); + Result.String = MakeString(PushArray(Result.Arena, u8, 1), 0); + return(Result); +} + +static void MutableStringRelease(mutable_string *String) +{ + ArenaRelease(String->Arena); +} + +static void MutableStringReplaceRange(mutable_string *MutString, string ReplaceString, range1_s64 Range) +{ + Range = Intersection(Range, Range1S64(0, MutString->String.Count)); + + s64 NewCount = MutString->String.Count+ReplaceString.Count-DimOfRange(Range); + if(NewCount > MutString->String.Count) + { + s64 ToAllocate = NewCount-MutString->String.Count; + PushArray(MutString->Arena, u8, ToAllocate); + } + + Move(MutString->String.Data+Range.Min+ReplaceString.Count, MutString->String.Data+Range.Max, MutString->String.Count-DimOfRange(Range)); + Copy(MutString->String.Data+Range.Min, ReplaceString.Data, ReplaceString.Count); + + MutString->String.Count = NewCount; +} + +//////////////////////////////// +//~ sixten: Workspace Text Editor Functions + +static workspace_text_data Workspace_TextDataFromStringChunkList(memory_arena *Arena, string Text) +{ + temporary_memory Scratch = GetScratch(&Arena, 1); + + //- sixten: tokenize the text + tokenize_result TokenizeResult = T_TokenizeFromText(Arena, StrLit("*scratch*"), Text); + token_array Tokens = TokenizeResult.Tokens; + + //- sixten: gather all line ranges + range1_s64_list Lines = {}; + { + u8 *TextBegin = Text.Data; + u8 *TextEnd = TextBegin + Text.Count; + range1_s64 Range = Range1S64(0, 0); + for(u8 *Char = TextBegin; Char <= TextEnd; Char += 1) + { + Range.Max += 1; + + //- sixten: push line range on newline and EOF + if(Char == TextEnd || *Char == '\n') + { + Range1S64ListPush(Scratch.Arena, &Lines, Range); + Range = Range1S64(Range.Max, Range.Max); + } + } + } + + //- sixten: fill & return + workspace_text_data Result = {}; + { + Result.Tokens = Tokens; + Result.Lines = Range1S64ArrayFromList(Arena, &Lines);; + } + ReleaseScratch(Scratch); + return(Result); +} + +static UI_CUSTOM_DRAW_CALLBACK(Workspace_TextEditorDrawCallback) +{ + temporary_memory Scratch = GetScratch(); + workspace_view_text_editor *Editor = (workspace_view_text_editor *)Data; + + //- sixten: get dimensions & scroll offset from container + ui_box *ContainerBox = Editor->ContainerBox; + range2_r32 ParentRect = ContainerBox->Rect; + v2 ParentDim = DimOfRange(ParentRect); + v2 Offset = Box->Parent->Offset; + + //- sixten: rendering properties + r32 FontSize = 16.0f; + r32 LineHeight = FontSize + 4.0f; + + //- sixten: calculate the dimensions of the glyphs + glyph *Glyph = GetGlyph(Atlas, Font_Monospace, 'A', FontSize, 0); + r32 GlyphAdvance = Glyph->Advance; + + //- sixten: find the text point + text_point CursorTextP = TextPointFromOffset(Editor->Text.String, Editor->EditState.Cursor); + text_point MarkTextP = TextPointFromOffset(Editor->Text.String, Editor->EditState.Mark); + + //- sixten: get the line count + range1_s64_array *Lines = &Editor->Lines; + s64 LineCount = Lines->Count; + + //- sixten: calculate the text dim + Editor->TextDim = V2(1900, LineCount*LineHeight); + + //- sixten: calculate the line margin dim + s32 LineMarginDigitsRequired = 6; + v2_r32 LineMarginDim = V2((LineMarginDigitsRequired)*GlyphAdvance, ParentRect.Max.y - ParentRect.Min.y); + + //- sixten: tokenize text + tokenize_result TokenizeResult = T_TokenizeFromText(Scratch.Arena, StrLit("nobody cares"), Editor->Text.String); + token_array Tokens = TokenizeResult.Tokens; + token *TokensBegin = Tokens.Tokens; + token *TokensEnd = TokensBegin + Tokens.Count; + + //- sixten: find the first visible token + token *VisibleTokensBegin = TokensBegin; + s64 TopMostLine = Min((s64)Floor(-Offset.y / LineHeight), LineCount); + for(s64 LinesFound = 0; LinesFound < TopMostLine && VisibleTokensBegin < TokensEnd; VisibleTokensBegin += 1) + { + if(VisibleTokensBegin->Flags & TokenFlag_Newline) + { + LinesFound += 1; + } + } + + //- sixten: find the last visible token + token *VisibleTokensEnd = VisibleTokensBegin; + s64 LinesOnScreen = Min((s64)Floor(ParentDim.y / LineHeight)+1, LineCount-TopMostLine); + for(s64 LinesFound = 0; LinesFound < LinesOnScreen && VisibleTokensEnd < TokensEnd; VisibleTokensEnd += 1) + { + if(VisibleTokensEnd->Flags & TokenFlag_Newline) + { + LinesFound += 1; + } + } + + //- sixten: draw line numbers & line highlights + { + //- sixten: draw the background + v4 LineMarginColor = ColorFromHex(0x10203080); + range2_r32 LineMarginBox = Range2R32(ParentRect.Min, ParentRect.Min+LineMarginDim); + PushQuad(Group, LineMarginBox, LineMarginColor, LineMarginColor, LineMarginColor, LineMarginColor, 0, 0, 0); + + //- sixten: draw the numbers + v2_r32 LineOffset = Box->Rect.Min; + for(s64 LineIndex = TopMostLine; LineIndex < TopMostLine + LinesOnScreen; LineIndex += 1) + { + r32 LineY = LineOffset.y + LineIndex*LineHeight; + PushTextF(Group, Atlas, Font_Monospace, V2(0, LineY), FontSize, Color_Grey, "%*.i", LineMarginDigitsRequired, LineIndex+1); + + if(LineIndex + 1 == CursorTextP.Line) + { + v4_r32 LineHighlightColor = ColorFromHex(0x10204080); + range2_r32 LineHighlightBox = Range2R32(V2(LineMarginBox.Max.x, LineY), V2(Box->Rect.Max.x, LineY+LineHeight)); + PushQuad(Group, LineHighlightBox, LineHighlightColor, LineHighlightColor, LineHighlightColor, LineHighlightColor, 0, 0, 0); + } + } + } + + //- sixten: render tokens + v2 BaseTokenP = Box->Rect.Min+V2(LineMarginDim.x, TopMostLine*LineHeight); + v2 TokenP = BaseTokenP; + for(token *Token = VisibleTokensBegin; Token < VisibleTokensEnd; Token += 1) + { + string TokenString = T_StringFromToken(Editor->Text.String, *Token); + + //- sixten: get color from token + v4 Color = Color_Magenta; + if(Token->Flags & TokenGroup_Comment) { Color = Color_Grey; } + else if(Token->Flags & TokenFlag_Reserved) { Color = Color_Grey; } + else if(Token->Flags & TokenFlag_Symbol) { Color = ColorFromHex(0xbd2d2dff); } + else if(Token->Flags & TokenFlag_StringLiteral) { Color = ColorFromHex(0xffa900ff); } + else if(Token->Flags & TokenFlag_Numeric) { Color = ColorFromHex(0xffa900ff); } + else if(Token->Flags & TokenFlag_Identifier) + { + //- sixten: check for keywords + if(AreEqual(TokenString, StrLit("var")) || + AreEqual(TokenString, StrLit("proc")) || + AreEqual(TokenString, StrLit("branch")) || + AreEqual(TokenString, StrLit("jump")) || + AreEqual(TokenString, StrLit("if")) || + AreEqual(TokenString, StrLit("true")) || + AreEqual(TokenString, StrLit("false"))) + { + Color = ColorFromHex(0xf0c674ff); + } + else + { + Color = Theme_TextColor; + } + } + //- sixten: render & advance by token + if(!(Token->Flags & TokenGroup_Whitespace)) + { + TokenP.x += PushText(Group, Atlas, Font_Monospace, TokenP, FontSize, Color, TokenString); + } + else + { + if(Token->Flags & TokenFlag_Newline) + { + TokenP.x = BaseTokenP.x; + TokenP.y += LineHeight; + } + else + { + u8 *StringBegin = TokenString.Data; + u8 *StringEnd = StringBegin + TokenString.Count; + for(u8 *Char = StringBegin; Char < StringEnd; Char += 1) + { + if(*Char == ' ' || *Char == '\t') + { + TokenP.x += GlyphAdvance; + } + } + } + } + } + + //- sixten: render cursor + { + v2 TargetCursorP = Box->Rect.Min+V2(LineMarginDim.x+(CursorTextP.Column-1)*GlyphAdvance,(CursorTextP.Line-1)*LineHeight); + v2 CursorP = V2(AnimationCurve_AnimateValueF(TargetCursorP.x, TargetCursorP.x, 0.1, "Workspace Text Editor Cursor X %p", Editor), + AnimationCurve_AnimateValueF(TargetCursorP.y, TargetCursorP.y, 0.1, "Workspace Text Editor Cursor Y %p", Editor)); + v2 CursorDim = V2(2, LineHeight); + range2_r32 CursorRect = Range2R32(CursorP, CursorP+CursorDim); + v4 CursorColor = ColorFromHex(0x10FF20FF); + PushQuad(Group, CursorRect, CursorColor, CursorColor, CursorColor, CursorColor, 2, 0.4, 0); + } + + //- sixten: render the selection + { + text_range Selection = TextRange(CursorTextP, MarkTextP); + range1_s64 LineRange = Range1S64(Selection.Min.Line, Selection.Max.Line); + for(s64 Line = TopMostLine; Line < TopMostLine + LinesOnScreen; Line += 1) + { + r32 LineY = Box->Rect.Min.y + Line*LineHeight; + if(Contains(LineRange, Line + 1)) + { + range1_s64 ColumnRange = Lines->Ranges[Line]; + range1_s64 NormalizedColumnRange = Range1S64(0, DimOfRange(ColumnRange)); + if(Line + 1 == LineRange.Min && Line + 1 == LineRange.Max) + { + NormalizedColumnRange = Range1S64(Editor->EditState.Cursor - ColumnRange.Min, Editor->EditState.Mark - ColumnRange.Min); + } + else if(Line + 1 == LineRange.Min) + { + NormalizedColumnRange = Range1S64(Min(Editor->EditState.Mark, Editor->EditState.Cursor) - ColumnRange.Min, DimOfRange(ColumnRange)); + } + else if(Line + 1 == LineRange.Max) + { + NormalizedColumnRange = Range1S64(0, Max(Editor->EditState.Mark, Editor->EditState.Cursor) - ColumnRange.Min); + } + v4_r32 LineHighlightColor = ColorFromHex(0x66B3CC4C); + range2_r32 LineHighlightBox = Range2R32(V2(LineMarginDim.x+NormalizedColumnRange.Min*GlyphAdvance, LineY), + V2(LineMarginDim.x+NormalizedColumnRange.Max*GlyphAdvance, LineY+LineHeight)); + PushQuad(Group, LineHighlightBox, LineHighlightColor, LineHighlightColor, LineHighlightColor, LineHighlightColor, 4, 1.4, 0); + } + } + } + ReleaseScratch(Scratch); +} + +static void Workspace_BuildTextEditor(workspace_view *View) +{ + workspace_view_text_editor *Editor = (workspace_view_text_editor *)View->Data; + + temporary_memory Scratch = GetScratch(); + + //- sixten: rendering properties + r32 FontSize = 16.0f; + r32 LineHeight = FontSize + 4.0f; + + //- sixten: calculate the dimensions of the glyphs + glyph *Glyph = GetGlyph(UI_GlyphAtlas(), Font_Monospace, 'A', FontSize, 0); + r32 GlyphAdvance = Glyph->Advance; + + //- sixten: calculate the line margin dim + s32 LineMarginDigitsRequired = 6; + r32 LineMarginWidth = (LineMarginDigitsRequired)*GlyphAdvance; + + ui_box *EditorBox = 0; + UI_SetNextSize(UI_Percent(1, 1), UI_Percent(1, 1)); + UI_Scroll(0, &Editor->Offset.y) + { + //- sixten: find the container box for the scrollable region + Editor->ContainerBox = UI_TopParent()->Parent->Parent; + + UI_SetNextSize(UI_Pixels(Editor->TextDim.x, 1), UI_Pixels(Editor->TextDim.y, 1)); + EditorBox = UI_MakeBoxF(UI_BoxFlag_DrawBackground|UI_BoxFlag_Clickable, "Workspace Text Editor %p", View); + EditorBox->DrawCallback = Workspace_TextEditorDrawCallback; + EditorBox->DrawCallbackData = Editor; + } + + b32 CursorHasBeenModified = false; + + //- sixten: keyboard input -> text op plus handling + if(Workspace_ViewIsCurrent(View)) + { + for(platform_event *Event = UI_EventList()->First; + Event != 0; + Event = Event->Next) + { + if(Event->Type == PlatformEvent_Press || Event->Type == PlatformEvent_Text) + { + text_action Action = MultiLineTextActionFromEvent(Event); + if(IsValid(&Action)) + { + text_op Op = TextOpFromAction(Scratch.Arena, Editor->Text.String, &Editor->EditState, &Action, &Editor->Lines, Editor->LastTextPoint.Column - 1); + + CursorHasBeenModified = true; + + { + MutableStringReplaceRange(&Editor->Text, Op.ReplaceString, Op.Range); + ArenaClear(Editor->ProcessingArena); + workspace_text_data TextData = Workspace_TextDataFromStringChunkList(Editor->ProcessingArena, Editor->Text.String); + Editor->Tokens = TextData.Tokens; + Editor->Lines = TextData.Lines; + } + + Editor->EditState.Cursor = Op.NewCursor; + Editor->EditState.Mark = Op.NewMark; + } + } + } + } + + ui_signal Signal = UI_SignalFromBox(EditorBox); + if(Signal.Dragging) + { + if(Signal.Pressed) + { + //- sixten: translate mouse position to text point + v2 MouseOffset = Signal.MouseP - EditorBox->Rect.Min - V2(LineMarginWidth, 0); + text_point Point = {(s64)(MouseOffset.y / LineHeight) + 1, (s64)(MouseOffset.x / GlyphAdvance) + 1}; + Editor->EditState.Cursor = Editor->EditState.Mark = OffsetFromTextPoint(Editor->Text.String, Editor->Lines, Point); + } + + //- sixten: translate mouse position to text point + v2 MouseOffset = Signal.MouseP - EditorBox->Rect.Min - V2(LineMarginWidth, 0); + text_point Point = {(s64)(MouseOffset.y / LineHeight) + 1, (s64)(MouseOffset.x / GlyphAdvance) + 1}; + Editor->EditState.Cursor = OffsetFromTextPoint(Editor->Text.String, Editor->Lines, Point); + + CursorHasBeenModified = true; + } + + //- sixten: update eventual text point extents + if(CursorHasBeenModified) + { + text_point Point = TextPointFromOffset(Editor->Text.String, Editor->EditState.Cursor); + if(Editor->LastTextPoint.Line == Point.Line) + { + Editor->LastTextPoint = Point; + } + else + { + Editor->LastTextPoint.Line = Point.Line; + Editor->LastTextPoint.Column = Max(Editor->LastTextPoint.Column, Point.Column); + } + } + + ReleaseScratch(Scratch); +} \ No newline at end of file diff --git a/code/vn_workspace_text_editor.h b/code/vn_workspace_text_editor.h new file mode 100644 index 0000000..65aabcd --- /dev/null +++ b/code/vn_workspace_text_editor.h @@ -0,0 +1,73 @@ +/* date = July 11th 2023 0:34 pm */ + +#ifndef VN_WORKSPACE_TEXT_EDITOR_H +#define VN_WORKSPACE_TEXT_EDITOR_H + +//////////////////////////////// +//~ sixten: Mutable String Types + +struct mutable_string +{ + memory_arena *Arena; + string String; +}; + +struct history_node +{ + range1_s64 Range; + string ReplaceString; + history_node *Next; +}; + +struct history_list +{ + memory_arena *HistoryArena; + + history_node *First; + history_node *Last; +}; + +//////////////////////////////// +//~ sixten: Workspace Text Editor Types + +struct workspace_text_data +{ + token_array Tokens; + range1_s64_array Lines; +}; + +struct workspace_view_text_editor +{ + //- sixten: processed text + memory_arena *ProcessingArena; + token_array Tokens; + range1_s64_array Lines; + + //- sixten: text being edited + mutable_string Text; + + //- sixten: text editing + text_edit_state EditState; + text_point LastTextPoint; + + //- sixten: ui building & rendering + ui_box *ContainerBox; + v2 TextDim; + v2 Offset; +}; + +//////////////////////////////// +//~ sixten: Mutable String Functions + +static mutable_string MutableStringAllocate(u64 Size); +static void MutableStringRelease(mutable_string *String); +static void MutableStringReplaceRange(mutable_string *String, string ReplaceString, range1_s64 Range); + +//////////////////////////////// +//~ sixten: Workspace Text Editor Functions + +static workspace_text_data Workspace_TextDataFromStringChunkList(memory_arena *Arena, string Text); +static UI_CUSTOM_DRAW_CALLBACK(Workspace_TextEditorDrawCallback); +static void Workspace_BuildTextEditor(workspace_view *View); + +#endif //VN_WORKSPACE_TEXT_EDITOR_H diff --git a/code/vn_workspace_view.cpp b/code/vn_workspace_view.cpp index 03479d0..fb95ec1 100644 --- a/code/vn_workspace_view.cpp +++ b/code/vn_workspace_view.cpp @@ -1,18 +1,27 @@ //- sixten: Views inline workspace_view *Workspace_CreateNewView(workspace_view_type Type, workspace_panel *Parent) { - workspace_view *View = BootstrapPushStruct(workspace_view, Arena); + memory_arena *Arena = ArenaAllocate(Gigabytes(1)); + workspace_view *View = PushStruct(Arena, workspace_view); + View->Arena = Arena; View->Type = Type; View->Parent = Parent; switch(View->Type) { case Workspace_View_Editor: - { View->Data = PushSize(&View->Arena, sizeof(workspace_view_editor)); } break; + { View->Data = PushStruct(View->Arena, workspace_view_editor); } break; case Workspace_View_CommandPalette: - { View->Data = PushSize(&View->Arena, sizeof(workspace_view_command_palette)); } break; + { View->Data = PushStruct(View->Arena, workspace_view_command_palette); } break; case Workspace_View_Settings: - { View->Data = PushSize(&View->Arena, sizeof(workspace_view_settings)); } break; + { View->Data = PushStruct(View->Arena, workspace_view_settings); } break; + case Workspace_View_TextEditor: + { + View->Data = PushStruct(View->Arena, workspace_view_text_editor); + workspace_view_text_editor *Editor = (workspace_view_text_editor *)View->Data; + Editor->ProcessingArena = ArenaAllocate(Gigabytes(1)); + Editor->Text = MutableStringAllocate(Gigabytes(2)); + } break; } DLLInsertLast(Parent->FirstView, Parent->LastView, View); @@ -28,11 +37,13 @@ inline workspace_view *Workspace_CreateNewView(workspace_view_type Type, workspa inline void Workspace_DestroyView(workspace_view *View) { // sixten(NOTE): This function does not ensure that the view is not being used anywhere else. - Release(&View->Arena); + ArenaRelease(View->Arena); } -inline b32 Workspace_ViewIsCurrent(workspace *Workspace, workspace_view *View) +inline b32 Workspace_ViewIsCurrent(workspace_view *View) { + workspace *Workspace = Workspace_GetState(); + b32 Result = (Workspace->CurrentPanel && Workspace->CurrentPanel->CurrentView == View); return(Result); } @@ -82,7 +93,7 @@ static void Workspace_ViewListerInputCallback(render_group *Group, glyph_atlas * v2 P = Box->Rect.Min + Offset; v4 Color = V4(0.4, 0.7, 0.8, 0.3); - PushQuad(Group, P, Dim, Color, 0, 0, 0); + PushQuad(Group, Range2R32(P, P+Dim), Color, 0, 0, 0); } // sixten: Draw cursor @@ -95,14 +106,16 @@ static void Workspace_ViewListerInputCallback(render_group *Group, glyph_atlas * v2 P = Box->Rect.Min + Offset; v4 Color = V4(0.3, 1, 0.3, 0.7); - PushQuad(Group, P, Dim, Color, 0, 0, 0); + PushQuad(Group, Range2R32(P, P+Dim), Color, 0, 0, 0); } } -static void Workspace_BuildViewTypeLister(workspace *Workspace, workspace_view *View) +static void Workspace_BuildViewTypeLister(workspace_view *View) { workspace_view_command_palette *CommandPalette = (workspace_view_command_palette *)View->Data; + workspace *Workspace = Workspace_GetState(); + temporary_memory Scratch = GetScratch(0, 0); UI_Size(UI_Percent(1, 1), UI_Percent(1, 1)) @@ -333,10 +346,12 @@ static b32 UI_DropdownSelection(char **Alternatives, s32 AlternativeCount, b32 * return(Result); } -static void Workspace_BuildSettings(workspace *Workspace, workspace_view *View) +static void Workspace_BuildSettings(workspace_view *View) { workspace_view_settings *Settings = (workspace_view_settings *)View->Data; + workspace *Workspace = Workspace_GetState(); + UI_Height(UI_ChildrenSum(1, 1)) UI_Column() UI_Padding(UI_Pixels(50, 0)) UI_Row() UI_Padding(UI_Pixels(50, 0)) @@ -447,10 +462,10 @@ static void Workspace_BuildSettings(workspace *Workspace, workspace_view *View) UI_Spacer(UI_Pixels(50, 1)); } -static void Workspace_BuildView(workspace *Workspace, workspace_view *View) +static void Workspace_BuildView(workspace_view *View) { r32 ViewHighlightTransition = - AnimationCurve_AnimateValueF(Workspace_ViewIsCurrent(Workspace, View), 0, 0.25, "Workspace View Highlight %p", View); + AnimationCurve_AnimateValueF(Workspace_ViewIsCurrent(View), 0, 0.25, "Workspace View Highlight %p", View); UI_SetNextBorderColor(LinearBlend(Theme_BorderColor, Theme_HighlightBorderColor, ViewHighlightTransition)); UI_PushBackgroundColor(Theme_BackgroundColor); UI_SetNextCornerRadius(3); @@ -474,20 +489,28 @@ static void Workspace_BuildView(workspace *Workspace, workspace_view *View) UI_Font(Font_Bold) UI_FontSize(36) UI_LabelF("Welcome to VN"); UI_TextColor(Theme_BorderColor) UI_LabelF("An impractical way to make a game"); + + UI_Spacer(UI_Percent(1, 0)); + + UI_Checkbox(&DEBUG_DebugSettings->ShowWelcomeMessage, StrLit("Show this message on startup")); } } } else if(View->Type == Workspace_View_CommandPalette) { - Workspace_BuildViewTypeLister(Workspace, View); + Workspace_BuildViewTypeLister(View); } else if(View->Type == Workspace_View_Editor) { - Workspace_BuildEditor(Workspace, View); + Workspace_BuildEditor(View); } else if(View->Type == Workspace_View_Settings) { - Workspace_BuildSettings(Workspace, View); + Workspace_BuildSettings(View); + } + else if(View->Type == Workspace_View_TextEditor) + { + Workspace_BuildTextEditor(View); } } diff --git a/code/vn_workspace_view.h b/code/vn_workspace_view.h index a90eef0..b5eb8e0 100644 --- a/code/vn_workspace_view.h +++ b/code/vn_workspace_view.h @@ -5,7 +5,7 @@ struct workspace_view { - memory_arena Arena; + memory_arena *Arena; enum workspace_view_type Type; workspace_panel *Parent; @@ -21,6 +21,7 @@ enum workspace_view_type Workspace_View_Editor, Workspace_View_CommandPalette, Workspace_View_Settings, + Workspace_View_TextEditor, }; struct workspace_view_editor @@ -32,6 +33,8 @@ struct workspace_view_editor workspace_editor_node *LastNode; workspace_editor_node *FirstFreeNode; workspace_editor_node *LastFreeNode; + + workspace_editor_lister_dropdown ListerDropdown; }; struct workspace_view_command_palette @@ -64,12 +67,12 @@ struct workspace_view_settings //- sixten: Views inline workspace_view *Workspace_CreateNewView(workspace_view_type Type, workspace_panel *Parent); inline void Workspace_DestroyView(workspace_view *View); -inline b32 Workspace_ViewIsCurrent(workspace *Workspace, workspace_view *View); +inline b32 Workspace_ViewIsCurrent(workspace_view *View); inline string Workspace_GetViewName(workspace_view *View); //- sixten: Builder code static void Workspace_ViewListerInputCallback(render_group *Group, glyph_atlas *Atlas, ui_box *Box, void *Data); -static void Workspace_BuildViewTypeLister(workspace *Workspace, workspace_view *View); -static void Workspace_BuildView(workspace *Workspace, workspace_view *View); +static void Workspace_BuildViewTypeLister(workspace_view *View); +static void Workspace_BuildView(workspace_view *View); #endif //VN_WORKSPACE_VIEW_H diff --git a/code/win32__main.rdbg b/code/win32__main.rdbg new file mode 100644 index 0000000..dab87b4 Binary files /dev/null and b/code/win32__main.rdbg differ diff --git a/code/win32_main.cpp b/code/win32_main.cpp index 056b1fc..456e955 100644 --- a/code/win32_main.cpp +++ b/code/win32_main.cpp @@ -45,48 +45,36 @@ static PLATFORM_SHOW_MESSAGE(Win32_ShowMessage) } } -static PLATFORM_ALLOCATE_MEMORY(Win32_AllocateMemory) +static u64 Win32_GetPageSize(void) { - win32_state *State = &Global_Win32State; - - umm TotalSize = Size + sizeof(win32_memory_block); - - win32_memory_block *Block = - (win32_memory_block *)VirtualAlloc(0, TotalSize, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); - - Assert(Block); - Block->Block.Base = (u8 *)Block + sizeof(win32_memory_block); - Block->Block.Size = Size; - - win32_memory_block *Sentinel = &State->MemorySentinel; - Block->Next = Sentinel; - - BeginTicketMutex(&State->MemoryMutex); - Block->Prev = Sentinel->Prev; - Block->Prev->Next = Block; - Block->Next->Prev = Block; - EndTicketMutex(&State->MemoryMutex); - - platform_memory_block *Result = &Block->Block; + SYSTEM_INFO Info; + GetSystemInfo(&Info); + return(Info.dwPageSize); +} + +static PLATFORM_RESERVE(Win32_Reserve) +{ + u64 GigabyteAlignedSize = Size+Gigabytes(1)-1; + GigabyteAlignedSize -= GigabyteAlignedSize%Gigabytes(1); + void *Result = VirtualAlloc(0, GigabyteAlignedSize, MEM_RESERVE, PAGE_NOACCESS); return(Result); } -static PLATFORM_DEALLOCATE_MEMORY(Win32_DeallocateMemory) +static PLATFORM_RELEASE(Win32_Release) { - win32_state *State = &Global_Win32State; - - win32_memory_block *Win32Block = (win32_memory_block *)Block; - - if(Block) - { - BeginTicketMutex(&State->MemoryMutex); - Win32Block->Prev->Next = Win32Block->Next; - Win32Block->Next->Prev = Win32Block->Prev; - EndTicketMutex(&State->MemoryMutex); - } - - BOOL Result = VirtualFree(Block, 0, MEM_RELEASE); - return(Result); + VirtualFree(Pointer, 0, MEM_RELEASE); +} + +static PLATFORM_COMMIT(Win32_Commit) +{ + u64 PageAlignedSize = Size+Win32_GetPageSize()-1; + PageAlignedSize -= PageAlignedSize%Win32_GetPageSize(); + VirtualAlloc(Pointer, PageAlignedSize, MEM_COMMIT, PAGE_READWRITE); +} + +static PLATFORM_DECOMMIT(Win32_Decommit) +{ + VirtualFree(Pointer, Size, MEM_DECOMMIT); } static PLATFORM_OPEN_FILE(Win32_OpenFile) @@ -257,6 +245,9 @@ static void Win32_UpdateCode(win32_loaded_code *Code) { Win32_UnloadCode(Code); *Code = Win32_LoadCode(); + + // sixten(NOTE): Sometimes the program decides to crash upon reloads, so we just wait for those to be over... + Sleep(200); } } @@ -271,7 +262,7 @@ static PLATFORM_TOGGLE_FULLSCREEN(Win32_ToggleFullscreen) if(GetWindowPlacement(Window, &Global_WindowPosition) && GetMonitorInfo(MonitorFromWindow(Window, MONITOR_DEFAULTTOPRIMARY), &MonitorInfo)) { - // sixten: This doesn't work when the window is maximized. One wordaround would be to set the + // sixten(NOTE): This doesn't work when the window is maximized. One wordaround would be to set the // window to "normal" size using ShowWindow(Window, SW_SHOWNORMAL) but it looks *very* scuffed. SetWindowLong(Window, GWL_STYLE, Style & ~WS_OVERLAPPEDWINDOW); SetWindowPos(Window, HWND_TOP, @@ -346,7 +337,7 @@ static LRESULT Win32_WindowCallback(HWND Window, UINT Message, WPARAM WParam, LP { case WM_CLOSE: { - Event = PushStruct(&State->EventArena, platform_event); + Event = PushStruct(State->EventArena, platform_event); Event->Type = PlatformEvent_WindowClose; } break; @@ -391,7 +382,7 @@ static LRESULT Win32_WindowCallback(HWND Window, UINT Message, WPARAM WParam, LP } fallthrough; case WM_MOUSEWHEEL: { - Event = PushStruct(&State->EventArena, platform_event); + Event = PushStruct(State->EventArena, platform_event); Event->Type = PlatformEvent_MouseScroll; Event->Scroll.E[ScrollAxis] = GET_WHEEL_DELTA_WPARAM(WParam) / 120.0; } break; @@ -417,7 +408,7 @@ static LRESULT Win32_WindowCallback(HWND Window, UINT Message, WPARAM WParam, LP case WM_RBUTTONUP: case WM_RBUTTONDOWN: { Key = Key_MouseRight; } break; } - Event = PushStruct(&State->EventArena, platform_event); + Event = PushStruct(State->EventArena, platform_event); Event->Type = Type; Event->Key = Key; Event->P = Win32_GetMouseP(Window); @@ -458,7 +449,7 @@ static LRESULT Win32_WindowCallback(HWND Window, UINT Message, WPARAM WParam, LP if(Key != Key_Invalid) { - Event = PushStruct(&State->EventArena, platform_event); + Event = PushStruct(State->EventArena, platform_event); Event->Type = Type; Event->Key = Key; } @@ -484,7 +475,7 @@ static LRESULT Win32_WindowCallback(HWND Window, UINT Message, WPARAM WParam, LP if((Codepoint >= 32 && Codepoint != 127) || Codepoint == '\t' || Codepoint == '\n') { - Event = PushStruct(&State->EventArena, platform_event); + Event = PushStruct(State->EventArena, platform_event); Event->Type = PlatformEvent_Text; Event->Codepoint = Codepoint; } @@ -551,7 +542,7 @@ static void Win32_ProcessInput(vn_input *Input, HWND Window, r32 dtForFrame) EndTemporaryMemory(State->EventArenaTemp); } - State->EventArenaTemp = BeginTemporaryMemory(&State->EventArena); + State->EventArenaTemp = BeginTemporaryMemory(State->EventArena); } MSG Message; @@ -634,7 +625,9 @@ inline void Win32_GetRelevantPaths(win32_state *State) int WinMain(HINSTANCE Instance, HINSTANCE PreviousInstance, LPSTR CommandLine, int ShowCommand) { - thread_context ThreadContext = {}; + RegisterPlatformFunctions(Win32); + + thread_context ThreadContext = AllocateThreadContext(); SetThreadContext(&ThreadContext); // sixten: Setup Win32 platform state. @@ -646,26 +639,11 @@ int WinMain(HINSTANCE Instance, HINSTANCE PreviousInstance, LPSTR CommandLine, i QueryPerformanceFrequency(&FrequencyQuery); State->PerformanceFrequency = FrequencyQuery.QuadPart; - State->MemorySentinel.Next = &State->MemorySentinel; - State->MemorySentinel.Prev = &State->MemorySentinel; + State->EventArena = ArenaAllocate(Gigabytes(1)); State->SleepIsGranular = (timeBeginPeriod(1) == TIMERR_NOERROR); } - // sixten: Setup platform layer - { - Platform.AllocateMemory = Win32_AllocateMemory; - Platform.DeallocateMemory = Win32_DeallocateMemory; - Platform.OpenFile = Win32_OpenFile; - Platform.CloseFile = Win32_CloseFile; - Platform.ReadFile = Win32_ReadFile; - Platform.WriteFile = Win32_WriteFile; - Platform.GetFileSize = Win32_GetFileSize; - Platform.SetCursor = Win32_SetCursor; - Platform.ToggleFullscreen = Win32_ToggleFullscreen; - Platform.ShowMessage = Win32_ShowMessage; - } - WNDCLASS WindowClass = {}; WindowClass.lpszClassName = "vn-window-class"; WindowClass.lpfnWndProc = Win32_WindowCallback; @@ -676,7 +654,7 @@ int WinMain(HINSTANCE Instance, HINSTANCE PreviousInstance, LPSTR CommandLine, i { HWND Window = CreateWindowEx(0, WindowClass.lpszClassName, - "vn - June 2023 Build", + "vn - July 2023 Build", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, diff --git a/code/win32_main.h b/code/win32_main.h index 7e8397b..4fdd9a2 100644 --- a/code/win32_main.h +++ b/code/win32_main.h @@ -3,46 +3,33 @@ #ifndef WIN32_MAIN_H #define WIN32_MAIN_H -struct win32_memory_block -{ - platform_memory_block Block; - win32_memory_block *Next; - win32_memory_block *Prev; - u64 Padding[2]; -}; - -CTAssert(sizeof(win32_memory_block) == 64); - struct win32_state { - win32_memory_block MemorySentinel; - ticket_mutex MemoryMutex; - - u64 PerformanceFrequency; - b32 SleepIsGranular; - - HWND Window; - - memory_arena EventArena; - temporary_memory EventArenaTemp; - platform_event_list EventList; - - char EXEPath[512]; - char DLLPath[512]; - char TempDLLPath[512]; - string ContentsPath; - - platform_cursor Cursor; + u64 PerformanceFrequency; + b32 SleepIsGranular; + + HWND Window; + + memory_arena *EventArena; + temporary_memory EventArenaTemp; + platform_event_list EventList; + + char EXEPath[512]; + char DLLPath[512]; + char TempDLLPath[512]; + string ContentsPath; + + platform_cursor Cursor; }; struct win32_loaded_code { - HMODULE DLL; - FILETIME LastWriteTime; - - vn_update_and_render *UpdateAndRender; - - b32 IsValid; + HMODULE DLL; + FILETIME LastWriteTime; + + vn_update_and_render *UpdateAndRender; + + b32 IsValid; }; #endif //WIN32_MAIN_H diff --git a/config.vn b/config.vn index f6daacd..05d0675 100644 --- a/config.vn +++ b/config.vn @@ -7,5 +7,6 @@ Dev { RenderUIDebugRects = false; RenderFPSCounter = false; - ListHotAndActive = true; + ListHotAndActive = false; + ShowWelcomeMessage = false; } \ No newline at end of file diff --git a/data/backgrounds/Apartment_Exterior.png b/data/backgrounds/Apartment_Exterior.png new file mode 100644 index 0000000..d3d57df Binary files /dev/null and b/data/backgrounds/Apartment_Exterior.png differ diff --git a/data/backgrounds/Apartment_Exterior_Night.png b/data/backgrounds/Apartment_Exterior_Night.png new file mode 100644 index 0000000..0affdab Binary files /dev/null and b/data/backgrounds/Apartment_Exterior_Night.png differ diff --git a/data/backgrounds/Bathroom.png b/data/backgrounds/Bathroom.png new file mode 100644 index 0000000..811ae65 Binary files /dev/null and b/data/backgrounds/Bathroom.png differ diff --git a/data/backgrounds/Bathroom_Foggy.png b/data/backgrounds/Bathroom_Foggy.png new file mode 100644 index 0000000..194053a Binary files /dev/null and b/data/backgrounds/Bathroom_Foggy.png differ diff --git a/data/backgrounds/Futon_Room.png b/data/backgrounds/Futon_Room.png new file mode 100644 index 0000000..c81aa3c Binary files /dev/null and b/data/backgrounds/Futon_Room.png differ diff --git a/data/backgrounds/Futon_Room_Night.png b/data/backgrounds/Futon_Room_Night.png new file mode 100644 index 0000000..50f9b5d Binary files /dev/null and b/data/backgrounds/Futon_Room_Night.png differ diff --git a/data/backgrounds/Laundromat.png b/data/backgrounds/Laundromat.png new file mode 100644 index 0000000..7f02453 Binary files /dev/null and b/data/backgrounds/Laundromat.png differ diff --git a/data/backgrounds/Onsen_Building.png b/data/backgrounds/Onsen_Building.png new file mode 100644 index 0000000..9e13b49 Binary files /dev/null and b/data/backgrounds/Onsen_Building.png differ diff --git a/data/backgrounds/Onsen_Building_Night.png b/data/backgrounds/Onsen_Building_Night.png new file mode 100644 index 0000000..3da073e Binary files /dev/null and b/data/backgrounds/Onsen_Building_Night.png differ diff --git a/data/backgrounds/Outdoor_Stairs.png b/data/backgrounds/Outdoor_Stairs.png new file mode 100644 index 0000000..21fcf47 Binary files /dev/null and b/data/backgrounds/Outdoor_Stairs.png differ diff --git a/data/backgrounds/ReadMe.txt b/data/backgrounds/ReadMe.txt new file mode 100644 index 0000000..1b0f115 --- /dev/null +++ b/data/backgrounds/ReadMe.txt @@ -0,0 +1,22 @@ +RULES +-Credit "Noraneko Games" +-For games: credit can be given in the in-game credits, in a note accompanying the game, or on a website where the project is downloaded. Any one of those is fine. It doesn't have to be all of them. +-For Art: Credit must accompany the picture where it is posted or on the image itself. +-For Vtubing/video: Credit can be given in the description or in a panel of your streaming platform or on the video itself. Any of those are fine. +-If this pack is re-uploaded to another site for download as is, do not remove this text file or any part of it. +-These assets cannot be sold as is. For merchandising options, contact me and we can discuss options. +-Modifications are allowed. (Change colors, add blood splatters, draw characters in the image, etc) +-Using for 18+ is fine as long as it doesn't contain illegal themes/content (such as homophobia, racism, threats to real people, or pedophilia) that could reflect poorly on the Noraneko Games name and reputation. +-Commercial use in a game is ok if you can provide proof of credit if I contact you for it and it follows all other rules. Contact me for commercial use outside of games. +-If you aren't making money from your project, you can use without contacting me if you'd prefer. +-Using this asset for Game Jams and Contests is okay! +-If you want to use this media for anything other than a game, make sure it follows the rules above. + + +Find more of my work at: +@NoranekoGames on Twitter +Noranekokgames.itch.io + +Not required, but I would love for you to link me to your project! + +Good luck with your project! \ No newline at end of file diff --git a/data/backgrounds/Restaurant_A.png b/data/backgrounds/Restaurant_A.png new file mode 100644 index 0000000..b418dcb Binary files /dev/null and b/data/backgrounds/Restaurant_A.png differ diff --git a/data/backgrounds/Restaurant_B.png b/data/backgrounds/Restaurant_B.png new file mode 100644 index 0000000..a2b070b Binary files /dev/null and b/data/backgrounds/Restaurant_B.png differ diff --git a/data/backgrounds/Sitting_Room.png b/data/backgrounds/Sitting_Room.png new file mode 100644 index 0000000..41d4336 Binary files /dev/null and b/data/backgrounds/Sitting_Room.png differ diff --git a/data/backgrounds/Sitting_Room_Dark.png b/data/backgrounds/Sitting_Room_Dark.png new file mode 100644 index 0000000..4ec3e27 Binary files /dev/null and b/data/backgrounds/Sitting_Room_Dark.png differ diff --git a/data/backgrounds/Small_Apartment_Kitchen.png b/data/backgrounds/Small_Apartment_Kitchen.png new file mode 100644 index 0000000..34356c1 Binary files /dev/null and b/data/backgrounds/Small_Apartment_Kitchen.png differ diff --git a/data/backgrounds/Small_Apartment_Kitchen_Night.png b/data/backgrounds/Small_Apartment_Kitchen_Night.png new file mode 100644 index 0000000..b0397e1 Binary files /dev/null and b/data/backgrounds/Small_Apartment_Kitchen_Night.png differ diff --git a/data/backgrounds/Thumbs.db b/data/backgrounds/Thumbs.db new file mode 100644 index 0000000..7faeca6 Binary files /dev/null and b/data/backgrounds/Thumbs.db differ diff --git a/data/backgrounds/Train_Day.png b/data/backgrounds/Train_Day.png new file mode 100644 index 0000000..d3af4e0 Binary files /dev/null and b/data/backgrounds/Train_Day.png differ diff --git a/data/backgrounds/Train_Day_Rain.png b/data/backgrounds/Train_Day_Rain.png new file mode 100644 index 0000000..33ba1d9 Binary files /dev/null and b/data/backgrounds/Train_Day_Rain.png differ diff --git a/data/backgrounds/Train_Evening.png b/data/backgrounds/Train_Evening.png new file mode 100644 index 0000000..a90d564 Binary files /dev/null and b/data/backgrounds/Train_Evening.png differ diff --git a/data/backgrounds/Train_Night.png b/data/backgrounds/Train_Night.png new file mode 100644 index 0000000..fa57ac9 Binary files /dev/null and b/data/backgrounds/Train_Night.png differ diff --git a/data/backgrounds/Train_Night_Rain.png b/data/backgrounds/Train_Night_Rain.png new file mode 100644 index 0000000..4600d19 Binary files /dev/null and b/data/backgrounds/Train_Night_Rain.png differ diff --git a/data/backgrounds/Train_Transparent.png b/data/backgrounds/Train_Transparent.png new file mode 100644 index 0000000..1d8705b Binary files /dev/null and b/data/backgrounds/Train_Transparent.png differ diff --git a/data/backgrounds/Train_Tunnel.png b/data/backgrounds/Train_Tunnel.png new file mode 100644 index 0000000..df881ff Binary files /dev/null and b/data/backgrounds/Train_Tunnel.png differ diff --git a/data/backgrounds/Train_beach.png b/data/backgrounds/Train_beach.png new file mode 100644 index 0000000..c398698 Binary files /dev/null and b/data/backgrounds/Train_beach.png differ diff --git a/data/test.vns b/data/test.vns new file mode 100644 index 0000000..4086dd9 --- /dev/null +++ b/data/test.vns @@ -0,0 +1,41 @@ +// This just experiments with the scripting language + +var times = 0 + +proc "Start" +{ + "so, I actually changed my mind." + "the editor will not be node based" + "I realised that it would just be slower to write dialog that way soooo..." + "instead, I present to you the.........." + "vn scene - scripting language" + + "btw something happens if you go through this dialog 10 times" + + times += 1 + + branch + { + "Return to start" + { + jump "Start" + } + + if(times >= 10) + { + "SUPER EPIC SECRET" + { + jump "Epic Scene" + } + } + } +} + +proc "Epic Scene" +{ + "woah... so epic" + @s "oh, right. almost forgot to mention that you can talk as different characters." + @s "you know... " + wait + @s #noclear "the usual" +} \ No newline at end of file diff --git a/fonts/DejaVuSansMono.ttf b/fonts/DejaVuSansMono.ttf new file mode 100644 index 0000000..f578602 Binary files /dev/null and b/fonts/DejaVuSansMono.ttf differ