Added new string template formatter
- Based on a template formatter I have in used/evolved in other projects. - Will eventually replace the current token formatter used in untyped code. - Has some x86 simd support.. (opt-in if avail).
This commit is contained in:
1 parent
8ba02de6ed
commit
b5b7b9ab4b
17 files changed
+1771
-14
No files matched your search
@@ -57,6 +57,11 @@
|
||||
#define bitfield_is_set( Type, Field, Mask ) ( (scast(Type, Mask) & scast(Type, Field)) == scast(Type, Mask) )
|
||||
#endif
|
||||
|
||||
#ifndef GEN_ASSERT_SLICE
|
||||
#define GEN_ASSERT_SLICE( slice ) \
|
||||
GEN_ASSERT( ((slice).Len >= 0) & (((slice).Len == 0) | ((slice).Ptr != nullptr)) )
|
||||
#endif
|
||||
|
||||
// Mainly intended for forcing the base library to utilize only C-valid constructs or type coercion
|
||||
#ifndef GEN_C_LIKE_CPP
|
||||
#define GEN_C_LIKE_CPP 0
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
#ifdef INTELLISENSE_DIRECTIVES
|
||||
#pragma once
|
||||
#include "debug.hpp"
|
||||
#endif
|
||||
|
||||
#pragma region SIMD
|
||||
|
||||
// Lane names use bit widths: sixteen unsigned 8-bit lanes or four unsigned 32-bit lanes.
|
||||
// Vector operations are available on Windows x64.
|
||||
#if defined(GEN_SYSTEM_WINDOWS) && (defined(_M_X64) || defined(__x86_64__))
|
||||
typedef __m128i u8x16;
|
||||
typedef __m128i u32x4;
|
||||
|
||||
// Unaligned loads require 16 or 8 readable bytes respectively. load8 zeros lanes 8 through 15.
|
||||
internal forceinline u8x16 u8x16_load (void const* address) { return _mm_loadu_si128(rcast(__m128i const*, address)); }
|
||||
internal forceinline u8x16 u8x16_load8(void const* address) { return _mm_loadl_epi64(rcast(__m128i const*, address)); }
|
||||
|
||||
internal forceinline u8x16 u8x16_splat(u8 value) { return _mm_set1_epi8(scast(char, value)); }
|
||||
|
||||
internal forceinline u8x16 u8x16_equal(u8x16 lhs, u8x16 rhs) { return _mm_cmpeq_epi8(lhs, rhs); }
|
||||
|
||||
// Extract each lane's high bit. Bit 0 represents lane 0 at the lowest address.
|
||||
internal forceinline u16 u8x16_mask(u8x16 value) { return scast(u16, _mm_movemask_epi8(value)); }
|
||||
|
||||
// Reads 16 bytes without requiring alignment.
|
||||
internal forceinline u32x4 u32x4_load(void const* address) { return _mm_loadu_si128(rcast(__m128i const*, address)); }
|
||||
|
||||
internal forceinline u32x4 u32x4_splat(u32 value) { return _mm_set1_epi32(scast(s32, value)); }
|
||||
|
||||
internal forceinline u32x4 u32x4_equal(u32x4 lhs, u32x4 rhs) { return _mm_cmpeq_epi32(lhs, rhs); }
|
||||
|
||||
// Extract each lane's high bit into bits 0 through 3. Higher result bits are zero.
|
||||
internal forceinline u8 u32x4_mask(u32x4 value) { return scast(u8, _mm_movemask_ps(_mm_castsi128_ps(value))); }
|
||||
#endif
|
||||
|
||||
// Requires a nonzero mask. Returns the lowest set-bit index, from 0 through 31.
|
||||
s32 u32_mask_first_set(u32 mask);
|
||||
|
||||
#if defined(GEN_SYSTEM_WINDOWS)
|
||||
internal forceinline
|
||||
s32 u32_mask_first_set(u32 mask) {
|
||||
GEN_ASSERT(mask != 0);
|
||||
unsigned long bit_index = 0;
|
||||
_BitScanForward(& bit_index, scast(unsigned long, mask));
|
||||
return scast(s32, bit_index);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ! defined(GEN_SYSTEM_WINDOWS)
|
||||
internal forceinline
|
||||
s32 u32_mask_first_set(u32 mask) {
|
||||
GEN_ASSERT(mask != 0);
|
||||
s32 bit = 0;
|
||||
u32 bits = mask;
|
||||
while ((bits & 1u) == 0u) {
|
||||
bits >>= 1;
|
||||
bit += 1;
|
||||
}
|
||||
return bit;
|
||||
}
|
||||
#endif
|
||||
|
||||
#pragma endregion SIMD
|
||||
@@ -1,5 +1,6 @@
|
||||
#ifdef INTELLISENSE_DIRECTIVES
|
||||
# pragma once
|
||||
# include "simd.hpp"
|
||||
# include "hashing.cpp"
|
||||
#endif
|
||||
|
||||
@@ -174,4 +175,153 @@ StrBuilder strbuilder_visualize_whitespace(StrBuilder const str)
|
||||
return result;
|
||||
}
|
||||
|
||||
#if defined(GEN_SYSTEM_WINDOWS) && (defined(_M_X64) || defined(__x86_64__))
|
||||
internal
|
||||
ssize strbuilder_find_template_byte(char const* text, ssize length, char needle)
|
||||
{
|
||||
ssize idx = 0;
|
||||
for (; (length - idx) >= 16; )
|
||||
{
|
||||
char const* at = text + idx;
|
||||
u8x16 chunk = u8x16_load(at);
|
||||
u8x16 splat = u8x16_splat(scast(u8, needle));
|
||||
u16 mask = u8x16_mask(u8x16_equal(chunk, splat));
|
||||
if (mask != 0) {
|
||||
s32 bit_index = u32_mask_first_set(mask);
|
||||
return idx + scast(ssize, bit_index);
|
||||
}
|
||||
idx += 16;
|
||||
}
|
||||
for (; (length - idx) >= 8; )
|
||||
{
|
||||
char const* at = text + idx;
|
||||
u8x16 chunk = u8x16_load8(at);
|
||||
u8x16 splat = u8x16_splat(scast(u8, needle));
|
||||
u16 mask = u8x16_mask(u8x16_equal(chunk, splat)) & 0xFF;
|
||||
if (mask != 0) {
|
||||
s32 bit_index = u32_mask_first_set(mask);
|
||||
return idx + scast(ssize, bit_index);
|
||||
}
|
||||
idx += 8;
|
||||
}
|
||||
for (; idx < length; ++ idx) {
|
||||
if (text[idx] == needle) return idx;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
#else
|
||||
internal
|
||||
ssize strbuilder_find_template_byte(char const* text, ssize length, char needle) {
|
||||
for (ssize idx = 0; idx < length; ++ idx)
|
||||
{
|
||||
if (text[idx] == needle)
|
||||
return idx;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(GEN_SYSTEM_WINDOWS) && (defined(_M_X64) || defined(__x86_64__))
|
||||
internal
|
||||
StrTemplateSlot* strbuilder_find_template_slot(Slice_StrTemplateSlot slots, Str key)
|
||||
{
|
||||
u32 key_hash = crc32(key.Ptr, key.Len);
|
||||
ssize idx = 0;
|
||||
for (; (slots.Len - idx) >= 4; idx += 4)
|
||||
{
|
||||
u32 packed[4];
|
||||
packed[0] = crc32(slots.Ptr[idx + 0].Key.Ptr, slots.Ptr[idx + 0].Key.Len);
|
||||
packed[1] = crc32(slots.Ptr[idx + 1].Key.Ptr, slots.Ptr[idx + 1].Key.Len);
|
||||
packed[2] = crc32(slots.Ptr[idx + 2].Key.Ptr, slots.Ptr[idx + 2].Key.Len);
|
||||
packed[3] = crc32(slots.Ptr[idx + 3].Key.Ptr, slots.Ptr[idx + 3].Key.Len);
|
||||
|
||||
u32x4 packed_vec = u32x4_load(packed);
|
||||
u32x4 want_vec = u32x4_splat(key_hash);
|
||||
u8 mask = u32x4_mask(u32x4_equal(packed_vec, want_vec));
|
||||
|
||||
for (s32 lane = 0; lane < 4; ++ lane)
|
||||
{
|
||||
if ((mask & (1u << lane)) != 0) {
|
||||
StrTemplateSlot* slot = & slots.Ptr[idx + lane];
|
||||
if (str_are_equal(slot[0].Key, key)) return slot;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (; idx < slots.Len; ++ idx)
|
||||
{
|
||||
StrTemplateSlot* slot = & slots.Ptr[idx];
|
||||
u32 slot_hash = crc32(slot[0].Key.Ptr, slot[0].Key.Len);
|
||||
if (slot_hash == key_hash) {
|
||||
if (str_are_equal(slot[0].Key, key)) return slot;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
#else
|
||||
internal
|
||||
StrTemplateSlot* strbuilder_find_template_slot(Slice_StrTemplateSlot slots, Str key) {
|
||||
u32 key_hash = crc32(key.Ptr, key.Len);
|
||||
for (ssize idx = 0; idx < slots.Len; ++ idx)
|
||||
{
|
||||
StrTemplateSlot* slot = & slots.Ptr[idx];
|
||||
u32 slot_hash = crc32(slot[0].Key.Ptr, slot[0].Key.Len);
|
||||
if (slot_hash == key_hash && str_are_equal(slot[0].Key, key))
|
||||
return slot;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool strbuilder_append_template(StrBuilder* output, Slice_StrTemplateSlot slots, Str format)
|
||||
{
|
||||
GEN_ASSERT(output != nullptr && output[0] != nullptr);
|
||||
StrBuilderHeader* header = strbuilder_get_header(output[0]);
|
||||
GEN_ASSERT((header[0].Length >= 0) & (header[0].Capacity >= header[0].Length));
|
||||
GEN_ASSERT_SLICE(format);
|
||||
GEN_ASSERT_SLICE(slots);
|
||||
for (ssize slot_idx = 0; slot_idx < slots.Len; ++ slot_idx) {
|
||||
GEN_ASSERT_SLICE(slots.Ptr[slot_idx].Key);
|
||||
GEN_ASSERT_SLICE(slots.Ptr[slot_idx].Value);
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
if (format.Len == 0) {
|
||||
result = true;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
for (ssize cursor = 0; cursor < format.Len;)
|
||||
{
|
||||
if (format.Ptr[cursor] == '<')
|
||||
{
|
||||
ssize key_at = cursor + 1;
|
||||
ssize key_max = format.Len - key_at;
|
||||
ssize key_len = strbuilder_find_template_byte(format.Ptr + key_at, key_max, '>');
|
||||
if (key_len == key_max) goto finish;
|
||||
|
||||
Str key = { format.Ptr + key_at, key_len };
|
||||
StrTemplateSlot* slot = strbuilder_find_template_slot(slots, key);
|
||||
if (slot == nullptr) goto finish;
|
||||
|
||||
if (slot[0].Value.Len > 0) {
|
||||
bool appended = strbuilder_append_c_str_len(output, slot[0].Value.Ptr, slot[0].Value.Len);
|
||||
if (appended == false) goto finish;
|
||||
}
|
||||
cursor = key_at + key_len + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
ssize literal_max = format.Len - cursor;
|
||||
ssize literal_len = strbuilder_find_template_byte(format.Ptr + cursor, literal_max, '<');
|
||||
if (literal_len > 0) {
|
||||
bool appended = strbuilder_append_c_str_len(output, format.Ptr + cursor, literal_len);
|
||||
if (appended == false) goto finish;
|
||||
}
|
||||
cursor += literal_len;
|
||||
}
|
||||
result = true;
|
||||
finish:
|
||||
return result;
|
||||
}
|
||||
|
||||
#pragma endregion StrBuilder
|
||||
@@ -113,6 +113,8 @@ Str to_str_from_c_str( char const* bad_str ) {
|
||||
// I kept it for simplicty of porting but its not necessary to keep it that way.
|
||||
#pragma region StrBuilder
|
||||
struct StrBuilderHeader;
|
||||
struct StrTemplateSlot;
|
||||
struct Slice_StrTemplateSlot;
|
||||
|
||||
#if GEN_COMPILER_C
|
||||
typedef char* StrBuilder;
|
||||
@@ -122,12 +124,39 @@ struct StrBuilder;
|
||||
|
||||
forceinline usize strbuilder_grow_formula(usize value);
|
||||
|
||||
GEN_API StrBuilder strbuilder_make_reserve (AllocatorInfo allocator, ssize capacity);
|
||||
GEN_API StrBuilder strbuilder_make_length (AllocatorInfo allocator, char const* str, ssize length);
|
||||
GEN_API bool strbuilder_make_space_for (StrBuilder* str, char const* to_append, ssize add_len);
|
||||
GEN_API bool strbuilder_append_c_str_len (StrBuilder* str, char const* c_str_to_append, ssize length);
|
||||
GEN_API void strbuilder_trim (StrBuilder str, char const* cut_set);
|
||||
GEN_API StrBuilder strbuilder_visualize_whitespace(StrBuilder const str);
|
||||
GEN_API StrBuilder strbuilder_make_reserve (AllocatorInfo allocator, ssize capacity);
|
||||
GEN_API StrBuilder strbuilder_make_length (AllocatorInfo allocator, char const* str, ssize length);
|
||||
GEN_API bool strbuilder_make_space_for (StrBuilder* str, char const* to_append, ssize add_len);
|
||||
GEN_API bool strbuilder_append_c_str_len(StrBuilder* str, char const* c_str_to_append, ssize length);
|
||||
|
||||
struct StrTemplateSlot { Str Key; Str Value; };
|
||||
struct Slice_StrTemplateSlot { StrTemplateSlot* Ptr; ssize Len; };
|
||||
|
||||
// format and slot slices must not alias output. output may reallocate.
|
||||
GEN_API bool strbuilder_append_template(StrBuilder* output, Slice_StrTemplateSlot slots, Str format);
|
||||
|
||||
#ifndef strtmpl_slot
|
||||
# if GEN_COMPILER_C
|
||||
# define strtmpl_slot(key, value) (GEN_NS StrTemplateSlot){ txt(key), (value) }
|
||||
# else
|
||||
# define strtmpl_slot(key, value) GEN_NS StrTemplateSlot{ txt(key), (value) }
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef strtmpl_slots
|
||||
# define strtmpl_slots( ... ) __VA_ARGS__
|
||||
#endif
|
||||
|
||||
#ifndef strtmpl_append
|
||||
#define strtmpl_append( output, result, entries, format ) do { \
|
||||
GEN_NS StrTemplateSlot gencpp_tmpl_slot_array[] = { entries }; \
|
||||
GEN_NS Slice_StrTemplateSlot gencpp_tmpl_slot_slice = { gencpp_tmpl_slot_array, count_of(gencpp_tmpl_slot_array) }; \
|
||||
(result) = GEN_NS strbuilder_append_template((output), gencpp_tmpl_slot_slice, (format)); \
|
||||
} while ( 0 )
|
||||
#endif
|
||||
|
||||
GEN_API void strbuilder_trim (StrBuilder str, char const* cut_set);
|
||||
GEN_API StrBuilder strbuilder_visualize_whitespace(StrBuilder const str);
|
||||
|
||||
StrBuilder strbuilder_make_c_str (AllocatorInfo allocator, char const* str);
|
||||
StrBuilder strbuilder_make_str (AllocatorInfo allocator, Str str);
|
||||
|
||||
@@ -13,6 +13,7 @@ GEN_NS_BEGIN
|
||||
#include "dependencies/printing.hpp"
|
||||
#include "dependencies/containers.hpp"
|
||||
#include "dependencies/hashing.hpp"
|
||||
#include "dependencies/simd.hpp"
|
||||
#include "dependencies/strings.hpp"
|
||||
#include "dependencies/filesystem.hpp"
|
||||
#include "dependencies/timing.hpp"
|
||||
|
||||
@@ -141,6 +141,7 @@
|
||||
#undef GEN_ASSERT
|
||||
#undef GEN_ASSERT_MSG
|
||||
#undef GEN_ASSERT_NOT_NULL
|
||||
#undef GEN_ASSERT_SLICE
|
||||
#undef GEN_PANIC
|
||||
#undef GEN_FATAL
|
||||
|
||||
@@ -165,6 +166,9 @@
|
||||
#undef get_hashtable_underlying_type
|
||||
|
||||
#undef txt
|
||||
#undef strtmpl_slot
|
||||
#undef strtmpl_slots
|
||||
#undef strtmpl_append
|
||||
|
||||
#undef NOMINMAX
|
||||
#undef VC_EXTRALEAN
|
||||
|
||||
@@ -195,6 +195,9 @@ int gen_main()
|
||||
(Macro { txt("cast_to_str"), MT_Expression, MF_Functional }),
|
||||
(Macro { txt("current"), MT_Expression, MF_Null }),
|
||||
(Macro { txt("txt"), MT_Expression, MF_Functional }),
|
||||
(Macro { txt("strtmpl_slot"), MT_Expression, MF_Functional }),
|
||||
(Macro { txt("strtmpl_slots"), MT_Expression, MF_Functional }),
|
||||
(Macro { txt("strtmpl_append"), MT_Statement, MF_Functional }),
|
||||
(Macro { txt("GEN_FILE_OPEN_PROC"), MT_Statement, MF_Functional | MF_Expects_Body }),
|
||||
(Macro { txt("GEN_FILE_READ_AT_PROC"), MT_Statement, MF_Functional | MF_Expects_Body }),
|
||||
(Macro { txt("GEN_FILE_WRITE_AT_PROC"), MT_Statement, MF_Functional | MF_Expects_Body }),
|
||||
@@ -268,6 +271,7 @@ int gen_main()
|
||||
Code header_debug = scan_file( path_base "dependencies/debug.hpp" );
|
||||
Code header_string_ops = scan_file( path_base "dependencies/string_ops.hpp" );
|
||||
Code header_hashing = scan_file( path_base "dependencies/hashing.hpp" );
|
||||
Code header_simd = scan_file( path_base "dependencies/simd.hpp" );
|
||||
Code header_timing = scan_file( path_base "dependencies/timing.hpp" );
|
||||
|
||||
CodeBody parsed_header_memory = parse_file( path_base "dependencies/memory.hpp" );
|
||||
@@ -1443,7 +1447,17 @@ R"(#define <interface_name>( code ) _Generic( (code), \
|
||||
);
|
||||
// Append slots
|
||||
for(Str type : code_typenames ) {
|
||||
generic_selector.append_fmt("%S : %S,\\\n", type, fn->Name );
|
||||
bool appended = false;
|
||||
strtmpl_append(& generic_selector, appended, strtmpl_slots(
|
||||
strtmpl_slot("type", type),
|
||||
strtmpl_slot("fn", fn->Name)
|
||||
),
|
||||
code(<type> : <fn>,\\\n)
|
||||
);
|
||||
if (appended == false) {
|
||||
GEN_FATAL("builder_print selector template failed");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
generic_selector.append(txt("default: gen_generic_selection_fail \\\n"));
|
||||
generic_selector.append(txt("\t)\tGEN_RESOLVED_FUNCTION_CALL( builder, (Code)code )"));
|
||||
@@ -1791,6 +1805,7 @@ R"(#define <interface_name>( code ) _Generic( (code), \
|
||||
Code r_header_string_ops = refactor(header_string_ops);
|
||||
Code rf_containers = refactor_and_format(containers);
|
||||
Code r_header_hashing = refactor(header_hashing);
|
||||
Code r_header_simd = refactor(header_simd);
|
||||
Code rf_header_strings = refactor_and_format(header_strings);
|
||||
Code rf_header_filesystem = refactor_and_format(header_filesystem);
|
||||
Code r_header_timing = refactor(header_timing);
|
||||
@@ -1877,6 +1892,7 @@ R"(#define <interface_name>( code ) _Generic( (code), \
|
||||
header.print( fmt_newline);
|
||||
header.print( rf_containers);
|
||||
header.print( r_header_hashing );
|
||||
header.print( r_header_simd );
|
||||
header.print( rf_header_strings);
|
||||
header.print( rf_header_filesystem);
|
||||
header.print( r_header_timing );
|
||||
@@ -2026,6 +2042,7 @@ R"(#define <interface_name>( code ) _Generic( (code), \
|
||||
header.print( fmt_newline);
|
||||
header.print( rf_containers);
|
||||
header.print( r_header_hashing );
|
||||
header.print( r_header_simd );
|
||||
header.print( rf_header_strings);
|
||||
header.print( rf_header_filesystem);
|
||||
header.print( r_header_timing );
|
||||
|
||||
@@ -68,10 +68,12 @@ word txt, gen_txt
|
||||
// Basic Types
|
||||
|
||||
word u8, gen_u8
|
||||
word u8x16, gen_u8x16
|
||||
word s8, gen_s8
|
||||
word u16, gen_u16
|
||||
word s16, gen_s16
|
||||
word u32, gen_u32
|
||||
word u32x4, gen_u32x4
|
||||
word s32, gen_s32
|
||||
word u64, gen_u64
|
||||
word s64, gen_s64
|
||||
@@ -91,6 +93,13 @@ word to_sptr, gen_to_sptr
|
||||
word to_mem_ptr, gen_to_mem_ptr
|
||||
word to_mem_ptr_const, gen_to_mem_ptr_const
|
||||
|
||||
// SIMD
|
||||
|
||||
namespace u8x16_, gen_u8x16_
|
||||
namespace u32x4_, gen_u32x4_
|
||||
|
||||
word u32_mask_first_set, gen_u32_mask_first_set
|
||||
|
||||
// Debug
|
||||
|
||||
word assert_handler, gen_assert_handler
|
||||
@@ -230,10 +239,13 @@ namespace str_, gen_str_
|
||||
|
||||
word cast_to_str, gen_cast_to_str
|
||||
|
||||
word StrBuilderHeader, gen_StrBuilderHeader
|
||||
word StrBuilder, gen_StrBuilder
|
||||
word StrBuilderHeader, gen_StrBuilderHeader
|
||||
word StrBuilder, gen_StrBuilder
|
||||
word StrTemplateSlot, gen_StrTemplateSlot
|
||||
word Slice_StrTemplateSlot, gen_Slice_StrTemplateSlot
|
||||
|
||||
namespace strbuilder_, gen_strbuilder_
|
||||
namespace strtmpl_, gen_strtmpl_
|
||||
|
||||
word StrCached, gen_StrCached
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ int gen_main()
|
||||
Code printing = scan_file( path_base "dependencies/printing.hpp" );
|
||||
Code containers = scan_file( path_base "dependencies/containers.hpp" );
|
||||
Code hashing = scan_file( path_base "dependencies/hashing.hpp" );
|
||||
Code simd = scan_file( path_base "dependencies/simd.hpp" );
|
||||
Code strings = scan_file( path_base "dependencies/strings.hpp" );
|
||||
Code filesystem = scan_file( path_base "dependencies/filesystem.hpp" );
|
||||
Code timing = scan_file( path_base "dependencies/timing.hpp" );
|
||||
@@ -70,6 +71,7 @@ int gen_main()
|
||||
builder_print( header, printing );
|
||||
builder_print( header, containers );
|
||||
builder_print( header, hashing );
|
||||
builder_print( header, simd );
|
||||
builder_print( header, strings );
|
||||
builder_print( header, filesystem );
|
||||
builder_print( header, timing );
|
||||
|
||||
@@ -81,6 +81,7 @@ int gen_main()
|
||||
Code printing = scan_file( path_base "dependencies/printing.hpp" );
|
||||
Code containers = scan_file( path_base "dependencies/containers.hpp" );
|
||||
Code hashing = scan_file( path_base "dependencies/hashing.hpp" );
|
||||
Code simd = scan_file( path_base "dependencies/simd.hpp" );
|
||||
Code strings = scan_file( path_base "dependencies/strings.hpp" );
|
||||
Code filesystem = scan_file( path_base "dependencies/filesystem.hpp" );
|
||||
Code timing = scan_file( path_base "dependencies/timing.hpp" );
|
||||
@@ -97,6 +98,7 @@ int gen_main()
|
||||
header.print( printing );
|
||||
header.print( containers );
|
||||
header.print( hashing );
|
||||
header.print( simd );
|
||||
header.print( strings );
|
||||
header.print( filesystem );
|
||||
header.print( timing );
|
||||
|
||||
@@ -144,6 +144,7 @@ int gen_main()
|
||||
Code printing = scan_file( path_base "dependencies/printing.hpp" );
|
||||
Code containers = scan_file( path_base "dependencies/containers.hpp" );
|
||||
Code hashing = scan_file( path_base "dependencies/hashing.hpp" );
|
||||
Code simd = scan_file( path_base "dependencies/simd.hpp" );
|
||||
Code strings = scan_file( path_base "dependencies/strings.hpp" );
|
||||
Code filesystem = scan_file( path_base "dependencies/filesystem.hpp" );
|
||||
Code timing = scan_file( path_base "dependencies/timing.hpp" );
|
||||
@@ -166,6 +167,7 @@ int gen_main()
|
||||
header.print( printing );
|
||||
header.print( containers );
|
||||
header.print( hashing );
|
||||
header.print( simd );
|
||||
header.print( strings );
|
||||
header.print( filesystem );
|
||||
header.print( timing );
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
gen_internal void expect_simd_lanes()
|
||||
{
|
||||
#if defined(GEN_SYSTEM_WINDOWS) && (defined(_M_X64) || defined(__x86_64__))
|
||||
gen_u8 storage[32];
|
||||
gen_mem_set(storage, 0x11, 32);
|
||||
gen_u8 pattern[16] = {
|
||||
0x00, 0x80, 0xFF, 0x01,
|
||||
0x02, 0x03, 0x04, 0x05,
|
||||
0x06, 0x07, 0x08, 0x09,
|
||||
0x0A, 0x0B, 0x0C, 0x0D
|
||||
};
|
||||
gen_mem_copy(storage + 1, pattern, 16);
|
||||
gen_u8x16 loaded = gen_u8x16_load(storage + 1);
|
||||
gen_u8x16 again = gen_u8x16_load(storage + 1);
|
||||
gen_u16 same = gen_u8x16_mask(gen_u8x16_equal(loaded, again));
|
||||
gen_u16 zero_mask = gen_u8x16_mask(gen_u8x16_equal(loaded, gen_u8x16_splat(0x00)));
|
||||
gen_u16 high_bit = gen_u8x16_mask(gen_u8x16_equal(loaded, gen_u8x16_splat(0x80)));
|
||||
gen_u16 top = gen_u8x16_mask(gen_u8x16_equal(loaded, gen_u8x16_splat(0xFF)));
|
||||
expect(same == 0xFFFF, gen_txt("simd u8 equal load"));
|
||||
expect(zero_mask == 0x0001, gen_txt("simd byte 0"));
|
||||
expect(high_bit == 0x0002, gen_txt("simd byte 0x80"));
|
||||
expect(top == 0x0004, gen_txt("simd byte 0xFF"));
|
||||
|
||||
gen_u8 high_lane[16];
|
||||
gen_mem_set(high_lane, 0x22, 16);
|
||||
high_lane[15] = 0xFF;
|
||||
gen_u8x16 high_loaded = gen_u8x16_load(high_lane);
|
||||
gen_u16 lane15 = gen_u8x16_mask(gen_u8x16_equal(high_loaded, gen_u8x16_splat(0xFF)));
|
||||
expect(lane15 == 0x8000, gen_txt("simd lane 15"));
|
||||
expect(gen_u32_mask_first_set(lane15) == 15, gen_txt("simd first set lane 15"));
|
||||
|
||||
gen_u8 window[24];
|
||||
gen_mem_set(window, 0xEE, 24);
|
||||
window[1] = 0x00;
|
||||
window[2] = 0x80;
|
||||
window[3] = 0xFF;
|
||||
window[4] = 0x11;
|
||||
window[5] = 0x22;
|
||||
window[6] = 0x33;
|
||||
window[7] = 0x44;
|
||||
window[8] = 0x55;
|
||||
gen_u8x16 loaded8 = gen_u8x16_load8(window + 1);
|
||||
gen_u16 upper = gen_u8x16_mask(gen_u8x16_equal(loaded8, gen_u8x16_splat(0x00)));
|
||||
gen_u16 low_ff = gen_u8x16_mask(gen_u8x16_equal(loaded8, gen_u8x16_splat(0xFF)));
|
||||
gen_u16 low_80 = gen_u8x16_mask(gen_u8x16_equal(loaded8, gen_u8x16_splat(0x80)));
|
||||
gen_u16 byte7 = gen_u8x16_mask(gen_u8x16_equal(loaded8, gen_u8x16_splat(0x55)));
|
||||
expect((upper & 0xFF00) == 0xFF00, gen_txt("simd load8 upper zero"));
|
||||
expect((upper & 0x0001) == 0x0001, gen_txt("simd load8 low zero"));
|
||||
expect(low_ff == 0x0004, gen_txt("simd load8 low 0xFF"));
|
||||
expect(low_80 == 0x0002, gen_txt("simd load8 low 0x80"));
|
||||
expect(byte7 == 0x0080, gen_txt("simd load8 byte 7"));
|
||||
gen_u16 sentinel = gen_u8x16_mask(gen_u8x16_equal(loaded8, gen_u8x16_splat(0xEE)));
|
||||
expect(sentinel == 0, gen_txt("simd load8 ignores tail"));
|
||||
gen_u8x16 loaded16 = gen_u8x16_load(window + 1);
|
||||
gen_u16 tail = gen_u8x16_mask(gen_u8x16_equal(loaded16, gen_u8x16_splat(0xEE)));
|
||||
expect(tail == 0xFF00, gen_txt("simd load16 sees load8 tail"));
|
||||
|
||||
gen_u32 hashes[4] = { 0x00000000u, 0x7FFFFFFFu, 0x80000000u, 0xFFFFFFFFu };
|
||||
gen_u32x4 hash_loaded = gen_u32x4_load(hashes);
|
||||
gen_u8 lane0 = gen_u32x4_mask(gen_u32x4_equal(hash_loaded, gen_u32x4_splat(0x00000000u)));
|
||||
gen_u8 lane1 = gen_u32x4_mask(gen_u32x4_equal(hash_loaded, gen_u32x4_splat(0x7FFFFFFFu)));
|
||||
gen_u8 lane2 = gen_u32x4_mask(gen_u32x4_equal(hash_loaded, gen_u32x4_splat(0x80000000u)));
|
||||
gen_u8 lane3 = gen_u32x4_mask(gen_u32x4_equal(hash_loaded, gen_u32x4_splat(0xFFFFFFFFu)));
|
||||
expect(lane0 == 0x01, gen_txt("simd u32 lane 0"));
|
||||
expect(lane1 == 0x02, gen_txt("simd u32 lane 1"));
|
||||
expect(lane2 == 0x04, gen_txt("simd u32 bit31 lane"));
|
||||
expect(lane3 == 0x08, gen_txt("simd u32 lane 3"));
|
||||
expect((lane2 & 0xF0) == 0, gen_txt("simd u32 mask low bits"));
|
||||
|
||||
gen_u32 all_values[4] = { 0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u };
|
||||
gen_u32x4 all_loaded = gen_u32x4_load(all_values);
|
||||
gen_u8 all_mask = gen_u32x4_mask(gen_u32x4_equal(all_loaded, gen_u32x4_splat(0x80000000u)));
|
||||
expect(all_mask == 0x0F, gen_txt("simd u32 all bit31"));
|
||||
gen_u8 none_mask = gen_u32x4_mask(gen_u32x4_equal(all_loaded, gen_u32x4_splat(0x7FFFFFFFu)));
|
||||
expect(none_mask == 0x00, gen_txt("simd u32 none"));
|
||||
|
||||
gen_u8 hash_storage[20];
|
||||
gen_mem_set (hash_storage, 0xA5, 20);
|
||||
gen_mem_copy(hash_storage + 1, hashes, 16);
|
||||
gen_u32x4 unaligned = gen_u32x4_load(hash_storage + 1);
|
||||
gen_u8 unaligned_mask = gen_u32x4_mask(gen_u32x4_equal(unaligned, gen_u32x4_splat(0x80000000u)));
|
||||
expect(unaligned_mask == 0x04, gen_txt("simd u32 unaligned bit31"));
|
||||
|
||||
expect(gen_u32_mask_first_set(0x1u) == 0, gen_txt("simd first set bit 0"));
|
||||
expect(gen_u32_mask_first_set(0x14u) == 2, gen_txt("simd first set lowest"));
|
||||
expect(gen_u32_mask_first_set(0x8000u) == 15, gen_txt("simd first set bit 15"));
|
||||
expect(gen_u32_mask_first_set(0x80000000u) == 31, gen_txt("simd first set bit 31"));
|
||||
expect(gen_u32_mask_first_set(0xFFFFFFFFu) == 0, gen_txt("simd first set all bits"));
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,631 @@
|
||||
gen_internal gen_Str bounded_format(char* storage, gen_ssize storage_len, char const* bytes, gen_ssize len) {
|
||||
gen_mem_set(storage, '!', storage_len);
|
||||
if (len > 0)
|
||||
gen_mem_copy(storage, bytes, len);
|
||||
storage[storage_len - 1] = '\0';
|
||||
gen_Str result = { storage, len };
|
||||
return result;
|
||||
}
|
||||
|
||||
gen_internal void expect_template_expansion(gen_Str label, gen_Str format, gen_StrTemplateSlot* slots, gen_ssize slot_count, gen_Str expansion) {
|
||||
gen_StrBuilder output = gen_strbuilder_make_reserve(gen_heap(), 1);
|
||||
expect(gen_strbuilder_append_str(& output, gen_txt("KEEP")), label);
|
||||
gen_Slice_StrTemplateSlot slice = { 0, 0 };
|
||||
if (slot_count != 0) {
|
||||
slice.Ptr = slots;
|
||||
slice.Len = slot_count;
|
||||
}
|
||||
bool appended = gen_strbuilder_append_template(& output, slice, format);
|
||||
expect(appended, label);
|
||||
gen_Str got = gen_strbuilder_to_str(output);
|
||||
expect(got.Len == 4 + expansion.Len, label);
|
||||
expect(gen_str_starts_with(got, gen_txt("KEEP")), label);
|
||||
if (got.Len >= 4 + expansion.Len) {
|
||||
gen_Str tail = { got.Ptr + 4, expansion.Len };
|
||||
expect(gen_str_are_equal(tail, expansion), label);
|
||||
}
|
||||
gen_StrBuilderHeader* header = gen_strbuilder_get_header(output);
|
||||
expect(header->Allocator.Proc == gen_heap_allocator_proc, label);
|
||||
gen_strbuilder_free(& output);
|
||||
}
|
||||
|
||||
gen_internal void expect_template_text(gen_Str label, gen_Str format, gen_StrTemplateSlot* slots, gen_ssize slot_count, gen_Str expansion) {
|
||||
expect_template_expansion(label, format, slots, slot_count, expansion);
|
||||
}
|
||||
|
||||
gen_internal void expect_literal_boundary(gen_ssize mark) {
|
||||
char format_storage[64];
|
||||
char format_bytes [40];
|
||||
for (gen_ssize idx = 0; idx < mark; ++ idx) format_bytes[idx] = 'L';
|
||||
gen_Str format = bounded_format(format_storage, 64, format_bytes, mark);
|
||||
gen_Str expansion = { format_bytes, mark };
|
||||
expect(format_storage[mark] == '!', gen_txt("literal boundary"));
|
||||
expect_template_expansion(gen_txt("literal boundary"), format, 0, 0, expansion);
|
||||
}
|
||||
|
||||
gen_internal void expect_open_boundary(gen_ssize mark) {
|
||||
char format_storage [64];
|
||||
char format_bytes [48];
|
||||
char expansion_bytes[48];
|
||||
gen_ssize format_len = 0;
|
||||
for (gen_ssize idx = 0; idx < mark; ++ idx)
|
||||
format_bytes[format_len ++] = 'L';
|
||||
format_bytes[format_len ++] = '<';
|
||||
format_bytes[format_len ++] = 'k';
|
||||
format_bytes[format_len ++] = '>';
|
||||
format_bytes[format_len ++] = 'Z';
|
||||
gen_Str format = bounded_format(format_storage, 64, format_bytes, format_len);
|
||||
gen_StrTemplateSlot slot = {0};
|
||||
slot.Key = gen_txt("k");
|
||||
slot.Value = gen_txt("V");
|
||||
gen_ssize expansion_len = 0;
|
||||
for (gen_ssize idx = 0; idx < mark; ++ idx)
|
||||
expansion_bytes[expansion_len ++] = 'L';
|
||||
expansion_bytes[expansion_len ++] = 'V';
|
||||
expansion_bytes[expansion_len ++] = 'Z';
|
||||
gen_Str expansion = { expansion_bytes, expansion_len };
|
||||
expect(format.Ptr[mark] == '<', gen_txt("open boundary"));
|
||||
expect(format_storage[format_len] == '!', gen_txt("open boundary"));
|
||||
expect_template_expansion(gen_txt("open boundary"), format, & slot, 1, expansion);
|
||||
}
|
||||
|
||||
gen_internal void expect_close_boundary(gen_ssize mark) {
|
||||
char format_storage[64];
|
||||
char format_bytes [48];
|
||||
char key_storage [40];
|
||||
gen_ssize key_len = mark - 1;
|
||||
gen_mem_set(key_storage, '!', 40);
|
||||
for (gen_ssize idx = 0; idx < key_len; ++ idx)
|
||||
key_storage[idx] = 'k';
|
||||
key_storage[39] = '\0';
|
||||
gen_Str key = { key_storage, key_len };
|
||||
gen_ssize format_len = 0;
|
||||
format_bytes[format_len ++] = '<';
|
||||
for (gen_ssize idx = 0; idx < key_len; ++ idx)
|
||||
format_bytes[format_len ++] = 'k';
|
||||
format_bytes[format_len ++] = '>';
|
||||
format_bytes[format_len ++] = 'Z';
|
||||
gen_Str format = bounded_format(format_storage, 64, format_bytes, format_len);
|
||||
gen_StrTemplateSlot slot = {0};
|
||||
slot.Key = key;
|
||||
slot.Value = gen_txt("V");
|
||||
expect(format.Ptr [mark] == '>', gen_txt("close boundary"));
|
||||
expect(key.Ptr [key.Len] == '!', gen_txt("close boundary"));
|
||||
expect(format_storage[format_len] == '!', gen_txt("close boundary"));
|
||||
expect_template_expansion(gen_txt("close boundary"), format, & slot, 1, gen_txt("VZ"));
|
||||
}
|
||||
|
||||
gen_internal void expect_template_shapes()
|
||||
{
|
||||
char literal_storage[8];
|
||||
char literal_bytes [6] = { 'a', 'b', 'c', 'D', 'E', 'F' };
|
||||
gen_Str literal = bounded_format(literal_storage, 8, literal_bytes, 3);
|
||||
expect(literal_storage[3] == '!', gen_txt("literal sentinel"));
|
||||
expect_template_expansion(gen_txt("literal bounded"), literal, 0, 0, gen_txt("abc"));
|
||||
|
||||
char empty_storage[4];
|
||||
gen_Str empty_format = bounded_format(empty_storage, 4, empty_storage, 0);
|
||||
expect(empty_storage[0] == '!', gen_txt("empty sentinel"));
|
||||
expect_template_expansion(gen_txt("empty format"), empty_format, 0, 0, gen_txt(""));
|
||||
|
||||
gen_StrTemplateSlot name_slot = {0};
|
||||
name_slot.Key = gen_txt("name");
|
||||
name_slot.Value = gen_txt("world");
|
||||
expect_template_text(gen_txt("one slot"), gen_txt("hello <name>!"), & name_slot, 1, gen_txt("hello world!"));
|
||||
|
||||
gen_StrTemplateSlot repeat_slot = {0};
|
||||
repeat_slot.Key = gen_txt("w");
|
||||
repeat_slot.Value = gen_txt("v");
|
||||
expect_template_text(gen_txt("repeated slot"), gen_txt("<w>-<w>-<w>"), & repeat_slot, 1, gen_txt("v-v-v"));
|
||||
|
||||
gen_StrTemplateSlot adjacent[2] = {0};
|
||||
adjacent[0].Key = gen_txt("a");
|
||||
adjacent[0].Value = gen_txt("X");
|
||||
adjacent[1].Key = gen_txt("b");
|
||||
adjacent[1].Value = gen_txt("Y");
|
||||
expect_template_text(gen_txt("adjacent slots"), gen_txt("<a><b><a>"), adjacent, 2, gen_txt("XYX"));
|
||||
|
||||
gen_StrTemplateSlot raw_slots[3] = {0};
|
||||
raw_slots[0].Key = gen_txt("raw");
|
||||
raw_slots[0].Value = gen_txt("a<b>c");
|
||||
raw_slots[1].Key = gen_txt("angles");
|
||||
raw_slots[1].Value = gen_txt("<>");
|
||||
raw_slots[2].Key = gen_txt("b");
|
||||
raw_slots[2].Value = gen_txt("SHOULD_NOT");
|
||||
expect_template_text(gen_txt("value angles literal"), gen_txt("<raw><angles>"), raw_slots, 3, gen_txt("a<b>c<>"));
|
||||
|
||||
char empty_value_storage[2] = { '!', '!' };
|
||||
gen_StrTemplateSlot empty_slot = {0};
|
||||
empty_slot.Key = gen_txt("e");
|
||||
empty_slot.Value = (gen_Str){ empty_value_storage, 0 };
|
||||
expect_template_text(gen_txt("empty value"), gen_txt("A<e>B"), & empty_slot, 1, gen_txt("AB"));
|
||||
|
||||
gen_StrTemplateSlot empty_key = {0};
|
||||
empty_key.Key = (gen_Str){ 0, 0 };
|
||||
empty_key.Value = gen_txt("Z");
|
||||
expect_template_text(gen_txt("empty key"), gen_txt("<>"), & empty_key, 1, gen_txt("Z"));
|
||||
|
||||
gen_StrTemplateSlot null_value = {0};
|
||||
null_value.Key = gen_txt("e");
|
||||
null_value.Value = (gen_Str){ 0, 0 };
|
||||
expect(null_value.Value.Ptr == 0, gen_txt("null empty value pointer"));
|
||||
expect(null_value.Value.Len == 0, gen_txt("null empty value length"));
|
||||
expect_template_text(gen_txt("null empty value"), gen_txt("A<e>B"), & null_value, 1, gen_txt("AB"));
|
||||
|
||||
char nul_value_bytes[4] = { 'a', '\0', 'b', '!' };
|
||||
char nul_expected [5] = { '[', 'a', '\0', 'b', ']' };
|
||||
gen_StrTemplateSlot nul_slot = {0};
|
||||
nul_slot.Key = gen_txt("n");
|
||||
nul_slot.Value = (gen_Str){ nul_value_bytes, 3 };
|
||||
gen_Str nul_expansion = { nul_expected, 5 };
|
||||
expect_template_text(gen_txt("embedded nul"), gen_txt("[<n>]"), & nul_slot, 1, nul_expansion);
|
||||
|
||||
gen_StrTemplateSlot duffle[2] = {0};
|
||||
duffle[0].Key = gen_txt("operands[0]");
|
||||
duffle[0].Value = gen_txt("eax");
|
||||
duffle[1].Key = gen_txt("mod_rm.bits");
|
||||
duffle[1].Value = gen_txt("11");
|
||||
expect_template_text(gen_txt("duffle keys"), gen_txt("mov <operands[0]>, <mod_rm.bits>;"), duffle, 2, gen_txt("mov eax, 11;"));
|
||||
}
|
||||
|
||||
gen_internal void fill_lookup_slots(gen_StrTemplateSlot* slots, gen_ssize slot_count) {
|
||||
gen_Str keys[9] = {
|
||||
gen_txt("k0"), gen_txt("k1"), gen_txt("k2"), gen_txt("k3"), gen_txt("k4"),
|
||||
gen_txt("k5"), gen_txt("k6"), gen_txt("k7"), gen_txt("k8")
|
||||
};
|
||||
gen_Str values[9] = {
|
||||
gen_txt("V0"), gen_txt("V1"), gen_txt("V2"), gen_txt("V3"), gen_txt("V4"),
|
||||
gen_txt("V5"), gen_txt("V6"), gen_txt("V7"), gen_txt("V8")
|
||||
};
|
||||
for (gen_ssize idx = 0; idx < slot_count; ++ idx) {
|
||||
slots[idx].Key = keys[idx];
|
||||
slots[idx].Value = values[idx];
|
||||
}
|
||||
}
|
||||
|
||||
gen_internal void expect_template_lookup()
|
||||
{
|
||||
gen_StrTemplateSlot slots[9] = {0};
|
||||
expect_template_text(gen_txt("lookup 0"), gen_txt("none"), 0, 0, gen_txt("none"));
|
||||
|
||||
fill_lookup_slots(slots, 1);
|
||||
expect_template_text(gen_txt("lookup 1"), gen_txt("<k0>"), slots, 1, gen_txt("V0"));
|
||||
|
||||
fill_lookup_slots(slots, 2);
|
||||
expect_template_text(gen_txt("lookup 2"), gen_txt("<k1><k0>"), slots, 2, gen_txt("V1V0"));
|
||||
|
||||
fill_lookup_slots(slots, 3);
|
||||
expect_template_text(gen_txt("lookup 3"), gen_txt("<k2>-<k0>-<k1>"), slots, 3, gen_txt("V2-V0-V1"));
|
||||
|
||||
fill_lookup_slots(slots, 4);
|
||||
expect_template_text(gen_txt("lookup 4"), gen_txt("<k3><k0><k2><k1>"), slots, 4, gen_txt("V3V0V2V1"));
|
||||
|
||||
fill_lookup_slots(slots, 5);
|
||||
expect_template_text(gen_txt("lookup 5 tail"), gen_txt("<k4><k0><k3>"), slots, 5, gen_txt("V4V0V3"));
|
||||
|
||||
fill_lookup_slots(slots, 8);
|
||||
expect_template_text(gen_txt("lookup 8 lanes"), gen_txt("<k7><k4><k3><k0>"), slots, 8, gen_txt("V7V4V3V0"));
|
||||
|
||||
fill_lookup_slots(slots, 9);
|
||||
expect_template_text(gen_txt("lookup 9 tail"), gen_txt("<k8><k5><k6><k4>"), slots, 9, gen_txt("V8V5V6V4"));
|
||||
|
||||
gen_StrTemplateSlot dup_small[3] = {0};
|
||||
dup_small[0].Key = gen_txt("dup"); dup_small[0].Value = gen_txt("FIRST");
|
||||
dup_small[1].Key = gen_txt("other"); dup_small[1].Value = gen_txt("X");
|
||||
dup_small[2].Key = gen_txt("dup"); dup_small[2].Value = gen_txt("SECOND");
|
||||
expect_template_text(gen_txt("duplicate first wins"), gen_txt("<dup><other>"), dup_small, 3, gen_txt("FIRSTX"));
|
||||
|
||||
gen_StrTemplateSlot dup_lanes[4] = {0};
|
||||
dup_lanes[0].Key = gen_txt("same"); dup_lanes[0].Value = gen_txt("A");
|
||||
dup_lanes[1].Key = gen_txt("same"); dup_lanes[1].Value = gen_txt("B");
|
||||
dup_lanes[2].Key = gen_txt("other"); dup_lanes[2].Value = gen_txt("C");
|
||||
dup_lanes[3].Key = gen_txt("same"); dup_lanes[3].Value = gen_txt("D");
|
||||
expect_template_text(gen_txt("duplicate lane first wins"), gen_txt("<same><other>"), dup_lanes, 4, gen_txt("AC"));
|
||||
|
||||
gen_StrTemplateSlot dup_tail[5] = {0};
|
||||
dup_tail[0].Key = gen_txt("dup"); dup_tail[0].Value = gen_txt("FIRST");
|
||||
dup_tail[1].Key = gen_txt("k1"); dup_tail[1].Value = gen_txt("V1");
|
||||
dup_tail[2].Key = gen_txt("k2"); dup_tail[2].Value = gen_txt("V2");
|
||||
dup_tail[3].Key = gen_txt("k3"); dup_tail[3].Value = gen_txt("V3");
|
||||
dup_tail[4].Key = gen_txt("dup"); dup_tail[4].Value = gen_txt("TAIL");
|
||||
expect_template_text(gen_txt("duplicate tail first wins"), gen_txt("<dup>"), dup_tail, 5, gen_txt("FIRST"));
|
||||
}
|
||||
|
||||
gen_internal void expect_template_boundaries() {
|
||||
gen_ssize marks[12] = { 7, 8, 9, 15, 16, 17, 23, 24, 25, 31, 32, 33 };
|
||||
for (gen_ssize idx = 0; idx < gen_count_of(marks); ++ idx) {
|
||||
expect_literal_boundary(marks[idx]);
|
||||
expect_open_boundary(marks[idx]);
|
||||
expect_close_boundary(marks[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
gen_internal void expect_template_growth(gen_ssize printf_maxlen)
|
||||
{
|
||||
gen_ssize huge_len = printf_maxlen + 64;
|
||||
char* huge = gen_rcast(char*, gen_alloc(gen_heap(), huge_len + 1));
|
||||
expect(huge != 0, gen_txt("growth alloc"));
|
||||
if (huge == 0) return;
|
||||
gen_mem_set(huge, 'Q', huge_len);
|
||||
huge[huge_len] = '!';
|
||||
|
||||
gen_Str value = { huge, huge_len };
|
||||
gen_StrTemplateSlot slot = {0};
|
||||
slot.Key = gen_txt("blob");
|
||||
slot.Value = value;
|
||||
|
||||
char format_storage[16];
|
||||
gen_Str format = gen_txt("<blob>");
|
||||
gen_StrBuilder output = gen_strbuilder_make_reserve(gen_heap(), 1);
|
||||
expect(gen_strbuilder_capacity(output) == 1, gen_txt("growth initial capacity"));
|
||||
expect(gen_strbuilder_append_str(& output, gen_txt("KEEP")), gen_txt("growth prefix"));
|
||||
|
||||
gen_Slice_StrTemplateSlot slice = { & slot, 1 };
|
||||
bool appended = gen_strbuilder_append_template(& output, slice, format);
|
||||
expect(appended, gen_txt("growth append"));
|
||||
|
||||
gen_Str got = gen_strbuilder_to_str(output);
|
||||
expect(got.Len == 4 + huge_len, gen_txt("growth length"));
|
||||
expect(got.Len > printf_maxlen, gen_txt("growth beyond printf max"));
|
||||
expect(gen_str_starts_with(got, gen_txt("KEEP")), gen_txt("growth keeps prefix"));
|
||||
|
||||
gen_ssize capacity = gen_strbuilder_capacity(output);
|
||||
expect(capacity >= got.Len, gen_txt("growth capacity"));
|
||||
if (got.Len == 4 + huge_len && capacity >= got.Len) {
|
||||
gen_b32 payload_matches = true;
|
||||
for (gen_ssize idx = 0; idx < got.Len - 4; ++ idx) {
|
||||
if (got.Ptr[4 + idx] != 'Q') payload_matches = false;
|
||||
}
|
||||
expect(payload_matches, gen_txt("growth bytes"));
|
||||
}
|
||||
|
||||
gen_StrBuilderHeader* header = gen_strbuilder_get_header(output);
|
||||
expect(header->Allocator.Proc == gen_heap_allocator_proc, gen_txt("growth allocator"));
|
||||
expect(huge[huge_len] == '!', gen_txt("growth sentinel"));
|
||||
gen_strbuilder_free(& output);
|
||||
gen_allocator_free(gen_heap(), huge);
|
||||
}
|
||||
|
||||
gen_internal void expect_template_crc_collision() {
|
||||
gen_Str plumless = gen_txt("plumless");
|
||||
gen_Str buckeroo = gen_txt("buckeroo");
|
||||
gen_u32 plum_hash = gen_crc32(plumless.Ptr, plumless.Len);
|
||||
gen_u32 buck_hash = gen_crc32(buckeroo.Ptr, buckeroo.Len);
|
||||
expect(plum_hash == buck_hash, gen_txt("crc collision equal"));
|
||||
expect(plum_hash == 0x4ddb0c25, gen_txt("crc collision value"));
|
||||
expect(gen_str_are_equal(plumless, buckeroo) == false, gen_txt("crc collision keys differ"));
|
||||
|
||||
gen_StrTemplateSlot scalar[2] = {0};
|
||||
scalar[0].Key = plumless; scalar[0].Value = gen_txt("WRONG");
|
||||
scalar[1].Key = buckeroo; scalar[1].Value = gen_txt("RIGHT");
|
||||
expect_template_text(gen_txt("crc scalar 2"), gen_txt("<buckeroo>"), scalar, 2, gen_txt("RIGHT"));
|
||||
|
||||
gen_StrTemplateSlot lanes[4] = {0};
|
||||
lanes[0].Key = plumless; lanes[0].Value = gen_txt("WRONG");
|
||||
lanes[1].Key = gen_txt("k1"); lanes[1].Value = gen_txt("V1");
|
||||
lanes[2].Key = buckeroo; lanes[2].Value = gen_txt("RIGHT");
|
||||
lanes[3].Key = gen_txt("k3"); lanes[3].Value = gen_txt("V3");
|
||||
expect_template_text(gen_txt("crc lane reject"), gen_txt("<buckeroo>"), lanes, 4, gen_txt("RIGHT"));
|
||||
|
||||
gen_StrTemplateSlot tail[5] = {0};
|
||||
tail[0].Key = plumless; tail[0].Value = gen_txt("WRONG");
|
||||
tail[1].Key = gen_txt("k1"); tail[1].Value = gen_txt("V1");
|
||||
tail[2].Key = gen_txt("k2"); tail[2].Value = gen_txt("V2");
|
||||
tail[3].Key = gen_txt("k3"); tail[3].Value = gen_txt("V3");
|
||||
tail[4].Key = buckeroo; tail[4].Value = gen_txt("TAIL");
|
||||
expect_template_text(gen_txt("crc tail after false hit"), gen_txt("<buckeroo>"), tail, 5, gen_txt("TAIL"));
|
||||
}
|
||||
|
||||
gen_internal void* resize_fail_allocator_proc(void* allocator_data, gen_AllocType type, gen_ssize size, gen_ssize alignment
|
||||
, void* old_memory, gen_ssize old_size
|
||||
, gen_u64 flags
|
||||
){
|
||||
if (type == EAllocation_RESIZE) {
|
||||
gen_s32* attempts = gen_rcast(gen_s32*, allocator_data);
|
||||
attempts[0] = attempts[0] + 1;
|
||||
return 0;
|
||||
}
|
||||
return gen_heap_allocator_proc(0, type, size, alignment, old_memory, old_size, flags);
|
||||
}
|
||||
|
||||
gen_internal void expect_template_resize_fail(gen_ssize printf_maxlen)
|
||||
{
|
||||
gen_ssize huge_len = printf_maxlen + 64;
|
||||
char* huge = gen_rcast(char*, gen_alloc(gen_heap(), huge_len + 1));
|
||||
expect(huge != 0, gen_txt("resize fail alloc"));
|
||||
if (huge == 0) return;
|
||||
gen_mem_set(huge, 'L', huge_len);
|
||||
huge[huge_len] = '!';
|
||||
gen_Str huge_text = { huge, huge_len };
|
||||
|
||||
gen_s32 literal_attempts = 0;
|
||||
gen_AllocatorInfo literal_alloc = {0};
|
||||
literal_alloc.Proc = resize_fail_allocator_proc;
|
||||
literal_alloc.Data = & literal_attempts;
|
||||
gen_StrBuilder literal_output = gen_strbuilder_make_reserve(literal_alloc, 4);
|
||||
expect(gen_strbuilder_append_str(& literal_output, gen_txt("KEEP")), gen_txt("resize fail literal prefix"));
|
||||
|
||||
gen_Slice_StrTemplateSlot literal_slots = { 0, 0 };
|
||||
bool literal_appended = gen_strbuilder_append_template(& literal_output, literal_slots, huge_text);
|
||||
expect(literal_appended == false, gen_txt("resize fail literal"));
|
||||
|
||||
gen_Str literal_got = gen_strbuilder_to_str(literal_output);
|
||||
expect(literal_got.Len == 4, gen_txt("resize fail literal length"));
|
||||
expect(gen_str_are_equal(literal_got, gen_txt("KEEP")), gen_txt("resize fail literal keep"));
|
||||
expect(literal_attempts > 0, gen_txt("resize fail literal attempts"));
|
||||
gen_strbuilder_free(& literal_output);
|
||||
|
||||
gen_s32 value_attempts = 0;
|
||||
gen_AllocatorInfo value_alloc = {0};
|
||||
value_alloc.Proc = resize_fail_allocator_proc;
|
||||
value_alloc.Data = & value_attempts;
|
||||
|
||||
gen_StrTemplateSlot slot = {0};
|
||||
slot.Key = gen_txt("blob");
|
||||
slot.Value = huge_text;
|
||||
char format_storage[16];
|
||||
|
||||
gen_Str format = gen_txt("<blob>");
|
||||
gen_StrBuilder value_output = gen_strbuilder_make_reserve(value_alloc, 4);
|
||||
expect(gen_strbuilder_append_str(& value_output, gen_txt("KEEP")), gen_txt("resize fail value prefix"));
|
||||
|
||||
gen_Slice_StrTemplateSlot value_slots = { & slot, 1 };
|
||||
bool value_appended = gen_strbuilder_append_template(& value_output, value_slots, format);
|
||||
expect(value_appended == false, gen_txt("resize fail value"));
|
||||
|
||||
gen_Str value_got = gen_strbuilder_to_str(value_output);
|
||||
expect(value_got.Len == 4, gen_txt("resize fail value length"));
|
||||
expect(gen_str_are_equal(value_got, gen_txt("KEEP")), gen_txt("resize fail value keep"));
|
||||
expect(value_attempts > 0, gen_txt("resize fail value attempts"));
|
||||
|
||||
gen_strbuilder_free(& value_output);
|
||||
gen_allocator_free(gen_heap(), huge);
|
||||
}
|
||||
|
||||
gen_internal gen_Str counted_template_value (gen_s32* reads) { reads[0] = reads[0] + 1; return gen_txt("ONCE"); }
|
||||
gen_internal gen_Str counted_template_format(gen_s32* reads) { reads[0] = reads[0] + 1; return gen_txt("<k>:<k>"); }
|
||||
gen_internal gen_StrBuilder* counted_template_output(gen_StrBuilder* output, gen_s32* reads) { reads[0] = reads[0] + 1; return output; }
|
||||
gen_internal bool* counted_template_result(bool* result, gen_s32* reads) { reads[0] = reads[0] + 1; return result; }
|
||||
|
||||
gen_internal void expect_template_slice()
|
||||
{
|
||||
gen_StrTemplateSlot slots[2] = {
|
||||
gen_strtmpl_slot("a", gen_txt("X")),
|
||||
gen_strtmpl_slot("b", gen_txt("Y"))
|
||||
};
|
||||
gen_Slice_StrTemplateSlot slice = { slots, 2 };
|
||||
gen_StrBuilder output = gen_strbuilder_make_reserve(gen_heap(), 1);
|
||||
expect(gen_strbuilder_append_str(& output, gen_txt("KEEP")), gen_txt("slice prefix"));
|
||||
|
||||
bool appended = gen_strbuilder_append_template(& output, slice, gen_txt("<b><a><b>"));
|
||||
expect(appended, gen_txt("slice append"));
|
||||
expect(gen_str_are_equal(gen_strbuilder_to_str(output), gen_txt("KEEPYXY")), gen_txt("slice text"));
|
||||
expect(gen_str_are_equal(slots[0].Key, gen_txt("a")), gen_txt("slice key"));
|
||||
expect(gen_str_are_equal(slots[0].Value, gen_txt("X")), gen_txt("slice value"));
|
||||
gen_strbuilder_free(& output);
|
||||
|
||||
slice.Ptr[0].Value = gen_txt("Z");
|
||||
gen_StrBuilder mutated_output = gen_strbuilder_make_reserve(gen_heap(), 1);
|
||||
bool mutated_appended = gen_strbuilder_append_template(& mutated_output, slice, gen_txt("<b><a><b>"));
|
||||
expect(mutated_appended, gen_txt("slice mutate append"));
|
||||
expect(gen_str_are_equal(gen_strbuilder_to_str(mutated_output), gen_txt("YZY")), gen_txt("slice mutate text"));
|
||||
gen_strbuilder_free(& mutated_output);
|
||||
|
||||
gen_Slice_StrTemplateSlot empty_slice = { 0, 0 };
|
||||
gen_StrBuilder empty_output = gen_strbuilder_make_reserve(gen_heap(), 1);
|
||||
expect(gen_strbuilder_append_str(& empty_output, gen_txt("KEEP")), gen_txt("zero slice prefix"));
|
||||
|
||||
bool empty_appended = gen_strbuilder_append_template(& empty_output, empty_slice, gen_txt("none"));
|
||||
expect(empty_appended, gen_txt("zero slice append"));
|
||||
expect(gen_str_are_equal(gen_strbuilder_to_str(empty_output), gen_txt("KEEPnone")), gen_txt("zero slice text"));
|
||||
expect(empty_slice.Ptr == 0, gen_txt("zero slice pointer"));
|
||||
expect(empty_slice.Len == 0, gen_txt("zero slice length"));
|
||||
gen_strbuilder_free(& empty_output);
|
||||
}
|
||||
|
||||
gen_internal void expect_template_wrapper() {
|
||||
gen_StrBuilder output = gen_strbuilder_make_reserve(gen_heap(), 1);
|
||||
expect(gen_strbuilder_append_str(& output, gen_txt("KEEP")), gen_txt("wrapper prefix"));
|
||||
bool appended = false;
|
||||
gen_strtmpl_append(& output, appended, gen_strtmpl_slots(
|
||||
gen_strtmpl_slot("name", gen_txt("world")),
|
||||
gen_strtmpl_slot("tail", gen_txt("!"))
|
||||
),
|
||||
gen_txt(
|
||||
"hello <name>\n"
|
||||
"<name><tail>"
|
||||
)
|
||||
);
|
||||
expect(appended, gen_txt("wrapper multiline result"));
|
||||
gen_Str got = gen_strbuilder_to_str(output);
|
||||
expect(gen_str_starts_with(got, gen_txt("KEEP")), gen_txt("wrapper prefix kept"));
|
||||
expect(gen_str_are_equal (got, gen_txt("KEEPhello world\nworld!")), gen_txt("wrapper multiline text"));
|
||||
gen_strbuilder_free(& output);
|
||||
}
|
||||
|
||||
gen_internal void expect_template_wrapper_scope()
|
||||
{
|
||||
gen_StrBuilder then_output = gen_strbuilder_make_reserve(gen_heap(), 1);
|
||||
gen_StrBuilder else_output = gen_strbuilder_make_reserve(gen_heap(), 1);
|
||||
expect(gen_strbuilder_append_str(& then_output, gen_txt("KEEP")), gen_txt("wrapper then prefix"));
|
||||
expect(gen_strbuilder_append_str(& else_output, gen_txt("KEEP")), gen_txt("wrapper else prefix"));
|
||||
|
||||
bool then_result = false;
|
||||
bool else_result = false;
|
||||
gen_s32 selector = 1;
|
||||
gen_s32 after_branch = 0;
|
||||
if (selector == 1)
|
||||
gen_strtmpl_append(& then_output, then_result, gen_strtmpl_slots(gen_strtmpl_slot("arm", gen_txt("THEN"))),
|
||||
gen_txt("<arm>")
|
||||
);
|
||||
else
|
||||
gen_strtmpl_append(& else_output, else_result, gen_strtmpl_slots(gen_strtmpl_slot("arm", gen_txt("ELSE"))),
|
||||
gen_txt("<arm>")
|
||||
);
|
||||
after_branch = 1;
|
||||
expect(after_branch == 1, gen_txt("wrapper then continues"));
|
||||
expect(then_result, gen_txt("wrapper then assigned"));
|
||||
expect(else_result == false, gen_txt("wrapper else skipped"));
|
||||
expect(gen_str_are_equal(gen_strbuilder_to_str(then_output), gen_txt("KEEPTHEN")), gen_txt("wrapper then text"));
|
||||
expect(gen_str_are_equal(gen_strbuilder_to_str(else_output), gen_txt("KEEP")), gen_txt("wrapper else untouched"));
|
||||
gen_strbuilder_free(& then_output);
|
||||
gen_strbuilder_free(& else_output);
|
||||
|
||||
then_output = gen_strbuilder_make_reserve(gen_heap(), 1);
|
||||
else_output = gen_strbuilder_make_reserve(gen_heap(), 1);
|
||||
expect(gen_strbuilder_append_str(& then_output, gen_txt("KEEP")), gen_txt("wrapper skipped then prefix"));
|
||||
expect(gen_strbuilder_append_str(& else_output, gen_txt("KEEP")), gen_txt("wrapper taken else prefix"));
|
||||
|
||||
then_result = false;
|
||||
else_result = false;
|
||||
selector = 0;
|
||||
after_branch = 0;
|
||||
if (selector == 1) gen_strtmpl_append(& then_output, then_result, gen_strtmpl_slots(gen_strtmpl_slot("arm", gen_txt("THEN"))), gen_txt("<arm>"));
|
||||
else gen_strtmpl_append(& else_output, else_result, gen_strtmpl_slots(gen_strtmpl_slot("arm", gen_txt("ELSE"))), gen_txt("<arm>"));
|
||||
after_branch = 1;
|
||||
expect(after_branch == 1, gen_txt("wrapper else continues"));
|
||||
expect(then_result == false, gen_txt("wrapper then skipped"));
|
||||
expect(else_result, gen_txt("wrapper else assigned"));
|
||||
expect(gen_str_are_equal(gen_strbuilder_to_str(then_output), gen_txt("KEEP")), gen_txt("wrapper then untouched"));
|
||||
expect(gen_str_are_equal(gen_strbuilder_to_str(else_output), gen_txt("KEEPELSE")), gen_txt("wrapper else text"));
|
||||
gen_strbuilder_free(& then_output);
|
||||
gen_strbuilder_free(& else_output);
|
||||
}
|
||||
|
||||
gen_internal void expect_template_wrapper_once() {
|
||||
gen_s32 value_reads = 0;
|
||||
gen_s32 format_reads = 0;
|
||||
gen_s32 output_reads = 0;
|
||||
gen_s32 result_reads = 0;
|
||||
gen_StrBuilder output = gen_strbuilder_make_reserve(gen_heap(), 1);
|
||||
expect(gen_strbuilder_append_str(& output, gen_txt("KEEP")), gen_txt("wrapper once prefix"));
|
||||
bool appended = false;
|
||||
gen_strtmpl_append(
|
||||
counted_template_output(& output, & output_reads),
|
||||
counted_template_result(& appended, & result_reads)[0],
|
||||
gen_strtmpl_slots(
|
||||
gen_strtmpl_slot("k", counted_template_value(& value_reads)),
|
||||
gen_strtmpl_slot("tail", gen_txt("Z"))
|
||||
),
|
||||
counted_template_format(& format_reads)
|
||||
);
|
||||
expect(appended, gen_txt("wrapper once result"));
|
||||
expect(value_reads == 1, gen_txt("wrapper value once"));
|
||||
expect(format_reads == 1, gen_txt("wrapper format once"));
|
||||
expect(output_reads == 1, gen_txt("wrapper output once"));
|
||||
expect(result_reads == 1, gen_txt("wrapper result once"));
|
||||
expect(gen_str_are_equal(gen_strbuilder_to_str(output), gen_txt("KEEPONCE:ONCE")), gen_txt("wrapper once text"));
|
||||
gen_strbuilder_free(& output);
|
||||
}
|
||||
|
||||
gen_internal void expect_template_wrapper_resize_fail() {
|
||||
gen_s32 attempts = 0;
|
||||
gen_AllocatorInfo alloc = {0};
|
||||
alloc.Proc = resize_fail_allocator_proc;
|
||||
alloc.Data = & attempts;
|
||||
gen_StrBuilder output = gen_strbuilder_make_reserve(alloc, 4);
|
||||
expect(gen_strbuilder_capacity(output) == 4, gen_txt("wrapper resize capacity"));
|
||||
expect(gen_strbuilder_append_str(& output, gen_txt("KEEP")), gen_txt("wrapper resize prefix"));
|
||||
|
||||
bool appended = true;
|
||||
gen_strtmpl_append(& output, appended,
|
||||
gen_strtmpl_slots(gen_strtmpl_slot("blob", gen_txt("X"))),
|
||||
gen_txt("<blob>")
|
||||
);
|
||||
expect(appended == false, gen_txt("wrapper resize assigned false"));
|
||||
expect(gen_str_are_equal(gen_strbuilder_to_str(output), gen_txt("KEEP")), gen_txt("wrapper resize keep"));
|
||||
expect(attempts > 0, gen_txt("wrapper resize attempts"));
|
||||
gen_strbuilder_free(& output);
|
||||
}
|
||||
|
||||
gen_internal void expect_template_slot_literal()
|
||||
{
|
||||
gen_Str value = gen_txt("V");
|
||||
gen_StrTemplateSlot slot = gen_strtmpl_slot("k", value);
|
||||
expect(gen_str_are_equal(slot.Key, gen_txt("k")), gen_txt("slot literal key slice"));
|
||||
expect(slot.Key.Ptr[slot.Key.Len] == '\0', gen_txt("slot literal key terminator"));
|
||||
expect(slot.Value.Ptr == value.Ptr, gen_txt("slot literal value pointer"));
|
||||
expect(slot.Value.Len == value.Len, gen_txt("slot literal value length"));
|
||||
|
||||
gen_Str assigned = gen_txt("W");
|
||||
slot = gen_strtmpl_slot("key", assigned);
|
||||
expect(gen_str_are_equal(slot.Key, gen_txt("key")), gen_txt("slot assign key slice"));
|
||||
expect(slot.Value.Ptr == assigned.Ptr, gen_txt("slot assign value pointer"));
|
||||
expect(slot.Value.Len == assigned.Len, gen_txt("slot assign value length"));
|
||||
expect_template_text(gen_txt("slot assign expansion"), gen_txt("<key>"), & slot, 1, gen_txt("W"));
|
||||
|
||||
gen_StrTemplateSlot row[2] = {
|
||||
gen_strtmpl_slot("a", gen_txt("X")),
|
||||
gen_strtmpl_slot("b", gen_txt("Y"))
|
||||
};
|
||||
expect(gen_str_are_equal(row[0].Value, gen_txt("X")), gen_txt("slot array value slice"));
|
||||
expect(gen_str_are_equal(row[1].Key, gen_txt("b")), gen_txt("slot array key slice"));
|
||||
expect_template_text(gen_txt("slot array entry"), gen_txt("<b><a>"), row, 2, gen_txt("YX"));
|
||||
|
||||
gen_StrBuilder output = gen_strbuilder_make_reserve(gen_heap(), 1);
|
||||
expect(gen_strbuilder_append_str(& output, gen_txt("KEEP")), gen_txt("slot wrapper prefix"));
|
||||
bool appended = false;
|
||||
gen_strtmpl_append(& output, appended, gen_strtmpl_slots(
|
||||
gen_strtmpl_slot("name", gen_txt("world")),
|
||||
gen_strtmpl_slot("tail", gen_txt("!"))
|
||||
),
|
||||
gen_txt("<name><tail>")
|
||||
);
|
||||
expect(appended, gen_txt("slot wrapper result"));
|
||||
expect(gen_str_are_equal(gen_strbuilder_to_str(output), gen_txt("KEEPworld!")), gen_txt("slot wrapper text"));
|
||||
gen_strbuilder_free(& output);
|
||||
|
||||
gen_StrTemplateSlot long_key = gen_strtmpl_slot("operands[0]", gen_txt("eax"));
|
||||
expect(gen_str_are_equal(long_key.Key, gen_txt("operands[0]")), gen_txt("slot literal key slice"));
|
||||
expect(long_key.Key.Ptr[long_key.Key.Len] == '\0', gen_txt("slot literal key excludes nul"));
|
||||
expect(gen_str_are_equal(long_key.Value, gen_txt("eax")), gen_txt("slot literal value slice"));
|
||||
|
||||
gen_s32 value_reads = 0;
|
||||
gen_StrTemplateSlot once = gen_strtmpl_slot("k", counted_template_value(& value_reads));
|
||||
expect(value_reads == 1, gen_txt("slot value once"));
|
||||
expect(gen_str_are_equal(once.Value, gen_txt("ONCE")), gen_txt("slot value once text"));
|
||||
expect(once.Key.Len == 1, gen_txt("slot value once key"));
|
||||
|
||||
char nul_bytes[4] = { 'a', '\0', 'b', '!' };
|
||||
gen_Str nul_value = { nul_bytes, 3 };
|
||||
gen_StrTemplateSlot nul_slot = gen_strtmpl_slot("n", nul_value);
|
||||
expect(nul_slot.Value.Ptr == nul_bytes, gen_txt("slot nul pointer"));
|
||||
expect(nul_slot.Value.Len == 3, gen_txt("slot nul length"));
|
||||
expect(nul_slot.Value.Ptr[1] == '\0', gen_txt("slot nul byte"));
|
||||
char nul_expected[5] = { '[', 'a', '\0', 'b', ']' };
|
||||
gen_Str nul_expansion = { nul_expected, 5 };
|
||||
expect_template_text(gen_txt("slot nul expansion"), gen_txt("[<n>]"), & nul_slot, 1, nul_expansion);
|
||||
|
||||
gen_Str angle_value = gen_txt("a<b>c");
|
||||
gen_StrTemplateSlot angle_slot = gen_strtmpl_slot("raw", angle_value);
|
||||
expect(angle_slot.Value.Ptr == angle_value.Ptr, gen_txt("slot angle pointer"));
|
||||
expect(angle_slot.Value.Len == angle_value.Len, gen_txt("slot angle length"));
|
||||
expect_template_text(gen_txt("slot angle expansion"), gen_txt("<raw>"), & angle_slot, 1, gen_txt("a<b>c"));
|
||||
|
||||
gen_Str empty_value = { 0, 0 };
|
||||
gen_StrTemplateSlot empty_slot = gen_strtmpl_slot("e", empty_value);
|
||||
expect(empty_slot.Value.Ptr == 0, gen_txt("slot null value pointer"));
|
||||
expect(empty_slot.Value.Len == 0, gen_txt("slot null value length"));
|
||||
expect_template_text(gen_txt("slot null value expansion"), gen_txt("A<e>B"), & empty_slot, 1, gen_txt("AB"));
|
||||
|
||||
gen_Str percent_value = gen_txt("%d:%S");
|
||||
gen_StrTemplateSlot percent_slot = gen_strtmpl_slot("fmt", percent_value);
|
||||
expect(percent_slot.Value.Ptr == percent_value.Ptr, gen_txt("slot percent value pointer"));
|
||||
expect(gen_str_are_equal(percent_slot.Value, gen_txt("%d:%S")), gen_txt("slot percent value text"));
|
||||
expect_template_text(gen_txt("slot percent literal"), gen_txt("pre %d:%S <fmt>"), & percent_slot, 1, gen_txt("pre %d:%S %d:%S"));
|
||||
}
|
||||
|
||||
gen_internal void expect_text_template(gen_ssize printf_maxlen) {
|
||||
expect_template_shapes();
|
||||
expect_template_lookup();
|
||||
expect_template_crc_collision();
|
||||
expect_template_boundaries();
|
||||
expect_template_growth(printf_maxlen);
|
||||
expect_template_resize_fail(printf_maxlen);
|
||||
expect_template_slice();
|
||||
expect_template_wrapper();
|
||||
expect_template_wrapper_scope();
|
||||
expect_template_wrapper_once();
|
||||
expect_template_wrapper_resize_fail();
|
||||
expect_template_slot_literal();
|
||||
}
|
||||
@@ -23,16 +23,13 @@ gen_internal void expect_variable(gen_CodeVar var, gen_Str label) {
|
||||
expect(var->Type == CT_Variable, label);
|
||||
expect(gen_str_contains(var->Name, gen_txt("baseline_message")), label);
|
||||
expect(var->ValueType != 0, label);
|
||||
expect(var->Value != 0, label);
|
||||
expect(var->Value != 0, label);
|
||||
if (var->Value != 0) {
|
||||
expect(gen_str_contains(var->Value->Content, gen_txt("GENCPP_BASELINE")), label);
|
||||
}
|
||||
}
|
||||
|
||||
gen_internal gen_Code_POD code_pod(gen_Code code) {
|
||||
gen_Code_POD result = { (gen_AST*)code };
|
||||
return result;
|
||||
}
|
||||
gen_internal gen_Code_POD code_pod(gen_Code code) { gen_Code_POD result = { (gen_AST*)code }; return result; }
|
||||
|
||||
gen_internal void expect_parse_info(void* result, ParseInfo* info, gen_Str label) {
|
||||
expect(result != 0, label);
|
||||
@@ -42,9 +39,14 @@ gen_internal void expect_parse_info(void* result, ParseInfo* info, gen_Str label
|
||||
expect(info->lexed.messages == 0 && info->messages == 0, label);
|
||||
}
|
||||
|
||||
#include "dependencies/strings.c"
|
||||
#include "dependencies/simd.c"
|
||||
|
||||
int main()
|
||||
{
|
||||
gen_Context ctx = {0}; gen_init(&ctx);
|
||||
expect_text_template(GEN_PRINTF_MAXLEN);
|
||||
expect_simd_lanes();
|
||||
|
||||
gen_CodeTypename type = gen_def_type(name(char), .specifiers = gen_def_specifiers(2, Spec_Const, Spec_Ptr) );
|
||||
gen_CodeVar upfront = gen_def_variable(type, name(baseline_message),
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
internal void expect_simd_lanes()
|
||||
{
|
||||
#if defined(GEN_SYSTEM_WINDOWS) && (defined(_M_X64) || defined(__x86_64__))
|
||||
using namespace gen;
|
||||
u8 storage[32];
|
||||
mem_set(storage, 0x11, 32);
|
||||
u8 pattern[16] = {
|
||||
0x00, 0x80, 0xFF, 0x01,
|
||||
0x02, 0x03, 0x04, 0x05,
|
||||
0x06, 0x07, 0x08, 0x09,
|
||||
0x0A, 0x0B, 0x0C, 0x0D
|
||||
};
|
||||
mem_copy(storage + 1, pattern, 16);
|
||||
u8x16 loaded = u8x16_load(storage + 1);
|
||||
u8x16 again = u8x16_load(storage + 1);
|
||||
u16 same = u8x16_mask(u8x16_equal(loaded, again));
|
||||
expect(same == 0xFFFF, txt("simd u8 equal load"));
|
||||
u16 zero_mask = u8x16_mask(u8x16_equal(loaded, u8x16_splat(0x00)));
|
||||
expect(zero_mask == 0x0001, txt("simd byte 0"));
|
||||
u16 high_bit = u8x16_mask(u8x16_equal(loaded, u8x16_splat(0x80)));
|
||||
expect(high_bit == 0x0002, txt("simd byte 0x80"));
|
||||
u16 top = u8x16_mask(u8x16_equal(loaded, u8x16_splat(0xFF)));
|
||||
expect(top == 0x0004, txt("simd byte 0xFF"));
|
||||
|
||||
u8 high_lane[16];
|
||||
mem_set(high_lane, 0x22, 16);
|
||||
high_lane[15] = 0xFF;
|
||||
u8x16 high_loaded = u8x16_load(high_lane);
|
||||
u16 lane15 = u8x16_mask(u8x16_equal(high_loaded, u8x16_splat(0xFF)));
|
||||
expect(lane15 == 0x8000, txt("simd lane 15"));
|
||||
expect(u32_mask_first_set(lane15) == 15, txt("simd first set lane 15"));
|
||||
|
||||
u8 window[24];
|
||||
mem_set(window, 0xEE, 24);
|
||||
window[1] = 0x00;
|
||||
window[2] = 0x80;
|
||||
window[3] = 0xFF;
|
||||
window[4] = 0x11;
|
||||
window[5] = 0x22;
|
||||
window[6] = 0x33;
|
||||
window[7] = 0x44;
|
||||
window[8] = 0x55;
|
||||
u8x16 loaded8 = u8x16_load8(window + 1);
|
||||
u16 upper = u8x16_mask(u8x16_equal(loaded8, u8x16_splat(0x00)));
|
||||
expect((upper & 0xFF00) == 0xFF00, txt("simd load8 upper zero"));
|
||||
expect((upper & 0x0001) == 0x0001, txt("simd load8 low zero"));
|
||||
u16 low_ff = u8x16_mask(u8x16_equal(loaded8, u8x16_splat(0xFF)));
|
||||
expect(low_ff == 0x0004, txt("simd load8 low 0xFF"));
|
||||
u16 low_80 = u8x16_mask(u8x16_equal(loaded8, u8x16_splat(0x80)));
|
||||
expect(low_80 == 0x0002, txt("simd load8 low 0x80"));
|
||||
u16 byte7 = u8x16_mask(u8x16_equal(loaded8, u8x16_splat(0x55)));
|
||||
expect(byte7 == 0x0080, txt("simd load8 byte 7"));
|
||||
u16 sentinel = u8x16_mask(u8x16_equal(loaded8, u8x16_splat(0xEE)));
|
||||
expect(sentinel == 0, txt("simd load8 ignores tail"));
|
||||
u8x16 loaded16 = u8x16_load(window + 1);
|
||||
u16 tail = u8x16_mask(u8x16_equal(loaded16, u8x16_splat(0xEE)));
|
||||
expect(tail == 0xFF00, txt("simd load16 sees load8 tail"));
|
||||
|
||||
u32 hashes[4] = { 0x00000000u, 0x7FFFFFFFu, 0x80000000u, 0xFFFFFFFFu };
|
||||
u32x4 hash_loaded = u32x4_load(hashes);
|
||||
u8 lane0 = u32x4_mask(u32x4_equal(hash_loaded, u32x4_splat(0x00000000u)));
|
||||
u8 lane1 = u32x4_mask(u32x4_equal(hash_loaded, u32x4_splat(0x7FFFFFFFu)));
|
||||
u8 lane2 = u32x4_mask(u32x4_equal(hash_loaded, u32x4_splat(0x80000000u)));
|
||||
u8 lane3 = u32x4_mask(u32x4_equal(hash_loaded, u32x4_splat(0xFFFFFFFFu)));
|
||||
expect(lane0 == 0x01, txt("simd u32 lane 0"));
|
||||
expect(lane1 == 0x02, txt("simd u32 lane 1"));
|
||||
expect(lane2 == 0x04, txt("simd u32 bit31 lane"));
|
||||
expect(lane3 == 0x08, txt("simd u32 lane 3"));
|
||||
expect((lane2 & 0xF0) == 0, txt("simd u32 mask low bits"));
|
||||
|
||||
u32 all_values[4] = { 0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u };
|
||||
u32x4 all_loaded = u32x4_load(all_values);
|
||||
u8 all_mask = u32x4_mask(u32x4_equal(all_loaded, u32x4_splat(0x80000000u)));
|
||||
expect(all_mask == 0x0F, txt("simd u32 all bit31"));
|
||||
u8 none_mask = u32x4_mask(u32x4_equal(all_loaded, u32x4_splat(0x7FFFFFFFu)));
|
||||
expect(none_mask == 0x00, txt("simd u32 none"));
|
||||
|
||||
u8 hash_storage[20];
|
||||
mem_set(hash_storage, 0xA5, 20);
|
||||
mem_copy(hash_storage + 1, hashes, 16);
|
||||
u32x4 unaligned = u32x4_load(hash_storage + 1);
|
||||
u8 unaligned_mask = u32x4_mask(u32x4_equal(unaligned, u32x4_splat(0x80000000u)));
|
||||
expect(unaligned_mask == 0x04, txt("simd u32 unaligned bit31"));
|
||||
|
||||
expect(u32_mask_first_set(0x1u) == 0, txt("simd first set bit 0"));
|
||||
expect(u32_mask_first_set(0x14u) == 2, txt("simd first set lowest"));
|
||||
expect(u32_mask_first_set(0x8000u) == 15, txt("simd first set bit 15"));
|
||||
expect(u32_mask_first_set(0x80000000u) == 31, txt("simd first set bit 31"));
|
||||
expect(u32_mask_first_set(0xFFFFFFFFu) == 0, txt("simd first set all bits"));
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,651 @@
|
||||
internal gen::Str bounded_format(char* storage, gen::ssize storage_len, char const* bytes, gen::ssize len)
|
||||
{
|
||||
gen::mem_set(storage, '!', storage_len);
|
||||
if (len > 0)
|
||||
gen::mem_copy(storage, bytes, len);
|
||||
storage[storage_len - 1] = '\0';
|
||||
gen::Str result = { storage, len };
|
||||
return result;
|
||||
}
|
||||
|
||||
internal void expect_template_expansion(gen::Str label, gen::Str format, gen::StrTemplateSlot* slots, gen::ssize slot_count, gen::Str expansion)
|
||||
{
|
||||
using namespace gen;
|
||||
StrBuilder output = strbuilder_make_reserve(heap(), 1);
|
||||
expect(strbuilder_append_str(& output, txt("KEEP")), label);
|
||||
Slice_StrTemplateSlot slice = { nullptr, 0 };
|
||||
if (slot_count != 0) {
|
||||
slice.Ptr = slots;
|
||||
slice.Len = slot_count;
|
||||
}
|
||||
bool appended = strbuilder_append_template(& output, slice, format);
|
||||
expect(appended, label);
|
||||
Str got = strbuilder_to_str(output);
|
||||
expect(got.Len == 4 + expansion.Len, label);
|
||||
expect(str_starts_with(got, txt("KEEP")), label);
|
||||
if (got.Len >= 4 + expansion.Len) {
|
||||
Str tail = { got.Ptr + 4, expansion.Len };
|
||||
expect(str_are_equal(tail, expansion), label);
|
||||
}
|
||||
StrBuilderHeader* header = strbuilder_get_header(output);
|
||||
expect(header->Allocator.Proc == heap_allocator_proc, label);
|
||||
strbuilder_free(& output);
|
||||
}
|
||||
|
||||
internal void expect_template_text(gen::Str label, gen::Str format, gen::StrTemplateSlot* slots, gen::ssize slot_count, gen::Str expansion)
|
||||
{
|
||||
expect_template_expansion(label, format, slots, slot_count, expansion);
|
||||
}
|
||||
|
||||
internal void expect_literal_boundary(gen::ssize mark) {
|
||||
using namespace gen;
|
||||
char format_storage[64];
|
||||
char format_bytes[40];
|
||||
for (ssize idx = 0; idx < mark; ++ idx)
|
||||
format_bytes[idx] = 'L';
|
||||
Str format = bounded_format(format_storage, 64, format_bytes, mark);
|
||||
Str expansion = { format_bytes, mark };
|
||||
expect(format_storage[mark] == '!', txt("literal boundary"));
|
||||
expect_template_expansion(txt("literal boundary"), format, nullptr, 0, expansion);
|
||||
}
|
||||
|
||||
internal void expect_open_boundary(gen::ssize mark)
|
||||
{
|
||||
using namespace gen;
|
||||
char format_storage[64];
|
||||
char format_bytes[48];
|
||||
char expansion_bytes[48];
|
||||
ssize format_len = 0;
|
||||
for (ssize idx = 0; idx < mark; ++ idx)
|
||||
format_bytes[format_len ++] = 'L';
|
||||
format_bytes[format_len ++] = '<';
|
||||
format_bytes[format_len ++] = 'k';
|
||||
format_bytes[format_len ++] = '>';
|
||||
format_bytes[format_len ++] = 'Z';
|
||||
Str format = bounded_format(format_storage, 64, format_bytes, format_len);
|
||||
StrTemplateSlot slot = {};
|
||||
slot.Key = txt("k");
|
||||
slot.Value = txt("V");
|
||||
ssize expansion_len = 0;
|
||||
for (ssize idx = 0; idx < mark; ++ idx)
|
||||
expansion_bytes[expansion_len ++] = 'L';
|
||||
expansion_bytes[expansion_len ++] = 'V';
|
||||
expansion_bytes[expansion_len ++] = 'Z';
|
||||
Str expansion = { expansion_bytes, expansion_len };
|
||||
expect(format.Ptr[mark] == '<', txt("open boundary"));
|
||||
expect(format_storage[format_len] == '!', txt("open boundary"));
|
||||
expect_template_expansion(txt("open boundary"), format, & slot, 1, expansion);
|
||||
}
|
||||
|
||||
internal void expect_close_boundary(gen::ssize mark) {
|
||||
using namespace gen;
|
||||
char format_storage[64];
|
||||
char format_bytes[48];
|
||||
char key_storage[40];
|
||||
ssize key_len = mark - 1;
|
||||
mem_set(key_storage, '!', 40);
|
||||
for (ssize idx = 0; idx < key_len; ++ idx)
|
||||
key_storage[idx] = 'k';
|
||||
key_storage[39] = '\0';
|
||||
Str key = { key_storage, key_len };
|
||||
ssize format_len = 0;
|
||||
format_bytes[format_len ++] = '<';
|
||||
for (ssize idx = 0; idx < key_len; ++ idx)
|
||||
format_bytes[format_len ++] = 'k';
|
||||
format_bytes[format_len ++] = '>';
|
||||
format_bytes[format_len ++] = 'Z';
|
||||
Str format = bounded_format(format_storage, 64, format_bytes, format_len);
|
||||
StrTemplateSlot slot = {};
|
||||
slot.Key = key;
|
||||
slot.Value = txt("V");
|
||||
expect(format.Ptr[mark] == '>', txt("close boundary"));
|
||||
expect(key.Ptr[key.Len] == '!', txt("close boundary"));
|
||||
expect(format_storage[format_len] == '!', txt("close boundary"));
|
||||
expect_template_expansion(txt("close boundary"), format, & slot, 1, txt("VZ"));
|
||||
}
|
||||
|
||||
internal void expect_template_shapes()
|
||||
{
|
||||
using namespace gen;
|
||||
char literal_storage[8];
|
||||
char literal_bytes[6] = { 'a', 'b', 'c', 'D', 'E', 'F' };
|
||||
Str literal = bounded_format(literal_storage, 8, literal_bytes, 3);
|
||||
expect(literal_storage[3] == '!', txt("literal sentinel"));
|
||||
expect_template_expansion(txt("literal bounded"), literal, nullptr, 0, txt("abc"));
|
||||
|
||||
char empty_storage[4];
|
||||
Str empty_format = bounded_format(empty_storage, 4, empty_storage, 0);
|
||||
expect(empty_storage[0] == '!', txt("empty sentinel"));
|
||||
expect_template_expansion(txt("empty format"), empty_format, nullptr, 0, txt(""));
|
||||
|
||||
StrTemplateSlot name_slot = {};
|
||||
name_slot.Key = txt("name");
|
||||
name_slot.Value = txt("world");
|
||||
expect_template_text(txt("one slot"), txt("hello <name>!"), & name_slot, 1, txt("hello world!"));
|
||||
|
||||
StrTemplateSlot repeat_slot = {};
|
||||
repeat_slot.Key = txt("w");
|
||||
repeat_slot.Value = txt("v");
|
||||
expect_template_text(txt("repeated slot"), txt("<w>-<w>-<w>"), & repeat_slot, 1, txt("v-v-v"));
|
||||
|
||||
StrTemplateSlot adjacent[2] = {};
|
||||
adjacent[0].Key = txt("a");
|
||||
adjacent[0].Value = txt("X");
|
||||
adjacent[1].Key = txt("b");
|
||||
adjacent[1].Value = txt("Y");
|
||||
expect_template_text(txt("adjacent slots"), txt("<a><b><a>"), adjacent, 2, txt("XYX"));
|
||||
|
||||
StrTemplateSlot raw_slots[3] = {};
|
||||
raw_slots[0].Key = txt("raw");
|
||||
raw_slots[0].Value = txt("a<b>c");
|
||||
raw_slots[1].Key = txt("angles");
|
||||
raw_slots[1].Value = txt("<>");
|
||||
raw_slots[2].Key = txt("b");
|
||||
raw_slots[2].Value = txt("SHOULD_NOT");
|
||||
expect_template_text(txt("value angles literal"), txt("<raw><angles>"), raw_slots, 3, txt("a<b>c<>"));
|
||||
|
||||
char empty_value_storage[2] = { '!', '!' };
|
||||
StrTemplateSlot empty_slot = {};
|
||||
empty_slot.Key = txt("e");
|
||||
empty_slot.Value = { empty_value_storage, 0 };
|
||||
expect_template_text(txt("empty value"), txt("A<e>B"), & empty_slot, 1, txt("AB"));
|
||||
|
||||
StrTemplateSlot empty_key = {};
|
||||
empty_key.Key = { nullptr, 0 };
|
||||
empty_key.Value = txt("Z");
|
||||
expect_template_text(txt("empty key"), txt("<>"), & empty_key, 1, txt("Z"));
|
||||
|
||||
StrTemplateSlot null_value = {};
|
||||
null_value.Key = txt("e");
|
||||
null_value.Value = { nullptr, 0 };
|
||||
expect(null_value.Value.Ptr == nullptr, txt("null empty value pointer"));
|
||||
expect(null_value.Value.Len == 0, txt("null empty value length"));
|
||||
expect_template_text(txt("null empty value"), txt("A<e>B"), & null_value, 1, txt("AB"));
|
||||
|
||||
char nul_value_bytes[4] = { 'a', '\0', 'b', '!' };
|
||||
char nul_expected[5] = { '[', 'a', '\0', 'b', ']' };
|
||||
StrTemplateSlot nul_slot = {};
|
||||
nul_slot.Key = txt("n");
|
||||
nul_slot.Value = { nul_value_bytes, 3 };
|
||||
gen::Str nul_expansion = { nul_expected, 5 };
|
||||
expect_template_text(txt("embedded nul"), txt("[<n>]"), & nul_slot, 1, nul_expansion);
|
||||
|
||||
StrTemplateSlot duffle[2] = {};
|
||||
duffle[0].Key = txt("operands[0]");
|
||||
duffle[0].Value = txt("eax");
|
||||
duffle[1].Key = txt("mod_rm.bits");
|
||||
duffle[1].Value = txt("11");
|
||||
expect_template_text(txt("duffle keys"), txt("mov <operands[0]>, <mod_rm.bits>;"), duffle, 2, txt("mov eax, 11;"));
|
||||
}
|
||||
|
||||
internal void fill_lookup_slots(gen::StrTemplateSlot* slots, gen::ssize slot_count) {
|
||||
gen::Str keys[9] = {
|
||||
txt("k0"), txt("k1"), txt("k2"), txt("k3"), txt("k4"),
|
||||
txt("k5"), txt("k6"), txt("k7"), txt("k8")
|
||||
};
|
||||
gen::Str values[9] = {
|
||||
txt("V0"), txt("V1"), txt("V2"), txt("V3"), txt("V4"),
|
||||
txt("V5"), txt("V6"), txt("V7"), txt("V8")
|
||||
};
|
||||
for (gen::ssize idx = 0; idx < slot_count; ++ idx) {
|
||||
slots[idx].Key = keys[idx];
|
||||
slots[idx].Value = values[idx];
|
||||
}
|
||||
}
|
||||
|
||||
internal void expect_template_lookup()
|
||||
{
|
||||
using namespace gen;
|
||||
StrTemplateSlot slots[9] = {};
|
||||
expect_template_text(txt("lookup 0"), txt("none"), nullptr, 0, txt("none"));
|
||||
|
||||
fill_lookup_slots(slots, 1);
|
||||
expect_template_text(txt("lookup 1"), txt("<k0>"), slots, 1, txt("V0"));
|
||||
|
||||
fill_lookup_slots(slots, 2);
|
||||
expect_template_text(txt("lookup 2"), txt("<k1><k0>"), slots, 2, txt("V1V0"));
|
||||
|
||||
fill_lookup_slots(slots, 3);
|
||||
expect_template_text(txt("lookup 3"), txt("<k2>-<k0>-<k1>"), slots, 3, txt("V2-V0-V1"));
|
||||
|
||||
fill_lookup_slots(slots, 4);
|
||||
expect_template_text(txt("lookup 4"), txt("<k3><k0><k2><k1>"), slots, 4, txt("V3V0V2V1"));
|
||||
|
||||
fill_lookup_slots(slots, 5);
|
||||
expect_template_text(txt("lookup 5 tail"), txt("<k4><k0><k3>"), slots, 5, txt("V4V0V3"));
|
||||
|
||||
fill_lookup_slots(slots, 8);
|
||||
expect_template_text(txt("lookup 8 lanes"), txt("<k7><k4><k3><k0>"), slots, 8, txt("V7V4V3V0"));
|
||||
|
||||
fill_lookup_slots(slots, 9);
|
||||
expect_template_text(txt("lookup 9 tail"), txt("<k8><k5><k6><k4>"), slots, 9, txt("V8V5V6V4"));
|
||||
|
||||
StrTemplateSlot dup_small[3] = {};
|
||||
dup_small[0].Key = txt("dup"); dup_small[0].Value = txt("FIRST");
|
||||
dup_small[1].Key = txt("other"); dup_small[1].Value = txt("X");
|
||||
dup_small[2].Key = txt("dup"); dup_small[2].Value = txt("SECOND");
|
||||
expect_template_text(txt("duplicate first wins"), txt("<dup><other>"), dup_small, 3, txt("FIRSTX"));
|
||||
|
||||
StrTemplateSlot dup_lanes[4] = {};
|
||||
dup_lanes[0].Key = txt("same"); dup_lanes[0].Value = txt("A");
|
||||
dup_lanes[1].Key = txt("same"); dup_lanes[1].Value = txt("B");
|
||||
dup_lanes[2].Key = txt("other"); dup_lanes[2].Value = txt("C");
|
||||
dup_lanes[3].Key = txt("same"); dup_lanes[3].Value = txt("D");
|
||||
expect_template_text(txt("duplicate lane first wins"), txt("<same><other>"), dup_lanes, 4, txt("AC"));
|
||||
|
||||
StrTemplateSlot dup_tail[5] = {};
|
||||
dup_tail[0].Key = txt("dup"); dup_tail[0].Value = txt("FIRST");
|
||||
dup_tail[1].Key = txt("k1"); dup_tail[1].Value = txt("V1");
|
||||
dup_tail[2].Key = txt("k2"); dup_tail[2].Value = txt("V2");
|
||||
dup_tail[3].Key = txt("k3"); dup_tail[3].Value = txt("V3");
|
||||
dup_tail[4].Key = txt("dup"); dup_tail[4].Value = txt("TAIL");
|
||||
expect_template_text(txt("duplicate tail first wins"), txt("<dup>"), dup_tail, 5, txt("FIRST"));
|
||||
}
|
||||
|
||||
namespace gen {
|
||||
internal void expect_template_boundaries() {
|
||||
ssize marks[12] = { 7, 8, 9, 15, 16, 17, 23, 24, 25, 31, 32, 33 };
|
||||
for (ssize idx = 0; idx < count_of(marks); ++ idx) {
|
||||
expect_literal_boundary(marks[idx]);
|
||||
expect_open_boundary(marks[idx]);
|
||||
expect_close_boundary(marks[idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void expect_template_growth(gen::ssize printf_maxlen)
|
||||
{
|
||||
using namespace gen;
|
||||
ssize huge_len = printf_maxlen + 64;
|
||||
char* huge = rcast(char*, alloc(heap(), huge_len + 1));
|
||||
expect(huge != nullptr, txt("growth alloc"));
|
||||
if (huge == nullptr) return;
|
||||
mem_set(huge, 'Q', huge_len);
|
||||
huge[huge_len] = '!';
|
||||
|
||||
Str value = { huge, huge_len };
|
||||
StrTemplateSlot slot = {};
|
||||
slot.Key = txt("blob");
|
||||
slot.Value = value;
|
||||
Str format = txt("<blob>");
|
||||
StrBuilder output = strbuilder_make_reserve(heap(), 1);
|
||||
expect(strbuilder_capacity(output) == 1, txt("growth initial capacity"));
|
||||
expect(strbuilder_append_str(& output, txt("KEEP")), txt("growth prefix"));
|
||||
|
||||
Slice_StrTemplateSlot slice = { & slot, 1 };
|
||||
bool appended = strbuilder_append_template(& output, slice, format);
|
||||
expect(appended, txt("growth append"));
|
||||
|
||||
Str got = strbuilder_to_str(output);
|
||||
expect(got.Len == 4 + huge_len, txt("growth length"));
|
||||
expect(got.Len > printf_maxlen, txt("growth beyond printf max"));
|
||||
expect(str_starts_with(got, txt("KEEP")), txt("growth keeps prefix"));
|
||||
|
||||
ssize capacity = strbuilder_capacity(output);
|
||||
expect(capacity >= got.Len, txt("growth capacity"));
|
||||
|
||||
if (got.Len == 4 + huge_len && capacity >= got.Len) {
|
||||
b32 payload_matches = true;
|
||||
for (ssize idx = 0; idx < got.Len - 4; ++ idx) {
|
||||
if (got.Ptr[4 + idx] != 'Q') payload_matches = false;
|
||||
}
|
||||
expect(payload_matches, txt("growth bytes"));
|
||||
}
|
||||
|
||||
StrBuilderHeader* header = strbuilder_get_header(output);
|
||||
expect(header->Allocator.Proc == heap_allocator_proc, txt("growth allocator"));
|
||||
expect(huge[huge_len] == '!', txt("growth sentinel"));
|
||||
strbuilder_free(& output);
|
||||
allocator_free(heap(), huge);
|
||||
}
|
||||
|
||||
internal void expect_template_crc_collision()
|
||||
{
|
||||
using namespace gen;
|
||||
Str plumless = txt("plumless");
|
||||
Str buckeroo = txt("buckeroo");
|
||||
u32 plum_hash = crc32(plumless.Ptr, plumless.Len);
|
||||
u32 buck_hash = crc32(buckeroo.Ptr, buckeroo.Len);
|
||||
expect(plum_hash == buck_hash, txt("crc collision equal"));
|
||||
expect(plum_hash == 0x4ddb0c25, txt("crc collision value"));
|
||||
expect(str_are_equal(plumless, buckeroo) == false, txt("crc collision keys differ"));
|
||||
|
||||
StrTemplateSlot scalar[2] = {};
|
||||
scalar[0].Key = plumless; scalar[0].Value = txt("WRONG");
|
||||
scalar[1].Key = buckeroo; scalar[1].Value = txt("RIGHT");
|
||||
expect_template_text(txt("crc scalar 2"), txt("<buckeroo>"), scalar, 2, txt("RIGHT"));
|
||||
|
||||
StrTemplateSlot lanes[4] = {};
|
||||
lanes[0].Key = plumless; lanes[0].Value = txt("WRONG");
|
||||
lanes[1].Key = txt("k1"); lanes[1].Value = txt("V1");
|
||||
lanes[2].Key = buckeroo; lanes[2].Value = txt("RIGHT");
|
||||
lanes[3].Key = txt("k3"); lanes[3].Value = txt("V3");
|
||||
expect_template_text(txt("crc lane reject"), txt("<buckeroo>"), lanes, 4, txt("RIGHT"));
|
||||
|
||||
StrTemplateSlot tail[5] = {};
|
||||
tail[0].Key = plumless; tail[0].Value = txt("WRONG");
|
||||
tail[1].Key = txt("k1"); tail[1].Value = txt("V1");
|
||||
tail[2].Key = txt("k2"); tail[2].Value = txt("V2");
|
||||
tail[3].Key = txt("k3"); tail[3].Value = txt("V3");
|
||||
tail[4].Key = buckeroo; tail[4].Value = txt("TAIL");
|
||||
expect_template_text(txt("crc tail after false hit"), txt("<buckeroo>"), tail, 5, txt("TAIL"));
|
||||
}
|
||||
|
||||
internal
|
||||
void* resize_fail_allocator_proc(void* allocator_data, gen::AllocType type, gen::ssize size, gen::ssize alignment
|
||||
, void* old_memory, gen::ssize old_size
|
||||
, gen::u64 flags
|
||||
){
|
||||
if (type == gen::EAllocation_RESIZE) {
|
||||
gen::s32* attempts = rcast(gen::s32*, allocator_data);
|
||||
attempts[0] = attempts[0] + 1;
|
||||
return nullptr;
|
||||
}
|
||||
return gen::heap_allocator_proc(nullptr, type, size, alignment, old_memory, old_size, flags);
|
||||
}
|
||||
|
||||
internal void expect_template_resize_fail(gen::ssize printf_maxlen)
|
||||
{
|
||||
using namespace gen;
|
||||
ssize huge_len = printf_maxlen + 64;
|
||||
char* huge = rcast(char*, alloc(heap(), huge_len + 1));
|
||||
expect(huge != nullptr, txt("resize fail alloc"));
|
||||
if (huge == nullptr) return;
|
||||
mem_set(huge, 'L', huge_len);
|
||||
huge[huge_len] = '!';
|
||||
Str huge_text = { huge, huge_len };
|
||||
|
||||
s32 literal_attempts = 0;
|
||||
AllocatorInfo literal_alloc = {};
|
||||
literal_alloc.Proc = resize_fail_allocator_proc;
|
||||
literal_alloc.Data = & literal_attempts;
|
||||
StrBuilder literal_output = strbuilder_make_reserve(literal_alloc, 4);
|
||||
expect(strbuilder_append_str(& literal_output, txt("KEEP")), txt("resize fail literal prefix"));
|
||||
|
||||
Slice_StrTemplateSlot literal_slots = { nullptr, 0 };
|
||||
bool literal_appended = strbuilder_append_template(& literal_output, literal_slots, huge_text);
|
||||
expect(literal_appended == false, txt("resize fail literal"));
|
||||
|
||||
Str literal_got = strbuilder_to_str(literal_output);
|
||||
expect(literal_got.Len == 4, txt("resize fail literal length"));
|
||||
expect(str_are_equal(literal_got, txt("KEEP")), txt("resize fail literal keep"));
|
||||
expect(literal_attempts > 0, txt("resize fail literal attempts"));
|
||||
strbuilder_free(& literal_output);
|
||||
|
||||
s32 value_attempts = 0;
|
||||
AllocatorInfo value_alloc = {};
|
||||
value_alloc.Proc = resize_fail_allocator_proc;
|
||||
value_alloc.Data = & value_attempts;
|
||||
|
||||
StrTemplateSlot slot = {};
|
||||
slot.Key = txt("blob");
|
||||
slot.Value = huge_text;
|
||||
Str format = txt("<blob>");
|
||||
StrBuilder value_output = strbuilder_make_reserve(value_alloc, 4);
|
||||
expect(strbuilder_append_str(& value_output, txt("KEEP")), txt("resize fail value prefix"));
|
||||
|
||||
Slice_StrTemplateSlot value_slots = { & slot, 1 };
|
||||
bool value_appended = strbuilder_append_template(& value_output, value_slots, format);
|
||||
expect(value_appended == false, txt("resize fail value"));
|
||||
|
||||
Str value_got = strbuilder_to_str(value_output);
|
||||
expect(value_got.Len == 4, txt("resize fail value length"));
|
||||
expect(str_are_equal(value_got, txt("KEEP")), txt("resize fail value keep"));
|
||||
expect(value_attempts > 0, txt("resize fail value attempts"));
|
||||
strbuilder_free(& value_output);
|
||||
allocator_free(heap(), huge);
|
||||
}
|
||||
|
||||
internal gen::Str counted_template_value (gen::s32* reads) { reads[0] = reads[0] + 1; return txt("ONCE"); }
|
||||
internal gen::Str counted_template_format(gen::s32* reads) { reads[0] = reads[0] + 1; return txt("<k>:<k>"); }
|
||||
internal gen::StrBuilder* counted_template_output(gen::StrBuilder* output, gen::s32* reads) { reads[0] = reads[0] + 1; return output; }
|
||||
internal bool* counted_template_result(bool* result, gen::s32* reads) { reads[0] = reads[0] + 1; return result; }
|
||||
|
||||
internal void expect_template_slice() {
|
||||
using namespace gen;
|
||||
StrTemplateSlot slots[2] = {
|
||||
strtmpl_slot("a", txt("X")),
|
||||
strtmpl_slot("b", txt("Y"))
|
||||
};
|
||||
Slice_StrTemplateSlot slice = { slots, 2 };
|
||||
StrBuilder output = strbuilder_make_reserve(heap(), 1);
|
||||
expect(strbuilder_append_str(& output, txt("KEEP")), txt("slice prefix"));
|
||||
|
||||
bool appended = strbuilder_append_template(& output, slice, txt("<b><a><b>"));
|
||||
expect(appended, txt("slice append"));
|
||||
expect(str_are_equal(strbuilder_to_str(output), txt("KEEPYXY")), txt("slice text"));
|
||||
expect(str_are_equal(slots[0].Key, txt("a")), txt("slice key"));
|
||||
expect(str_are_equal(slots[0].Value, txt("X")), txt("slice value"));
|
||||
strbuilder_free(& output);
|
||||
|
||||
slice.Ptr[0].Value = txt("Z");
|
||||
StrBuilder mutated_output = strbuilder_make_reserve(heap(), 1);
|
||||
bool mutated_appended = strbuilder_append_template(& mutated_output, slice, txt("<b><a><b>"));
|
||||
expect(mutated_appended, txt("slice mutate append"));
|
||||
expect(str_are_equal(strbuilder_to_str(mutated_output), txt("YZY")), txt("slice mutate text"));
|
||||
strbuilder_free(& mutated_output);
|
||||
|
||||
Slice_StrTemplateSlot empty_slice = { nullptr, 0 };
|
||||
StrBuilder empty_output = strbuilder_make_reserve(heap(), 1);
|
||||
expect(strbuilder_append_str(& empty_output, txt("KEEP")), txt("zero slice prefix"));
|
||||
bool empty_appended = strbuilder_append_template(& empty_output, empty_slice, txt("none"));
|
||||
expect(empty_appended, txt("zero slice append"));
|
||||
expect(str_are_equal(strbuilder_to_str(empty_output), txt("KEEPnone")), txt("zero slice text"));
|
||||
expect(empty_slice.Ptr == nullptr, txt("zero slice pointer"));
|
||||
expect(empty_slice.Len == 0, txt("zero slice length"));
|
||||
strbuilder_free(& empty_output);
|
||||
}
|
||||
|
||||
internal void expect_template_wrapper() {
|
||||
using namespace gen;
|
||||
StrBuilder output = strbuilder_make_reserve(heap(), 1);
|
||||
expect(strbuilder_append_str(& output, txt("KEEP")), txt("wrapper prefix"));
|
||||
|
||||
bool appended = false;
|
||||
strtmpl_append(& output, appended
|
||||
strtmpl_slots(
|
||||
strtmpl_slot("name", txt("world")),
|
||||
strtmpl_slot("tail", txt("!"))
|
||||
),
|
||||
txt("(hello <name>\n<name><tail>)")
|
||||
);
|
||||
expect(appended, txt("wrapper multiline result"));
|
||||
|
||||
Str got = strbuilder_to_str(output);
|
||||
expect(str_starts_with(got, txt("KEEP")), txt("wrapper prefix kept"));
|
||||
expect(str_are_equal (got, txt("KEEPhello world\nworld!")), txt("wrapper multiline text"));
|
||||
strbuilder_free(& output);
|
||||
}
|
||||
|
||||
internal void expect_template_wrapper_scope()
|
||||
{
|
||||
using namespace gen;
|
||||
StrBuilder then_output = strbuilder_make_reserve(heap(), 1);
|
||||
StrBuilder else_output = strbuilder_make_reserve(heap(), 1);
|
||||
expect(strbuilder_append_str(& then_output, txt("KEEP")), txt("wrapper then prefix"));
|
||||
expect(strbuilder_append_str(& else_output, txt("KEEP")), txt("wrapper else prefix"));
|
||||
bool then_result = false;
|
||||
bool else_result = false;
|
||||
s32 selector = 1;
|
||||
s32 after_branch = 0;
|
||||
if (selector == 1) strtmpl_append(& then_output, then_result, strtmpl_slots(strtmpl_slot("arm", txt("THEN"))), txt("<arm>"));
|
||||
else strtmpl_append(& else_output, else_result, strtmpl_slots(strtmpl_slot("arm", txt("ELSE"))), txt("<arm>"));
|
||||
|
||||
after_branch = 1;
|
||||
expect(after_branch == 1, txt("wrapper then continues"));
|
||||
expect(then_result, txt("wrapper then assigned"));
|
||||
expect(else_result == false, txt("wrapper else skipped"));
|
||||
expect(str_are_equal(strbuilder_to_str(then_output), txt("KEEPTHEN")), txt("wrapper then text"));
|
||||
expect(str_are_equal(strbuilder_to_str(else_output), txt("KEEP")), txt("wrapper else untouched"));
|
||||
strbuilder_free(& then_output);
|
||||
strbuilder_free(& else_output);
|
||||
|
||||
then_output = strbuilder_make_reserve(heap(), 1);
|
||||
else_output = strbuilder_make_reserve(heap(), 1);
|
||||
expect(strbuilder_append_str(& then_output, txt("KEEP")), txt("wrapper skipped then prefix"));
|
||||
expect(strbuilder_append_str(& else_output, txt("KEEP")), txt("wrapper taken else prefix"));
|
||||
|
||||
then_result = false;
|
||||
else_result = false;
|
||||
selector = 0;
|
||||
after_branch = 0;
|
||||
if (selector == 1) strtmpl_append(& then_output, then_result, strtmpl_slots(strtmpl_slot("arm", txt("THEN"))), txt("<arm>"));
|
||||
else strtmpl_append(& else_output, else_result, strtmpl_slots(strtmpl_slot("arm", txt("ELSE"))), txt("<arm>"));
|
||||
|
||||
after_branch = 1;
|
||||
expect(after_branch == 1, txt("wrapper else continues"));
|
||||
expect(then_result == false, txt("wrapper then skipped"));
|
||||
expect(else_result, txt("wrapper else assigned"));
|
||||
expect(str_are_equal(strbuilder_to_str(then_output), txt("KEEP")), txt("wrapper then untouched"));
|
||||
expect(str_are_equal(strbuilder_to_str(else_output), txt("KEEPELSE")), txt("wrapper else text"));
|
||||
strbuilder_free(& then_output);
|
||||
strbuilder_free(& else_output);
|
||||
}
|
||||
|
||||
internal void expect_template_wrapper_once() {
|
||||
using namespace gen;
|
||||
s32 value_reads = 0;
|
||||
s32 format_reads = 0;
|
||||
s32 output_reads = 0;
|
||||
s32 result_reads = 0;
|
||||
StrBuilder output = strbuilder_make_reserve(heap(), 1);
|
||||
expect(strbuilder_append_str(& output, txt("KEEP")), txt("wrapper once prefix"));
|
||||
|
||||
bool appended = false;
|
||||
strtmpl_append(
|
||||
counted_template_output(& output, & output_reads),
|
||||
counted_template_result(& appended, & result_reads)[0],
|
||||
strtmpl_slots(
|
||||
strtmpl_slot("k", counted_template_value(& value_reads)),
|
||||
strtmpl_slot("tail", txt("Z"))
|
||||
),
|
||||
counted_template_format(& format_reads)
|
||||
);
|
||||
expect(appended, txt("wrapper once result"));
|
||||
expect(value_reads == 1, txt("wrapper value once"));
|
||||
expect(format_reads == 1, txt("wrapper format once"));
|
||||
expect(output_reads == 1, txt("wrapper output once"));
|
||||
expect(result_reads == 1, txt("wrapper result once"));
|
||||
expect(str_are_equal(strbuilder_to_str(output), txt("KEEPONCE:ONCE")), txt("wrapper once text"));
|
||||
strbuilder_free(& output);
|
||||
}
|
||||
|
||||
internal void expect_template_wrapper_resize_fail() {
|
||||
using namespace gen;
|
||||
s32 attempts = 0;
|
||||
AllocatorInfo alloc = {};
|
||||
alloc.Proc = resize_fail_allocator_proc;
|
||||
alloc.Data = & attempts;
|
||||
|
||||
StrBuilder output = strbuilder_make_reserve(alloc, 4);
|
||||
expect(strbuilder_capacity(output) == 4, txt("wrapper resize capacity"));
|
||||
expect(strbuilder_append_str(& output, txt("KEEP")), txt("wrapper resize prefix"));
|
||||
bool appended = true;
|
||||
strtmpl_append(
|
||||
& output,
|
||||
appended,
|
||||
strtmpl_slots(strtmpl_slot("blob", txt("X"))),
|
||||
txt("<blob>")
|
||||
);
|
||||
expect(appended == false, txt("wrapper resize assigned false"));
|
||||
expect(str_are_equal(strbuilder_to_str(output), txt("KEEP")), txt("wrapper resize keep"));
|
||||
expect(attempts > 0, txt("wrapper resize attempts"));
|
||||
strbuilder_free(& output);
|
||||
}
|
||||
|
||||
internal void expect_template_slot_literal()
|
||||
{
|
||||
using namespace gen;
|
||||
Str value = txt("V");
|
||||
StrTemplateSlot slot = strtmpl_slot("k", value);
|
||||
expect(str_are_equal(slot.Key, txt("k")), txt("slot literal key text"));
|
||||
expect(slot.Key.Ptr[slot.Key.Len] == '\0', txt("slot literal key terminator"));
|
||||
expect(slot.Value.Ptr == value.Ptr, txt("slot literal value pointer"));
|
||||
expect(slot.Value.Len == value.Len, txt("slot literal value length"));
|
||||
|
||||
Str assigned = txt("W");
|
||||
slot = strtmpl_slot("key", assigned);
|
||||
expect(str_are_equal(slot.Key, txt("key")), txt("slot assign key"));
|
||||
expect(slot.Value.Ptr == assigned.Ptr, txt("slot assign value pointer"));
|
||||
expect(slot.Value.Len == assigned.Len, txt("slot assign value length"));
|
||||
expect_template_text(txt("slot assign expansion"), txt("<key>"), & slot, 1, txt("W"));
|
||||
|
||||
StrTemplateSlot row[2] = {
|
||||
strtmpl_slot("a", txt("X")),
|
||||
strtmpl_slot("b", txt("Y"))
|
||||
};
|
||||
expect(str_are_equal(row[0].Value, txt("X")), txt("slot array value"));
|
||||
expect(str_are_equal(row[1].Key, txt("b")), txt("slot array key"));
|
||||
expect_template_text(txt("slot array entry"), txt("<b><a>"), row, 2, txt("YX"));
|
||||
|
||||
StrBuilder output = strbuilder_make_reserve(heap(), 1);
|
||||
expect(strbuilder_append_str(& output, txt("KEEP")), txt("slot wrapper prefix"));
|
||||
bool appended = false;
|
||||
strtmpl_append(
|
||||
& output,
|
||||
appended,
|
||||
strtmpl_slots(
|
||||
strtmpl_slot("name", txt("world")),
|
||||
strtmpl_slot("tail", txt("!"))
|
||||
),
|
||||
txt("<name><tail>")
|
||||
);
|
||||
expect(appended, txt("slot wrapper result"));
|
||||
expect(str_are_equal(strbuilder_to_str(output), txt("KEEPworld!")), txt("slot wrapper text"));
|
||||
strbuilder_free(& output);
|
||||
|
||||
StrTemplateSlot long_key = strtmpl_slot("operands[0]", txt("eax"));
|
||||
expect(str_are_equal(long_key.Key, txt("operands[0]")), txt("slot literal key equal"));
|
||||
expect(long_key.Key.Ptr[long_key.Key.Len] == '\0', txt("slot literal key excludes nul"));
|
||||
expect(str_are_equal(long_key.Value, txt("eax")), txt("slot literal value"));
|
||||
|
||||
s32 value_reads = 0;
|
||||
StrTemplateSlot once = strtmpl_slot("k", counted_template_value(& value_reads));
|
||||
expect(value_reads == 1, txt("slot value once"));
|
||||
expect(str_are_equal(once.Value, txt("ONCE")), txt("slot value once text"));
|
||||
expect(once.Key.Len == 1, txt("slot value once key"));
|
||||
|
||||
char nul_bytes[4] = { 'a', '\0', 'b', '!' };
|
||||
Str nul_value = { nul_bytes, 3 };
|
||||
StrTemplateSlot nul_slot = strtmpl_slot("n", nul_value);
|
||||
expect(nul_slot.Value.Ptr == nul_bytes, txt("slot nul pointer"));
|
||||
expect(nul_slot.Value.Len == 3, txt("slot nul length"));
|
||||
expect(nul_slot.Value.Ptr[1] == '\0', txt("slot nul byte"));
|
||||
|
||||
char nul_expected[5] = { '[', 'a', '\0', 'b', ']' };
|
||||
Str nul_expansion = { nul_expected, 5 };
|
||||
expect_template_text(txt("slot nul expansion"), txt("[<n>]"), & nul_slot, 1, nul_expansion);
|
||||
|
||||
Str angle_value = txt("a<b>c");
|
||||
StrTemplateSlot angle_slot = strtmpl_slot("raw", angle_value);
|
||||
expect(angle_slot.Value.Ptr == angle_value.Ptr, txt("slot angle pointer"));
|
||||
expect(angle_slot.Value.Len == angle_value.Len, txt("slot angle length"));
|
||||
expect_template_text(txt("slot angle expansion"), txt("<raw>"), & angle_slot, 1, txt("a<b>c"));
|
||||
|
||||
Str empty_value = { nullptr, 0 };
|
||||
StrTemplateSlot empty_slot = strtmpl_slot("e", empty_value);
|
||||
expect(empty_slot.Value.Ptr == nullptr, txt("slot null value pointer"));
|
||||
expect(empty_slot.Value.Len == 0, txt("slot null value length"));
|
||||
expect_template_text(txt("slot null value expansion"), txt("A<e>B"), & empty_slot, 1, txt("AB"));
|
||||
|
||||
Str percent_value = txt("%d:%S");
|
||||
StrTemplateSlot percent_slot = strtmpl_slot("fmt", percent_value);
|
||||
expect(percent_slot.Value.Ptr == percent_value.Ptr, txt("slot percent value pointer"));
|
||||
expect(str_are_equal(percent_slot.Value, txt("%d:%S")), txt("slot percent value text"));
|
||||
expect_template_text(txt("slot percent literal"), txt("pre %d:%S <fmt>"), & percent_slot, 1, txt("pre %d:%S %d:%S"));
|
||||
}
|
||||
|
||||
internal void expect_text_template(gen::ssize printf_maxlen) {
|
||||
expect_template_shapes();
|
||||
expect_template_lookup();
|
||||
expect_template_crc_collision();
|
||||
gen::expect_template_boundaries();
|
||||
expect_template_growth(printf_maxlen);
|
||||
expect_template_resize_fail(printf_maxlen);
|
||||
expect_template_slice();
|
||||
expect_template_wrapper();
|
||||
expect_template_wrapper_scope();
|
||||
expect_template_wrapper_once();
|
||||
expect_template_wrapper_resize_fail();
|
||||
expect_template_slot_literal();
|
||||
}
|
||||
@@ -38,10 +38,15 @@ internal void expect_parse_result(gen::Code result, gen::ParseInfo* info, gen::C
|
||||
expect(info->lexed.messages == nullptr && info->messages == nullptr, label);
|
||||
}
|
||||
|
||||
#include "dependencies/strings.cpp"
|
||||
#include "dependencies/simd.cpp"
|
||||
|
||||
int main()
|
||||
{
|
||||
using namespace gen;
|
||||
Context ctx = {}; init(&ctx);
|
||||
expect_text_template(GEN_PRINTF_MAXLEN);
|
||||
expect_simd_lanes();
|
||||
|
||||
Opts_def_type type_opts = {};
|
||||
type_opts.specifiers = def_specifiers(args(Spec_Const, Spec_Ptr));
|
||||
|
||||
Reference in new issue
Block a user