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
|
||||
|
||||
Reference in new issue
Block a user