splink/source/language_layer.c

1610 lines
36 KiB
C

// NOTE(allen): Things I added durring this jam
// Inf32
// NegInf32
// AbsoluteValue
// StringSkip
// StringChop
// "strings.c"
// RangeuContains
// RangeSplit
// bug in RectContains
// bug in RectOverlaps
// StringMatchFlag_MatchCase -> StringMatchFlag_CaseInsensitive
// bug in StringListJoin
// fleshed out Length/Norm types for vectors
// TODO(allen): change the other scalar functions
// StringSplit
// S8Range
// f32Round
////////////////////////////////
// NOTE(allen): Scalar
#define FMod fmodf
#define SquareRoot sqrtf
#define Sin sinf
#define Cos cosf
#define Tan tanf
#define Pow32 powf
internal i32
i32Ceil(f32 x)
{
if (x > 0)
{
return(((i32) x) + 1);
}
else
{
return(((i32) x));
}
}
internal i32
i32Floor(f32 x)
{
if (x > 0)
{
return(((i32) x));
}
else
{
return(((i32) x) - 1);
}
}
internal f32
f32Ceil(f32 x)
{
if (x > 0)
{
return((f32) ((i32) x) + 1);
}
else
{
return((f32) ((i32) x));
}
}
internal f32
f32Floor(f32 x)
{
if (x > 0)
{
return((f32) ((i32) x));
}
else
{
return((f32) ((i32) x) - 1);
}
}
internal f32
f32Round(f32 x)
{
return(f32Floor(x + 0.5f));
}
internal f32
Lerp(f32 a, f32 t, f32 b)
{
return(a + (b - a)*t);
}
internal f32
AbsoluteValue(f32 f){
union { u32 u; f32 f; } x;
x.f = f;
x.u &= ~Sign32;
return(x.f);
}
internal f32
Inf32(void){
union { u32 u; f32 f; } x;
x.u = Exponent32;
return(x.f);
}
internal f32
NegInf32(void){
union { u32 u; f32 f; } x;
x.u = Sign32 | Exponent32;
return(x.f);
}
internal f32
SignOfSide(Side side){
return((side == Side_Min)?-1.f:1.f);
}
////////////////////////////////
// NOTE(allen): vector
#define V2Expand(v) ((v).x), ((v).y)
#define V3Expand(v) ((v).x), ((v).y), ((v).z)
#define V4Expand(v) ((v).x), ((v).y), ((v).z), ((v).w)
internal v2
V2Add(v2 a, v2 b)
{
v2 c = { a.x + b.x, a.y + b.y };
return c;
}
internal v3
V3Add(v3 a, v3 b)
{
v3 c = { a.x + b.x, a.y + b.y, a.z + b.z };
return c;
}
internal v4
V4Add(v4 a, v4 b)
{
v4 c = { a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w };
return c;
}
internal v2
V2Sub(v2 a, v2 b)
{
v2 c = { a.x - b.x, a.y - b.y };
return c;
}
internal v3
V3Sub(v3 a, v3 b)
{
v3 c = { a.x - b.x, a.y - b.y, a.z - b.z };
return c;
}
internal v4
V4Sub(v4 a, v4 b)
{
v4 c = { a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w };
return c;
}
internal v2
V2Mul(v2 v, f32 f)
{
v.x *= f;
v.y *= f;
return v;
}
internal v3
V3Mul(v3 v, f32 f)
{
v.x *= f;
v.y *= f;
v.z *= f;
return v;
}
internal v4
V4Mul(v4 v, f32 f)
{
v.x *= f;
v.y *= f;
v.z *= f;
v.w *= f;
return v;
}
internal f32
V2Dot(v2 a, v2 b)
{
return a.x*b.x + a.y*b.y;
}
internal f32
V3Dot(v3 a, v3 b)
{
return a.x*b.x + a.y*b.y + a.z*b.z;
}
internal f32
V4Dot(v4 a, v4 b)
{
return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w;
}
internal v2
V2Hadamard(v2 a, v2 b)
{
v2 v = {a.x*b.x, a.y*b.y};
return(v);
}
internal v3
V3Hadamard(v3 a, v3 b)
{
v3 v = {a.x*b.x, a.y*b.y, a.z*b.z};
return(v);
}
internal v4
V4Hadamard(v4 a, v4 b)
{
v4 v = {a.x*b.x, a.y*b.y, a.z*b.z, a.w*b.w};
return(v);
}
internal f32
V2Length1(v2 a)
{
return(AbsoluteValue(a.x) + AbsoluteValue(a.y));
}
internal f32
V3Length1(v3 a)
{
return(AbsoluteValue(a.x) + AbsoluteValue(a.y) + AbsoluteValue(a.z));
}
internal f32
V4Length1(v4 a)
{
return(AbsoluteValue(a.x) + AbsoluteValue(a.y) + AbsoluteValue(a.z) + AbsoluteValue(a.w));
}
internal f32
V2Length2(v2 a)
{
return SquareRoot(V2Dot(a,a));
}
internal f32
V3Length2(v3 a)
{
return SquareRoot(V3Dot(a,a));
}
internal f32
V4Length2(v4 a)
{
return SquareRoot(V4Dot(a,a));
}
#define V2Length V2Length2
#define V3Length V3Length2
#define V4Length V4Length2
internal f32
V2LengthInf(v2 a)
{
f32 f1 = AbsoluteValue(a.x);
f32 f2 = AbsoluteValue(a.y);
f1 = Max(f1, f2);
return(f1);
}
internal f32
V3LengthInf(v3 a)
{
f32 f1 = AbsoluteValue(a.x);
f32 f2 = AbsoluteValue(a.y);
f32 f3 = AbsoluteValue(a.z);
f1 = Max(f1, f2);
f1 = Max(f1, f3);
return(f1);
}
internal f32
V4LengthInf(v4 a)
{
f32 f1 = AbsoluteValue(a.x);
f32 f2 = AbsoluteValue(a.y);
f32 f3 = AbsoluteValue(a.z);
f32 f4 = AbsoluteValue(a.w);
f1 = Max(f1, f2);
f1 = Max(f1, f3);
f1 = Max(f1, f4);
return(f1);
}
#define V2Max V2LengthInf
#define V3Max V3LengthInf
#define V4Max V4LengthInf
internal f32
V2Min(v2 a)
{
f32 f1 = AbsoluteValue(a.x);
f32 f2 = AbsoluteValue(a.y);
f1 = Min(f1, f2);
return(f1);
}
internal f32
V3Min(v3 a)
{
f32 f1 = AbsoluteValue(a.x);
f32 f2 = AbsoluteValue(a.y);
f32 f3 = AbsoluteValue(a.z);
f1 = Min(f1, f2);
f1 = Min(f1, f3);
return(f1);
}
internal f32
V4Min(v4 a)
{
f32 f1 = AbsoluteValue(a.x);
f32 f2 = AbsoluteValue(a.y);
f32 f3 = AbsoluteValue(a.z);
f32 f4 = AbsoluteValue(a.w);
f1 = Min(f1, f2);
f1 = Min(f1, f3);
f1 = Min(f1, f4);
return(f1);
}
internal v2
V2Norm1(v2 v)
{
f32 inv_length = 1.f/V2Length1(v);
v2 result = { v.x*inv_length, v.y*inv_length, };
return result;
}
internal v3
V3Norm1(v3 v)
{
f32 inv_length = 1.f/V3Length1(v);
v3 result = { v.x*inv_length, v.y*inv_length, v.z*inv_length, };
return result;
}
internal v4
V4Norm1(v4 v)
{
f32 inv_length = 1.f/V4Length1(v);
v4 result = { v.x*inv_length, v.y*inv_length, v.z*inv_length, v.w*inv_length, };
return result;
}
internal v2
V2Norm2(v2 v)
{
f32 inv_length = 1.f/V2Length2(v);
v2 result = { v.x*inv_length, v.y*inv_length, };
return result;
}
internal v3
V3Norm2(v3 v)
{
f32 inv_length = 1.f/V3Length2(v);
v3 result = { v.x*inv_length, v.y*inv_length, v.z*inv_length, };
return result;
}
internal v4
V4Norm2(v4 v)
{
f32 inv_length = 1.f/V4Length2(v);
v4 result = { v.x*inv_length, v.y*inv_length, v.z*inv_length, v.w*inv_length, };
return result;
}
#define V2Normalize V2Norm2
#define V3Normalize V3Norm2
#define V4Normalize V4Norm2
internal v2
V2NormInf(v2 v)
{
f32 inv_length = 1.f/V2LengthInf(v);
v2 result = { v.x*inv_length, v.y*inv_length, };
return result;
}
internal v3
V3NormInf(v3 v)
{
f32 inv_length = 1.f/V3LengthInf(v);
v3 result = { v.x*inv_length, v.y*inv_length, v.z*inv_length, };
return result;
}
internal v4
V4NormInf(v4 v)
{
f32 inv_length = 1.f/V4LengthInf(v);
v4 result = { v.x*inv_length, v.y*inv_length, v.z*inv_length, v.w*inv_length, };
return result;
}
internal v3
V3Cross(v3 a, v3 b)
{
v3 result =
{
a.y*b.z - a.z*b.y,
a.z*b.x - a.x*b.z,
a.x*b.y - a.y*b.x,
};
return result;
}
////////////////////////////////
// NOTE(allen): matrix
internal m4
M4InitD(f32 diagonal)
{
m4 m =
{
{
{ diagonal },
{ 0.f, diagonal },
{ 0.f, 0.f, diagonal },
{ 0.f, 0.f, 0.f, diagonal },
}
};
return m;
}
internal m4
M4MultiplyM4(m4 a, m4 b)
{
m4 c = {0};
for(int j = 0; j < 4; ++j)
{
for(int i = 0; i < 4; ++i)
{
c.elements[i][j] = (a.elements[0][j]*b.elements[i][0] +
a.elements[1][j]*b.elements[i][1] +
a.elements[2][j]*b.elements[i][2] +
a.elements[3][j]*b.elements[i][3]);
}
}
return c;
}
internal m4
M4MultiplyF32(m4 a, f32 b)
{
for(int j = 0; j < 4; ++j)
{
for(int i = 0; i < 4; ++i)
{
a.elements[i][j] *= b;
}
}
return a;
}
internal v4
V4MultiplyM4(v4 v, m4 m)
{
v4 result = {0};
for(int i = 0; i < 4; ++i)
{
result.elements[i] = (v.elements[0]*m.elements[0][i] +
v.elements[1]*m.elements[1][i] +
v.elements[2]*m.elements[2][i] +
v.elements[3]*m.elements[3][i]);
}
return result;
}
internal m4
M4TranslateV3(v3 translation)
{
m4 result = M4InitD(1.f);
result.elements[3][0] = translation.x;
result.elements[3][1] = translation.y;
result.elements[3][2] = translation.z;
return result;
}
internal m4
M4ScaleV3(v3 scale)
{
m4 result = M4InitD(1.f);
result.elements[0][0] = scale.x;
result.elements[1][1] = scale.y;
result.elements[2][2] = scale.z;
return result;
}
internal m4
M4Perspective(f32 fov, f32 aspect_ratio, f32 near_z, f32 far_z)
{
m4 result = {0};
f32 tan_theta_over_2 = Tan(fov * (PI / 360.f));
result.elements[0][0] = 1.f / tan_theta_over_2;
result.elements[1][1] = aspect_ratio / tan_theta_over_2;
result.elements[2][3] = -1.f;
result.elements[2][2] = (near_z + far_z) / (near_z - far_z);
result.elements[3][2] = (2.f * near_z * far_z) / (near_z - far_z);
result.elements[3][3] = 0.f;
return result;
}
internal m4
M4LookAt(v3 eye, v3 center, v3 up)
{
m4 result;
v3 f = V3Normalize(V3Sub(center, eye));
v3 s = V3Normalize(V3Cross(f, up));
v3 u = V3Cross(s, f);
result.elements[0][0] = s.x;
result.elements[0][1] = u.x;
result.elements[0][2] = -f.x;
result.elements[0][3] = 0.0f;
result.elements[1][0] = s.y;
result.elements[1][1] = u.y;
result.elements[1][2] = -f.y;
result.elements[1][3] = 0.0f;
result.elements[2][0] = s.z;
result.elements[2][1] = u.z;
result.elements[2][2] = -f.z;
result.elements[2][3] = 0.0f;
result.elements[3][0] = -V3Dot(s, eye);
result.elements[3][1] = -V3Dot(u, eye);
result.elements[3][2] = V3Dot(f, eye);
result.elements[3][3] = 1.0f;
return result;
}
internal m4
M4Inverse(m4 m)
{
f32 coef00 = m.elements[2][2] * m.elements[3][3] - m.elements[3][2] * m.elements[2][3];
f32 coef02 = m.elements[1][2] * m.elements[3][3] - m.elements[3][2] * m.elements[1][3];
f32 coef03 = m.elements[1][2] * m.elements[2][3] - m.elements[2][2] * m.elements[1][3];
f32 coef04 = m.elements[2][1] * m.elements[3][3] - m.elements[3][1] * m.elements[2][3];
f32 coef06 = m.elements[1][1] * m.elements[3][3] - m.elements[3][1] * m.elements[1][3];
f32 coef07 = m.elements[1][1] * m.elements[2][3] - m.elements[2][1] * m.elements[1][3];
f32 coef08 = m.elements[2][1] * m.elements[3][2] - m.elements[3][1] * m.elements[2][2];
f32 coef10 = m.elements[1][1] * m.elements[3][2] - m.elements[3][1] * m.elements[1][2];
f32 coef11 = m.elements[1][1] * m.elements[2][2] - m.elements[2][1] * m.elements[1][2];
f32 coef12 = m.elements[2][0] * m.elements[3][3] - m.elements[3][0] * m.elements[2][3];
f32 coef14 = m.elements[1][0] * m.elements[3][3] - m.elements[3][0] * m.elements[1][3];
f32 coef15 = m.elements[1][0] * m.elements[2][3] - m.elements[2][0] * m.elements[1][3];
f32 coef16 = m.elements[2][0] * m.elements[3][2] - m.elements[3][0] * m.elements[2][2];
f32 coef18 = m.elements[1][0] * m.elements[3][2] - m.elements[3][0] * m.elements[1][2];
f32 coef19 = m.elements[1][0] * m.elements[2][2] - m.elements[2][0] * m.elements[1][2];
f32 coef20 = m.elements[2][0] * m.elements[3][1] - m.elements[3][0] * m.elements[2][1];
f32 coef22 = m.elements[1][0] * m.elements[3][1] - m.elements[3][0] * m.elements[1][1];
f32 coef23 = m.elements[1][0] * m.elements[2][1] - m.elements[2][0] * m.elements[1][1];
v4 fac0 = { coef00, coef00, coef02, coef03 };
v4 fac1 = { coef04, coef04, coef06, coef07 };
v4 fac2 = { coef08, coef08, coef10, coef11 };
v4 fac3 = { coef12, coef12, coef14, coef15 };
v4 fac4 = { coef16, coef16, coef18, coef19 };
v4 fac5 = { coef20, coef20, coef22, coef23 };
v4 vec0 = { m.elements[1][0], m.elements[0][0], m.elements[0][0], m.elements[0][0] };
v4 vec1 = { m.elements[1][1], m.elements[0][1], m.elements[0][1], m.elements[0][1] };
v4 vec2 = { m.elements[1][2], m.elements[0][2], m.elements[0][2], m.elements[0][2] };
v4 vec3 = { m.elements[1][3], m.elements[0][3], m.elements[0][3], m.elements[0][3] };
v4 inv0 = V4Add(V4Sub(V4Hadamard(vec1, fac0), V4Hadamard(vec2, fac1)), V4Hadamard(vec3, fac2));
v4 inv1 = V4Add(V4Sub(V4Hadamard(vec0, fac0), V4Hadamard(vec2, fac3)), V4Hadamard(vec3, fac4));
v4 inv2 = V4Add(V4Sub(V4Hadamard(vec0, fac1), V4Hadamard(vec1, fac3)), V4Hadamard(vec3, fac5));
v4 inv3 = V4Add(V4Sub(V4Hadamard(vec0, fac2), V4Hadamard(vec1, fac4)), V4Hadamard(vec2, fac5));
v4 sign_a = { +1, -1, +1, -1 };
v4 sign_b = { -1, +1, -1, +1 };
m4 inverse;
for(u32 i = 0; i < 4; ++i)
{
inverse.elements[0][i] = inv0.elements[i] * sign_a.elements[i];
inverse.elements[1][i] = inv1.elements[i] * sign_b.elements[i];
inverse.elements[2][i] = inv2.elements[i] * sign_a.elements[i];
inverse.elements[3][i] = inv3.elements[i] * sign_b.elements[i];
}
v4 row0 = { inverse.elements[0][0], inverse.elements[1][0], inverse.elements[2][0], inverse.elements[3][0] };
v4 m0 = { m.elements[0][0], m.elements[0][1], m.elements[0][2], m.elements[0][3] };
v4 dot0 = V4Hadamard(m0, row0);
f32 dot1 = (dot0.x + dot0.y) + (dot0.z + dot0.w);
f32 one_over_det = 1 / dot1;
return M4MultiplyF32(inverse, one_over_det);
}
internal m4
M4RemoveRotation(m4 mat)
{
v3 scale =
{
V3Length(v3(mat.elements[0][0], mat.elements[0][1], mat.elements[0][2])),
V3Length(v3(mat.elements[1][0], mat.elements[1][1], mat.elements[1][2])),
V3Length(v3(mat.elements[2][0], mat.elements[2][1], mat.elements[2][2])),
};
mat.elements[0][0] = scale.x;
mat.elements[1][0] = 0.f;
mat.elements[2][0] = 0.f;
mat.elements[0][1] = 0.f;
mat.elements[1][1] = scale.y;
mat.elements[2][1] = 0.f;
mat.elements[0][2] = 0.f;
mat.elements[1][2] = 0.f;
mat.elements[2][2] = scale.z;
return mat;
}
////////////////////////////////
// NOTE(allen): color
internal v3
RGBToHSV(v3 rgb)
{
f32 c_max = V3Max(rgb);
f32 c_min = V3Min(rgb);
f32 delta = c_max - c_min;
b32 c_max_is_r = rgb.r > rgb.g && rgb.r > rgb.b;
b32 c_max_is_g = rgb.g > rgb.r && rgb.g > rgb.b;
b32 c_max_is_b = rgb.b > rgb.r && rgb.b > rgb.g;
f32 h = (c_max_is_r ? (rgb.g - rgb.b) / delta + 0 : c_max_is_g ? (rgb.b - rgb.r) / delta + 2 : c_max_is_b ? (rgb.r - rgb.g) / delta + 4 : 0);
f32 s = c_max == 0 ? 0 : (delta / c_max);
f32 v = c_max;
v3 hsv = {h / 6.f, s, v};
return hsv;
}
internal v3
HSVToRGB(v3 hsv)
{
f32 h = FMod(hsv.x * 360.f, 360.f);
f32 s = hsv.y;
f32 v = hsv.z;
f32 c = v * s;
f32 x = c * (1 - AbsoluteValue(FMod((h / 60.f), 2) - 1));
f32 m = v - c;
f32 r;
f32 g;
f32 b;
if((h >= 0.f && h < 60.f) || (h >= 360.f && h < 420.f))
{
r = c;
g = x;
b = 0;
}
else if(h >= 60.f && h < 120.f)
{
r = x;
g = c;
b = 0;
}
else if(h >= 120.f && h < 180.f)
{
r = 0;
g = c;
b = x;
}
else if(h >= 180.f && h < 240.f)
{
r = 0;
g = x;
b = c;
}
else if(h >= 240.f && h < 300.f)
{
r = x;
g = 0;
b = c;
}
else if((h >= 300.f && h <= 360.f) ||
(h >= -60.f && h <= 0.f))
{
r = c;
g = 0;
b = x;
}
v3 rgb = {r + m, g + m, b + m};
return rgb;
}
////////////////////////////////
// NOTE(allen): interval
internal Range
MakeRange(f32 a, f32 b)
{
Range range = {a, b};
if (a > b)
{
Swap(f32, range.min, range.max);
}
return(range);
}
internal Rangei
MakeRangei(i64 a, i64 b)
{
Rangei range = {a, b};
if (a > b)
{
Swap(i64, range.min, range.max);
}
return(range);
}
internal Rangeu
MakeRangeu(u64 a, u64 b)
{
Rangeu range = {a, b};
if (a > b)
{
Swap(u64, range.min, range.max);
}
return(range);
}
#define RangeSize(range) ((range).max - (range).min)
internal Range
RangeUnion(Range a, Range b)
{
Range result;
result.min = Min(a.min, b.min);
result.max = Max(a.max, b.max);
return(result);
}
internal Range
RangeIntersection(Range a, Range b)
{
Range result;
result.min = Max(a.min, b.min);
result.max = Min(a.max, b.max);
result.min = ClampTop(result.min, result.max);
return(result);
}
internal Range
RangeGrow(Range range, f32 x)
{
range.min -= x;
range.max += x;
return(range);
}
internal Range
RangeShrink(Range range, f32 x)
{
range.min += x;
range.max -= x;
range.min = ClampTop(range.min, range.max);
return(range);
}
internal Range
RangeSplit(Range *free_range, Side side, f32 amt){
Range range;
if (side == Side_Min){
range.min = free_range->min;
range.max = range.min + amt;
range.max = ClampTop(range.max, free_range->max);
free_range->min = range.max;
}
else{
range.max = free_range->max;
range.min = range.max - amt;
range.min = ClampBot(range.min, free_range->min);
free_range->max = range.min;
}
return(range);
}
internal b32
RangeContains(Range range, f32 x)
{
return(range.min <= x && x < range.max);
}
internal b32
RangeOverlaps(Range a, Range b)
{
return(a.min < b.max && b.min < a.max);
}
internal b32
RangeuContains(Rangeu range, u64 x)
{
return(range.min <= x && x < range.max);
}
internal Rect
MakeRect(f32 x0, f32 y0, f32 x1, f32 y1)
{
Rect rect = {x0, y0, x1, y1};
return(rect);
}
internal Rect
MakeRectVec(v2 p0, v2 p1)
{
Rect rect = {p0.x, p0.y, p1.x, p1.y};
return(rect);
}
internal Rect
MakeRectRanges(Range x, Range y)
{
Rect rect = {x.min, y.min, x.max, y.max};
return(rect);
}
internal Rect
RectUnion(Rect a, Rect b)
{
Rect result;
result.x0 = Min(a.x0, b.x0);
result.y0 = Min(a.y0, b.y0);
result.x1 = Max(a.x1, b.x1);
result.y1 = Max(a.y1, b.y1);
return(result);
}
internal Rect
RectIntersect(Rect a, Rect b)
{
Rect result;
result.x0 = Max(a.x0, b.x0);
result.y0 = Max(a.y0, b.y0);
result.x1 = Min(a.x1, b.x1);
result.y1 = Min(a.y1, b.y1);
result.x0 = ClampTop(result.x0, result.x1);
result.y0 = ClampTop(result.y0, result.y1);
return(result);
}
internal Rect
RectGrow(Rect rect, f32 v)
{
rect.x0 -= v;
rect.y0 -= v;
rect.x1 += v;
rect.y1 += v;
return(rect);
}
internal Rect
RectShrink(Rect rect, f32 v)
{
rect.x0 += v;
rect.y0 += v;
rect.x1 -= v;
rect.y1 -= v;
rect.x0 = ClampTop(rect.x0, rect.x1);
rect.y0 = ClampTop(rect.y0, rect.y1);
return(rect);
}
internal Range
RectGetRange(Rect rect, Dimension dim)
{
Range range = {rect.p0.v[dim], rect.p1.v[dim]};
return(range);
}
internal b32
RectContains(Rect rect, v2 p)
{
return(rect.x0 <= p.x && p.x < rect.x1 && rect.y0 <= p.y && p.y < rect.y1);
}
internal b32
RectOverlaps(Rect a, Rect b)
{
return(a.x0 < b.x1 && b.x0 < a.x1 && a.y0 < b.y1 && b.y0 < a.y1);
}
internal v2
RectGetDim(Rect rect)
{
v2 p = {rect.x1 - rect.x0, rect.y1 - rect.y0};
return(p);
}
#define RectSize(r) RectGetDim(r)
internal v2
RectGetCenter(Rect rect)
{
v2 p = {0.5f*(rect.x1 + rect.x0), 0.5f*(rect.y1 + rect.y0)};
return(p);
}
////////////////////////////////
//~ NOTE(allen): String
internal b32
CharIsSpace(char c)
{
return((c) <= 32);
}
internal b32
CharIsAlpha(char c)
{
return ((c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z'));
}
internal b32
CharIsDigit(char c)
{
return (c >= '0' && c <= '9');
}
internal b32
CharIsSymbol(char c)
{
return (c == '~' ||
c == '!' ||
c == '%' ||
c == '^' ||
c == '&' ||
c == '*' ||
c == '(' ||
c == ')' ||
c == '[' ||
c == ']' ||
c == '{' ||
c == '}' ||
c == '-' ||
c == '+' ||
c == '=' ||
c == ';' ||
c == ':' ||
c == '<' ||
c == '>' ||
c == '/' ||
c == '?' ||
c == '.' ||
c == ',');
}
internal char
CharToLower(char c)
{
if(c >= 'A' && c <= 'Z')
{
return c + 32;
}
return c;
}
internal char
CharToUpper(char c)
{
if(c >= 'a' && c <= 'z')
{
return c - 32;
}
return c;
}
internal String8
S8(u8 *str, u64 size)
{
String8 result = {str, size};
return(result);
}
internal String8
S8Range(u8 *first, u8 *one_past_last)
{
String8 result = {first, one_past_last - first};
return(result);
}
internal String8
S8Zero(void){
String8 result = {0, 0};
return(result);
}
internal String8
String8FromCString(char *cstring)
{
String8 string = {0};
string.str = (u8 *)cstring;
string.size = CalculateCStringLength(cstring);
return string;
}
internal b32
StringMatchGeneric(String8 a, String8 b, StringMatchFlags flags)
{
b32 result = 0;
if(a.size == b.size || (flags & StringMatchFlag_RightSideSloppy))
{
b32 insensitive = (flags & StringMatchFlag_CaseInsensitive);
u64 size = Min(a.size, b.size);
result = 1;
for(u64 i = 0; i < size; ++i)
{
u8 at = a.str[i];
u8 bt = b.str[i];
if(insensitive)
{
at = CharToUpper(at);
bt = CharToUpper(bt);
}
if(at != bt)
{
result = 0;
break;
}
}
}
return result;
}
internal b32
StringMatch(String8 a, String8 b)
{
return StringMatchGeneric(a, b, 0);
}
internal b32
StringMatchCaseInsensitive(String8 a, String8 b)
{
return StringMatchGeneric(a, b, StringMatchFlag_CaseInsensitive);
}
internal u64
StringFindSubstringStart(String8 a, String8 sub, StringMatchFlags flags)
{
u64 result = ~(u64)0;
for (u64 i = 0; i < a.size; i += 1){
if (a.str[i] == sub.str[0]){
if (StringMatchGeneric(a, sub, flags | StringMatchFlag_RightSideSloppy)){
result = i;
break;
}
}
}
return(result);
}
internal String8
StringSubstring(String8 string, Rangeu range)
{
range.max = ClampTop(range.max, string.size);
range.min = ClampTop(range.min, string.size);
string.str += range.min;
string.size = RangeSize(range);
return(string);
}
internal String8
StringPrefix(String8 string, u64 size)
{
string.size = ClampTop(size, string.size);
return(string);
}
internal String8
StringSkip(String8 string, u64 val)
{
val = ClampTop(val, string.size);
string.str += val;
string.size -= val;
return(string);
}
internal String8
StringPostfix(String8 string, u64 size)
{
size = ClampTop(size, string.size);
string.str = (string.str + string.size) - size;
string.size = size;
return(string);
}
internal String8
StringChop(String8 string, u64 val)
{
val = ClampTop(val, string.size);
string.size -= val;
return(string);
}
internal String8
PushStringCat(M_Arena *arena, String8 a, String8 b){
String8 str;
str.size = a.size + b.size;
str.str = PushArray(arena, u8, str.size);
MemoryCopy(str.str, a.str, a.size);
MemoryCopy(str.str + a.size, b.str, b.size);
return(str);
}
internal String8
PushStringFV(M_Arena *arena, char *format, va_list args)
{
va_list args2;
va_copy(args2, args);
u32 needed_bytes = vsnprintf(0, 0, format, args) + 1;
String8 result = {0};
result.str = PushArray(arena, u8, needed_bytes);
result.size = vsnprintf((char*)result.str, needed_bytes, format, args2);
result.str[result.size] = 0;
return(result);
}
internal String8
PushStringF(M_Arena *arena, char *fmt, ...)
{
va_list args;
va_start(args, fmt);
String8 result = PushStringFV(arena, fmt, args);
va_end(args);
return(result);
}
////////////////////////////////
//~ NOTE(allen): String List
internal String8_Node*
StringListPush(M_Arena *arena, String8_List *list, String8 string)
{
String8_Node *node = PushArray(arena, String8_Node, 1);
SLLQueuePush(list->first, list->last, node);
list->node_count += 1;
list->total_size += string.size;
node->string = string;
return(node);
}
internal String8_Node*
StringListPushFront(M_Arena *arena, String8_List *list, String8 string)
{
String8_Node *node = PushArray(arena, String8_Node, 1);
SLLQueuePushFront(list->first, list->last, node);
list->node_count += 1;
list->total_size += string.size;
node->string = string;
return(node);
}
internal String8_Node*
StringListPushF(M_Arena *arena, String8_List *list, char *fmt, ...)
{
va_list args;
va_start(args, fmt);
String8 string = PushStringFV(arena, fmt, args);
String8_Node *result = StringListPush(arena, list, string);
va_end(args);
return(result);
}
internal String8_Node*
StringListPushFrontF(M_Arena *arena, String8_List *list, char *fmt, ...)
{
va_list args;
va_start(args, fmt);
String8 string = PushStringFV(arena, fmt, args);
String8_Node *result = StringListPushFront(arena, list, string);
va_end(args);
return(result);
}
internal void
StringListRemoveEmpties(String8_List *list)
{
String8_Node *new_first = 0;
String8_Node *new_last = 0;
u64 new_count = 0;
for (String8_Node *node = list->first, *next = 0;
node != 0;
node = next)
{
next = node->next;
if (node->string.size > 0){
SLLQueuePush(new_first, new_last, node);
new_count += 1;
}
}
list->first = new_first;
list->last = new_last;
list->node_count = new_count;
}
internal String8_List
StringSplit(M_Arena *arena, String8 string, u8 *split_chars, u64 split_char_count)
{
String8_List list = {0};
u8 *ptr = string.str;
u8 *opl = string.str + string.size;
for (;ptr < opl;)
{
u8 *first = ptr;
for (;ptr < opl; ptr += 1)
{
u8 c = *ptr;
b32 is_split = 0;
for (u64 i = 0; i < split_char_count; i += 1)
{
if (split_chars[i] == c)
{
is_split = 1;
break;
}
}
if (is_split)
{
break;
}
}
StringListPush(arena, &list, S8Range(first, ptr));
ptr += 1;
}
return(list);
}
internal String8
StringListJoin(M_Arena *arena, String8_List *list, String_Join *optional_join)
{
String_Join join = {0};
if (optional_join != 0)
{
MemoryCopyStruct(&join, optional_join);
}
String8 result;
result.size = join.pre.size + join.post.size + (list->node_count - 1)*join.sep.size + list->total_size;
u8 *ptr = result.str = PushArray(arena, u8, result.size);
MemoryCopy(ptr, join.pre.str, join.pre.size);
ptr += join.pre.size;
for (String8_Node *node = list->first;
node != 0;
node = node->next)
{
MemoryCopy(ptr, node->string.str, node->string.size);
ptr += node->string.size;
if (node->next != 0)
{
MemoryCopy(ptr, join.sep.str, join.sep.size);
ptr += join.sep.size;
}
}
MemoryCopy(ptr, join.post.str, join.post.size);
ptr += join.post.size;
return(result);
}
////////////////////////////////
//~ NOTE(allen): Additional String Stuff
internal i64
GetFirstIntegerFromString(String8 string)
{
i32 result = 0;
b32 found_first_digit = 0;
u32 integer_write_pos = 0;
u8 integer[64] = {0};
u32 read_pos = 0;
for(;; ++read_pos)
{
if(string.str[read_pos] == 0)
{
break;
}
if(found_first_digit)
{
if(integer_write_pos == sizeof(integer))
{
integer[sizeof(integer) - 1] = 0;
break;
}
if(CharIsDigit(string.str[read_pos]) || string.str[read_pos] == '-')
{
integer[integer_write_pos++] = string.str[read_pos];
}
else
{
integer[integer_write_pos++] = 0;
break;
}
}
else
{
if(CharIsDigit(string.str[read_pos]) || string.str[read_pos] == '-')
{
integer[integer_write_pos++] = string.str[read_pos];
found_first_digit = 1;
}
}
}
result = CStringToI32(integer);
return result;
}
internal f32
GetFirstF32FromCString(char *str)
{
f32 result = 0;
b32 found_first_digit = 0;
u32 float_write_pos = 0;
char float_str[64] = {0};
u32 read_pos = 0;
for(;; ++read_pos)
{
if(str[read_pos] == 0)
{
break;
}
if(found_first_digit)
{
if(float_write_pos == sizeof(float_str))
{
float_str[sizeof(float_str) - 1] = 0;
break;
}
if(CharIsDigit(str[read_pos]) || str[read_pos] == '.' || str[read_pos] == '-')
{
float_str[float_write_pos++] = str[read_pos];
}
else
{
float_str[float_write_pos++] = 0;
break;
}
}
else
{
if(CharIsDigit(str[read_pos]) || str[read_pos] == '.' || str[read_pos] == '-')
{
float_str[float_write_pos++] = str[read_pos];
found_first_digit = 1;
}
}
}
result = CStringToF32(float_str);
return result;
}
internal void
CopySubstringToStringUntilCharN(char *str1, u32 str1_max,
const char *str2, char str2_term)
{
u32 write_pos = 0;
while(1)
{
if(str2[write_pos] == str2_term || write_pos == str1_max - 1)
{
str1[write_pos++] = 0;
break;
}
else
{
str1[write_pos] = str2[write_pos];
++write_pos;
}
}
}
internal void
CopyStringToFixedSizeBuffer(char *dest, u32 dest_max,
const char *src)
{
u32 read_pos = 0;
u32 write_pos = 0;
for(;;)
{
if(src[read_pos] == 0 || write_pos >= dest_max)
{
break;
}
dest[write_pos++] = src[read_pos++];
}
if(write_pos >= dest_max)
{
dest[dest_max - 1] = 0;
}
else
{
dest[write_pos++] = 0;
}
}
internal u32
CStringIndexAfterSubstring(char *str, char *substr)
{
u32 result = 0;
for(u32 i = 0; str[i]; ++i)
{
if(str[i] == substr[0])
{
if(CStringMatchCaseInsensitiveN(str + i, substr, CalculateCStringLength(substr)))
{
result = i + CalculateCStringLength(substr);
}
}
}
return result;
}
internal u32
CStringFirstIndexAfterSubstring(char *str, char *substr)
{
u32 result = 0;
for(u32 i = 0; str[i]; ++i)
{
if(str[i] == substr[0])
{
if(CStringMatchCaseInsensitiveN(str + i, substr, CalculateCStringLength(substr)))
{
result = i + CalculateCStringLength(substr);
break;
}
}
}
return result;
}
internal void
CopyCStringToFixedSizeBuffer(char *destination, u32 destination_max, char *source)
{
for(u32 i = 0; i < destination_max; ++i)
{
destination[i] = source[i];
if(source[i] == 0)
{
break;
}
}
destination[destination_max-1] = 0;
}
internal void
CopyCStringToFixedSizeBufferN(char *destination, u32 destination_max, char *source, u32 source_max)
{
for(u32 i = 0; i < destination_max && i < source_max; ++i)
{
destination[i] = source[i];
if(source[i] == 0)
{
break;
}
}
destination[destination_max-1] = 0;
}
internal char *
ConvertCStringToLowercase(char *str)
{
for(int i = 0; str[i]; ++i)
{
str[i] = CharToLower(str[i]);
}
return str;
}
internal char *
ConvertCStringToUppercase(char *str)
{
for(int i = 0; str[i]; ++i)
{
str[i] = CharToUpper(str[i]);
}
return str;
}
internal char *
ConvertCStringToLowercaseWithUnderscores(char *str)
{
for(int i = 0; str[i]; ++i)
{
if(str[i] == ' ')
{
str[i] = '_';
}
else
{
str[i] = CharToLower(str[i]);
}
}
return str;
}
static unsigned int global_crc32_table[] =
{
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,
0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,
0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81,
0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49,
0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae,
0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066,
0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,
0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686,
0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47,
0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7,
0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f,
0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f,
0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30,
0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,
0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0,
0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4,
};
internal u32
CStringCRC32N(char *name, u32 n)
{
u32 crc = 0;
for(u32 i = 0; name[i] && i < n; ++i)
{
crc = (crc << 8) ^ global_crc32_table[((crc >> 24) ^ name[i]) & 255];
}
return crc;
}
internal u32
CStringCRC32(char *name)
{
return CStringCRC32N(name, (u32)(u32)(-1));
}
internal void
AppendToFixedSizeCString(char *destination, u32 destination_max, char *str)
{
u32 i = 0;
for(; i < destination_max && destination[i]; ++i);
if(destination_max > i)
{
CopyCStringToFixedSizeBuffer(destination + i, destination_max - i, str);
}
}