From a30a89727205b1ae2b7956d991903e1cefd6b6af Mon Sep 17 00:00:00 2001 From: krzosa Date: Sun, 29 Dec 2024 10:10:09 +0100 Subject: [PATCH] reinit repo after broken git --- .gitignore | 7 + build.bat | 13 + build_file.c | 65 + package/FiraCode-Regular.ttf | Bin 0 -> 188504 bytes package/index.html | 153 + package/run_server.bat | 1 + src/app/app.c | 2 + src/app/app.gen.c | 274 ++ src/app/app.gen.h | 110 + src/app/app.h | 3 + src/app/app.meta.c | 189 ++ src/app/app_wasm.c | 212 ++ src/app/app_win32.c | 132 + src/core/arena.c | 132 + src/core/core.c | 41 + src/core/core.h | 5 + src/core/intrinsics.c | 126 + src/core/lexer.c | 493 +++ src/core/log.c | 27 + src/core/math_gen.py | 291 ++ src/core/mathx.c | 520 ++++ src/core/mathx.gen.c | 519 ++++ src/core/platform_defines.h | 69 + src/core/scratch.c | 36 + src/core/stb_sprintf.h | 1961 ++++++++++++ src/core/string.c | 59 + src/core/string8.c | 454 +++ src/core/type_info.c | 403 +++ src/core/type_info.h | 314 ++ src/core/types.h | 538 ++++ src/core/unicode.c | 226 ++ src/core_test/core_test_entry.c | 110 + src/meta/build_tool.c | 5187 +++++++++++++++++++++++++++++++ src/meta/parser.c | 307 ++ src/meta/serialize.c | 224 ++ src/wasm_app/debug.c | 3 + src/wasm_app/main.c | 19 + src/wasm_app/ui.c | 280 ++ src/wasm_app/ui2.c | 241 ++ todo.txt | 23 + 40 files changed, 13769 insertions(+) create mode 100644 .gitignore create mode 100644 build.bat create mode 100644 build_file.c create mode 100644 package/FiraCode-Regular.ttf create mode 100644 package/index.html create mode 100644 package/run_server.bat create mode 100644 src/app/app.c create mode 100644 src/app/app.gen.c create mode 100644 src/app/app.gen.h create mode 100644 src/app/app.h create mode 100644 src/app/app.meta.c create mode 100644 src/app/app_wasm.c create mode 100644 src/app/app_win32.c create mode 100644 src/core/arena.c create mode 100644 src/core/core.c create mode 100644 src/core/core.h create mode 100644 src/core/intrinsics.c create mode 100644 src/core/lexer.c create mode 100644 src/core/log.c create mode 100644 src/core/math_gen.py create mode 100644 src/core/mathx.c create mode 100644 src/core/mathx.gen.c create mode 100644 src/core/platform_defines.h create mode 100644 src/core/scratch.c create mode 100644 src/core/stb_sprintf.h create mode 100644 src/core/string.c create mode 100644 src/core/string8.c create mode 100644 src/core/type_info.c create mode 100644 src/core/type_info.h create mode 100644 src/core/types.h create mode 100644 src/core/unicode.c create mode 100644 src/core_test/core_test_entry.c create mode 100644 src/meta/build_tool.c create mode 100644 src/meta/parser.c create mode 100644 src/meta/serialize.c create mode 100644 src/wasm_app/debug.c create mode 100644 src/wasm_app/main.c create mode 100644 src/wasm_app/ui.c create mode 100644 src/wasm_app/ui2.c create mode 100644 todo.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..918ac43 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +backup +build/ +*.wasm +*.o +MSVC/ +multimedia.c +multimedia.h \ No newline at end of file diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..3c44916 --- /dev/null +++ b/build.bat @@ -0,0 +1,13 @@ +@echo off + +if not exist build\build_tool.exe ( + mkdir build + pushd build + cl ..\src\meta\build_tool.c -Fe:build_tool.exe -Fd:build_tool.pdb /Zi /FC /nologo + rem clang ..\src\meta\build_tool.c -o build_tool.exe -g + popd +) + +build\build_tool.exe + + diff --git a/build_file.c b/build_file.c new file mode 100644 index 0000000..4a7db48 --- /dev/null +++ b/build_file.c @@ -0,0 +1,65 @@ +#include "src/core/core.h" +#include "src/core/core.c" + +#define BUILD_TOOL_LIB +#define S8_String s8_t +#include "src/meta/build_tool.c" +#include "src/meta/parser.c" +#include "src/meta/serialize.c" + +#include "src/app/app.meta.c" + +int main(int argc, char **argv) { + int ok = 0; + + ma_arena_t *arena = ma_create(ma_default_reserve_size); + meta_app(arena); + + bool run_server = false; + bool core_test_target = false; + bool wasm_target = true; + bool win32_target = false; + + if (run_server) { + os_systemf("start /D ..\\package ..\\package\\run_server.bat"); + } + + if (win32_target) { + os_delete_file(s8_lit("win32_app.pdb")); + ok = os_systemf( + "cl ../src/app/app_win32.c -Fe:win32_app.exe -Fd:win32_app.pdb" + " -I ../src" + " /Zi /FC /nologo /Oi" + " /WX /W3 /wd4200 /diagnostics:column" + " /link /incremental:no" + ); + if (ok != 0) return ok; + } + + if (core_test_target) { + os_delete_file(s8_lit("core_test.pdb")); + ok = os_systemf( + "cl ../src/core_test/core_test_entry.c -Fe:core_test.exe -Fd:core_test.pdb" + " -I ../src" + " /Zi /FC /nologo /Oi" + " /WX /W3 /wd4200 /diagnostics:column" + " /link /incremental:no" + ); + if (ok != 0) return ok; + } + + + if (wasm_target) { + ok = os_systemf( + "clang.exe ../src/wasm_app/main.c -o main.wasm" + " -Oz -g -I../src" + " -Wall -Wno-missing-braces" + " -fdiagnostics-absolute-paths -fdiagnostics-format=msvc" + " --target=wasm32 -nostdlib -mbulk-memory -msimd128" + " -Wl,-export-dynamic,--allow-undefined,--import-memory,--no-entry,--initial-memory=131072000,--max-memory=4294967296" + ); + os_copy("main.wasm", "../package/main.wasm", os_copy_overwrite); + if (ok != 0) return ok; + } + return ok; +} \ No newline at end of file diff --git a/package/FiraCode-Regular.ttf b/package/FiraCode-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..b8a44d2db0a18c12d7cf03c3763ef23991c3b5ab GIT binary patch literal 188504 zcmc$n2UrzH+xKU-9YE=zh{`z}nhorV9hGJ;fC^T`t{`?SvG`{W1c7PcYW{m;q{yU?9A+xd&Dy=?V;&P3%Tp#IDyByAyZtj6yWzxG)X4@>#Y>H!G5aRco9k+T@9GnTRHS31V`N=7)&z)vQq)Cg`jM)`n z%poZ;J|)?(TpEt_IA*4#_{8>`0x})ZI2@~4$=!SP?p&qRkBm)f$5_KP$vxXAchV+~ zLi*fDZ`jGRN2Mu~aL9OeHgkkBLpe!fZCDDkqoW)@GL!ZAy`Kj$X4~dP!8d8P4Zljm z41oyY%tjA))7=70>esPTZ#^>!dBE6QqV`IA_1q~!Wh2x-w41FRE5By4;T(luwGHe? zdOc?2{uc#5dDu3zZG%INNfe47McIX1RCrz~-{bguq0%~5N=iZQxom6^DKX_Hlb*Wp z_a7x@@}LuIMu*m52bq0#wn1-;O>g;r8^#u8TtZ$2)p#T^AzzMjgDE|JTy&R0&q1#IaV^cEXHYoz?I|?aL3By;7*b!F|JHdCcs^( ztc1H-K~0nk$_2QumDh0JEAJWCoHgXH`D@5uE36@Zt)f;DZe^`9+^SkNxYe~9a6>g% zL#w4Vf*Ykp!EK`@!0n)QgPWwm8d?vnC){fqW*K_vC6l!`+8f5vS8fa0{s-^ZE2hSFQR=J4lYW+~DVo;!!%o|||fOSl6o!(LhZ`LMp+7Jf7# zH@j@{bIjng7JrU9qnmQGLzZv_{v8&-ihNdD{2FHPLW|#ke5PCcHW>YuHgel9?c#RK zi^W;emsHj>x2}IQG-OJN&2&-uS?EZPR?AvhP!CXvG}dU(4XaenLRWbJEFhI+CBf)Yh_`J68Kl@&of2dR2LJ*ea>^g zq^*SVqgg0sL#02@`==8Bnum4de<_8|OGcm2h!?ZXgxZwASSo|LPd?&{CH|g+)!JQ9 z+XUE>`lOFOny4pfh7|g91X)KzN4=*yAzfQoAOUqq#LUV$MvDKfWrb<^N6&V{XdTWb zvDs`H+sJmaqwE}e#}yvN`{8Ojh0o8@r$GP9yOZN8m z9`-)=DfWvU3=S?11s#ewly!)880C=au*2cB!zG7X4u3ejbCew&9HSjuJ0?1&I4*ZQ z=y=-kp5qHAd#6CB3Ql284V_vzjdeQUld=c>+$&MD5zoi{n}c0THS&iShI zUFYX6>-yaFlbgXU+HIiQdbgc!N8HZ2 zy>R>NZsYFi?&V(Cy^MP`_d4!P+~eK*xleOn=)T5%oBILxYwlTjZ1cF~G3F_fCnQgu zJWcY%=Sj?yl4nGo$$94F*`4QR-rRYM;a}stad{Wzz2}kNql!nEN3utX$2gDa9_u~s zdpz^_m`}?Wl&^Qb>G>ArTbu6>Pg_qn&tT6G&q&XPo(Y~^J$rc$^jzq<#`Cu4frkZN6!a|Eq~MT(TMA}*74hogHOXtQ z*Artg;}GK*;}qjI<3W?1$=B4uG~2Yylx}+F?do09JIcG4_ZIJ;%)aIx=E>%n<{jn? zAI+znPkWznK0AGFd|iF>`}+H~^_}W_!1s4QXTNa2Mt&{)+WB?&TkW^iZ@*u z>4lFMk&4(AaVb)+$fzPGi@YvsF50l@s-mxo`4lT!tWU8m#SRraQ|wNlSD=4j$-v5i z?E;qv9tr#@@Jf(ZP{p9IppHSQL92t(f({3r3c3((rZg^Fa5EMYnkXWqsvS!v$M>#vQk;UvJquFl^s>~NIBPXEz0#T_kFo7<*t;= zE?=~KO!<`ZE6P7B|1nq&b_lK&+&6f6@Uh?*6?`icuTZfgqTeW%B)m0x?bFbF8+R$o0h2#yX8B!-CDx`TxLdbxS;UVKgz7IJS zav|hu$n%i*)p_*-)qSfMt6rvh*4%^D4AG_TR2 zMvodJYpkfTp~l^s1!`8V*}Ud=H8{QsLu*|ThVIRW{ z;jZDv@S@@6!)t~&2yY(VF}zp!knjoNv%;5!ZwlWNemwj_ct-f6@V60igkwbhh=7RF z5!E8^h)9a)A2BLoYQ%zwH4)n*evCL1@ms{5i02WXBW)wyBfTR7BZDJrMmC6S z9@#OnSLBe$36ZlRmql)h+!J{`@)xv8STDF<|9WfcU8`@dA6P%Qe$D#z>&Mn_ zSHFAx0rf}KPpv<%{?7WB8z>D*HK^90Zi68W4m5bs(5qo|!_^HxHVSSuw9&=Ju8mtZ zp3rz&<9UsjH9i%kMma_0kMfTy8C5wdBC2sz%cxFKy`lz3jfnYHADtXMFnVVE(b+L} zF?nMAV#>tSjAC~q4o33iQrRlz=Cz@VpdZX!srmvc2H?wQz z-ptgjXtQ$7#x%PeTP?PBY)ovM*sigCV~52~jGYy`Gcqvxb&l&1*FSD@+?=?~<^`IEHt*DYar4J5inXZQVpNMoEzY!bX<58wyOwKO z?rG)JszIwMtxmOeYF(stZ0k9#ceH-grbL_2HgnosjL#qM8y^^7F1}iPWPGFexcG$l zZt;EMm&9+4zuGoW+mdbL+b(Lmx$XOepoH=XAqlk-8YeVQXqS+f&^KX7!q|kV33C&c zC9F@_p0GdRc*41a-x6*mJa3n~U1Ymi?LM_H)_zp`lO23JOzn`-v1G?39Y1#(-sw~4 zW}SO?p4xe5=Q~~Ucd6JVw#$qzKX(>1B&vbm|NgI= zUAKJQs&}j1Evj2ew*}oY61@}4Bt|4QP3)A|D{*Y%)Wo@oOB2^7Zb{socqs8?;`ziY zmVfsWUnTJ*`y}@yb5fC{Qc2a48YjgiwN2`h)H7*-{%>Z|iKLI+1H0Gm-m&|H?wh(F z>Hb^yr`@xXwPc6nJjq_k)sovJ_e!3Syt@bM;n^d;$H*R2do1md+2dKy$~`;u?A3E* z&#S#$dKr5a>(#4QYOig$o#@m1K<`t%uk~^4Q=m`pJ`eg9@7u5M{(dF<_3M|Gk}D-| zO4*d^DUDM)ru0l1m@+bDYRbZtH7Sq!oBOxuKfC|={!a(w9#D8dg8?H3tQoLlz~KSs z1{N9EbYSYh6$8@-?i_gVZ~xv53K=wY(3$UYe;4vy*Y6g7_uF8H!4>dt(%_|oHw@lB z_~76ZgD(yKFeLAg3PairnK)#|kcC5b4LLF-W5~mywnP1gRvFrE=)j>PhE5y0eCURu zJBI!|^y9GN!`cp;G3>;!$HVgt4;$Wj`0(LVhOZfZY51cNb|Xw9%8qC}B5B0r5m!g5 zBi%<99XWL5nUR?zpNw)JWg1m~RFhFjqb7`6KI+nF_t8G1tBh_oI$?Cu=>DUB9TPLA z)tHWBx{sMVX2qE8V-AivHRj@&-^V;2^LA{mvBk%>9h*2dW$e1K2gaTp`)HicxUg|; z#&sRnciiA{)5a|vxALoh$H(0tpKE;J_=e+qj-Nh$$N0w+%o749L{8{6Veo{p6P8R^ zKjDW7Zzp<944arVapuIe6VFV%KS`NnoK$a8vq_sL?VXe{*<-TLr(pQ$D8JrshrcOD&yRJ+(n<%hax^{Zq%J z&PZLEx;yn$>b2B|Q`uDK?<1#goN;)@?U|l48_pa&bN$RKvutM>XO*4Rde;0|S7v+6 zj+wn^_WL=d=fut#JZI6IgL5vp6#<|fS@GHekXmu4<~wM<&(w9IE&*s@N``YxNgY{RlW%YIsR zbJ@$~%5sOGk6xaugt$PXl3n{ZC3VOIc4RFm3vnHy7Jj7+f@Ztm0DGERqU!Rt46Mxy=v2{ zgR3sBy1$yQc3)k1buFbPHcx~+3VQZJIJ-znz zI=^+_ughNFWc`};H#YcfXs}_@hAkToZMeAM;fB{6m5uE;rfodCsnn+Nn=Yr>q?J$W znKm?SM%vl5dz*7@uDN;I=2e?_Zho~Ta7*8|Iy6L$~aJ!SW@ zJ!SS(-4nSdW>5P)6ZdS`b9`^{y^Z${-Mebbc&L7A;@Z@0bgB1?eJoxjEg?{Y(CrpMYIOFTB|*sNn8j<-Jk%L)Gz zktb44tT=J$M8=7GC!VL;hLbnnwsPA@#Y;q=whw@?3Z#`a9$nMP;&oSAdx z{8_uR70z}%yZ-F9vxm=~I(y;l)t}Ahf4orhqIxmxV&cV&i+^0=mvUdqd#UWDs<_+a zz>bCB{#6SNuN%$$6Z-e;#{4_>Y~PjncZ=_x#QXpMGb-7@pMAQ*?S7 zEhpfz!OV&AJ-f03iCq)BGWtCOo=sa)Q2vDCzMP$4<58A|r+oBeke4^a@y>*gYI=OsJTbCAko=uSM zTl`PT`&(L-IF@)uW`j6U9=TQA9&i^Y+ zUmaWH@f_NPa6`OCUH- zCMUw6b$?JFTHC4D z4}FHSrt9`gfxXZUu|}XDFlL;4I(M_;a>9B3U*}3tt)l%m_=bN67^L(l^Va@liD(WKQEo(2WtF%rohZ2@@|9YM!JQ8_Z zY@{odh5pdb@o;u=S?iqp@8{)cKdrNRJw<+B`{`GSqK!Guv9wDMr*!~x(dmQJD=Qc~ z^Zy)EnAQAj6aGKW7Rr;oUT#dJ6*JoPHkJ&jhfBlFgiUqD;K+Ky%h^GmzM8kDo0U+VF-9lqpYZJ%COeGbtW{W8Y%aZ1;VoMVWt zw{&d1rsRwxKV9oeeRa+Wth2QDpmCowy`GNZkpA!IU>?IBU7Xrk&w=IUJGDZX1#9@X z)Tx=vA*aTmE(ix9pdu&(g1)s6aw_E13^WFHK{yBj6+syg1k5hqI~4$VfD^C-%9nkQ zW0u==$9Lcbcnt1>o8SugC8tds&pKv-ci;th4DN!P;0pK!z$T7|0M-k~9Uu*?1z0m2 z7v{`M_#I~>{Ci89iO|tN?^8?s037$mSh9mAflde~o9Do>t@CQf7S3LdO`r|IXfPDi z2BFkXRvfE3wgoLf6VMQh217w@5b9XfZ86qq{o086gR$xu>{v=ap6gglKgQgkF)GHe z7{AuFfWjS3ImfAW3|qrUU4k8vhodL31>~J2OcDU?l10?uAfj8h8 zcnEHT8{il?=vc~OH`oR?g4JNDV<~DA;V|E^n8Qqv3dVzx;1d`OQqX2<_n$cQbm#^; zfcao1NCo4;Nbm^^1}UHoz;)0e8sM7a5D994%Ag!5A#ASOpZee%I21uTU*H8ifD6cN z?Hl+t=lKqp&;OLp{)5|e`&VDmf9aFIhB*{*33l)WUcdvmfZQ&@_8-72i~)?3FW5h^ zzYlKNKXE7me1RA604^Xm;y!>^;F|pteO;mJKIWqRMSEOzTuyxPJ9n@@X@A6izZk<# zh@~~#9&5Zku5I?~!Ah_g%mui**-r-d!7YZnGbviz1-GBJ1Md;F)GEQBOREWY3b}l% z9ulj&t#!AV+6A!#IlWmlP@4+ZPtPX^Z(?z&xw;8%D7_0L#p~`Px=WN==%q}jQlx@< zKI_Qk19g8>y~GxJiL3MwC*57ByH)ga`{*@np@#(P>8t4J$5IG?u7?lR-B`U2)%DU_ zk;_->b%@pTnX17}^qrIga$ zyLu^sdMWMo+Sb)m7Si(x)bmN95dOO!-d%TRXZ3(hmMaC}R!~#mwikYKxvOs5Zn~SI zhd-lIvPY_Te=K{siub1APi8!8mD&mZ`xMSftGy7CLH?{fdiZ8N+))pop@)>Gv$BWl z?p-~$48>+Al0SPA<(#!rPZ^=7$=2Nx6v7JX>GM;1=?}d=;rdza^|OZPXPNZ$qsYx# zqlcW;^M9q+;k+KwOHcEgp60WjvzcO9q@MEw%9%y!W%W>DrR+g^nu3&`b<)qBriY~K zY5vg9YDMFa;~i1dZIwPItLj#*r@L{wTSpy$*rvL_fu6E~dK4je4g$A`Zuv91|E})O zqNglUQ{Db?x?4#P@2ld?G^w5*Qddu3Rre3o-B>-Oj^c&-;QdLYT%(8A)BU~mx|JtC z#~Y(~`%V)xNRMr^Of1~>!8mNXf^fcvkx2D<_AvJY>H{BhgyLI%lqRG!Y>i%7N zxQp)IqY!>auX#tR4?m@+nW5LMnLZku30vvmKkKfGZl46*pP{F0uBU0P`|Id#EftT*a4+5OrTeiiq5f$Ko+nAo^pMVa`bhoUNZtRPUgy@jizin|*+lm@ z()|g#U-Wg1?w>~Cyq)f!rTaVT{!x1RNxFZJ?#|FdrtAI*x_`Lt@2dN2>;Ce(yGIYX zs{1eKZnPfK41S5>jZeJM!tivgD3snFErNG@=CGFf+oLn}w@0_?Z;zhP-yZ!%e|z+I zdV7>>^!6yXrT0d;9lbZogXq0cUY_0?&dz7ExFL^dU zB}tOPFVlOY{3^XS%CFITqx?3#H_G4Rz0tD#gH&D$mQ;FsR5IZ0(btlT^cMdPN$;gB z=`g)XDy7q#q|!-xlT@xtZ<5OO=^awJ0lh;iH>7t+}A(f-(9a1?O?~wY- zG4$T3+=Si$m7CJLpK>#L>r;-Uw?5@Kdh1h;r?)=kwv1N9o?k)h_;%u7{4db!(i~~OhVoGW=~klKmNJ?}1Bz+HmSCn&S9dOhY*J!Pi*p z|8KM;&bR#rRS{PF8%#l1PN*-b{nS>fBgKvTcVxf9m;fvt{O}KqMfhmY089c4{v~^n zZp1gBvH}3<^Z!EpSMdjMY~>gD3CbtsA82ohr!+i=pN zNVn)4So8Wf+8^ipe1q-?8~6>*AS@@;w>jHOakLJQ{mGtWKL)5h2!GoSdK#eaocg^m zpz{&OoxY%-Lw2#^0e?97dOq$$7~ut~SmIk+l;+(KKy#?91y3lAf9gN$*tVvpe5fxe zUItNs#(ggk4Xo=5=3e$&9MgF0{xg_S|rf*b_E-wmYaU^o0UE(vN+450XF7A5}= zfYzFU7Ain#ZcyLs1Jpi((tZLpz#u?%qxMo>ln3$L!T~7Nhw|xc@lzZ70BTDl=md%a z8uLp)6rj9%3bM1IR6fnO5`fxv!9qQ#BUle$L$(*txa$S1?IAxwb)fMx6HuGz`t&1k zw~zv*x?TiPfZ9p(m(o(*#|iYcp6or%g4Nz+H#&!~#*tDztTrV@e$;om?WRDR1F9Qc zBU}L0XAZ~(D30mF8uBKxvR?_F;hbWW&zcn*i#W zy%BMgH=T1EP`_KtoB{u5FbR|f>7WkIDJWFW_Y(X=KowBk;wPmxP?<(h05k$^KyF}< zII5Q=&DZ)*=VYVLvo|PdS?G@})F}(~%t9Y#rvYjk#z}S@!pZ{7k*p|?064(-%5DMB zern%;O}hwb*5Evh+pMi%02mJnfdIsz9%M(|cGSl^K@Y^00_8v#fOE3zfiYk%fQ{(f z&mX{S9HTF(53Kv3>>W7njWn=z7RHM;kF09&qc8QmXs^-|{{NCzpT|hUngEoqkJ0N; z8Y9!dcK~hB*Nx3kl);Vyin9lMz$wddeJG8QngC;0pM$Wmo(JVaP`nk&gW~%Dnj2K+ zRX}U~0YLN43ZM;l`05ra1fioBgs7`dAHIH=o(I0vmD(in6YRi9u+WEhQ z)z1I7N_PHFkZlQTAO7De_51%49P^LRufvt0m|yzZQyRJo^asr?It4ld><4{8N016I z5B2>j)(icbItohZD4w_gXxv%tycYgNfX3YjKa$ z{^0*hI>tWJ+{N`M`yF@)Ft@U^z(;TmECrZ9*}IU=4Pege>2M9u@0(tu?yCT7!MB51 zND~76S-`g9_&ofSrak=eP^|Czo~SdFz}m^lo|F!G=xYtuVf|YFpOoeh&ezLD{W$8$ z-b1NxA3+O)WKc&*ldw+e{c-{d_`gk`v;5;Y&xG^ox<%on5f(ok)BOVRbvhhd+rnuL zy=vh%@cz+AXmp>I@e+jqL`xCBZ?<6JdQ!NmS>%>^-Jb?Jm_ms5vzX3;p z@%tI=|AcY*`3#Q3a31phjQW1wgzE*?%y+*c9^>zAFNC3gK4DCJ>ZqiB%tRP$`f)XQ zkFZiX7!E&yu#fwMzNKT-<3B zMZT~LtqJ-V_QY7qg=5%DpYQ9Sl|V^=dBYP0`uafm(3*g8$j~<&b4)+?no#mv;oT8W zP+8XeaIDvl^1c8_slHU^2yhQzkDx2X6Brjdzgmtd4Xtfxhn{X4Z21!Y(qNZsSX92EV|DuT^m%a?ju>#Of$qY>vPu(n&Dzf@1EJIdyc;#}?^RNvzxKfMi<&YG6e z6SXZ$wu8-eTTpw+hKSSaPHm^Pn)J{9{kvbk_o|QZKmh&QaaT)NF~nniW)%RnxuaF_ zg@uW>ZTJ%$qa4~l{;62Av$u<5YnZ-XUc)(+{(-6A3jdPte`?FWiIbWsX$rKkpmi|LGixqx1fD?r4mQ0$<7_zhooGegk`a1;tsza{8@d zq`cFYG}drROW{_m;onM|Gmg$9a;E!l?X<>ITL|m%{|mL&jg;z?vtHJ?ob{!9yRXxI zeGaQ5($IPTl>WcvVJ-VFiaC+J8_?W9Jt%*|nuf}?hUJW>vaD(TtOTdJUW+@>H~N#=$#y3 zPoO_*BmK$Z?+qOd-hs=2>`z$gO23oPuPwMXkbW}a`u_viiMrLp-UW7gjce71ya=0v z_#}WjynTed1MFnoCwxADW7O~MRweBP%KrpAzVpC2$m2a%(q6&N?`|R-{rV2qwLjx$ z3;!qB@E!X36Y5Imy}1oP>Ph}j5y*QL6m_A!&)Zl_*!sWLo%S-czah<9BsAwS`KkYN zD)|Z8Gvz%06WIf{ptye(jy;c_&+D(!=Ljboecp$<_XL)<<=ktrE2u{a0Q>V57S2H3 z0Itn^1%5M^A7p?opdY|*6S=d6K!FC3;N_!1Cg_L!(NEcDksrn^zYcl; zgmYKmVj&PZ05}3F8{>#i1D`-9z;%#c17s)6xj)aN^NWN2fX=N2=v?$2-+=P*ttkEe z@hXmg0vPvtev~K1vVPANYSHRY$`5^}*Ok(`1I!bC9#EYr59Ep8iZcENA9lJ68((45 zcrCrzjGbT}yf`mLyHowIS@4%0rO7t*mmR+P?>p&O#&19AI5%8YntmIO9|i9@GkbwD z4r&r8^t*U(fqVyQ7AR$*J_7jV%Gb6Ixs#KY^A26_7p^S`k!&pWd6QEKrU>s|b`I zp;ZOSVQ4jhOmAF-2sEl!bqgFyHmV88PN5dupkWp~py40_kewneP+MzRp!U_aKz68O zAs9-1MNr-Af%+f>&|5DA<=fCg6qMepXbk9`3OXj@0KM@*!~=>W=(p-kKvU2I(C^Uc zofph2{Tw>)JJ4L97@#c#@>3|i7XlO;DBh(dlv7ZAFO5*By=?^AJ1CV+U@qc2Aq?LM z!B@mUI}3B5?JX>TQvEuDRiKlF4baXOu#&Sb7WP8BS~vjhX5kPt5hQ^Vpu2^0&}0kO zpwxbPzJToX0i=Nbn12v_RYr$v(18L)g$@#ErJ-a~pir8@ z0wo=~SfG%7mI&m#(4_)=)r;X9DLTq@=yHKF5xPR4%z&;GXl0;eJD>$ZR}1iEF2>df zG@3VS1w1cwoj@rGT`y2hLdotx!TO7@wCHHXp_>E-2&4W08lAgYpd5#80b4Qe$iGdX zy@zfWD3_o+1X?lZ4+8lCbf>^T#@!{5k3n|}xCeBPKq&>KaRB6Bp!)>!&(Qq>`Bx~7 z44Ri+AE%b-doFSq?hJ^>vn-(TOf449OddtE%D9tfq3b<`yJoJu* z$osQi!M6QD9b3%CT$vOxCE7I2J7 znhpdrK;a?SL@3VD&y%4R*jV`K9Dw(o7{}Np@XaNBu1|n3D&h}81^D_LdMEhp1#AY?L4dzJ6?-3)gZJtfr*wejg*pq^d?=L% zmk~0V@loI6&(hr}F_T4J|65b&+;>`YtaBinW8FHJ8&dz}LSRFD{_9 zla~oNAvIojjGpnV)q5YSr7 z=@`&HkGB`lddxcrXb(v5Hxsm8^G*WVm+{U5S}*l`y)Haexhl4zgei{Z7D!K>!DO$K>H?Xmq5d~l6DKUo6x-iEdz>qC>_Us z?;|u_pkcg8mjxQ?Cs93s275|Z1RClqT@@H4=rw_cx=Ysu+6U~<&^(#rT#PXp=?D$= zmk}YW@MA2=nm~I9g?$O_HWaoav`na-K*Jc5a|yJ2P}q^s9zY!h+Eb`2a6|Yrs6PmR z{{<9lo?H(8SI`;)e6xYcVFG+dfyo$Ogwz6xF-Ax)p%`O?)Dns@Mo5%TZ-B9-q1`gZ z6(O~PVoVVd#-}{M!YC-lj{F_M-#{@&2xT~Qh(M|c9VsxlL+Kn~$O|1UkfNYt1j;bz zSTG)WMnfkGq^8ixU<$&UK~n`1*%@O_UV`vS&}Co^{B@yg!8-WkpzFa#_{oNw1QPXK znn1$(Aa4dVZhnHE6iCz;rv%bt=xKq3@hP7XNYsyK1rqfaoew0eHS#&|GyE7!@_FzJ z{OzH?f{Os-Nxme&Hli&}A!vW2AQ3^=Pr6?v=(pliP31YD7iC)5qN!*7D-0r}wfg;E@# zy$;=TDn9TRgHoIy{NeXF5v0V2mwC(F)r+8ZhD1swB6=^&uJlG0H?&v=zi0@^nzodx_Rw2OfDP)b(;$J|o731~m1 zBntQ~Xp(^TR!VmP&x9rmXrHC@5b)d3o&wr$DZK>z4z#y`_FqaL0ly3FE1-Rt(oewe zK~n^@FH`yp_=v)Eq8I^eg{s(lvfcB5d z0s(&lT_~Wvq_RlBpF$T4IE|wv0{#rTR6u)7Wto6Khmu_Y?Kc&&1>i5BD+RRoR8|T2 zE9hzg?LCz>0{$AhRzUkuWu1VYoh$1Fv>#PA2sqYVWut)hrwZ8@@ORKO0qs?l%>w=& zxmLVF5n-bI|Q_!ReliAUQgL6pna`E;{(v1PuVS?eXc@t70@0~ z*(;zuuCh-cG3b5)?Rk|00*ONpf*)}%jrT(WNroO4(Ee9BB9Ij5Q336Rm16=)g&r5s zzF0XSkThtzfcD7r43Urw&{G21FDs`7k`45XfcDPHS%G8={YfC9G0Hgs?WvWY1(F@~ zyny!B$_0Ux3;K(I_S(v?0x382qJZ|@^!$;K?4g$hwEtFq6G#rwD*`?QdQ~7fLazz< zQ0R4mA2|f#oaY;x4P>e-_&xT_B5mF&2#vH-tKrzk;sW22{i{Nvi7*B*$ z1d1_4@Oe;-8$v1y#aJQud?>~TAr*sSOb~nl6yt!90-@-8f-i)kzX>S_iasXzA}IQm zkcvammjqu7MgI{}2`KuE;7g$BCqgO-Mc)v7DU|vHNTs0E2Y@ewlHGw+8cMbX^c+DY z`vU$FN;U;_f3K1q0p0tmWIMoDLdjl0DhDMS0lo@Kb^&z1tCB4MUk#=911T6vZ3cV| zl-dcT3XHj-{|UO6azlR-bT8#KRKWT|hY9F;t=Dh?>jfPlpy#|^BL%Dvbd-Re^?JeX z1Zx9*A)wzKd7;e&N7*nXGa67X#*onlIHGQ_gAwDxi2jvfFC+TUNPU5Fj3xm4$Y`e# zwlPv$CPL9qM%X~E2}PTY)eyc28Un%)9tDjA^$@-e+7L8FcuQ!kK-vavE|9iEDGo?G zpsfYc4^Xtn*b(O@LOTJ}m!2gUlR*!JyFz<{Uht!zjJ*MDBbR}~rpA8o*MX*h{_s=z z1HeG|+e2X^<9G1m9OF=cK9Mm-jbp(SltuBWU>3qFLT3ZCRl>1xF<6Cgr$f&O3}nNz0z*OQPXdDxdJbGbKJ}p&1v18p@sdDp4806~L!OvN#w*|| z{IIq0y1-BXdP89Ff@TN|Cg@F&iTpc3Z-YDVH;3L8$Q_~g1add%eeeMJlZ_vONAT~2 zJ{CySZ-0O%cpidv$M{rW@P%To5psX%OM#pMeI<|wK;H=Hxs36x0RQ(AGrj}wQ5MFE z@dNk-KiU4Xz~B$f5*R!gGZ}ylu1Pr8ggHzoW1ubq1-3Q00*oUC_A%uV$Y_TtuYm4{ zO&%a0(iDeMzJTs^O>_?Witceuuqi?Jy(ZYygm!Bf117AsgbX{GP(MP3jZGy43T$tx z04n0#($G+WjQ%&(7SJ;cQ?ful3hfPGKN;i6G(bT68WYAhLHlCUAOY>;P2YjR2)Bn0 z5pcA}G!%?OKC7VP1+-T(O%Tvt+cZ%?_Xnm)U^4RI(DeekPcm&1(EXEXmw@hvOuGei z&t%#spnE27Ng$(aZ`6sP=VIRI3qmUkMV}Bd>f()lAvD;@8};^v{ptCcH*8PP^D}SQ zn9yKXZ(jjDQ}YfIXu;4Z5RLTbp)sHp{1>6E1@bv4)dOg-w>OmywC>PU0qr5Y(H?^K zCEnB?Kzk8yw3(p&hd1W7_YC+Qpfd%u=kT5-pnZ(@Yys_EyypmLzv8_{K>G~uG_V!< zxI%9Tw3blJUqWjEy$vvDX}{uqS3r9O?|a|@!pUY21sd87GwIO2&y2nxXg_Y21$3Wh zRs^({G^+x-Z!n|H1nvFJXahm_e`d6wp!-WR+C$KNl^M1ml%-I#ji9})8FnP-KFkce z5Oj}gb`;S4gxOg@_Y-E=f}ndWv%7%qea&baq0EJ1To826VfGRzr=bC$D8kP|%Lr&+ zY%VLHdv0?%0o@arBL#GiWo{#&`!;jDfbNIQZ3W6MXgdMjtC`yi=pN2ITtIt#^9TX$ z<;^3(DAZ>+bTk+PKl;!-7L0>`J9NB&?g`8j1hn@zlN|xwLzpoq3EKOcmkX4aP^urG zy}Ws)fSxg!R|)7|*}Pgn_W|ZL0=lO+uNBa}f_a@lSqxn-pyvwa4Fb9kHg6Qrvj_7g z0o{N5V2lx3DX1jSqM@=t3xO&E+ROT=0`~(`VWj_7_tr-;KfzX;j3kkG1D8>k(HHD55XcUirA+*L&^aY_+fnvN8 zbf4-oA7DHxzd>OOg6>UyU@xD=@Lz*25zzAtAM~LQ^&RZzvs^&;h(72ypOpxI2t|Jo z^o+!3wSbL1^*NJOV8V znpdFJg<^~mT2*L1fmRFZDbTt>^Me9NQy+@4OK6RtUIMKH)F{w;KrvPctuxeHpw)ny z1zKgOk3g#gMQ0FNTd1Evs|ocNXo-v=SO?$dVt(i^LOlsZzY*#{DEf|2FG10Vg!%-k z3e^&rtLuq5c9ze-ikj7V|@Y5*oD+eM+cVQ1mIG?uDX1361Q8J|)yc zP_sZId;18~D^Op7+8>I}BQ&xl`jk*VK?4NpZfGHaMtxFPpdN%45opxUMFr|_&|(6$ zFEmh~UV{b+)R)lW0(C#Mgh1U1Eh$jnKuZbK3}|VAdK_9tp!S276{y#tL zjmC9NfqDxXDp21;!vyM8Xt+Rq0gVu-hoO-IWW)Sw3DkGc+5+`=XdQui8CqAM-i6i^ zs7Ijn1?oL$1A%%J+EAcQf;JMU!=Q~p6vo30XtY2b2aOS^L!eCr>R@P7fjS)8OrTDO z#tPJF&^UoQ7TR2(j)Jxjs3V~*1?mWBE6^Hs7!7SBP$xp;L0g0mf+h&mq0n}qJ;J9# zI|$SX(2k%J!lytx3)Jz@E&_Enw5vd!1??tKCqolKGUiBbXb*vU2TJoFsJEfL1ZpO< zw?KUWr7;B5XHc3CK>Y*SPoUn1rU=wi(EbATJam9S{RuiypdN!#n}PZe`kg?14y8T- z>S-wT4N!lDl3jpGV}BTc?bVV{>Pw(jhmtLT8UiJI0ks;GYz@?^P#PaVr8z-%0cvF^ z*%YWWhf)P9jeY6^^tnpoj>Z8{Y0k|6i{P&bT@2R2KL@%VY=A!rx>2AeLN^Ii^nu?N zflBc^1S-Yv7N``zSD>ar4+_-HP?`fk-3lc;9*3X$KV6`_5t=u&jX*O)F~11S2ihK>?wT(Yw)5`_{}ud62g)Ece`qr3fjwR>=n{d7@&ho| z1289)A<$>wIqHo316}}XOI|425|9l)@-M0iRMe-a1`G%{G8Tj}L?{Rk!k8lz)W1Y- zU=P17V>cO)@yw?%bTL>8e;_msY=s~7$wD~<{cbk9EkGL;cl^4%f)2eS!(Te-FfEcP z4wLaLwkOBQtS7T)#aRV57+-Guo@Yz>@vX(((q8GflrCMDZRFgtqwFRJ$mQf3a+ust z9wLvIC(5bv5_yfBCa254%P;X)wLVHkrKZwfE$*he*?EofTIluCXfQe&-Hio}0mkCS z^2Tb$Fk?MqQ)8^Lxv{0OwXvhIyYV~y|D~K|Tx48j+=Tz1@`&*#;|1eY<5S}+W0uLr zl*{C4@-mrC{-zS9il$IgZBw$TziEhRoN2OYy=jwaw`rfZN&sd)cJ{x>~^7Z!{>NnPJy5Azdb$*-tw)pMv+wHg4@1WlizvF(V{C@Jg z;CIRIj^87{mws>kUHnb{<^3!Bhxpgvx9%v0M~#X0ZRfN1iTE$E@}v>RKotI zd~@l|ty$UG3Hbkb7;J80C0Iqa7&iYTdBEnoVDn?J`DJ{~(hfFvmHp+iy3Ld20rE)L ze6l=MUM8=VH_IpGXYwm0zv8P@QbLvSy3L2e<}ZwRqHc6DdKgW{LdFutU}K0e(%A4% zo3}A`GA0{`8pj$_jkAr5jjN4m#vR6E#&gDt#_Prx#y2JdZ0=$zU^3}8uVArxZ_@zN zP}6wb=DYs1xx071KW!f69Rr(>_fCb))4XqZ--gX4*xVU5&j*_an9IQCZOrY=!_7-! zb4jC$*}qTfM)@(imE{sOXR+Z`EoN2HgAh>Yz@LwsWz}Yld{Oo8jTs^ zqm)%V=m>E)rEvPmEQQ)$%naB4#SpVa4~CP0RU~6$2F}g+l<_{} zUB;`7=NV5kmQ#GjNZs8JU&os)<7LL}jLeLi85tReG7e@OxS5>cdTrkIDc5E(b{%UF zV;4RL6!xFwKUAH7XCYWzcrutMVV#!G%D=$9OsSy1;Qus!BK*RY@5--a^owFc$K)zl z`IV{wtx>knb2qHWSlQ_A`M>?pUGx8sLk+cS zy6>hnhOP&H{n1WqxPDSfI$ygFJ_+D{AfJI5Bm>%PfSqY|Ho*3VV8YPC(8{ouF`LyC zW3!vwFF!V0Y_JQk*+Jnp=e~r0%crN=Z-eod^G6va-%-lSe<+ib46UeAPLUNwex&S^ ze^)#)zMsgqDVGGzQwwi5V_)-l!zz(q!ES;TW*Vqk~ z!7|xH_LO~KA35WkYl^CsRx~AD87<#cCMeUDiOLMEj24J*?gsI^N=fCUl3y!sU|LD# zxO`kbp}kiwX~pCR1}-1t9!e>UoS)@i<@-!y5>ps``PP=@V$RIS^05M}AS=ZJaWyN! z+*vrQ%OY8A)(C%7e}FY$T^ao^-(6UL7R?^832Y1-$EL9HY%)t_Gud*skS$?L*$-?B z+s3xDx%kVaU)j&>0=t6mhaO~?*(>&pywfxR+$}PRoO9CjU8t-*hv=3&aj&7 zG&{w@*jZMeU1GJ^c~*~IWR2NX))d=^SaypwW52WJ>^5t~?y;8aE^EQ=uy*VZmcSmf zw(Jp0X75=dd&4@h=d3$>$GWlCEQ!5kz1e3rfXi$k_6G(wl-sf)+=k_5Q&~gy8*9z( zv-a$Xc1yddeb(M;Sq4eV);?%=w7c4U?XmWU_C$NA-PSU-N7_B@fz(2JDYcYdY1g$I z_&?BJ%cG<>_=@v8Ww=z);BLq(MJdCiXemZ&A~lm@rH4|S)Exge`g7@pbYJ>IdLTWP zo=A_Rr&2p98((~GFLjVQ%3PN44d~YR5_B8sgA^})l-f$4qy*`+)JZmI*QCy}jnqZz zD%;9-Qa344O5$ejgYQ{;@tV8_zHS|kuU?1n>O7Jc<%M`zUKw9c&d&?*g51Qtxi9zQ z{ycye;pOmE>U&XG(tKwVPRd^`Am0er^+Vy&T*Baj`;@kLkzJvdOuU;SH$9X!w zaD9@W;%D*Y>ofcp{u{r7{|SC8zsfiA3;aC)m0#qS_&NSFzs&dGYuQKnX?$V(8os@K zo!93#cmtln8}ggH5&xYx=C^nh&*ahkHjm+VcoTjXU+BJvZ*kw}h50VNTq?s?;7i#? zz6oFLe!%1KmF?#I5pRJnZMWoq@K*TVc5D8Wx8cw5HSg!VEq{S8dcWfB`D@;RzrnY= z-||lU9q-KF^Dg`Y@5(>&Zu}EZ9N#?!r#qK_m%KJ(h?(iIr5>B6CZ|edk>df_z20BkCfc_D9N3V zmh$j1QeHk*^5Em7e0;p*$tOtpan&fmCrJhQWXX$9k&HZ5GV!UBH~(HT^J$U~pDy|G z8Im8LDf#nR_*(dEsSuxo?}g8mitu?-Q9fTP#urF|e4!M?7fHqWVyOgQB9-JzrBZyE zRGPQLx5vBV>*Fh>a(tCkp0AdI`5LJL>&voPFZPM`VOeYtSFsColZ(hj_vWM&`yURXOe`$c!PfC#n8uG~_@%8XR1|KL zoK{|bsN7PvD9aUl#ewHjepfat%M`9i$~0xFJVC3VC2QT)+^VM9sCKG@>ZsbQ2Gv&0 zrM1*rs7|W0T0qUGdaC)g80E6EQhBYMQ7+(X>PwWJ%2MT-@`Ex@`BmAk%vCNbZ(OQUFP;0NXQ`RbLR4=VLuG&qsIIWP@Sh=eFrd&~ODAyEx?LgV6Y*HR850yvC z24%gnPMNPfQ0^=DwANazmY{Xfx@xtxI$Av~K&z+~)}nCj@2NG^s%f3Hj#_msOpDOM zwNNcyOVa9URkd1LH?6+bKx?FR(0XW{wHp72yDyKgs<`66Gxy$kAqm+NLfArxfC!lP zUS2j3$SNWNB5r7bkOUG831(r_QbeWhqM}koM8zGcQi_yPiikT!tJYF$l~T7_MQdG< zCH%f;=G^yQLj3jj>7T#kGwp64g-nlplew3Q6%GG>#sr#yXh5Kjsko$)F zrTa(sU+y2=&)t3QVrZ`_yN7UjAp+;`kIWxL1Sx821m&h2n_s2F!Q z6yCe;5*6znbGIv>yGu#;FYZG3J@-HEPIsI8o%@0NlDo+LoBNFt?os!9_kjDB+wQ*a zeye)8N8H~j+eZjriy~@4Qz01Ac{h7PWUE!{F z?{KekuXV3+uXb0uKX-3(uXKOvE_ZKpZ+CBWZ*^C>H@IuuyWLw} z_n+=p?icQ-?rZLw?(6Q0?q}}5-M#Lg++w%X9p{d9$GB&>quo(%nLEjy>`rk@+$Oi# zo$oGiXS$8;rth>cM&wavu(%tAj z<>t79++J>`JJ22AW;+9EwF_OY|4yuh{)WkDVv%280q%3HSD<@eg0^}}*wDZ=(3-1+ z3ynG$I_iW~i?12@pjZDXVxXmlT6OrMK^$z6S4F(_n&>GKpl63!hee`D63NzS`1(Pr zHC&`wdqp}l=swseuZs-POJrIjL~m=P$g)mnJCaZ1xB&CtB5|SB4!v?QbjT&vInW?Ku_i%(%(Ny$dsIVn)WE8nVx5aQv<|vs zuBeA)IMrGr8ek<{32R}VxXgM$G+IA}4RfA#2=>+3?% zimyqui-oXGmWf5ya_cHs7mLLbd{g2IaV6}Q>EfsGO8iVLhwXBy^|`pp+AdaDKM_}3 zpNMP3wPL0CxwuYTFK!Sw;)@eE<9idgidEt^SfuN)uD(O87I%uf#NA>IzD{uuzD}`L z+=uT_+%MLP2gHNoA+bR`EFKY$;;R*phxPa-Xi(DSq^Ui9z6~07J9P98e9dAfzGksY zJSTRG=fxiAY|`{EL)Vk0-wQoY+Maa!o6zxZvyR`db^l+X+y4f={sDCQ-=WX{0bTwP z^!O*x;q+CEe?eD&0X_XCbo9TWpZ^2>{4I3z_t48fKqnuCK0Xdzd;*_>5cq0_EggIl zL*a`UG0?rSGETs7Yxk6qouaVcvmGbBE zI(fa;us6w@9WH;Lj?91&IyVY*fmewNJK1*QzTwz~n|I}UvALMfTDtm=}HT+`N+AHm! z>#x4tVBZKY+0F2i-3tHYZT9W<9rkMbPWvwVZhMXW3;Q1XUVE*5pS{k$-(C;@<%961 zZLlAjZ3HwQVqy3b<$^Iogo6p#r?O)kj>|fhk?Pu+6_HXR%_HXSS_V4VS z@DlB^pR;${&)a+K7wi{lr3z2ztM+T~ApHTpj@Rusuugr`ehcf>KiTitf42AAf3e@S z|7yPnFVp+>2Uv~&omS)ahxSMCeSBhnYX8&z%>I}Cx%~y!>0iRX_iy`a`#+~K@tP|(N zll|u;I!R8llj5X0X->M6;q-Dco!;==_A&M&>`2naIj|ygol~5_&Jbs)bE-28_T+GF zQJxOl((eSEppy@;S%FjN6gkE4`i^o&SOPXMuCM)8e$^JpVSQ-C5`?auz#FoTbhcSV_O2SNmJB?tdIB^PTWt z;7dBrmCjF{WzNr><<3>k3g>F)8s}PPrSo&=I_G-l2KX&*f`{c6=T>KxbDMKJtm)Ox zoz7j(-LPzb0c-eP*rfNts=Xif?E|osAA$w`Fzn|?VLLw#`}RrLtxst?_Gwt;n_&%a z(Khn4u&aNgt>zuh?_gK|-q{6T&2Hy;_-tN)RsE9lvh#{%!$Y-}%5f;QSryfqyt;lYi`d0;~C- z+K&DlmiZxA=wHER{~8whH?Z`-gLQrw-$^ZQRkR*-05&mSZiGiev@BV54*xz<4V_d9oNO`!Ur$Oi&$gyz$zmS>y4gztwHOH z6gSmPbJN`nZ4vgSwFcH4{oMX!8^S`&fvuS9p5hL6hqy!CQ{7?iY3^`$ggeqb-OY3T zZomz?`EJN9a0}fc*sCS5Qb)sD9RsU%9IV$ex7@98E8QyhOn1C{mOH^c+nwm1!*=Ys zuwBn{r@8057q}N{`*pf|iF>L06L$tI+gWVQ*20RN!xrs4_cB<#^R@lE0PBDjtO43! zuP=lRzt~-Z6~h(U=3WMid^xQ16|lswfkjT%_;s+;Z-5o zzZ9}eeOEgCT^S^Nd7mA_(j@;9tNKEPV!?^u2O18anju;%y#D};YyZSXIwfxf_+=u4~` z{*86Xf81}}Z?Tg39;=fdux2@mwa#&@e@-Y%2_==S9OWvdd@4ruP_Zh`TYab`e1R)P zrK&WQt};|Fm8p8GEY(N#RsB?dH9%#nfhtE0Qn~6Be5Gp$)`F+1Vd^wB9R9+Q>U5Q- z{3@V=@E?X~wX2G-qAkJdZL~TAE4#639M*efSjkmj)mMde-*~L{CSYwi5v#*VYBGGD zJ>bDx318q1@aA2urjS2NoolVIcB`paIZjjOV-0#CR-zYUC3=av6zk#{SZB^s)oQk? zQMIa0%~5l)vTV@n&PFvKtIB4qsxQZyvsJaJcC}C~Qj66RwNzc9u2er&%hb=*a&?tj zp{`cfsB6_q^>cNdx?bI&ZiIjOW_63YRjpFDsoT{ZYPGr(9_zc+8ubfxkGfZ_RrjfN z>VCCeJ)j;`52+36VfBc5R6V91S5K%X)kgJ{+N6G|o>tGO&FWWbi~6Y5fIkj6oulA@H)QjpR^|E>e{_xk}+5Ustr(RcYs6VPV)m!Rq^(Xa? z`m@@v{sM3LU*Ylon|fb;0AJtV;qm(i{CXe3&-V#DegA})?_cci`%-x=Wn`+E8ke2MVLz6X!y z&F~)Yhu8A2*5BZ1d>7v21HL3*vMPv&SFvHgiUY6eYLR%krQ2N1#GQgMZ8|cgN z4f5ssPVo))4S^5lRNpY)X};mU5x$YW)8UKv`vSh8FW(pP75EC_kuUa@_(u6g`_Ax< z@s0J3^OgF_RO#%Nx`lPV()rajEzM27(&o9%O?8*~N-L{t+S}^l%4-^0YTD<|X{=iu zTVC7TR$Wt5*VLveYN~;aOG|Tgo2t|_RHX*SR)*1hl}xqLP>nk?Le1CS)R5;71Y*w& zqdVhgSGUBCkKo9&CaANFd}lGGvkWD5R$D`3ZC%`i2(lp*+= zADSB>V;VOQjGY@scj~DR)kkntgC^XgL5n$U>e;ekp09yvHgIoh(3NVMBUl(WFOuD` z2o}a)Hn*j&uBoxQskWg;HR=+n#;Dc|mOG7*LN%GfHdQZZZf$F6UQl1BnxfG13+y_y zVzX)R=1vXXY@~{7j_PhLhEq!viBO?xHB4H&$Qke#J8jg>+M>`4g51r@RJ-BY-id2F z*F`&bvv%Fh+D$iGWEd@qFzT@=+|3r5Znh+nsFs*+wp3?}T^eqdgc=R;x=MnnrEs*l zbE+v`IMq<|qOprd&TnsQYgo{@B(`B>YjtDW%*N(LvGXFgjp5svMI&1mRM*tCsFsnf zt$F^$NZyfc&8>4{7tO@~$i{}b)l^A=yuvD7S^54vzWVuU#%jLbRAzo2UyD7we8vlz zYaYY%y!-{6UdUHHFXZL>iy6PdT#HIMov(T2953Um7iWBbCBs?n0P_tn-vG-U@Z@Iv z0LvR-c>^q0faMIZd;w2Bk3RDcuzUfQE6>C8p|PU&R@v+i#ffR(~CL1*h^>n#Z14L z>6bG7QqEt>`AbdykXPTOOsACTl$Q56AJas2LtD*~M82bGa_$l3+!M;Vh03`l%DDy0xh2ZE#VR;|1;89=da}am7Kql^H*~ID$ZZU`Kvg873Z(w{8gO4it|^Q{2?BR zA?`_`Jd;1fJu$>RFqFsj$RjS4$N70C2>A{Fke~DW4gZkQ@OdFW=l2`_As(3_9+4q$ zBo?uJMNFSZT8KwfD8Tu>kr3qa2f6)%Tn|BR$B;J@dFfm~L2lm==MQ=5TyH^c_aN6_ zklR1x%|zS}f?Tg5mOJRdy*T3sxxIosa`TyQKJ(3Ix$`}_89$%p&1ZS@S+0DRGoR(l z_vG{FGyi;+FQ4TKd3c`OUOjkxSl$rR4>7+G^9eDXd`~{+7h?GfxIC;@^9z`M0n;g9 z{sl~@fcX_LokGrE$n{Xj`3pIJA?Gjl+LhCbIlb6RXZpoVznJNlGW}A{UuyD)yqUg~ z>69{^Ql?W{(W5;)6Y*V_nTRQtu|#D|sf;O=8A>5fmz1$&Wh_~_cjcNa=N2jFmMG^I zDCd?d=N2yKmZ;#8Rj?ctEJp>)QNeOlaEU6Ie+Bcc+SxgGqR-_QBkCJ1@9KoQGV*UU0bz#X7&8})mlddp)D&Hv^F$0H#yUhWLF}2T0QQZv#?@x&#qpupc>1v`Lk=Q z-Y1Qq%^9?C`N_~S}jxeRQ!LTVUj@A6z znwy$ii|?*&=!1zGO}xz`6-e+Iet23d~=S)T>D7X`T&1zDd3xfcalp9M{C@>dzXg{y8g zT=l5IRkuE_di3C`TM<`1ig4Af?XNO+1Fnp3>vJip(4)tj6Or0 z`53*1IP>8ZaFywu{wkv&o6@Qhn=x}Aa#aw^IjAtx7q%&P(=^@T^jo$QE8NG=s=QsKhajyR$=QsKh zaL#Y~jlaswR=9FKn_0|XW%M4dET7SHh_ih8Oh2FL8#~BfW%`f5%Ggo|{Z&S<;mYMS`VDcG!{|B0xt|$*2YDFZ%!-IJzOjV;RYnit z%6LW}A@1RE`AV5?X_Z^8C!yJxU);Gg3Awd(jcwJwI-Y`-BEhD_Ha!J3&=ho;o`M?n z6lBJAS-EU#mx~*G&1N35Tk4xlN_oI-)ss;hCZl$qj22)bszE^1ZKnC72hlZ?3*C8> zMsjq-Br^#eN#ux!p4KI9ar=+o#~i0?09S2k;Hot?uG-?j zRclUMwdH}U)}a0>GuPwF`OR7jbE39oarNL#&#cV=XSzj2 zn7^^8Fc&f%qwx`EI!5y&&UB0g0C_pxv^eHI&Tlk6;+$?YKjNI<*o26)9L913Jx({a zDdtX2H)}`4nVzxWFefsKZ?)68W6AQh$nag8r3-IIen6)+HTpnZ5 zVlHO>X3c^)^EYc3#F@WY!ywM|%~}TK<2ks{(=nWG#v|r(re|yz%-PJ(tU(axbh8#g zocWnG3F1u8tVux68wbYX4;GfD)KoWNZmn)=UDDWCJ-eZ%wfSUFwQBcQgp48%_|2fl zRSO;Pn?ZuBR%p2DO2w7a1Nl9c*0nT`q#tufp237LOgjo=uqk40+7>k%*I`>dEMk4z zV@`8>3*R*?WW3gf#Rjhxb8nNmt!tQD-)0b-8knGAL@c!gG_eNq%tV5#ZeUz>MdGR( z7*}19xay9BtFA~~bp_(8J0`BhrbM+(&GU5;^bHk(ZemRl=++d$+))uU9u-03nIh<0 zQv{8ui=gjxT{X4OZ_zyT4e_8G=0UfHhq)sj8jpBrJi|lZ8Xg)^^PoFZFKmqG@scx- zonv`rTrXzgf*Y6X#Y_l@bG?`Wi#YcMV*~~KWeX)sB;uxC{boQSZtB%tNOBdOrwB{A0MWSu>4K%Abxh|^xf zJ=~1GJhNN~=9%RRt_EIcS~^f@R54nN@e3;x7F4%jBc^Uno35}#9c|Ggiz+Z-HnvRa zBFQ=$Nlv2pWp!;~gk%OpP)NWko-1(;>MVw$sh*T*8U~cwiHd;{oxHqK7Th0-ozpP4 zy``=eTZy_3D$P#Rx~_HQ&&0--2Bk$+e;C*|x&dZJ^@}!vIKa*zxUD{5dnMO8^ob07UiWv=wDt?+*zUb^3VW4-Tsiu0d zsQek;yff3g(WnEwSQs3>(dp^obU3B3o-y4bH4HUVlP+YWBg~9U8(mYV>zMIPDSe{y zMD;YoIVvrx)uNzLHJlj*_6mOzl}O`dMWIKHTM{X^+oUcfO4Y^gTvI6q7F|tA1{A5I zj3~g&2sq7vBU3pE5}DbfFr#Jx$k`G-3s5|n2lo7i+SFlFy5KzcMdOrW=cMyD~E zz9*+dgz0l~Vwhw~r2S`lC5i$?h@?e9!?`oKo}y+_DB!}*!y1sH&J}8UBEhwGDm0Fu zKw)KZkLsr8HVlx4YOT_Oh1xt1*$a zUTKw6+1{dkErC4ag~XM$WLa^~+Gbe#*sj<6_HkaU)vVnE0kdWZ1k4&RfJYv*xB)gC zg2s;oxG@0(#&3jtdM$%%nGZ#o4{M6^l_uztL$%Ezd&;vk%i{oUSXkXy*Hlv%SGTwU zhF4QtLv^FcR$3f`M;+kCX=;mYfMteAPUL^!dD`M*A zFKAnWgxJRBxeYbd^f*fozHM%ar-HyuR73Wsi$=rqVAPNS-Yd;BJ3oP-@r~kYS}kaN zridFUgWM5<#%GFBnI%_Vp}7Wv1@SF)^hlYJGQo>L2|>1=@IA3iC(PmnLH}|sru1y4U?_1E=4s)RFKRbE(_dT|tK-_5)1~qk7xmB} zT6!=9W1ljS708eR1DW02Rv*U%BlyMv67U-b2%c=?b(V3U1^mW=hO1f6`gz?}XdDTF zLgQFKeltw`JO|+MO&P?^OjuY|l{g#Egk3h3mLa87W13Hh*Y6=- zzlVx~J*cwE@U-1!jA1%(h*$8TqC$7JNnH?yWgKXMkl9qh)$k2r)1`3^<71$PxCe)L zz=bePw6sN~7l*LEtgdaTZfcv@yr`~aBHA^j6GWFFjCB42bRj|-<{^b4rA@VA65hSe z-2X>Wb#k|qG*wf1QDjf+j?&4fI$yXHk@U`j{1@scC3h;5udxl!f7aHy`qx)`nYq&B z7%Hz)H0m17)DSYe#etCVIpS(uI3eS6MBGd&A>*4w+;rKH@r4IM+(kp&MMK<0L)=9} zW?u|!D|<9FEsW-Dc9cNT=#G%_5(YxXO9;5>ydmSIN1Qv6@z#T)84MxgjSYlKE3sn5 z0m;>wk1Hn$MxqMA)F#JQ;K?+Jy>xDK(uCuBCaz>llI zI8Kp|%VRdVkj{L}rWoQZzj2@f-^^1X_7jDS!xwOFbhBxOe5E~1)y{5S%r)!px2V3M zt?oSZ`Ifp18`|opHj{BzTi)E#1pUHTOoug8h}B<+)nACsg%Fz?A)bFjtgk|>HbXqW zhIoDr@%$Pxn}O&uW!YV%36J}8dH(2%5S|P?gn?ZsXaqgXehecrbUlp3ww8wKx$O(4 z;R0s_53#a>c!Cokta?J_c|AQ~3(dklowadJFFi3wNPDCVJR+0_&(Qec(sTxgYrzAX zW-FVY%{~gcGj}`l1V$ia_7%|Wjhz%S`#Xqpw_zJLWcF=<&)v=J@gmOo*g6cE zeG%~GZejNJ!I$S8c6Wx@VCN@Hkj^s%+hhDB3gFC--J>CPQ1KHbfslDR7W^2`?5ja8 zrf2r#Q684Z?2{mFthW%`>>;x!j_H8w%j}(l4i8JS-vWM2$Lwz*&gC+D-@xbkW%q7~ z-M=BTZ-Md{ba=>vw?`SJZ7&S_*}2%83&|uzcBlEh_jq#UkdVh^m&>x&lUjAc)Z0C zGJErwc36J1H;;0gd<8sh7jS74WoP!0lVW^;N*- zDF_z#Xr12xBNBcVEHV~`BX(9We!n(76f z96j-PLS35|OEa;m=9c->7Bw@0%-Xtz4K;P;rbJcPPU3`Kof0NDc~p9Lf={EZsBUva zSftEtY@V$Z%Q;x)>MgYt_!cJ5Ij63+p?yB*PST)hb&J~=kZAz07}030tymIrLcfy| zw7try33W|aJ-003XwSOn3Q2?Rt2?g_4mmuw;FUBHtE>4{b#=9zC4Fv7bNhm>Sh^ns zi;DWxH@7q_MRip-POF~X-dIgbR?g|xH@COeX-q#Wv>?yOL7t(4JRP$bb}eo~Ykk9Eg(mYT!y}n(5oh0l} zehA)5;4PI-J1aWbbH?jx%IXB_M69PyLN#i{WG@!p*GZ3tbOVi+I!WWQ$Bt?c0kv}E}4y3 z+I_4!^wRjoog6J3c2~Tuv`+041;#icae768qA6rW!6UaiOHL$?Er&3l=-h_C@km8? zIQB}>o{{!WkpyNW({=%mWPNL@o^Hbf&GhRi4>*m$uE2C}SegWAD%w`))fpQ0m-qJI zCv8+{IwwIhy5KYUu!voEH@a43Aenb*0h26|BMdQx-r z{?U~FUW#|^T)ih%K-Y!~S3uWWOih%hu#=typeVzjMg0<9QQZ9YRx({^ZzUn>9uH@b?UNVe zx6gw79$}E*;tBBEcL9DYFTiir1^9ubAa9ig^9p<| zWA&0`1H!XLa~aY@zePL}Z`kUmP>@}3Y4r3SUN##X;dgv>G*%bO+?tnq^;1pJZyf8y zM57mI6UZPNUW6R0-%{4l9`r7`W923{-cl7+6Q{|hnk6lbvI+CwWfCvL zw>4RVmo_xj%ciAEmMoD?a-tPmT6K1|Oe>vUnJo^KmrlzT8_TESck$V!)3ZgzIg=-5 zizJ*hkZrv;b@Dmc)??FvW3}PM{A`@XY~j3kd`$p+B~Bc6a2`)0P8m+dsY%_O`J~TS z8gNpcp8rFh`2QhK-2ad#_J7FJ zW3G}HVB&5Q*U(fVmeDd$ES`fWd_)tSaUtsUFYSpnic9tH1^Rc2{+*zIEA;Oe{ad7e z17w7X;mx>+!C0s_iT-r*fXJlB21E*UhF`?vHy{*#gH{KA^R2`94Ow5|x4`-ozlGKT z{1#d7(t6u^yA>~gSo_*Q(|Va6p~Y$9)bso4-vs@OFJb7hfL}2U_Yx;so~y5CEBZPN}MT%?`(i#qW+~?78qOfi_`w`i<8{(Yib?e z0Kk2^{_UfG>FWW4$0wapZ{s{OI`4|0I1UTHN%)AzMcj_lHLtLKW?hZ*(r(nJmEVCg zxbDU|TWfJ<`FfnBwgG30KZeuGp28WV&)__>tvE;gw>TMW7iw=0P722vs5l>dAI@Zd z(|Q}{g73#^I&?bw-*JxTM>xUzGn@hbCC>f+56=Jo!8&Rk$64*RaD`9ABZVKQl@{WBQmQwUO`Jb**jdi_J>#z&KV^LG_@pxrpZWBe58}V}%(64H&y25n zr)pf)aQw&Oe|Y6ygzGAsDyQL^SD8_9UB$fe{pCB$pT>Xxa)+*E%gdU|pDz8ZbU%Jq zmbR7VlqQW^Kkjx)8{0AVtFZ_1KV$5av1Mb+#_SohwfyNZBgbTqJ$S~NGj14LHhRbC zb)#2}P8)S()aRqdk1828eAMugc_o*YOe&dFyu0|Zl1W8Vizels716^s1aUa(3o4 z=1k4827Wy7(t%~!UuD0aeN%P|zTctnJ(YX#^^cML-|N4-|APJ(^mqDw)^AO}W&MWt zOX<6-?<0N3_bur=ywAIRUhcE7&!v50vyS<9W<8O$Dr-zuZtpjH@5vk1dwlPaneS!3 znz=Z0Ugo8}I(mK8>tL_OUekNUWE{#^oH6h8b?INHzm5M{=~Zcm(%wrwnEH0=i#n`J z9F~}n@M*#u3EL9ZC)|+GmM|k>d_qajLp|TXUf|4N*!)r8Z)(v-qEV>587TLDgl%fden7sGkbZ{qyD z#W*i;ypx7=5)b0s#Vt73ahhF%vo-gP-y=6-B+_V%LC;OF96H~R&b0!>q4Ngm6s{76TzpaC6^v1omU?DF_175DB{!F}Z+F!T43O+`zp_I}?aeIn|^H zz$3iTE;zr9Ae7^ZE-5%4O{ZM1?hdC=-G&eoIJwNDBSyT9bBrUg7x6`gNNgMCd@p8d za;e%1h;(vPP?tkl%W&f51e_V2i*o3e9jAmsI;rlE>0o+P7kHLuVU`dNTuEU=I6aiR8_?2{mXM}S8 z)1cSQn9N@bowQu%Dj-Ut$rAZDFzt|rj00pbQZChU3F^uD^@!&gEaXMc4<+Gbz^?QK z&Kwu{_|}W29|Ser<$s3Q*BTPgvL66!KjMT(HTXH=FXKwzbBWZ=w zo}oYdCgi%Ea*ES6z2(4JiuhWlkM!a55T`jNOh5D~W~r0uSD<8XGyM^UerPvx1t_N& zuIXn3=V`=8GyUO+uSYxyar)xQN&!0w=cMzYtI0D>N};3(jc_itThdqWCTGL%{4e8hsPz5dKm9Itn%nu;+0O;WJUN z48UH%$%I>?U|0{i!j($*dqZ#3A>i$XzMwKN5BUyejz*k$_y_gNDCjOg=XcF7p8)hS zoRGLzZPGP35Z@j-i1VGV!#UKmaK?2$&d-j)+1&5ryzi|zL;Mz;RXzvjsE@%J?^*aV z$~XAl%3hq7x&dF6S%wqZr{nx}(^D|gM2$rA#ymb}Y& zr)XMjfb25C!wY>QW&yIX6W&BXFiI$RcqP+lhA3Ip2`?8AXoLWbPE+4PpvFor1?-&& z45L((OaqKY0pr0Q7A2SyOG*ML51@lc>f&!;JBt#~4Xn}79FiN~I}#=6#U(L;6%pJ} z3G8uE{55bJqi|ON_9-ZR8n_^WTYQ&=(OeALfpJl|0mO%KiHN_}+<{D7(-@1iVHTYX z3_T&N1UmCrpZ)m^&ivekzD}om(s`b*t3T@eDd18I?zQ+up)jK&2c;BGFu_mX#iDqL z4zjq!zsX1dY?Kk-9hi#~;K$) zJrIV48PMS7AcndIq#trg)X*iEIGg1@oSjVZ^W?=Gr}>idqHf6sq;DM|CQ;%^S~3Za z+J?f3-VrOU5AFppFc;JL`k-~8yb!sX<&BmtpOl*|l3H|=ryP0QaGr}ekE8Pp#7XzR z#rH8H5(YzaYoZ>nz7U)^)BP)&ktcIMenO^;8svO95@g zDYvhx%@HWgBLf2g$&MKek6yd!0w{eF2V>qAwN_rMA{SpY z>A>23A7-H^@a3_U@EhnpIBXl_HGPnNU95&aA{jAL==363MZ?T22CYgp9~uvcu_!%A z9w3;Nk=ToIh5<5LLl|e+6iXHj<{2p&DU#}fWyrT={;}k78b8Uv^4~-IX6NIJc^uzs z*~60Z_b2y_rds%hB@5p;^@xV015*r&MNh*_d+Ns;md3ePgTv}m-;ChpPl2vn4!oV6 z@a6z-&Z$pC@J_!6cvFFQS0}s)z?*QYna4HXAY{ld03PZK^+0@uL@gEOaj#F+5Y$qB zY$ph6Dfo3K2qX)B-U)))qyUq3*!Lz$O9qK43XfuoA%D0W2X3HW1?wBDcohO6;bDR+_0z-j2D+;#=@d;dWB&)#Jh{O;;d0bH0 zd^7?(%@b*QcA&2j;9CUx?Cz&P3LmIqF&P!Q!L?s%#!}c@C$LU9t9?) z_-`>`@Ls?vjvjy@{mTaL07Po+803I{fkprw=zVqV|oAgKw z>l(BEci>~3g0B&pnj{MZEy?AJ17(5lYCK{j(9m7sRN^bK3b@Cz$nuA;m1+p}jrEI1 zp$G0V5qg5Y`4(l=cO`%9iygqXb=o7EBE}ci$R0&1F=?dkDR%p15G@@vD>XMu?A40I zHxQ*$Zy#rSVLaC2VJ{GTts-dpw9=BGj)O_MKH=B^ErEQ-GPnsn>M+)Fx}|W&yui1T zM1W?)2q)B+eaxAwWjJJi$s9ZRodQoIwSw|sJ&M}w_c`tZ%#k2S2!I!49L&5j00A$( zh9il-M5=@AD=f>o-Y1^~}8G)=4*Z}ypGrsPCxNRRKEvF@N@#U(C zuzYerQS&<`7sfM29Wv4$6)w4NTH+MyZw8|EQSL5HnJSs2_zy`>eKMEpK*K$bhRR+` z=H4`v^8p(z>)(hkkD7kLwbkC2S``8(w_V414 z&3zS^2uS%>Q~Vh5ZJLIKRk`U;kD#270y4}ySBZkZ+FYo``KxpD(0^sizp&4cD+`v>QIP?Gj1u$fGMZp8l2M65=_ zp1wifSt8r?p|3PQjFxP#4}FE)0&;@OY0ylA_9AT$xF9}gE5Z)Mx9B*e8nn@p*&DJq zaQa$65VBVz9ao0?NrB~u{?c=f#wc3E3ao8i@WF(3#zwNbo8-&y#Q zF128n9@!UThyqeR(ez^wFX5aYGyS2K>^rpYP(UF;U+6&&UFyyL)+sk-ak9GO)#rdE z2ErZ!oezmp2I_I3r&|aDdVO}ll6q(0JBC*ul%B%C7b#5#%xoD6sIPDFy{2x;)8|U` z_E{SDpypA8I9rYQLZab;b!Gq2{YNto)PEm@{==hq2+Smd!yGY_gnQkO%_Ikio1O!_ znS}i7!gUQ#>;F2FI1yjG?t%7Ery98>zLK5OT`4n0qV|Sr`TjQiYF?OIvWY6S2SIR`zRr>Z zz8*m936cgDAB6!287tD;k2=#euP+?hgFdM@reA8!-I9Veegm?wOYx#iX zfD$j*H53LcV5~4NX9kz+qwdR<_8D@uXv%*kN@))wK8vXk1iaE-1g}X0sQ(ZKW-JN= z#xPcx7ustUm&?qPT}D|NY~DU*-v5)PRDifQ?-PW0O}Av9NBTU%ye0sG(C4nMyquqK zxeoqFxiJ6s9j(jt7V%02-@e0`3PHdt^$T<#OS3=Sb~R zuGB{`uYIO@X%AlN48->{6@n126_|Bj?ENC{NgC8Rh2C2kE4mJ~ZwY-KTa8HV%B#c@ zagOHoI#Ejb+7hwUCgm`6GGWJ010o)TWWkT#5l%@mQMQWWZ0U^7{Sc_eFm~gO1BSYti?`AoRaXqeUsl1msweN!BvSh#o;9 zvxTVX;I;1^EaT1H*ZCl{eu~k0FA$}iEr@$sk079z^S&ihhNiIA%h?AALJEzU&b)3m zyzGB<&npL|=#6qA-}9OmbWQK+OobrCs|90ZeDCqNCuwGz(7TASbo-`k$KJJ6_jcF5 zIRVgwo&-Mmw#2{>5x<9Xv$rb;TC?|cy{|Jo26g}o0d`8HJR)lvzBfi=ubXvC7UmQR zNc}zW=>0C@-n_EYlp`zCl3AGpsg6jB7-N&cE~7a3^gaw34&zs+_x=CF;-+;UXL#TVh$j$ zBbRxhUadu~)w0pz85ldryPc@@br44+48dLY6P8RrgmnQy6?;*PuN67cK_UAFedRjH zUJCr3>AaJXJrC&!>6>(ZqJdh=d=n$ev;*1O6Qb9TncI<$0Q_wvQzrUx=3|`uSwQUU zoXpvoG;YxI5QwkLoRm2pu@yS7B$-%cW-h?5uBpsfNHc+?Fp|iWahN3!JLhU?eAq2) zf;^o@dgd@_szW-*uSuR>I}m@5<oGtM z0`iE4fPXKna?)0(nb9wL?E(ZL?HZ=7Ybvc#yr^aCiSIuifCp_mo-$j5FXoxHtbo@c z8$3ZRt;z)4|BB>HS&Mi+)7s29IhLK4lk#a=4&WwN5@bk9A#Fu@9zhe@ATuDxo2NsA@qBZu{5s~?9|!cIp=Uct%nBaJi4M{|=$a)j*`L8rL)Iip5OC}shIH2Pq3q6=Z!W*0`!u?9-uEN}@6{7r+ zS&PAWy5|0X=8hUc-!T10L&(Ote=jYb3A`SOW=1;hyl>7P5~x z^lX>JcbgCKSUt%nnS>F5$CoMJ{Ul3LJ#^h6mW3clOWJ@n!+ZGEZQpk|aX1 z2I)KclKo2*+Dw4|!qAPD2Qv=I)BXIEG#VP))XRNDDe*nTy?P-C@q(SvyP!AK3-LlL zQRqE5ikC?IioJi`*fNRxK^Fn|_i7$!i$rgByT|ZI++xYZEqYvl2YD43IQ91Pq1LGYrPu*-TT@g}2zk#d)&Fa^5w8_rD-;!$r&JUM$1_rwEj zM4^`_Nx1#=^OIDbSJbd>d1XVU3N6=aqLe^oqtQ!}5(IY%7{gXJ{xz=zj9&^~4+dSY zR3c#?_6NHuQNniQK>&r@HJ4?GKSs0>ZeuQYVXWSja2Iod-c2yUV1-Mh9(En%BTMi# z=5>;nCq7~=lt|JO!-gY`UkY)i4h5)(%LV_^_^8+n>|L2s zp2Qim2Iach;~e%>qfN1W(;DFn?%AC=himk-C|)<{oh+&Yj2Z0j^wFGM_y?zgb}sUm zvOwR!9-gCf-k`Y;#r;$cmUTT~^vp>bAf89$I@}A9$`o>O&DH<9owau?1$k2NPy7X* z;}6$J=3?xvMPl>tMgi4`3orh}Xt^?{!D^j`U&38Ua@n__caJjxuxm*!d#Sw?kl~t- zg=aiar;A#vA#qb0&C5`v9+S zPihl|I#enZnh<%Hk|fqHjEb*eQ?1jmB_s*#uZ@7*!1M`*a=NeL3CUMe7s8s=_26y; zY#Y|P+fr*8Q@4q`E^6P`ldFp5GG4cn`mhV@(QS&MK5Jn|j^sigcA;Hj#%S0=Etl@Y zF`gy05Glg`mZcjG>4wK7#U!zGS;(ISs#(5{DCx{THc5&=t;84jjb-?I~ zouhLJNI)g|0>9Ls$R42G(qrm=?Xj?FS1{b7T}FTM0jzrtCBLonwh;Yf^s}^gaW#?? z45L38y)D_(@hzGk?;9qs!&+)%^6kmD8~KtkUm?h}dy9sF5Hc132(O`!euLwL_F(8{pXm{2Lr9D}pIC(g}y8M43~= zF|&4@Vjl)X+6U~9SpJ(ZyUyt>zk>e>;Rc%H2r(l9(bK@fW=Y^Ypnn)ZCx_)Hni{C z_$6AGP{~O9&@<^20GqB$rnQgo=1^Lz372bK+-^DIcKlw1;K88j#WF#CnjqR56_`0h zBYsJ4$IRViJBSU6PLyX}A{w&r-r??Kug{cj}S4A^GiY{oyO zQ}9-;-#IkrLDuk-2Q+dgK_?7`v_|@=kYp-GH08VyFo8Emg&2eDEDeK&1?s!-FJMC& z4jN{UvoS^lMk+$MkE2XBLb#8^qbLw0f>%G!X}Ws6;%%9vCSM&v}UHHuz>pU&1v8I=?tTcXv{hh|n#XV*!gj%HP@?#v?2vb=) zNi#lCk+bEcz%BxIQKWPz*MG6bZ8P_q(rw0bbzX}IYXjCRR5~A?FIFmaxR|oh>|<*+z;O#w%)CkUCwzxMdmXr~!9Q^!sI-Rr_9M1}gJqossBQJNGJLxM zGjj&vQhSiT5zeVPA22}kJ={;htVZ)DrBUk&JgcJ1>)Q@b!$|aG`qyLHhaQe#Yx^~g z^JM8fAK{l<$G1%UiTl%H^rkL*hop-s0J{p5NTUkhRGrtdDw#9(C4A$AnazbjTl?~K z;NIfHO5B$%+mOph3O~Lt$;Vc_*6@%8SCWXOQt$EH?n7;#B$YyKQ2=&1NhMHMc!SI2 zFEd;eY9C<>pj1yAfN_fsIxfhnS*8O^1+5Fsi(j3tW+2UkXsITmkI~puutH~`Mxyqo zwD!)|GL0jS?$@A@!!nI!nNV_-WMvy3=a-^cR8E!OA^@dXWp#WABe&BxACqw{*eaS&NVtn z=NjBa$?GhDUZSQr$wJ3-GSZo@pO&%kzAn+gC~&GwK#qKp&%r++0j5bBpSbr2_x|*c z80C!DK|1lci*T2s12rhsty+RE9z**8M)|wCcKk%cz8&FZ&P@=|v3Ec}Z-uW!bFnu9 zLgA5U9unRfwF16yDrHw5&<^$j&Es^<13lTE!MO=SJfP$4iS|U?lN4nfJUd+53j&$~ zjRZSEa~e)`~3 z3W5A0RyZ0;7P{1ysIa~PJ}YH50fS) z9184bQMaQ{lC*jy9^M7&>J36KK;LN8c?W2!WEWBoxF`%`Z;(~NJO&^I?-bK7I2h^j za2*z9yY@$Ff6%5kDo84U-4?>9H!%e)i-@eK7yBNJTGT7vg18r@G^HS42U$+BNOABa zlEkw^RQGvKK@`pez{uWm(0io)&!`m4_nh*gPNDW9>pE6TOmpim$Q=if;($4aD1Z zpTa}vIIg4M7>I!u?&I`z`Z<0l-~^p~XD@sr->L7_VfBMLqK>L#>bUAqCw!Jq_|kp3 zzQIva;nV`W#eT1SpS{k$-(GJ&U_WR-WN)w^wy_FEpHHN>;r071R35Kw6?6{b&ec&Z zMs1n5wV&MthX~jFw!9GifqJy`9?MLxm=HvDg_LKHT`zd>q{Y(34 zN5B^NyYmdF*e6rb# zNtVgMI7c8sO1#_ojrd%ADE5k7Vw>0??hy-c&Qh#(1as#h>qFRSn<3#ajFqF9?z($- z=>U8V|G@iYpI{gI3mb0@b?N;%WIH)KQCjV_bUskSaGpsJV^(4uC0psvIHy##sb%VC z>Q?M|+@{}0+o+z>?~^~Ro>80eZrT>SSH9J9=zJBNwsHmZ#Uh;BB3&1IC_ZPbb0zjJ z?^XAyhj5ZjvU8?$95eJU)IIt=wwLiH+pBn^m)=L;hd0vSz&q*L&P7hGgLitNcdmjJ zbG2HjZcsO>Th#4pwYp2KQES!x>OuG$x8oh$9eB%kC*JAZrJhr}^}FpaSZPj`bCxsF zIfu^gan5&UIUDeB>ry(&N8wZ!^{{#z@5yb$n{tlxjI$a2>oKbb+VP;%0iXC{b&a}K z-Kiec*5uvl4!rL>%&B&k=<`kDF!v0_lX7P`W1Mn&Q{I`cZc^)ko96UZKgBiG$;9)! z(n-Odkc}CumvadyNc6ZQXS6d9bt2IJ64AG(W9Om)J?FUH;moyq+OOlyp^Ke5XO2_v zv^w+-;6nWt;5xjid(Lo;1m_uD=+vm2(R+Wu$wt$hnYcUbOm=2C&6xMU zcP8Q8!vEk&A`-knP?d|p+dmrvMpfCRhW9SdKe+WH$ z8%ES#+&_qZ{w&7VYq);^y?!f3+pD-=k1_CTjJ;QIe?LaV7L3G~ala1Z<5w7$FX8?^ zjF!z9rFc#ZdhT9~o@X#>U(hpSI(}0H{6N-LdVA9wt)kv~2JaL@zhIst4MV!(TFiQ} z^tL&4O(wKWn&?UTLnMfFdM_St(T|m-^8b@_2kf;tnW~$4ko2E@qKnSE&UKulcpiYx zIBLg9NN=h?sdv;{YEf7hcCQJgm4`Z~T2IuTkTPg((iP;lxCH+$^zu)j56I_5&mJy= zc3O%59?e*ze=5$3^I2(H zAEu+7%B>7Kk-_Sv&pXJ}=N}B99%B6r{bP@H6}0rn){XY3_NUfOc8A?z-3%?=&$oDf; zw$`f$FfToz_dOnjolK6^$2u(y0r=VJ=glBZ!okx`4=2(%8513EUdk4)WT4# z{H*f^CJ#I%d0?_gQ-g8_<1{antc1r4i#tEmNJ>c}L#JG&CGcFi%LG_S}>lP2~-+w3t_r)LHtaoP|c~CkTSy>s6 zsmjTtCdb98KB;{MC6zXeZfHyF_>NEqoJN}UR0#QVw;IyLT2|>mdyoirY8mhhxL*qNbP+z|= z6r94i?}VXPoO@7TpD+|v>p?NwdfXBXv?D~aGt}pk0``fA?VAzC?ioen`!F;ComZZK z&&2sX%AZKKjV}K{-@w#?={leuO&Xb`RvS&Tf}*nJayx= zv)aqfxow8Hy8iOv9lo^unewIfnvSyh9qxiP(}%d#11GPnyZDL;iSd;;k|p!A6aRFY z-9>cPWoaOzmk$EuD3zX;kpbR?y%hRct_D?`m(}+1_?%;qBPG{t zVOoN%D>Ne`#a=V<$~(?{aKnT~Toy;;wvrn4ttSeeeAJaEXsWTj;5L;t>J=0mSH z|9tbg4P$S-;Ov{4OV4{~@%XFG==gWKc;hE;_F5-KAKENpH(zwlg!!Y2O0M3&{-q=B zIoV>vs^bd+Ra9=xQP(GUqV?iS!caZq8EEWi4L!bxq(cccRDFF$7+3ddgR9P<(Mr9d zhiUI>XbVgb_zfo8mmWX-)KfC%DS7kpR=4{2c3E}gei{P~@Hc9FtQbpy@8xmPYn`}F z$G_XONyduFM=zpDoOADx6$h`q7NyYKw7j$zpnRC2A;UgQTnD+&nI0EHzYEz1JNAjd zvCl=IW1m}nL@AY{XY=J>?5ul?oRj$Z4J zfBf>xPD=5uC#;umNxSaAo%W=oJ8!+!DO+`{SNs0Wi&Fj%XYTc&n8XHm zc;A`XT^2O?zxO`*=}!*#o;h>o%;_^16!EdS^!DKt3H!pKoZA8_?2CAo$*F(}`y!rY zaw?$0z6dCjQvo&Rwn1QER!2aE)e+A!t0SPo>U>DS0WPNu3OTJ`%XOJ}n%NieG_xA-kwQ= z9p2t08?l`X94d*g4y&kPb!M^^zl9aw0L(Jm8Bq`rGnj7J?zHOa7LxT9+f3sQR+~1$j{o03H;sqnMOwNZT4IxxG57c1SsUD~dp*Rere^v+87(XN7$_&f__bRLU>E;mT056?W zP!=d18Ks=7kruTlpDZ8;6YDYrIRy0xBNYUn$(WCst=vx<1|J}bSj*;Y zzma|rVeB{y+L>sY8}Iss{6<=}*rhsc8CSA5R0#n^-d5!gmrHMA2ZB#x4>6C(hx4PE z+yG_TT|iqZ911RBck7|pz08i$Nqo<@H9{T8r$JC$oZ%11P3V$-44L0!xb1M~p+40+ z#y6&sPIBs;WZr4y=&a?NHHC)Onz(Dr~_wRh8c^SnzFS)i(=HY^tf*v~_>YCh73U^xW|`iPzmv$ncX# z>G@9)ruCo?=x_M%YhuIZ&&S|06(D%Uy+;n^yIVlT?iSCo-7TPEcZ+A)?iNt7yT!9? z9}B41#{#-o4{bh0PXo6?;#ur&?4@b-Jyc=F{(SbZ|L~Md%?)^9XQ@FXqH%C@BW@Zn zf*TZ56L@pdY1Ny2*Cp0$9A2nSVGJ>3&_@n5 zqIT!B9~^3mhL_sGg;=CI33a{AO3F?J6t6=SJW9>S#nlAJ5 z;LKX!Z*v>?ll*VCda#O?x+~;y;BLlYuvClORJsVwXl> zm$u;hROjouy1Ky(z&V3;8mN+Yj$a>n`FvFA#8Y{Xw;rh>GoBJF`R%w=H|3a1M#`>> zlPOV0eFSXxfNc`6wKlTEtjwJUs+fPw@euAgvk;18eDH#p=loWF=@7~J-^1y}D^EE zdShG4_Wva&eFKSKc&zMj&6XYeYqm&-*Z+NLh*@w(OknK#QHO~~gXFg-w)B7WSFPk@ z?CY4za?GU-p9>osDqG7DjV=;UIfrkaPBwo`b5GTf=z5|#wRz{2E2VqoFXwETWvOhF z#wc&9qux80l|+q}G8y4mxr|ISkP!i8x<^1;ayxY$x(5{Kp}2krc4`tNX8?EtlfXFA zV5S&v?H!;CMQCamH&|LrssW3?+O_GQNpk{ErH)@8F(`9!?&XAqS2K!kPn;ceVDhNc zQQn&~;!Y-#)Ws*~OdX#*VvO5@Nh2ah+V+eoO_;ND%Glrq!-w___ZvDfz@h6ju17f~ zd@d1UF74P11(eN2KwGxtP{a?QS`TgaqTyMlF~qYi#IxKoH9V_&!RB33|2JaD3{<}O zelf>g3|zPxwC^B$2N%MFC1w@|xk`S8f5y|Y&%RH%xN6jr-Yzy{2d`Lv_fs-wnqR++S(z`i1QakM{;1z#| zd-GA8gDrU!9tvJzJa9!o+i|{zf-8nl6>I?aT2x&@oA6q=IM_2|*q+6TE+(q33vR9{ zydHn3Bs-@BR&?5$JBw59W3-*}go(2>l_Vdg&c~?p=}W4uFL&UV;3GUURVDt3+Px3l6#Ne^ z3iDDbZ+*9Y$D@liCHwAf+sC{URomJ>;Za1Yq*|)tu{3{|^KYR6{{)nAN<+uO!Mr z8#k_1d6zQpnLo>ENEI~jC{{q3J`vECc5E~b<*}H6;@5zR?1deEXafuyjVUV03||JYK?M~ID#kO7Wgb`_c8-b4*{Cb(GDb1& zd9KERZ$c7`(1C}x<^;(b6J&A_+sdbVJNELOZRvhs&h``Zae3C_K?UO+qYjL| zbcv1*DeSkorYPozVME$1Ztmwla@5h1JvEaTPVZ=y*2DVbHYIIXJZ^xR>!FGPw2vNI+W=MnsE0mnfEu&;3cSx7pz13+s0K}&j0S4WxS)ly ze88ppM|#@k>z+2I4LFcQ)nTrG3~#RfZ*cMqIN2953f9PHAe=eo5$Q8t_W#CLOIKGS zxXPY|kAw`ZjlI1bb8N3l`Jqj0?9lLmAs(3%V@uqIWz1Sr@!)pE^hqueKK(|l@lIN1 z)isMgAHQzi*byUU4rtki#Dq_2O_s@HRL=C*$8HX9RrRt@avBmD{l=uDC3_D~oF9c% z=-Oj?=-5c*0Y3IVcluP9Wb)ajcPpyF2{8-48tcSrm@p~1in;YvKR}+mD`uw88^d%v zXlzZ~%%G^5;|01+EG)=^A)BVGtqGm+PVULMtxBqX&nz4~f9&L}KD|=q(%`V%XZv=% zD9lVyUowBW>BjB+lT+UCR-SqVvAuQ@<1_~kyFeCoi9&n(#_j-1tVsLD^|aXQGqSjZX^R3}b7|aU1u-s+EYD zP5wgUT!4K16LcXVR(JLyyMUBI&;3&usH_3pDw{lNU?&^6;j@}KS5{-S7@ zXUh*i$$fk3Y;UQJH#WUwlntKsGHz*PV+2d#1My)ozona{#=?|kLf~EL2gUC+1U`m7 z9SeCQVY>$=`Poh0=(#3l#nGz8ch{}_X!)Y|7R=t}-X(X&R&q?1op(kBt_<0+d~Ky_ z*8UE*Z8FWAY)U7uT22?!udAMCCw;!H^z-F`!_#i~?^Yh$dUqWY7F4$Mt<#?}o{a%@ z*VPYH_C{qNFnuQD10gdJ`>X{#5ByQXGqwla$llnv*!`b;`gI#UP4|;YRi*8QlugQi zld&sejmsYyg+mg6X`)p3d!MYP+Hj8>1& zA3HX~Z%ojcnPvAEEc~z_>--!^q7jR>wUq4SLUMaq`N;lG^M?$_6776mnEK<+&Q7M} z(~6q?2f+b88sAadgxr}kz6dDOZvv_qK$(6M(7M+`Ya5_k>xpNdHb9MA=rKGqfHGYu zo_*c`<+_kVH7xxr8*PKf!nk9Os+$}3p*hcGG9GDkcEOeWUrdv&=fv6%@apDfXKL;4 zurNL1D1GbyU}M*=eM16l`#Nhh*~ZNlh6OGTBUk>Nu}oXw47qANWT*pHQD0>+H;0ji z-)iduIc@HQ!OkehJTt__l_Kpc`LebGoJE3{m(&2jVAy+HRe4EjzPrIQs6~~{pb?wH zr%a7}Gu0))rbAbcv1eoVN6{CLt1gCZ?!R(za!Ek$?00il4|Sh1G4||=tcnyGnYMJr zs>DT$RBN}Jx9FV}F?aXW7A@KhwzeM_ylPx|h2PpZ&rk=4(e9&ymxMUauSbi+Lpqq$&nuM6T4KY?<#MHh3#Fh$kL1H zlUzf4D@6Ss(_Ic)JZfG|VJg7c?REI1_h=10vAe1uR)BtbNV#tpt_pP9XaM% z80}BUi^jMGJUIwD&+qdbGtpH(v3SxKq*vX~#E)Jg(*}@@z|`=z{w2mkF)s6P-V_>uXX^x%t&e~z22i#>0$SSu<$GN~ zpEf}GUKh}322i%w1@yVhc6Ah;EZ1W07RdMYV>GjLfPRU%{U$MX@$++W8!9)m*fV0E-~+Z$sxSUKr*0|%MMCFWF*-$_R2FsEghQ)}Fi%x9#+j94we z#c(F_vhDQF-7Z@n=g=T>VX!a0nKpbl=q)(%+qxdzRu-!qljJJU89G99W2%=bTrIBh5Xj3!E11>_CvQzb^-6iucfb6zF zW~-c+T*yzg-{Mh7>k@b)f2c3Pe`9#t>bSw&3T%~)(-AY(;T;Ta33(mu{^UypSUp|u^s!w ztDGHIF0Fek{WEj&&=GYX>i%D4wahcjeVo^5olfL-(Qi^W4+h6tdw>Ms!$B99oxH>XBp zD=d!LYllngH>|zQxLF?UJB`R)*UIPol=8x=4x>Xo=S}FM{?fBw2QR> z^PiM3c@~t^@ksP4*7gzijRo|H#4Q4y!l10;A0t!?Hlm0LxJA&i*<60)n>Tlu-xNp7 z(c`#n08`GvRJdTI6mtz_*usD@Js{tWU zlO?oPWu@+7P%+SD5V_}JXDMs&pM#m387c<4ehU_wNfGO=#DN1v*W%}1C&Sl<_pR$3 zQZRLZ(k?P*jpwrP++5EkjT!Q&7yV~s-N*Cj%ig3zq}+e};;XkykB45pweeUen<1lw z^XUF7JYwr4X2{k_uCK*9eTa4HAv{VR86(1dH8j?_-`I?X9u)q~Ltp-M8mfjWQqm2!mqQJb{13y zg&)hSexs_W&5&JglURg1DkF1Htjbvgxyxt^bM>W?v6FLHi0c_}g+;qui?eZH5yUvc z2t{)c zh@Oau7J;Z_bQ{{%4jpfnEP788rFgAw75zWpp}Coho3ng-?_ScQt7lD-qhF5!Hm&Eu&Tk)ev57;Bzo|wpKg~jco9& z@e^KcIFDU76*7hxj$1!8D4DlMy}41v#br=GmCGQwfkIVls}5u5Zk^oEcSOIjDND!B zIWTL?N*7Enlv_i}Z&A2cc;B0DB|$2i;W zN#>p3i1Uu{^YowQGbF^r#oN&?#5KLV_L{2C9bS#t0m@pdyD@7YBSK0Zt+}Bez(#BL zRjpmO1RZ$$$&)G)-=0=Yiyu0xk4ul9M+>Cn^gr5DC0!|;Jhcl7^OBJco0@l<@fd>q z3s$*=R?rm>r4_Ftm(H(Vtt@)Y2v|2U!V|2;&saC)FpTyiPYXD*M3{#j&U3{K4hb}X z8$V&WSLPwGoDdu`vj#5=bq-r?S`b0g#3_@?`#_m^otRd(NoA#2%$+*ZSJu>iTNpZF zPl-fO?!&NSLvn`u-8;J!m zZesE-w9szB@imOa58K&WIu3L(uq590)!N zE~mxO&xiy5qIcqmBmYa)rO}W4x6nR+V%0V33*gWHmz;upz3x<2mUJ@Se79#{cqQMNSvTa+*j{+37XKJiCx#h)uyJ@E2 z4(3!{-n#q7mc3WDY`IZz=JGbCDcH_$`bVz`m^Lu@10sxYa##Ky_OdH91j~>h#)2_o zJqTI_-MAh`&~9i@wUK_t~jPa|KSk8c>`Oin#9bJmF!sV8T}ye+Bg{vbK?($nY7TeV94ApV1#^ec(;KFH4a zAd!uty0~fAuClUSyYRhdGDiAao~f?jR*B8INo*`_j*Tj^@#s6QQ_(8pUUBM*gecmD zY>F7;VN1J8`^F9Ii&gD~U66w+w=twem!28wz!3Q%+Cr&6;N3EkvE6q?`y{>tuYGw3Mw=% zMnN~B56}6kd`y+XYIW4+TGsxKUO@f?tH%<=rO*Y<=72f@HO>N$q1AlgZ<}e~K5&4& zZ=6LRwkLz>LxDzRWJDjR-T=ZD;891u7A6`SC}~qSGqnx+|By+uK^8`~b+d7{#Q&*e zzq;Qh_I9>+upWdu!A9#2cD;u5lO__2e5*dK>ZX0r+R-W>1^E8O#l7N>5}Svl0O8cf z^rzzsdef1_zPOk^V0#7Hns+H1GcM|WX7y=;x&>xq*x}4_Fn9`3^hjeB@Nd;-(9Ed|=g`e%TUe{E^qG{)T7CJSC5i)s z0iQp7Hh1*wI02tFF2bRKrp_b^OSWzZkM!k06pp0Nx^ zA3gEKQ~z#%Yjo~}G1(K64vmhU{k=LRVt4F>%g9$=_cdGHXfOKz;(39osNk zbp>t6?`$W{B5Oy0kNka&3VgKZ9CP8^6;Q@q0c9Nw_`)aF{)|Qi8M9z@su-}+)=k}X zR5KLWw5_&omTmAwpz<7FJ5u+nAu-p2SA{iOWHHe{V46izv+?QqG?duqLk?c0(YX1b zSwS6r+}wOR2F)6T8K1$7S^q8T?O+XDSV-PXA+^|1Q?r{?yi5C&r?;dv$|9gcr?rBG zV-myP?`*-L+*mr_!aCeWvuAR_( zijI*RbnZD?yFX!+-RdsBs~=yMB7xl#RG~i>`_a3BgZ$^7{Ab>sfQ~6+-ks2}ZW?$< zyl31-^q9-hs%y2bY#c`OkHCt4KD-sivG9%vjs+bN_& zza02gdHh0(EAZV=QBkqMw?JK z>pHgiMLBkFCYzzQYX%%h0WA{GV={Ti`Bt`K+Wal=??$H-%l{IywX2ux8f5OfByH@{ z-&A6$^5o55Etxo>SRF;Bt6%PX4~G`8eU1%CUmCa4 z4fvo2R5<3MwPgnQY+?V|St`ErWcQut#47jCa=PM)RQ&j_sq4m%U#~~0y1H3OQBL#+ z&Qv$dM%NeB0(luw83r^k?kZcId30$Q`(>+n>cwT$Q+4}ZU@x}}t#{5mbD`@)Vs^G}sXe<;h9BLmZy?#y5^ ziM`23GiBKx)BuHV(r^^S46L!>tdTA~`#bqg@frH$^&xY*4ESG zq*IDN`$@7_CMzEk#}z5%DcA$Nhl%fc*26@MlCaBX!Txl^X!=?&?5??uK`B~$rtCu2 zsnBr$jiGaorL>l2C|jE)y}cl5^T4UYZk8URf0ylMC(Hc2Dc3-%t zY51Ahab68TPar$ffu~#{p=bs7HAWokygE=mpPp{!JWvW~4I)6vx zu`ll4eHn+pKYbC$#vOrqu$|qScWpDe5i7I$c@MZy!mV{?#^@$}c#-%nSxQ>epSiW8 zyfEh(Q#DdA<(@Rw?Y)&3?&YT!6l8PG&_6k6n6Ip>AsbX8cykhC4-TFD=vEwY{P4K) z1jotKY;`=6N0_QUp+g7wgG;y8yqREEU>mw9f^^rj3;e>J9N1-06-EOw3Xb||`}KK* z!1|U=UEQ2<*o!3+S(X@@ifL;UB?n3N4l1dm@4#{EX3SV@-7Rs}(Bw#WXWx!heY|b^ zk9TzrJ}}-S@tuk5l4eX2lOH`u*^Gq|ax5i9gYQ_MEsC5xJaP zqk~gs*~OgMw2!oYg!A*P60b@GT^8&lMHI%>DkfX>LFn?>#Ki`ki-W2Qrk75vDLHmz z%MWS!e{NIWCEdtZP@s;3Rwr*w&7bmi!PfVtsO(m+X6qCR`Pc{fU?+;1o-${0L3n;m zcThvurrod^>%aW+uxf%!PWbEl~vA6b9=>$$yf3%4$^FfxYs4f~%2%gJk=&L+go#KRHRJIDj{|Esl=;+0a#f6W@uI11QffSsMUh2NcI zW+Pl|=w3oxSl6mK5(_rwqrQ@2Ki<7(;r@jc#F&1NLY&CJM0$!2Q%et}f0UNI^ObeK z=1DP0z#gBZluHpSSF#z{;jbV049uW$p#@VwvW=yUiKW44W`?8L9h7X1m3waYC7;WC z%BEIN+wv3rIR#e}v|d2JpflCd27(!t<|rkk$>6h++2F~_Rw+C!4P)=46I3mci|K&g zf!@5!OfUHugRV)B|?zVEld$ zB$;`=h*O}!AxX}}Oq73BaVA~c+xIo0H_p;Ir^(P->%?h}zFk${88>%vuf-XL+PU)G zg>%eIFcQb4xei}(A~0)=|ESL)zK{b`X(9W2VffIY;SnA_*D(s#FCn3_q+T<}I@0Jld6A z=m#|Ksyx6y+JQ8jQV?gv408o;sGdVF$OkQy zi}})2oEG!xAx?|^{MM({sVkQ32_%7U|5^<@wC?$Hg9Bsg-rruOy~y3{by1UchfN`0 z=)9Q@@ciE6bYoqG|43dN$Ew~-@7$|7Rlgd|3a66Py*{SJI{G7jkC+zgh}{K?+wL!> z@y=l6jeKNCif&{~4mtwjLTptVnTt$^7)D;&p!!;0%Elfo$Xk>D-O?HSlyHo4QEStcg4#B-grZ zE?i!v=Ja8WwCZ~Hmg?Z)!&Ce7!lBJYXS(_ie`9kdSy&F@#DxWgKC)s6yd&d18SDr7R z>t~-nc5KG1ospBvXGNXg9=YkGx34aYp0R7gj_GWUX|y}Py}^v>`WW5=iE^=yo9G_~ zuXRRW>*+MQ(_wYy-kc=g$Zn1uyq(>LL`;qQk2@X)IBt!NUf#`gcB_tFgGOXH|HqvV zpez*fhu9zTr{}&Iq>njuvICDO#VIAhe$~*1b4titv9l!bqnBW>OX6Gz$Hevl*NbRA zHeoy5S$>n&(=qS=)^O4!H9I@D;jjsN-wTuoy{eCI`MXdM?0>42jGDTGuO4*KlJ;?v z<1RAKGS1-?wdA>2jjb&*%6qE$H|x;=R%q3yjS_W~>^O?M2nuWai>#sA zt~QQ^?JRQ<1NvB;&w8pFH0bMc2SRL>2~9HydO}~5M*x2Cllku|c$}TULGaE*ejR!# zbPHAR#u5``BrSq}>&2+b!>om5Excu}j@jtwoenh-t zzc_sI;Vc?8e0I-4b|dU(ug!sdBj~6EeXwtd+`dT>`Z&`d?u5sx+RU?|sKLMrA{&ai zRYJ=;f^#iIWSFG2GH@MiSnr%0)3!}rg39VkB;p;K;6#6x9M;yDJpYn@F`BsoT*YpO z4o7sX%m`JANZ9&<^?GmYSNZNCdUb&105`w! zuE92*gIzj@$o;nD`q>fgo4=Z>tnI~9txT>kD<-v~W2`}^36I9W4mCRQ-bQQC8WBq> z8yPmq2uXW>TCF9s&cZ4QtLP@E?nbQ+9U|XWA1i018X7f`3D)geMjy-cVb{Jk*C+ED z!>bv;HQKVmYqk_B4X7w!{&t{|*6j`3LfXyoSn+2(b_<+GOY1r665=ok&z;}sz$Rq%qeQl*3P z9EX7N9Ea{+BXqN&a`9{j@>o}yR8GOz=UBZHRNHO^;sGTN7T%0 zOfAA?S@vpvtUPADU-+2InRTT3^6$2nf4iL26<4)YVp>&5n@n9hkv88Ccb+q1BHc$T zzo+lil#v1S{l`S2PrhSx@DUlc1R)s9$naiyzF#<0>=%)z;q?&?6~3feVKlErwg7kh zfaY%8<{KDMUMLag3(&*MjudL7K{Gd|xJ@2evS`bW4OvfeV)lPHZRr$Mqs><DS9@>09;|{l2e8z(X-e0joq#bsI4_1iBM_O;6$4Am2W&)(!tRkp0 zg}S<;$lr2Y!*rCx5~e}B6ap8En|Pft25MezqGbrjPDCVj@!YwYS`F#pUA^p^huMJ-vCB8s0n>*352D|V>O ziDp!3dhw6hq|@)8SJQ+azTR?VV#$m?~eC|>hAH>o1->FJ~iFq#0 zV&8C|m>Er$ibQIQnNc&~W?nx2kHc7Y(!p7~rQp3!n)$(oq?G0Bqx}P?Iu7Yyl3Fuw z@us#lLk9Qq@1s`NsEormcA0WKxAe|}oV|zFFPRxNZ^1(O30XrI)wSKePBvD$%$Vg{ zRXTFXup!F>PCZJ%90x)NF|W^38{a!1t+@eTdL zhr5IgRI3lE)KhoW{Z>@EcgK-qCvoY*-TPLSd230$DLoQ1Dd5nS5g0Ue#F<|bq}jR4 z3Nw@QFP*75W20g^h1r=2pn+S6FkEiIim)t~0VN{FwGlo|F_{F+>~@E;kC7q!oD3}8 zTeg#Kkl)XPZ>q+f4!Rp2a+elmCDoo1)gIjCT>m|?rX-u}JS>S~pG z@&;*W7>^*&O2g|OC?M9R z<=%^j4Ouq4@Wv`nSjZHN6bsuXs_TtnD`yRazCr56*)v3lZsM8@DJ}P#+0mm9B#qAv z9iKDir}p1WSwG>;J@d|l%?YRVB|Cpw8M%4F=#0@z@2XQKOqlBxFd%wV$V{ga*W}1m z8-x94M9xk)zbw8qtVOGlOG4-Eox&TstD(SCe$kAuN?kBF)lEkRb+d{eq4DB(C z%Nv}`zok}Sbyq0{+0(D2)mx|)m#Q8@(lAy6?Bo`VWyx;ZgJ;Y0IM#Rk;wIZk}WsG!{p6ErK4$)mCS{)U4c4f?-dh4FTEtM5`g_D5PcDBQ2AWGCS87uDC8=Q<17{!`xgz z-5yZa7QSL1$kYFk8zKgtznq?3Uh`^(sHTFpp`Sv(^gi@oDI$;}vQ^ME2*f3PzeL=C z!RsF}4DgEP@@*h^_{kdKF<{UZpN>#?5RRd&Qj|BcDdIeDw z!PTCyIIQerc=3_sxt*-o-g(0MX_wAc?0Yx&@iyJ%M`uxIm^m*E7a(QrD|&C1_Wq;# zar%3~5PyU<+53ik8p!+>aV6MxsX`YavCF_LItGr;Ua<+6fi;SdjO%}d<+_YjVZK&d zcu3uupbHFihH&&~*F|mWnN9h_B4HkOp+m=i^-qJW(ljqh>s@oge z-%~nEraih_x3(dLNO@Y$Z{NIQ4=3_Q&U&|u;w={|$_A~c4@l)|{AItIhU2f*s|U>6 z`Q$B9_j1Q-RRqnb_@!bo`E;EWq@2K?WTlUkqb!wjKw~VZWmTPKh{n32{sK8lg8ZWC zOG_MU{6cAmmHKSV)vNzrU+)5R5)Ksk6@- zP!_=V-s@syKDvCPn!eQ}MsCGM`nM2Sq>z{f$%8FocVQUwdozg9Cj4xve#q-SXm`<| zoxrle`Z43gO5s9@Hq-@C8By9ksWHBp3AU2sh1{e=GZtRY zOuM$&r>bo9{Hf}P8Ri29Sy_5S?_AdWm$NsA?JGOK)iW=tdPRBCy-oXnS(Zv#eDaVu zU3BT}H~_oqOYEv%{8S4vz}kK^jqv2X%xOW{)hI_?P6DasG{nWx1Klt}dku z!`_Pa7~lV!?Cj8G-n8z;+!ZTT&fZJI$1UpGI&I0kJa1Low&vN!#&tiAj~G3=bEe<4 ziTRe}6+jkUiTMWOh-yxz;cI7`Oooh!byI;?vh zMCvf|{PuZl)wf}laq1tdzKz=r{xdtPM&9J}FuXC~M@02_3bVtV1Mpt32C6(z%lv*O zeXledO05$=uK4?_18*m8OgyvitG^F^6ihhR zI%Yc>gzR;Vhqkzk@@(v~^d)1e14Ba}&RcXQap}~+6~U?Z^2`4h6dH6arMNgZ^NtiZ z*?)X!|9&w;eTSvZ2%q0s<1sgQ{-)qb9`P>2V#6nmXX9Umie=|C+A_W)@#yM?eWR*Xb8?z>8I#}UoC~bmhx5DbXJ7V`Cox{~_l=u@cPAK^G=un^SOUs8 z%AvT;n`!b>Sm|ivX7Cnxe_`YCFrY|z!Jz6=2duwPzPzWEhK5|UaiNrormDi-h@HVqkm@_&Ixs_thk1eKsyXrRS ze}--+W!LH6kAY_*IY8N3{X%)H&EMnleu_)P3$zKa4w|3wWdJLd31c&|(f1(9byak6 zht>{W)BOYNhB(=_!r4=_Y8TU=k9?jx!6Dw#J^tbja)9B0|Du`#9ISSROqIPo>ZSP4 zLYJvhwGH0Y%UCTn`=sipX81&k^^Ty=zxjlrljI4|EE^pqL}-kg+4M*KprS1``T!cY&e&jA>5myo ztu{N(ba`XZfZ?mBIZf@kxo1?rX=&ar%ib6?wahq8)oD(5JwLOvqtYNHl*(7CGa8>}q}T4NOeEyxcL&6kDRBP?fuFHrC^ zJ3f#I?W-oLF?FWfNz1LIrT&d#wlEkg3S)hNH~y`!@e4N26y582d?2}0JFGwgELZ6R zu3;Mb8nr|7zDCkZ5RJ}8^$77Q-l+qv5Krt-=vg0a)B=9#dj@eY<=&6>UI!!kd@Oud{K{Ry_*Z{ABr1KuLf{$>TqA(1jmWa=jkgUB;xSz(d{pxNiVgP` zr+!vIAJ$6F>xx2G1#JMD;{jV&TWk{`tn&!ldS;GL7bQ7@a+VJyg%I5 zgf#2tUwd9S02 zHr$DSb`Wtmiy-i&0-kti)Y^!gi}Y#d>)-u!>D-;x8atmEK3-j19BrCeO0GS6;#N_N za&X`AQ^({@^p`_lu9@Pnpo7P}bLFIh_M?NN@|LjgdKP0A6&S0%u)2EAK#75Lwn`0? zksfx=y6{HaxwMP4uA%~8%d)i_HP2%ShDr7*Oj%a!EtxAg+d`8`f%23lcAPkQ5kHFr zy~59H8uJrM4Yyt~JFl;-vTiD#S`wvI(QzYH&N><#>Yp)c**6NT9zmSm^ zChYifQSuim=fNKFPQ5EDvrg~t>lE+hCGNpu`%J5a_^fpOV0!P~4XF)855mYwj=OKq zoqvC4^!btFeDX(USGA&T$m6!#SC1<62@Slca?bv1-@e)cZ_oMt`|m%Ub#PDLf%As) zled3@ckBiaWA;4?>~;P#KRx#*(^&QfLowUMfq)Np8-C17Ndx@XMj-K&;t2{F#DsL0lg+maL?Y>k67Vb)yf-lZb-v>8rlYh`IXrqB2hMY68{`5k zW!d(N1xw?X5}8)S%#Z)_0!gofhS>K+-Z&{bW=dAa-eqyycfuX`p92kM{B~gXA>f7_ zR0Dj>S#+|6x&5$_DN`)~OrKIhIIknW8y7(QV@v{%Yf6J%SHj0@wv zA{`^V$^$db&563e7KyKw#bEb09C{tzn)Oa)@1C>0yt4kO#HDV~G)NGtVzFY{owlEXjZN z0BZU?l=gnMW&g&C(n@yw@f#S&CWc<48EvHnDrtgpkbFX&_@BSXhg0cba%CzVNDi!8 zg{S}li0U%N(7;=4Z`4hAYD~6Rmm!Xu@-9OSF=z<%-$#7uyW})|oKW|4Hhn_h(ny_~ zD?L9UFRtw<8M$?2DW)=b4Rs}kqgAJ-*3ICwT*Ghh2dljcJ%Fw7p@kkipTc(rYyX23 z>u$spYS~!V45)4Wdowe=>QrJ$Kx8PbB)gO&@&G!lbkF~vxA7;KtcStxS-@re z2d+c(o|Q2sCH$Nn-t?`}FDEeQjOab!*q%z8!@Z zQnylWwP2>1X`;rscmKLMY$T4!I0s%kMuHCeV=Gp>z8?9d(~x~#eu;%<4c&Y!L#*%N zF*bG(S$XbxNmd=^`k_-DmS3g?^K(7D^5@OV_Vmt)AM55eX6#UR8lGxC zV$n2hC*R}jcjl4|achORvLxr)-A}JwyMOx{)0by3*GZ5Hd<%~28i;}jE;}o`->s^W zzA#t5l%{57DG+uRTi&GM^4EBt$xsK@)f=Tv&glXy?g*JUpruA{AVdq5>^?1cdgkEn zE=QM6%o|hpE0AYetmO5p;R8Jas&#Er))?*Dw?gLtXkGB{ZO7Cj1o zi>+HcdixMVV%?6jb;F{wk#A$!mgcCxDoeo4q4kp$|=qZx=HhpVwWCUu?%sD%5VKEoz^>y=DO=CU;dR*4hO=YB#l#LoQ z#y??PWEp}swgyS)^cxNB(3PFp@Y7#suIuOHwLz{8EG$tJfJ)*yL`{R*u#`6y9|`r# zjLh6RGWT4HW?5Olu%W|zd^Ts$2XJ%y z;$yMWa;b=tu>+G}2RyOP&6o$mQWnm{L^7rcM_Kizh9)2Dt}(@bY#0>%Og$rl(AwJAQWb(&|zr zNBy4Z%Rb=U1ofv4$!>dI%&xA& zO|ti^YW7#drkSIk4^$c3ErAvzgXpH6j!m+%WMc8QlQYGeGsfb@Ex!Nw8Y` ztID(T{NRfiyqw9CtBgK$%4_plJQwonJ`}Yg_MCy%P zGh?TCyOC?i-ir>tH$d9~&?fpLMP3wXQGHgFv(-Vq7reWdMx{Ddd0man=+2-o-aSCx z%F6Kzg6iN2(zll?J*E4zuIUq`I^IH}cm;?_khnQrGw3%aXB^6-c{my@nrC;R>ux=V z^sVF4C+Y?)_swH|%^)Z>e)kr>iOFt&(^YGXaSjGfvKt(n_U+AiqQk+ZAqNq-?`Blu z$LntVD#_As$}v)uUQkR_h3m#-`Gy1>MGoRw<-w;}#PqLDea%;^o2l_JpwF!DyxV7R z(vabxmBj;{8Lbk!yBV$ZYzD|QptlrI!gq%8Q%*6jHmHBnd5XqQ|6*UFUJg6a0(|D3 zdyp?(`kieScEv%H&b@xKwx$fSsVB4N;HCg`a?<6g|N2|$kWjjtbkrX<)SE$Dn zzhPTS3zx1AKbo2Ge!T2Mo)%^(r#(O!uMMypQMDBV5UqzYdPT)R(92^TK8tf=ZulEF z17gPFE=19}*O24ku?P{lBw6UEM)x6-9m)@_vX?KXy{=DT)Z^7P+-cQsI2%KTTniKb&U^(&A?!}NSGCabhJx{u0^wVH}MsjfVEuDz4$ z2Rc%o%aO#8a(*w>{q9LgY_+ua*kgZ}fn%&UkM(8!5BtJZdyeJx4S042-mHkbxs%IW z%QljkiKU4=CM0c4v;89m_aE7d_eo9Is8q%l2Ze5$GbOgCsojL(6K2{64)jhK+Sf5^ z*3SK%{mYh050zfC68!dW^iM(al<%3J6I8iRckg;xG-X)!kf}F6T5~)iF?Z^M)q_Y2 z)lU|+!O%SBpm#O+fH)WDb$LD-L4X5q_2GM&wP7&RZQ0PrhmwkV)`7WMm4P$vd=R&M zb;i>dpL~_FX!gJuPq!(qYPIyI7(6rMaQfufnc?ZXx6NB;cQV|=@4zNd1}>FBLi=J= zZ67zjrK9c4!$0_FW^ho&tqb3izHuwk)9GFk`N`K;PEh)A7xD3Da!krHwb+ZxJJLDi zx;gD8gc|y(C0hqRLhmNsH!^EQZ~=##bf_5{j|=G5uY0Ff-J~`LFI%>?cMX-^4$cTY zIK-o0a6dFes@2oVE#`OfO1!X*Y^Tjg9DQ;0n>CaB`V}uAK`rPZzi30wgN7-fVGoxv zrq^ANRMh6sM33BSD3JQl2e<{m{?Ov!tg(lNcsT|;sa5FFn_5otf_d@Ho19nT=~RrV zw4g7V49P9YFY-4$wlc_->)PXRmmd4pCjagjPUkOQvUgOJ=hD!Swc(qxJYol4GbQN( z?=8OfEC-qL5kuph`i?tN_}y7cOVTr&s(X3klYnOXpJZTW8qCZeGvl{@;H=i`_=%1` ze(=b~MA|>2EW9dev`2!kNsSHZ?Ta}fhv>t){g|KGI0kdmd6@>Gn6vED@@8i4y7kE?QQ!Wi zEbs2}2`fFPE}efSdH(02p@G$*De22%rMSHDlfq+%xx^2hJbGh7Xo9=kW!|J|t48<^ zjp^4vbiDs$=B-kwjCKiQj^H+ZL4h%WAsr2|bSeces-+iFN3|=t*rh<=lkr7OYB7>{54KcHn$twjP zeEQHJu8=l-7r!_mKIUj_)EhC!7n6#*njcE3G@R}{r1tC({6aWaB8O^jB3aQ7 z4jv>!4xRj$T#`0wfV!*wmTj8<$|tP>w;ixw+OsH^)hR?VRb1`BO&aq5$gjCqtqfin z^z!Xfw;n9GyO|EJAqOmwLyqk}b>jJqLz@B+Fi-=>Z)`>fh9ebw+&P*CGXf8RStXKWga2c`T#m9kf?G!g3*T>UI z4RPCl^%suTeR33`rj)3Q+t6RA4>0jPYJF}q!JbE{s}mbP6ins*}W7vA1Fuzd5e z2bIVAZ$;zO@gmkzSVTvW;}ZRD{mFTL)kT(GvB%buLhP_d1xWEu#vj%w^9Wtak3iEL zP!|L0nt|uqe!>`-Oi(1uC2uK*Z-#m!qYyi)f$}-z+LLz8?Oc4Ka}I`9Et*easkh`^ zcgt$HN8c9oo1dEnc9FiZ3NZe)K$d@4c`S7tnH;)n+0p8($h8vUy#iCO#JAVfjUq=2 z7}*6h1bcWS=!pY8uZzt^@5I$Ay1heN2TvB84{_?=+D7(ee#z@Y^LXCs0Nv5(#v1(G z3(}jzJCG3t;O7hY8Q1ecm!MlicSj%lncvYT>7Ivd8bdM(im${*x{Q6Yo1BlMhoxPt zg;5=~srC}X27T1%j=UoLY{Rjq^3f{sGfvcDG*|%OP;;n!zRZmg-#zTJc39Q%XM)~PJ((s8CGQa0{bcX-d9J<{A=yQGObb^xoEC%_ue7%M^U3%~yn zSoJruf#UV}*&3suR}AaJ@Dl_THbm}8U8D>6q}yir{eWeSE)g)twY?R6_M+a1{?lyt?q;MVF?vc`wc1X<#>FnBAD(V5cdh%n`I#FV zs)HC+UKka341lT>z9#TX_z4)4XQE7yz=zJoEpPZ6#{<~EB@J#MtGqw^NM23Nfi*{d zeMVNbUVU>FQqQW=ns#zX-LZCuN^$eV%L`O7rz^p;X~4+7bPP-C!_RY=TC#w$;fyeD z64J0wn7f)X7etyIyMAQ$3L5!GT6XGpTgtyp%}@Q4&RCHdv|jZ?;@?+(r%yar7YrwD zKmQta={@xF*PqkBd<#~4k=DOo`8$!#N$k6H?Fmec#)58U9It;5lyp`zIA(`Cb}jH> zAO@cgcykVCJB7h_a`;9rzwCL~Kn}mm;fLYXgz@JgH5`tvT;PG+VMW>VOSEtg4o7!~ zcwQQ(g$v(;?+72$?_L%D>n)DPA6nxBU3 z2V$gH{*;J*nVZh4m?@kJ zCKLR+dM^o<9*ujvW+d*amp%%Zl<2f1ZvDFv{$+J1h(j0p>y8g+`fg26-V{_y$0yrI zhB$h9_VL*BOGWBWC*n#|zbq+yCv{NY@We6kWv~PaX4?;AHD|d3A0*+xFBE;ucnwBg z+v)U8n z_Z;f=4(Yz~rD`zgg`*&Mt!e{R^3a}jkMJh4!5jzEd#(95NZ?xAARILV3Gi*2dTYBL zK2+}F+x6tn4`~~z!)F7;jWq*3@O|@QT<@j(l0Eax^dC2e{EY&wmDt6s15evb04c;6 zW>^=nOtq?&m08arR`jbQbmDmuc-J9#+OY1f9aP`Q>VaNLnf4BeVDeLVv_s35*ezBZ zBX)~~^QqWH4hpKlkU%~XlYb6}{6pklYrQQ0Lk?AK>-Ni&d<`Y>|AX-Y756~hB(4|7 zgE}IV{4=E{ghqReJ~DjsLjw<3H~Bz?hyGQ*YxirVsQC`&zUZlij4m&cJEC zZ@w|KjyHLJgs$06pH!7x8y4ZVF=^4+`7@8L2-zJrF5;p(WjB5QryuEid&4~!_Vu6l z{O0XvaZYXveEcw??uJo&DHgMAYy2I)UzFkV5q_f`8>B_*j4^>EYd|5reba1psGS8eSRX&W?`ca@W*qIM4+RaCSc` zpD*r5G8JaP{yOh2GASeP-iuxcNm}=O$X%> zTjBcZCh$(m6$Yod9L{JK&v#ZvvX!AsJ9g$PFW_CYaAso+;a19A{`^S}XOxTQt(C=m zg^zGJTQOm`?G$c5U$N&7T6+$=346}yXTBk$e*>oz;CY z+?&9=XyIW^;8sd|{`^c1XY>m^)=GB{hn!*Dg?f0`x<8mCQV<7tvL4>8?g^LNsT|Jc zEBp~V1)(eW-_%cWP$ zhOgs(4re<@z&k1LGfhL^<#0xifOl3>Ise~k0`H_)a4TgfhqFY%H)=+|z+9fkjBr~E(6eFt1r$M*luy>}Ol8bw4D zL_wOUh)7wK-bFwVP>OUZQltqeAQnJSYzU~>OEj^?*b<{L<(XnkqUm{0V_r;*(bSh_ zEbLzX-cBN&{vHH6_~qpoIcZR`QJQ)84P|2FV=A{(Qa?}5Lm`gw*w zodZ|MDgHAx@OXwlXbd>*TM0f&^$o38{gAQsi$fF5u=HPhXt)3TWKr{$neQ7jk zAct{VYrSC0MMD(Owr*f;a%TBypGf)5RDUoUyZ3;P(!eoCYx+>i-_<8Q;F>-WAw|38 zAES0){7P*;i5H}DEYwbnHW3UC+Gz06_F+R0xTX(b8#Hz+XBa(mk9X?p^R#yTS9i;IDOqS9QVv(GBj?1xK8S4j<9G3;wze z4y&NN^S0m%AE7DLX`t=FAi&)L_`-6YogF=WoWs^Qy48}cS0Dlw2V98>^$EtxZ8~;~fUCADA?!WY+2! zGLQ^;O0}W!>Xi>)w@i3?<-X&AROfsFJCcLtSJ??$8Xu*>=pNTtQ8kb)%S+q$ff79X zK#mJpaHcSNb#zF};?Ske z2O3OF*i=&gT24rCna@@G;U`V-yNX z5nTSuaa1eCfyw-HN5sep1IIL4j2LQenYysr>qt;t@i@WNR%JBr-Nxa*yAOn~CAQ?r zPp5iCi7N30Fajr|GQt&6pf;M8(o&^$0_jdNN^xl!NV6Jbq&*gbISiKMZ4a6qxF{$t z$8mn#s?+g4mCqGz`nY(`#)N`7^A0<&uiUz7^_FR?eP6&Rp4wJB%*$nMx zs^%mw@tHC;cf55}?DRRb_AkT$*p_dyJq9KUnV#88eeGRl@lcr(^s!V5gGT&Qyx!*9 zRD{!Jx9~_z9M1O3#WnI%+d2!fh>llOs!xhAf_#5pFxl z-rRA)GYxC7m@($)69a`N)ZT*I^X{Ohp)F$sFs5Rif_-!JKsr0+XwC#&_8P7_Hkldi zu>f}+k2Tu)#IPY;;A?{O{+5nnW5K>abDbskM&1eE;X{3cOy?xXG!Yx@eivz9ljf9I zbTT_wnMg;;xgn$sF=dmu?lBt&)c=zEEKW$Q@D~4=Tj?Wk<9Tmv1UxMs`|-B;^eg8` z+>dui%vsgDZ{9C2zt1ly`Q;z~_@x9bssWb`V_si)P?Xy1)=Z=*LGqQjV;H%g5k7wI z1P7}P)7Ky2H>_9Pj_}8Autrw9H-Jl@#rx^F3uqwr=de4Is6d#+*%2PAY7$ZSjq62k zV(XXYHEmjABIh_E^(jjk> zalloN_0ahs7;M?AYHrd#-MD2 z1pGEQ(vyitx+{RYRQP60bkLrN- zKa+smi2NN9`px=_v@7fb1&{cBk(}a+5HMB9N9wh_pVsIww4sjnk zxOtT(lPJ|D7mrDnwq_nFQTz#2B!7aa(m0d4kJ!vFp zI#{yOhtfM)sF4mSJcJ$~Z``%b6#k4`?75;#E9hT*9#x^A^Q8+Jt6C7H$h#Y&9p$ ze#&HP$3-U_5C2p{xrNd(MX+an+bM#Hv5lQAb3nu20pVkZs1Z#}2@A908cPYEA$}E= zyuH+WO~cAn@c}DiD{|j%-uP~A#g_s?^41OGIP*FC>py(w-H#5xIfb*o{Pf`mjjQh+ z*!No_rQ}TP4SaxpdV-y*sp%K&9n%C;Xacic4f(t3#ZKXo?qCdVK+UWE49i8mC*+gQ(Eube0;5 zbMVA{niNBrwv2vie zVmR4kB;I(Dm=!$TDJ!l#nl*?U)bz&u`jqW+#i7IAfBT=WQ49*ixwzuhN%ka@8W6@N zU>uA$I~(=`K{kdy3l?}~k|hVkH?sV)=O!t6!-LquC!Rk*>aztWpB>kBXR8gYxE+g{ z-&#}X?qR0RSgy`B<9>&kNwoWnq`jaLjhrRg(RS(<2Dn3&)e}t3j*Ku%y|ic7_6u_l zF4~Hf*Yqbd4lXJrLt5%Y^^Ue14NrEfPMdO7V$4cEsY6&8ZsWVHJ*Q@{$&!q%t)6z9tgHu&4Dj^y9kF+$ucxQK z>65lLf_bKR%#qH!l^<3_a#k&LXrVaSN zn90<^coPhP_!YS4aTW|VVLp3M^cgeOJ;;?`q^h%Zp2ENR@Sw4LL3rc9f%3H+-yiS4 ziV^9`#yZ9%-9bbn$ej&{oDLvN7__rZUVY|?C&)kES|fUK#pTJ0C!RD|Grt`-l~`yc{&7 zzS0EM=K=HvN=>cchCij0&K8ZIcO!_c||oCwn(F~Lg*$RX-S0(C7+mxp>t;)498N4c{i>>iP07n#3)lx%f)n*Y8IeSCL7_h#5IT zX0FFzlP$q&jSKi;FpEBc{jVRtkF$jo#14x;21aZc@X|1#gAz9w8_?|?cD&)t1vY_J zK9fCykM0o%UTh>M-fB2ho@_7>$Fej`bW+uoCm9UD@5TvJ#DvMC^66%+Ulm_$-bUIz zTKR%$ZK_=swe{9Dt!wj!wy7GjSy7ak58=kA)UENZV^UjVrxqW`f9gV9;+Au9(d`+^ zg$c9Ey{$8nJ@W!Hh3vP^;Yf{{6<%}O3(N+_p5GCFurcxw@%!=};`mNh?OO5b_g{1t z#I~f&+Y)18Ii+nv%*h5HZ}9zA@Q4$zgkMgIRHSRwP*2!8vc_P;^5hLO)6?DCEtpsSFff&llSz;nctd3ZYxI2`p(jlM&hV?>Ceitgrc;j z*IUzeB|j%#ydvh)xJ6!;fN>qicJWB3tV#C)YKEGf9gGU7#nd{WdN0AKH^E1V>&LnE zZSUnSyxbsfUbAlPLSg+X9DUjHPVU0@h@l|t69mD8ulsh})8AE-6$Wp=boxE6e!1|@ zOE2JCsJ*@Y0N}5PBLY|Y9Y{Vye;I-jyO}(NZXVMmi*5dDP6d{_In{CW z*cpp5_)wLfk00Oa9=^FXeaT>x>ZFzp)7=ZljOjnpTdDLMv2TQ@+f;AUL6#PLea*lD z@)caaoDA`|!{2M>S>d-sWMIbp6Hg~f^Xw-kKT6Z=+-FYCr?jgEu5=8@;YF~bBm40j zPj-XJPrlW77);I>8%$nXp4{(=f&Do!`CtuKueE5?adl+CaQ>a%T>X+bi|Oh^sQ=Cn zh8Frq2?{`!H6cny3Ggjt9Q4+Zq0(f*xSc0Y+NUI!w$C`|P*t*S#c=g8x$WxM`ljVe zT1yUIeShQY87{fov(t8nvqo8%3?3T3p-dhfKf!3&Om8l%Ju&mMGtZsB@xEv(=lYZW zWYCf?H)p@vo`8i#-RTsg5VO+Fxj+XSwNWHK9?l(j)NaE^>@LK-)bqRv?F^)Yp|5Qz`R9dIb zLOw%`m%pic$Sbh3W`JvcZLI27zJEYfnTIM(GfEQB(*jDwYJMEXq7@EOevFBfz^xUB z@8iP0rG`@cS{O?2tt+-)KBEqXQeA`G^Dj&#v`Ny6!Y~(0TG5Chw52_vrmJ*N&#t^? zZ}bHnwW}|v`6wOKyQ_rJ|ItBT=z@;<4pa(V3 zLJh|0L5;Ogg<1!t`)V|FkV#N@JmJ~wf)1-@wNWTN$%^H*HL(7Wtq}H^X4@-GOKV#L z(3-9+>b<8+1I2r!Y3Xij0J=Xi4b*>R8v39|nzkow4bT%k(zN|yYk>afk*4hxTLbh; zk2Ed4Z4Hd{+AitYw0)AEJtB=#vM1E^pE@XQuawtpSYOalY_FMAf-bLlfi5p?om9eT zZyl8K4udN0Tj-#)Hznu;29+!Timzk=j9};1^n{w8rX@&Fii4Eb?AN}aqu%KYY96YC zQhcQnMw{uNv>zns7(FQM0SRiM(B-9VV^GC?wGK+#CP5$QL1{lpC5+QS1y>Ces}P-D zOzKIk)0P2lSF<&NN2J>Jk*cL4I37jzELoQdYW7OiQWaeP>*G>E-N(EOD)&m&QX9Mv zg}EnITB?Kl`$*MNAN-GC%%1ORDT@)%jT(~0Oxr72%p)vmAN7Qqw(Fp@^-^B5Py2$7 zVlntq3A()I4!XRwl~M_#+0I5OFKwL!9mAqCBq(i_1hx2HR|4fJ236euN(ZIgAE< z<&%AUCQtJA7B@Ng_&7NF`X+oAEZ#lU(@!mb)n}4}udl--AMH1pB!1(6U)`^p|N6hd z2lf@;3m;OTL4&*vTpaPxFIT)9GW_r0lYQS`{Yp1exx5hQX-)8LO5qV?tLfylBZALqp z<=GX8Kla2~_@|eHqnDSXgO~Q3Or7lHILR#}W3tl(2iMqH>Sg-qgUgwhSwVUK3+7Q7 z;X`?;C)D(e4odl1%4^1M(~$D&prbDLmDfB>2c^6(l`wj=4odl%LFu`gT{uvFmY@$9 zl=-O{zF$iChSR(~p{7g&lKfb-ZIW+FYFkgJ)HVr9TOs9@+SU^)wXG*qYMTV5ZIeon z+9pBixR#(}%qhMSl#Xi&YC+fCHBd?i2G!6(%1h}WK_xm!P&%%q5@0)al|aX}lvk~T zvT-ft1>wNAQVH5|?eh3>O(dTw+?v{>9X0-DYc{%le8e_7aKX5V--U>Oa@%^kp&vQC zH$Tcf{oi1Sk^U$QNs|UKa*2+2JoKM0{ux3({5u$8z>DFJLa>X5+OZ$}-;RBwM^Kft zPzeChM2e`en(XbZl>qrx`V!#do^AMR0U!=~M03DBl7E(t7s)>>jhCKKX}m~KI$or_ z(s=0!mBvd?s5D+AC><|S3DS6xpme-QP-(nKP&!^Ds5D+AC><{hsu?d5l#Uk(DvcKj zO2>;-f;3)eq!>MMPl7^UkS%8;hQXO7L32a5!rw=y+V+vEr6P1IObI>T)un=(y;8MQ zg>Lox<5EH0$Gi(F_e#}L8@ko#-l7Q?->Dr0GwsBM`f3d=ab+_O)MvlkjOaa#6sGZSjppc1GXBb}hDdQC zSve(duF|cgMr6m}-23+oA3H0${?W63 z=tc|f8?qIv_jj-jeJdFu&MD_@%h^p}n!7pGCBpQM7wHErcn0OsTg;^07w8m}L3pfh z*b!`QS|Xkvv6obm4AZ?WqW5xg4reG^ihJbgs*|`8Fz?~+)k0*)3EDfA_%jdX(6h|U z*lsPlXlaG>_n|lrr&~D)?Jk7*<8WCXloIBx{;tIrK}h7Yr+%(eJzL57N4KOdn3uUJ zR%~wOU)+5mp5AkidVWL486i~sYW`9W^=bE7@`*Zx);CwWI|CFkkXgtkf)bSa^g}&A zSY3oG6LT1I`a%aAs=rZ;q%2=f7UNUnR+E>$I(6(B``n$2c70yF@aty|eqDNc>vPAp z?LKsDn;>>%3OhP7xrmlO&P_84ugjXA*An&|@%;2d;(vziAR9)k#LuCgNn1g{*_00Z zK?l0qb{Obj15 z(h@5K|Kw+gW65=6)k^ZEn9Dhc)^+3y+)3H-`sQ_lZ^tFU4OnjiN1K78=|0*qz+LYi zWD^9D4I7QU6Vlo})^F{&_%FZt=F5NIy7lj6EfxVeA#}FBin!3JRkgaDjv++Q2?KyJJs}h;bBJ}Yvmw}A#r<$l zd?OSa`cA2CfqgFub+0WJJI1YE4ZmLnhQc*q33m!?6uR2L8PJA$3z9pOYTle#?b%HK zjXxaWRvW7>@A!PfEQ`8@*|mOU(QnHaWgaU`eg5Ka+c&Jk}9MbHl=0ktH5a#WwE?TOM6z zJS$*PczZ!&Ltt|JTk=K8J5yp-#XqTt+?c=U)x!9+q@chpwHc*OE1e5wl`i)2&540T z=jbUt=*vZ*3}QVIy#{Y2yJXaevtBEEk?I>hCEmq{Qx<-(Vae-J^L;iZtZMf!3Gzzu#NL1g zapu^OSr=BX{-88|W~Q&tfjvdbl_n+}L^F}?2EU91j*_*gasOg%pDk-JGU@CfiH3BE zVW=CoJE~!tzhmyy!fi7N*U(;9=T#V*zu#F1JXV;0JiqL%MN5wrI91+~FS55QH8*b+ z@1FJ(e>%6Pq|SYLW?NZy%0F7$ua>8tc(C;w67?d*^G!9!2zHZpQ~CpS`E z#XA~J7G7Mt0^D5(Y#o5DIpUk$K^rXMb_)toN~^ifm_FzoEr^_C@se(Y`rH}Y!>r@) zE?L;Pr|jdF)(;nKsaa4IQN1xRtaWinWy;eFre=6o7cP5ls!Q9#)W&#w``PuXhsg!gZA_yU#g#X1 z{0!&hmrKza6h}^hdzIX*-LO{0j7teIr2&mSkk)0nfX#2un|)IJ{vUR45TQZN6OYQ& zhNhJjIhzBB*U|EYC$kp6BQI}KJ^O+9<%N3J<;pF$kER`cfA0QWJ2#YNw8WR@z0uVA zPEI!|V9KKTdP)J^8Vt6gqg$!9;Nqqy4ch#d%#N$}^Q(@V8WuKH85Z^kdGM~d2;$&9 zb%huh?xu{0P`ZWFSq@`R2oh;9n`SV0*Ts`NF{m;G;75~;#$1cnq@2hH$ z=vFT~MF6se?qQ2{#wT1@+^Mo+QgJItcNbKHN~e*ExP7FjRP?NUIcJ#fimL~>>|v^pj^17yY|^+O zzBRNk=8{mAv^&c!d%?1uSc1Y%j4W zMfU1RVf(rhiEV`m4G|?DY^-`WBG+eq{QCAStMi7LUMz^4n;zo3d1-xB!{UX>3#R(cpBVv))O1b|f>3{NX=u1kY~hVn?!0U#F%haf zR9osQ$WqleWDVI!F+bdCA`Ad6|DH7bVubqNZux%Y$J5I{TwC|b%pC8vsVjE`SIqF4 zJEc*`ZV+?LM`XUda^*XahS`2T``YtrTt^J&w*UVn4bOBM^WQQ4!I|cW$h36jzY_;; zB{$WJh3pj_lU;4mAC5<_Bt5fg-KMmH&2!TWFD$LUkpKUWRDexoC6@AQ$UrP@Lg^Nl0*=tOjB$F61{b$T zQ8y{~NA!#tvC-i(qPGOC&dOdL9K1R^YjqIuFH6fV%q`9-O>9p}YEMksoS3i$w>v@8 zM*?3LUxm6&P&2g)m^6eax0SrJsFlz0 zYh6mR4Ar0Qe%9Y>>g05Xtg2%xn#ft7j5MFh-1Op^6CGS8xJ9gk%88xQXv->;XNU6G zTp+0rXoXIz(n>&$1DMOLYVwVo6T7!1YOhz)q~fU&GeSa=2M2luR4L~>&kWkktxt<~ z4Hz^iV0vPSn@_mCoy+(s6Ju>0;;qNUOm(ESB8rw@&22{HBuu8~?*lQ6nP$jW9;k_G z^GqB!PdRa3fbRU@u`?zmB?}XCXZS1#!sd>=b<;!JirveducfC9 zwDng8h5D0whC|Y_>>S1?goNZyP1u^6usP9ewD@cESRZ@e$?h&_n@{Jje2LWe8eNaG zUx%9)3%DtRr;!_rGGsh41Ad!W-;ZUwyjItfbQPxQQSUtTJUcp(13zeaIwfymwCC)( zM_V>OjoU$ecU9GIDXU0Z932ytAbt=wSE=+54e=*;0(=yvVLpk43*x3m7#aq9&q&J< zTPBPjV(o7?aS92sur)Nawis!L4&;b!=UsjS@L{(BWVyL1yKz4zQn?YrDYUvBDSGy- zt#d|@|H9zdrdgW~bcmg&osw+H?5N5)5L;eZsF|5J(9Iu|b~I z@(SdElwc4g@>H^NASD=NT-RHW5)9(Qz0a3mZ>IqdDZzb-y3CMg6Dt`~g5|-wC%=I~ zASD>YipvAea2`>~##Jz!D|k!cJ|Gll3UT2ofHTcQai$PEytRr!D9#jOgFLI{R62gU*W%>w{yj1OysA8F$yCfFh zc^`V;SglsEHjlZ4rxqXFLTPU#b~tDallB%!3OMZDTu zwDFzacm9F<9wlt2bgNhXHwB$PL!_3qiq_O(GPi@K9e-K;P5`OXfM|2(7A1bXHt<)6Pxk{$>wV0`*i zRg+KDoY{L@qxN9-Dw;ZThOQ%BB7!#Y2@-0zUK^-IjfiQ{ZvL3iLK8F-n!WRc+1d%M zgVRSC_j6na=Y6K32zD=a=lJe3YxikuG^3C-koftJnZkNdtfTK4jqltCE*qrbGA-@; z)*8TkkHaj9l1?4r+g3v|hp0d88jRJ)mN)V42bN&6O|Z{^jfly1#+X9#t!A>VeI#Rb z-0Tx+zVi~OcT3aXGyco|etaXG8F`QX#ZVCJ|B~>3!4C@_(Xz8>**$p$#{5uv?iS;V z?nC4+dLfGSg0qG{U&YYjt{zx2tCi6{WV)Z1R?#_Q9&VLgC(+(Qk2}?C`a})h=V8EY zyM|}&s83#xYq2AMs(yc|3Q2-{mn1K%y3``?!bhn^-R6q#{z6B>oe9A4DTd=%#43bI zINChE&uR4g-@!eOk@nrkQS!NbkhfLp8QDcoeR4KqUDEWySoj~OcJGD%PTiZvgk2Ns41PkiM=VwS z4$Kbs^xIWkzqzbDU90*fxw!{m+;$f!_y4{gDDV6R{c=S!VgqC|Wr-5re_KlbcMS_- z4SSxAVkX7ClN=$%Lt%pq3G{>wlEb9z9`17nqvL8&8@(Atk^+JsqWO5!+d5gV4G4pRVDpIIz7bPBA|gDd zMv&&nsZ%2&rcR9{Em~Nq`^=f{QzIj%_V}hW`4BY0k{xPAKlmZcT_1PugyEK9>C+-2 zrcDhG#}aK8zrCYvrqVq!(p@=|y)Bk=U+}jyZyQ+|b$ff4>FXmxX=c35^;Z?}ZRGk) zcV%Rx(w)97I|UB-3Tr2ly)Ui0@y3{uUA(bROXE@LMoccQ`b5g~{DRO!A$fVzS7n84 z_Ff$SaoDt(GpD%&!$tAyBGW2kBJ*N`BGZ>AMkM;rP7aEQ&q?iAs)x1gBb=@$1J(nW zpFyv$jOZpK%`yn~uYlg@P2l3nH8S#rhqs~LkVaN-+$WV)CsIT63#Rk8w5<}YW73z+ z4Nvr+lN1ylpPiNfd=|$><;4b5EBYDokT>8HVej2Q^M`_$v&dOKjNi#M!>wBl_edSwH-m(gVt(VUQt`D6GE+R6L3~ShHIhvfr!@6JKFXk8LTbj_+Qk0bTK-In;^^nCVl<}Gn12<{!U1bCCjzURj`JBVLQYZUc>90A zGh4hD>3BxINL#vS70r0nZ;r&Pl!tXZ^SmsXdmxl56#Un+GDNbZ%gWL3u6X_d_pIWK zicTM4v?7>NjZ&t?pPvz^u(5nF97{IcXxfWzkcYC>D0`PA5B>Bc%U{VuGO@Qn1TURc zALee6j}!)ihsJk%36Q%Cf{udkmdGB6clg+!WH^~~^BXMs53f&3JNxcEjGehC>5Qf%7kXj@j*EZ` zpTj{0JdSzJ+glL-)VI~u-#!(ezcc#KH!{6LQAi?)5lTy!KMoF?C z#9#SFMI)02##n?iF&fTR=nZ%UmL-kHPQtcJx+LI=I0ZZ27QsP{p^Nw901BW*?IAE<ToL5y>t#+1b zj167nuK-^2X3d;8uHSftf5i+%Bdt)Z4NRHb;FJ<1*3$~>M+JM6Z3zpe4H)EG7)!Q! zF&v6JNAg*URC>N@kRd_?@FB(Iu6UTtxor@<@xe7X$O#EUE?+5^A3a+YAKq6SK<0>t z6{+{ntykD>lo8Gy(!&9{5Sf+E%HrHKsDtqiw>7+>Vaa`r7P zl*$%JX_iv_XyAbbSIm-dA|_`q6zSga6DDciL7VJ_<5J2jyo28;46X3vDup6`0BRkD z6PSrCgc~R1a%Z)*h6rQ1aT_;MnrlixPKb}5&v-La+$4l|j?=!G$=?w-wYK6lq(lo? zXh#aTC!L0s;3I^qM(}r3BX;c)f7!JQEhO<(C0d2g&8RUfzjMnjEN|HNw2aJ2uvPl>1~{ zLR&O+8nFFbY~w#>@1dPt`jzx%mx?7l-qS0G9GmOq6ZfR?{<>a;j%8JNIB2S%b?`6o zpDM-!H`L+BtYoNzN=%usuWNrJf1 z(1X>|^+f4@l|PGG#!0o{raDQD?Xx9!X-kHT*jF2AGKyB>lklVoYl(c5_w3o;W=bas zdCP83pzj%jnkD|m9m^7TlXU!g&X4S_BK!Qr zd17YQ&tB#D;YbiHUGti+S>kV5BwgH{)l7e+ihHxTFtXE6OsNvn{SXkL{YhI=DJ$fa z3?D5vyfRaKnj~h5cc1lPmHUV^+DkvDrarD-yqif9#iuh-{%#*uz7I5#5dLL87pGgX zG2_QWm%=}iWAX8rbE7}9;9utYsqfPdE%iTm{0GYmE!~;to*eVh7%2xGrw3$jgIaYM z7f!OoZqHNbGjMtv*wb|99?vfD77%i_b0T+(kHu{O=wazhCu+LT;{~`-iy&o^SBk4^ zidT{{$b$IfZdo1c7P;kARrTpDk#4c8w@~~pp@h4d5@?(g&c>ozEIn0-%c3QCC%d{P zdrKwcOC_AHt~#YHfm1%qvIqw%!jUCv(p&jne8%+3jpw#r;UAB*?L z5A&+Z%Bu7p#*w&v{2N;oYifOy#H5-UBc?s1CHvR8uBs->og>{wHH>nJ#BohaY8~%q z80-Qr>Q$Igr?Yt(bm^MEQJZ=d->M$S{XrZyYo>97;QQwcpfjqrV=ljfcW7iLU4hoq z@*VFdZ&v1EO>ICk^o*KW}M$!r%ulaNDYjg%qb(YlgXZZOT4@S7V z1HH=bveNSngbK0;cY_!B`Ys4&W)bb@1KlhmoQ8QD7Oe>h73}yMDqqYtCTU(74GCp%W&AI-q_~PyHNxRuWxr zQhI`}Hz|E_{wPk*AU{Fdw&QPUM$IioPx<{%LGfz%0cbowl@oALb>qDrJ|kQ~+RcYV z{?M@9y$5>EB;khMgO242y1ST!9?y_iR-F<27xI0KUR2J})kqD+#PSAyPc3=xez3v0 z`@yFI6hr+)BWcKCY~Neh?M-;Q^xvgrsLbj6g7xp42j)p1sf>>|ZdCVgX(83nkueU_ zL64gM!8jy;Y6zkZ`ClA|g6dt!E*dvYduX~W7CbLW1wmgbAgUcF)98EY{+8?@8-S`y zbkZzWjpJ=(Ou@3?Cv>hib@1cb!W?r^`Qbhx8KLmZEsc*z3Y?uhcxq+dull~ZzbA17 z6^*B_L`&(IvBsThbaX?pgB|*J$QEk_*g+z#pf^aNjkIm%>FNJzf{Nl6)xD{)bwyS zA>4+?A|n&fQ_@NX?I}N51m(&{`pZq|DAGvXp0XLPOMTh=)T1!$$W!fPJSiOQA(*Xl zi&IACSu9+?@k!09$m6@1+=hJXC7R8qHx<^+Umb0!w{RkKybK<=q*)P=X#VI{GFgF> zWtksH{LpIO^OKq&SJ{nttlUd!&CMZpz)cVgBvW74^OARV zsuCdguQIv+bH^{zywo%q^jm?|77nW|Y>!+3^@{YzIm;#!1P68hx!eWSQ1wc>03%OP z=N}bqmXOojf`>75H3nsxqs*I{G7%+-x$fs3bgugl@MRL5&24u4&ziaJXQrbQ)m?Cq zv@;Jg9lPzWh}-dk`iT1Q^V|-0+1=Y6?(%fhp~(sFZ;nhRPxr;1UFy^2hH$qO?i!yi zjYGY}4d(7L2=(dSl#$LK;oW=#r|!e2N^Qtt!oRq2ZEf&T$R6lYsDI%B|1L}E?4{GZ*mZ5f9aKVr?VFK#!-DQKr#mRL zxM;sorhd8nIqnD5B>toOA-&4ijuDAM+Ij@l${zLT>Ok3)&g808W``*)^TEbPA`MBR zTkVh}Nt(3UA*H+_t%GP}j3$MxkZ4k9M2SBHlMFk`TxW`XcdaOv4$$MDa()j?At);|9 zKz6ezr78b~sx*zCgg;`M>LY$V{&3NhuzY2Ak)w{1_`#r;HBR}#thY^0G#Ui`mhAJ6 zg{lz#tSY3Rp=vt+qG~$6*|TJ;kLX^XmP@FIx@gg#fh-0Fth_r8@=WBm)J*0pV3;jzGfmV1@&=2tGvd3Kq6 zj`)dz)1+djvG0BO@ym8o7EPYqAGh|78$ryzZobntluW`jhsd^8+}`^-G;v4RjM!oV zr@?Nk*U zQsaEG;H0IUt;KQ9cxheT(xod_2(O>Eu(q^1S#aEGS!mcY=VJ>_SmHnAb6z?lw8rW5 z+N}+Z?d^>XTOpxjP2v@ts<;;x`FI*#tEGXRp=Kq3HlJ;3GGRx=Q!?NhSGt2oIw_Vr zwfLNO&b0cltmg{m99h=5{=XK{9z^Fx6e{+L9}h~LmgnZzUv#&e zzppy=nT3Yp7sR}Osb7)r?0($%X=m2VYRw)%96CN0pR~UcyO`VaWO{6Hh66BEi6#6A zHkVp}a?n6KlRF=ehWLWUS=eW)CneHFu zsw4%^b8-9=sy|hxTv@Mm6)#joRlx2Fn*YI8<3%?0vcHT*b@J`PdyBJ)S83tTS$Fb>kX8+#+T>a+{~93tYJNDf09V<>Zoit|fbCll(m5R$x{{oC^>aO-s!} zsgmw9nne>#5m=z0m7$Gh-mt?W4%@bEWraWyk9)*?^)K7ki|4uh>LfB}=&rO)vtwJ6 zafrtISNFXUFUo%|FDHZQ>+9VbtMivCQOh%^#U8Z`LuBA+dQNMMD~bef+S0vunnT2S zO>LG|JapHWziitT&{A=I_&X2d%RPVn@fW^8)y5ZyPD2Khtsjc3ILCEMwxlecRvSMc zx_0)Z*G)+?YMoqKre3Jt!-dhF*@Rl@$rz|l#~0`s{6o~HW@8TR8|ta%o-a5PP&#E{ zXnqmdC1yCxDzaPDB;)YeG5!qfuR!(GjPp6@# zNYs>$_R%?);GSXn5986&mHuE~)_Xs|oXMuggDEEUw+YeLEpF z6=!miV#@*|l_dz947D1U)wUpK&)iwzCH@``2|*rnLhQyQ?-urm+er3N{A|iSqeYe>%*&3T6u+)g z=!qU(W$;Gy{c3)O>S3?9QNM>QRXCubiB!LaF0^h!f4|FJQ|F>;(#&1sZf@L2w$e)J zNA&|;i{;SkCj>vNIPE$lW37yI}_`cCnPMdOGL~GS1t40XMiUTaVR44g?hl?9OCal26%{hKTtjE6HiUTI* zHN%I_n(^nB{&{U15WO`3(P(@7-GS7l^-3{tsPVx1!r0bgpMH1lIV12SxG3vMh@R^Fh9Pwd6I3mi@ zm>aN-B;4-*{-3XH(3UPracPJW*?8|79ox#ZxnOjOe!{SHj6T-JoFuMlSQfXg$z1S|x2-Z^UXx9{)*>n3^+R zZ}>^0pU>_!2zi-P=Rin+``|n3qmb6L`W%HuXe>ET4G7hmzQ&?A|Dnq9k1>kNqg7Yv zm`rGno!v~wq&Q~?JMCt5?}fN6OMmr_{us_8PdFdS8`!jQfyV)lS~R$ZMR zt4JeZ-lg-Flb#i>sfBctGtx0ElB8j^s0KY!hH2~RA;<*T$DQ-AQrSbo6G|P;hr3Jr znOm)sFH%1XXLM4AO4%-KA!R8=SS$iXPIFw09-bX<_tmu$ibHxyTIv~)sI4iv?;d3o z82)G)59v-|p~tL5{RVnmPR9s8khZ*x%}wF_jGpW*%`b=%m2DRsgaAnjp_LFSU;;yK zKUpm<+9@s}^+JGHvQ8``YuC}k8;I;SXTx8@&6@E2`GLy(CG}|T<@R=@JS6@27x;^; zEE8%W!8Wo}n(z_1ckgQd=Pwp$9}4J*x-CoRF9|jHMOVJ1SwS!ALf=zQYa%5qew@aT zOCRpch7IBi5lXsuKpDXg!LRUe{Gzkm$9ho{UGq)XT%!q#K+PY^rebE=j~TlkPQqi9 zq`b5y%kL=hOqs&LHBicD-pr}ZmUPA1eq^U5uE%5?8^nTKz}wE^XY0gEM7fTPSEWjt z$}7Z+|BZhZG7>ACep1vIGfX503^js``h=3#J>!GD#!U7evCt?md%8!0=3!W1PVggB zIOnkrV?FIXZD%RRP8{nw-qS8xNvg&=Sb2{3VlYdO@m_Yb5O_gkapY|w0r6=Vfqmme zwrhgWY;{3uekpc~M@LulmNX7+x+XR)eY%H71@m(l+&r+!=8W zF~jT*-@#~{Te?Bw3g(69o+4Fe#9zc;Pm?Oe2K5GVO=Op^^PRuTeiB|12<8^)d^5Jf zF6sHO>vVfoOuV@+9|H|i#{*RaoTuQ%9)5LkgQrJ*{(@F_%h-~c#f=`G^?3`LJ*;Aj z)jtJ?hD@I}%hPQ5v>48u{xETm_1O?2ki3&cMJMOi)rBq!&O23zpYeIx0U97!$|B0lviDStVhQnjq zFP4^G+!7nRg?_ikHWj^8Qu0!f^jA1&FN%q(fB1?w$CHu%d_WxNT`!w0OoV~KMxjLS zS4iP*@m=wqU8Df|v^yWHI>*0FEf7s!=z&+D5tjO5-GlZRN-&2cq8c%64^^+&##*g< z#KouVUlP&+)f%&HOURP_LQQbZQ>p5m+`QRqVq(_JroU33s-gRkP?|ML3qu_rppLzo zI-qMZZE+N>qsx8OQ|s)eI_fK+IxX_Sk2*zEF|2KaPP@gfCU0gNxuLEqX4OOXS>dJ` zOMI+cg{osui}Rm>k7oUYZN`df_GdlXe+eCY6ZxqGgc^gzMJjKID}Pgcl&lrYxLDP< zxW*_&1f{U!1RVv893ig>C8B^^|}+5F&L ze&ZC?FN&d*)|wkbS;Q#4g3SP4D~aI|WCt|q1xv%>9Z(wb7p;fw9%3eU+z}a-U~4eo z;KAYHk*TX41atM2oFiT)rn$BeNe)Wa1Pk#BE}m7N%x#~Q?t{4rlJF!}j63Be=usPB zHeBdB&AIE08(Wzj(R%INl&-e6B=iMha40J0)%ucG%CeuGpWZrYpm<$PEYj;63Zu)s ziEpjEWXihB;?380{INFoVpIK_g^2+llhJ|do9)dlhvqz2@0buRY1q_K#h8J153A(B zwtjd1cx`|$S`W?tj*z6${D%#tt;hZxLuc+l*6SPM_kCTyjXVE=_`$=ank_ksiBfq& z5>`fO?@-Q^7CTXF`PW#x++XC*oqzbFe3;(CB)Cwkxq}O@YON=5O00N?;s)q7eJHgs z6wQEsu7yIn*>wI(SSN4L%+C|3SDk7aT~verM*p{+PIfUOlOE6fuLY`zT;9$jI?Bs4 zGE#j|^TRWxHYlhzCAmI0xSp%jdyqE-KWq;sU!~@z7i8oYB=NG0?2LT+YjJ$b92^)p zw=FhqW4xLqw9LW5fpc4D$F;=62VsM|Zkt&Ak`dMd9D#S342}wlI~AL-aw&gF4ir6o z<8f(d=Of<}A}4qJYYLg>qBI(+baizzgx9sGt*xkJ(#s*`iT!i7z_i5!6Q7gu~FC}=z3htmr1D5O>DhZ2F>M~(I`0AT3zUn@IGOZKZ z-HY3x&55_jVc62nkeG!W*>254gn<)|We-HFG zpmrwh0o*|N2wQ~p0y)$8ORd_Xui{s%csh`*_T_B4bqMQ^6X*{ov~{nxt=*tMYoWlS z2pBEv1EW4|MIA<@urEud3!`pUDd;>ywo1qr#;|rXI@^HGGlWs%v}S%?OUp3TZGNQ0 zNvXIJYzZ*`sS8IAXCLF#D8)9s4J(MooAQ=+mYN`XOR}b!yxi=kK0Slsl>A zCvhljz8=mI^m3O63|!k4@d5hhg8nkl^>?7Ris~we*Z7@yK`WgF>F^%g(N-r@3e$vV zsfMMApseH1s6zP>syo87qUmAL^eNI#wxE^@k7A83GzoWdD>Atc~ z*c-wnxsvWH^FtISGyvcag_fRA&6Q+{uZ;B^`SeInEAiDVxl;X639%dPClsMMSTBR|i+!$yo5xwGZ!96^<~}y6gM8d58>>;` zP2qR-ZuRawGR?wcM4OSh&1eGEhvG3Cc;rz$V$cqHe<)7FcEe?VuW}CtDfp3lC#v4> zN2E! zIhOcmsbMo@5kD)>k-~pEPCNRU6T1@iN8F?m@!DwL$#i32H%u_oxw)Ch4QzNN-7Ula zTB4SNB&4dJ>V1hMLSBgq9w$l@KA^vtGpQF%PzF7s+ZYRICg>tAVu^Uu%-Y%D_xK3yq*A0Ag(wwz$OpSG9pe65gqw*Z`7n z9Xoi=Gs8j5?#~BE6j618BKfi$d64iD^9m3E5{f6Q#G%=zo1tgT0VU&FPiKg}ZodW_x4^+*O`T=;l*x!(1R^|c8J8Kir3X2m4Zus9Uts-^czLSmD27h6|b86IVw^n`+{pr zF%BkT*WD7KLI{x?YDWiTpbMb&0`-$)*D5^~zduK17=L@`PW3g8S9gfzWQ|bKai2dW z&Jt&lEN1IZ?R*{bc?Q-3vC|+%t~I}~oTb&fEQulcnREn|1>yhAn@V0REPSyVjywen!PG*id>b&tjHX?ok>vnd0q9^kcD>3|UZWbr zw9Yq>GX;7g)&1qv8I1bKG%n*=)vpBG>d141Ki+YHd{2v}GCT)wRLE0k+1l0vy0-IY zgHm}ZpdtvmGgyV|k(7BCJKe2?JW`)HTS2PlL7Fa@KkLTjWV@e0{yn|9ahTpZH zk*LoU%hg9!77j|K1Al{jA~tlK6M_+gbzEjDmkIAkrG=t*X-QZaw^Ty^f%N8%aiP6Vu02uBaU!V_-ccV^A0%l?2Qtn<+3;`iwSN;>;uN<)^ul!wZkVMgQkJwlIc5=p z(gsv!A3}$G3OL_@O1}Vz8ykG+O&0h+jO-Va_ zh(3|JlLiB+^EyV*N0{;C?4AdvR#!fRCYAd55HTLPG{@uolglL!JZ3|k3}uln7;v1Yw!d(Wki1RgR&VEi%KVcR7Mh!)y= zo;Hy8RlD$U@3O%onzHGPqMhxfi4BvjW=0|U1wI&tIfaa_QQzP!YQ(SU%%}KKYM*`$ z+If#l*UW?(&7fOr?7LdSci}-f9^R%Ha6A16x?|sv4`*iwUC7SE;QD=Q`cb^Ynz+5!A3pW~o3S&ERa?_2o!tdXnCTt>E(m$!^`HC+YWa-+0Y{x z39cX*j2j<702BD=EEP zC5Jn&Ua=oPF7FpNDgH$%4~5fyijB<_d+{cBp5$D{e>t1>)35#95ly2fPBv*XnLH7y zXE!;dKcjS?O1jS>R931F3dhAyKocsxpoMfqiJ#zr5BQm+G#Rxp5M3mt!8VT9rXY{r zTV$eY316>GLoU5E1bjqchS!vcTzYAI{X=WaU79rH(n}ML-@k9rPn(8ZdTH|O_ulJ= zlcXgqkxMVlpx>W+(Cu57b4Yop#)4eY9W$9PM%UL}B~ct$o}MN3%JJtOsSH|oZ^|%Cp-FX7MuQ1q*ixvr ztr#KE!ZAI*)2pc?iqb$+(_~pDeNRyeS{ksGG%4nm-iKm`)g4EyjVU3Md}IN#5O^db zWijwd&XuLg=0U&Chp%j*Y>})Q5qWj8M%fzN3)L#yBHNC5m!}XvcSQD_?3C<`>?PT& zRIXb=u3NcU;=^lxYriR+ezecpZ!KK={r>{4t%H8+{}zsCs+MGuiJ6(1BX%>Ok&rc{)sN=51B&G+8hjQ_~Q&6~wfbl+UO?pb|Mf@@*)nXlix`TrH1 z;-;-jA5WPKmXaRtO3|A%!ux-p+g_Jll>MLfz5_a{BWZW~rWpw#A&s&?SsEdsgu;Mw z0!bi1LP7|U1Qto;oHNEO$s}WJ9OR6>!U@~3{_S;G?8OOZ?DcxRUg!1NUayTYpqZ=p zRo{DOMnZV~-oNL(_s;o2lkV+XT~%FORbAa({fg;z%#^=p`m^b;rcW_%{}0nYO+T7` zHg%f1FyC!veV8A!!jp<%QSjSsEQO_G$)g?Su9%gxN_g^PSUnCEo5-fJYuGF{mn~#V z*-EySVf|^TF0nB^q{NU^`t+0@&+Eo_PSEk3RTS zdgW^R0BY?2V`YXDTyp zHeGA_jcE_YJ2#kaGTmyr1NoWmM^?>8Oplv>XL{Q79M>s9*qVCPS6ogUdTo!FzZDPI z@e=WpJwBPoZ~9L6|I^nz>m(e|E3f+ROlzz5$M*4W^x-@P%clJln)ucK)D={v1I9PA zg=}W;|GZi*PRAYgyzwS{r+rh4=;HtBFE>1RVF=wz%cLPZDyOeJp`25+5=CfQ^oJW7 z8gBT*qD7}~sIR}_v^2!y$0E8((K9!MILJv47(LIay8`B-IUTcRbE?pW)q)@XfIcaFFp_24vH=Z0fc zO^l`yy<6&IwDTdgRa-8o{xP(eOkWg%wvD->hG*3RYunLD;Ov+kKh3{Al*M0J+XHG* zy@~4(|+wJR@j=qc_Ka$n{>+`;e=Ic z|C?Ar5x6#3_S#6jU|1Ly7Z(;9-;11H71Q?kQ0*a4$)$XGWGfIG=($G7gLiz*leDb! zjL?7Mo9ouSxpBjr>({@zVeF2v_^aPhSGR-CPM3z%;-afSyK%1`UwGda`g8l{Lwo#6 z{o{;fh&W`KnkfcrOr&Kxh+3gPo^l=|$j;BS>V*E^;~D|eEuGLG^F$mLCasK>PjT8_ zm&6>xw^qYdM@8uG{LG+$V_i?0PREMo!pLH}(Q?^cou%zAyUXp$Y|ZNGb{n`51Vfv- z(2l6iqg2q~3t@y38YYC6OXF*0>xq|?yE^AX+$3~`{yHv;Jp2?yDXT@o$N8D_ zGioN{uF*EI9TY1vRhx|xLVxu|mdAz|CD{=53|+w!+hIYHu||{fWnc~iGh`unWijDN zwn*Lgg1VnAoMoX6h8ETzI|}QZqlJfrN4GfNaK0_1Vz&}3x7f&Q)x&PSd|9ij-HFL!CRng@ z>PgXu&kynyVA7M$@7hJ5_DdbgdAcvmlb$rc!S8Fkh>i&X&e7&KIt$w6RzT>w;0P6t z=@2NWpCcS=cYY`OT=G#G+UdS9PdMg+fJsc@7#I>@e&g~j?b2x-0NwWOz)W~ky;?aASI1rB0cKyQZXuwvKnF;`bPfPNqZDY+Yu0P+ zbIPF7D5b5)=X4UN^HmyRoL0_FYCY5^J&C*#KJDNF@tJdhw=31qPR{|f2W(D1hn=J0 z%4yf=KVKz3*52MO#bb^b92cuNF778FD}ZWg$7X^h>|+j`Bjr(tSOt!G5szvA71bkF z0i?QDYb(xP0~@i!C<#pPU#AuP?YKCPV}dzj0}q->#o8T$i&-umHh4|gh-2G91zRG_ zyVQZ_vBN&Rq|ufP{%E0%FSfcJ*0a4{?dT z%DJRJLmbuGO{_wSxkoux<~J@mfRm07Z$JJF=Mp`Uh5uBZEolc&xx{uB=u*pGXDh6PkRoC7lJDgrX9(Ef-PVYdDpf6miTAO1Z9d@fRIa<1N?bT!T>^ z9<;jJMAxsHvVbj{t?O8=7vKp4BgccfkW%;JJw~wTvSXCxy};E6$fi&ipmor?R@Su; zmE*XOjwc3_SW;gxA0iBN3r0{~80+}Dv4t&jwI+E!x}DLa&W*J$c`jxWox0s|^?O|= z3@AEoXg~Vz*{3yRxv?R_F4q_;+_3X*AAqd5?gO~vBDz+DuL~h_+j$TB?JjH8a&Gh( zOBzeGC?hwPYza*r?=BfQaLKzJ9p@Gg9Ju&g$Lbr$3>Yxx#?`A2*A5s^dsrIcd6Npa zkG~ zzzO!t>B8^Nsi8s`juI*}VHM$(HE1(hz14%AjlB@lOP%6dS0nHW9PaB*vf(qk{bKTE zHGkKnd%NBIVu}pw-mcp#?naf15{*sjHVWBLALM?qyI&EHyOqVb;Ob?uDx0@4>$SUj zLN!WiE} za1rqlK+&J4AGq{RcL-(!0zFHw?)5vN(G3XBLmB{;a!a`7rw0yD&@R?oK>>TUov(+v z>eB`VL^V|VE-%IbJ=Ob&vDf)1W;+Cde?x^pt-~7tKjcA^7N#dmnBZ>r447=poWj1OttW+T0oCWOL59u)GAnC!h?NG zFFw*j4!0^Z?e*978RM|EB0`g+M0{3mr*{o&E*p9bf+T|v5nVnMYSf{)+nPpT26Be< z7@XGV;PPd#+K?H&E!=Qi>;p&0U*yzrEyhRFoG5}%qc8V8iYaIWYxi`&m!y&VXPTEu zW#EVrMzF#rpQorH4(X zXH6xc z|MjNLC?ep-ooh3~9lFeegVgd^_kmXAFSjn!5rG!##!D*S3kIy4{-S906;W2MzC5%A z=N#WSMD3zGA8=*5`yRUI%(Vb`?DkbimdRh?rkg~cEb$dR1wBh^87+S8(NT;xgd}(C zDZJ0}cGQ4ht*3CvZf&6H54{_@`xYdGpmXg(G#L7cth7sg0PEhghgMe8*%VK@n)Wa> zq@l_f*#QdE%iT4ZtIq-t^jVTw!>eg`YX<7Gz0$1dDfct3Zm!2=IQL2BP>+D?M7xj#2zmk9>L9G95n+ z8Ym(jb6e&yi{cU6({wG~Nh&;iUfo|bT10O>4?9HV_4y4Ci>%uS=@!rlzN*D6d;lm$P7u z5?rzg42-zHi&Gp2!NRS+58rzn6i3slD2a*HEa}O^nEq_1X;2seOqx;-bbWr6dXXks@{q9Ac$$4K_ID(*57^+I zQ1tv+6ruIVfM$Ras~7z6!-9YPwBW*p1wRQP1~_q}aqV9Veqi^yU?QkuO2ZC@T;$9h zh&7Rw*ex*Iw9K>v(6#j%<{+~<#%9B|B1!=((xxt+x4>@W>o~HIV+iLpg@gpz^n3<( zJC(!=26K=-gmxo&EU92xj%&(yVAiu9*leOlfdop1V8)g)o)W=k&cSvyp?T7QhVaPj zMpbPb92GI{z$4kal>srK{k_8@Qli2FlVU4u(dHpD3lgnyiT%U-1+$C&Qu++9@E&+i zRC;<;Txu%YxHm4@8s2|kM1P;i(3k~|ELEG zBJ+HG^CAoGPK}Gn%#4XkRkw}y_Ztut7LyU$FF1BoRwH}ExaA$?A7RavqQirO6Juva z4;%^kxWn{Y$*=d!rIMfHfVe<=d1fxfD6jid+H4b#G&5KU{yMK7H=Y$2sK-5@XX|hf z+P{Gj12{(gbmV(a7=R#OfdxsXQ9oHL<1c2eY*P`3X9|E$INk*U@bV9QSURlvA$@{SgjZ0aqo(i zQEYFMb#mjl$$?E?HEYIkqkB&tjeC zp?b#$qgz`?SFfqAUc(;GPE5+pO-jsucI68zR=v1l#fz&}ys+|F&#( zOpF!dp+~Q5p~H>VS&idn1vYxsZWuFWL#>*l)4`~j=yheYINN!%tgfnB&2|~M8=tU% zTC_P5Ck5anrP3g5^rgcKLNE+d_~`{^41f}`HxC>J!}SwZAM?Mr_N}*S@AiLS^@Qsi zCb?@etwQK+h}F#>j-Z07QH0({W#nB z0~03fZ$z~j6B1a4Flg|ZCv*hFv%zYE^kQbZpuUwnD}Tc7y1LyHfJ8(HOOr&=qTb@d zhM9H)n|MyQsg$}sP_S8X+!fY=>U|uGF!!Ea)Yg2cHku8obBU*CEL^=aBi0Gwk0ji55I!gya6+ba}qGk{LqTtfD@sdWT9!^w(^0t!Kp3NS2XV$ z0CmH06rAv!^;=GYE(m5SodkDTuav_tuN)KrT#a0<>y#nLwfO@$P;>bP3)oO=49 zXQVqX-!8V&Y>O{@Ub+jim5ygj?ByWJ0_V&2@o#>nA!0Orq6%TqiO4y3FT3T1$#>m2 zaZc)_2iWq9v$quHubKN-BnNwINd`SA(p*;U6^<^1`gdiG%EA#gO`Z(#empU1c21>jbkdmlvm0{jKkw*JKfmuj7SYkcBJTUaW^0M=zjn{H8xo_Z#Si_%sddL1_wN37 z|GsZ`?fQ1#{%?1~N1e<*lInnOKU1_Rm7h`JUP}(%772X+h9)?iujav|nnL1i(DD8u zm@ubPK!m2#`{Y~_xt8ge-g6BcMQx#7EV)`Fpe_* zo5I0IYn+JOTyZ$YEspYG#aSt8f|!uiD$R*p6UYRnOxn6s5t-n>wEw&7UliV%-6&6U zoM&g#XAE{eA&f|$k*=l-w>VpcTNXLb3WFhYTg6$T56*q@qkMg6scy!^gKo-_`NxJs zlP4c)7<*{SltW`v$7W@XO&vaOp8P@e?$*{l)gyM#yk=MR%7Np`%Eu>9y=Hm~mxKe- za-MyUuRULh^%d;ub#UJ1iyuvL#Iol$o?W-@?8c2}*RFYeW7GAGjn_Ag-`Cil~5 z{9@hf8#cbacJ1pMH@v=XcjLbCt|EvU^u@0CyNnD2h#1`E$`l|7K3zG&oysWSrpF$5 zjQP&PhYu^GoO!~Foe$DGY4PL8z4}LRCXsT^Xo-%NBi#FS~m$t0K*l-1U{TS6+kcVKJH% zU@r+yr=rf8rTG>%Y-&(0`^F$gd(M$rJ736}p7-_h9bfMV4chqklc(ni zjcG^aHJjeqpq_VLNXeQtap}>OuWe$E4ex9k-Jk}rZxS=max6fn&m}{wW^1uny!5$OE?P%W2&s6)}z9s zN7ciZ=gqr}=InYKRw@ZnBx3hM97<7Ue(uxg9nFT-Qg*9;iV*B8xE@d}Y${Mc$-8ml zo(F4Nez)V~Y@dV2zT38U$ilj&Yf}cU7ymk!4W6rRt*PJf^F6m;+PiJO{6f&>8()2U z=gEn4N}LypM$j5b(}V|4V&D6;9-Z8J&o%XXn&b^H zuNb}ai8)(7*yY3CRNFrm(*DM#37c@v!}NP*OuTt!-(7w8yuW$Q6FWz*dUaEO*77$Y z?PK+J@>Y`Jtt{vM2F$b0Gv?x1smojW0e;EbdO5={Y1I7^%DMF#vffgLDXc!B%u-l@ zGI)lXrDh?AqH0;f4yX$#2b~=)w3fF}psTh#S8buL+Nhk?k{;#s_VaVKDLgeu9~_KmI4;>k zuqiW(-$4RPbBybo>q`uVKuE>SpJ)78_+l{x1prwrB7Ovqzy@Le7^B>gkj+G#1lYO&dR?%$d6x8BAA-(1Q2HsSt) z4Ha*RA?mB|u#kZKO+r`pZJ{>*`UB3*)w_%1az>vLvxVw=n*9RB(11|kt%?oow7O5U z_G`YUT2Ma=O9?31Ibz1|SYzRVdVgE!sChJ2h+r>^Z}8p}>KZGQXnhhAUXMO>qkh-u z+0$HoZKX>h>(LYTbBPRf^~p+CxoIBdUY5396<%MDa!t~GJ>ZqP;EnSr_dM{Uagw{< zW>-01qa6CPF(Qg}QK^rJz=JsO;2DmU`);g@2S^;RkEY^!J_{}XM%PQy$MUW4p)wJR z$82kmn1EP?EaM1~1Stsp*#2majj%X3g34>t0*C z_BHv|Pu1oR7PKvT^Yz7Ti_fXEzWI(F`Sbj?MQ^-2uWjCk?5@t!6OO*-dUE+|M<*;< zb9Nn$-9gUdb!XS0#k;_ZmpCtyT+~j}bu?!4p(|T3zLoeqDNPM9dJ`H2@6PW9Mcpo3 zlycR<&LhHXY&LyBZDPkz)&w7ea#1Sqvjer9s4;j7dHN|wBRs4e+L9%D=B0F=S05Co zXxS-|PxxV0d(54nl2Vg8y}zc6j$b$2DL<=jcfKmXl@PjGOhe>axePun^5ns%)v`e9 zKAg$LrXFmLm`D|CuGatFkp4$0)x-FHZ+P_kNV7@!m z3m(O=-A+)y((Cg!Ar|DS4<`s*QI+FvXA`Cwm;FLj!VY0euN7%@odI3Eo@&8Jzv)?x z7>)G~O^&-cMW$Y>ac*#~`XzcKQ)Jht(gEo;_=P@bEAlN_jOA%~T_Ak$ym{P#mKe znWLs6ThvC=VGo|^ZA2sWAae9*U_~PjiapYAoFc`4_q^ZZx7g!%Pk24=#~9_gtZ3EZ z=nOfMi^s$C&Y}}Q2!MY`Xuo-Y zA^m`arm?)qK0+E&1YL~B)hFEFh1=ZU)eiS}*5Lkrwd=e@h`;KfYS~x*A!u7fe<^CD z`8)M7bXW%Cc}w%zLAHjas`J$CSSoyyZB^I9ZyvZ)I=5>Nbi@7Xza+E#1foro0pa%) zZGzWyf0qSUUqkQaGmu!}OT!leO5>9Ikwy0*wf#2rE#b0Lp_lkFQUW_Vi>2qB_Fe49 zUDR*CG}R%Wb}DvbAdbaPl;eIel!LKr8jV6trAq?;VMf>hfhx_NMwc+6puE>ps6@LE zV-%qaVr5OPy{5+QjHLJ68ts?xiS}##uw}z5Dcf+RGgA8;zPzGhxqeOfM8CFdn3U>_ z?*`yQ&tI-$ZgBBef+M<5nyL?MH}6Hp~v0IZg;Vh7#@iG*?QOA0j`lzvx~cKzbMJ@PfS!! z^JCoQdh@fW+%~;7qqW?8cKc7SBZA?SS4x%Qmy*6x5B;|{Y*x8i$vsY6*7XnZlDt~- z6Awx)@^p$Ro3`WH*Yb2+JK(x@(Z$u7F21?B`o7DPSnRrMcoL$izU#bnS>B3~afTi# zq<(CsM573)xQ?ji0ZuK9s?DS50ZuL2Nl^+xn0>VI*6nPWtsrD~Y(v(+)|{<<>%iLQ z79QGCGJeq5TNX~5GhleplBEmw&uVL5zu=Z)dF$xkj9+vA81=T=hi0d&J=5^Fy3@^V z6YpDi?a8|0ISVQp_tr04T$?_osi<^I{lr%1&XVh=EZ$jPO}fs&Is&l^E%vNoZMJE# z5(>xu8f=BAw~`q06aONGEV+&~W6PG7VFJh!N4(rZU_SeSwszf~lJTBZx)n zlE-MdN=4MxSHu*cg0UNzjI)dq6UDcOkBl~(`{hj@K6P={!*>+Ut@cTb50A5&M?H6Z zOUE_IbI=d-e>gy|i;1 z9Ky3zw;x7}9UbYD1^jLkPq63cON6h+T?#w>06O}P2~`?vh|w?ffl zYfqe5`xup6$h^c-MkfOC`EapRO=H{FuQv&Raf4VYh+H%Ado)Lm`-6}LeUKn-*zo-Z zO3pF5^a^A9oqX8wWoeBj?rSU5lNoUjlZ)P$?gSO{Nwmi-S+ zZb&!(>S>U!F6T-0Tk$?+mo!`|F~LJ9f%b5o!pSwOl&QD|`=DJ@Mi5B{E~ipscRJbhrCa>B6tQdM2si!Lf8i9 zh%k0x+7T(^h-8trbZ$6uMEt?wEAPAXv{Ftl=g-l7R87n(6sq_lG3M0-TL4;B4$<=#imd66%c< zLV|EGgg=6zh&9s`w}NO$PNYD+kI#|dNNfr}Cu-UcBbgvdPOrXF*Kv2ns>invt~s)4 zbm@xQCSTXM^FZs0J2sR)^i17dYpyF_I(+A)v2#W(z&?es&WJ(6KxdJ1hx(xr>C;tIDhO=Q~!j_m?uokVK*Jc35?}@%s?%H7__!pm^wt9zuLsl!S)()EHWHC?<`V%*te?nRyCA;C7lD2&Tk%PkFgV5)V_P@h>kt% zN0Z=)>-~Jri^I)NUK}pE&gcAj=`Woe{o`SuHtprPlE)-EpXn+UX3KQqt(nUtMy(>f z>@3LUd`7Cy?>rSk?I6ep8%vh9TH-l<=tTEAoE;sJV1JU zy$W>CicHcE1K_!w#Mp#RKh{n(>VA%kjAdU8^I(`C;A;b@W1KJ4&vp?eI`1~lc9Eaz z^U;rG{x=3rqeG5xu&VTS&%<6u96BTpb<7Ztb{-mSr!!gYqtUXhCNHT8=ME^8b*Mau zIvGO2BF=!}o+iUVJtOGYQ^$ky&_@H=*IBAR8+lOuK)rAy%T(n8lA05A8tP+0V26<4 zy!7w`JJn@N*dyx1*=*;|`yVxl7egV7dAyFkCTw*q)obBZv4rlGb+&1>ncpx3)H&zh z*W1@ExG(jB0(9~yXsucDK54kJ#n8!@7&PlpT&q+zljhYl_4&uUMw#B@S2vL!rmLXg zW6Y;5k?#Ya`eOXjg9Z`3*Rj%ZNi;j=$Ulom!R0=VpFTS*vq#l9HRgm+*dYvYp6PHt z`vm(d`-qLdv=@n|VP6Z>v*H{7UA>4A0X5n`en~Ihxz?>0o$p0DAEFlh?`y@z7u6fI zrpJPeiw&SA&x4xE|1&+W(*vSN-HX3OmZ!RRk!3#gAJqG93NhX4HUWCiNi{UA(Q`aB ztkG>g^w6)ap29xz(62@hA#L54pKR24p~Q`m=bOjGadm>8u0iS&o+|-sYI@Xcce4d2By8&daj%bcgfzLRp9N1@;;3SnTR@ zOgBxCm;~=f*{Q*TKNAe?c`L3}D!bgh%CX(#FV^F<5I^Nk2>xd@%7R`fgI>5>vPq|Z zo(*5eCjFrcGp6v3?s6U@-{`_;czfCbK*)c3@VN`f(_p&EwDVeyr?6OV|8|#;T<>DoCL|%5uox5X)E+2zqn6=ZF`FYHtGd69_9X2Z7(m9D% zH0i6G(2B>=wrg+>#~^rwIP(>x6p0`cuOcxd$QM&{X^3C?;sw9Z^HdIJL2O&@z~X|+ zX%)j-DhrAS4lKwU-ddG4Jg=nLxPrSa!>V`B4wR1fJgwuW@~KG$1xfUZ%N`sf+#$zj zq(!C_7pKsrF$*$<_$$xvaBOC0#EoJM*3DqQ04=$qrZGM}n#k2uqS<{yb$V^xjLIun zo;GsK>>B+%Yf;n>t_IUQ8L0i7ZW4Z;VDgvdfnSp_n+aL!$s~hW#wVC&@6J?%x%7!q zgUxl!iQt`7IAr*=%FK$PB@-%!O&eZNl0^J%8CKC!Sy+;kjPFy2d0r>ZULQMq$iSk) z;nRj`t&mibU!^^P7U9<`rlED__T+y(=lZ=;d}^wT>+v}`@wOpD6FpjCxVxE*<}yBo zO;T3C)+R5jN&h+kFg*D_| zU7Z~`JGW8Z4o&pe&g-Co6zQ)o;X0cy7Kr6?q{7c;6+Xt5h2r1EQfVfxti~9PuFMyg z#9O*QC1#OO9^X*Kf`U|VHPhr-LYTJ#%mj@20 z+YTIH>$Fd)fLsRdHi%u{bc2umIXYxO45Xbk*gO8$!v+NHNze$a*fjY*=<4IT-j~$d zyFsFz4<4{+B*Xm06OK`M-R_B%2R70b)be-&*w3@P5e(_HW0dnF$0)_dVLWvR|Ac?I z=n=Zn!%r1s1|`DD!@^UC#EqQrZZtfi(;)Vsf!9uJos62t8kKrfiQT>6p>P+tQK_7e z9yVYJeY^3a8#gcyG|zbmub!-hoOnQc+yzbO22Fznyf_@dNP;`A(S^wPH|h;8iP72< z`&iMI&MKU4al^rb=739l#_t<8S2``O*5WosyX$RkG&qE?3*}&odX^JkapZ`vc$70; z@`T4vCPn*S>RHa*SpeO08oI}jXzknzH-D(L@q(7NyU)c!_k#mxV@Qfs(0N)akZT-I zi{*}|9Y0)gbd^;2u6oBhc1o>U$EF$x3ci}`_i}obx`AzJb0({SZEOpyde=7|_<8L9 z=#CFknW1ZRCb4gM{p&+>(`L?_H#03abp04vE^q`D97L3wL-Q4yc8hp>$wp73ZZ1Mb&X zw1)@BTpFr7zXEZpR)z4C`XlFx=h0=qz#X_&-K3FLenDsC#E`}Td>Zq6n6}c+_0m?j zuG3U*ht{)K)Et!`o#uIf7y9BMA;iUZy#?~z=Xaq;xwwM}99b8t3!!4#na6k!WDjYr z>v1mFpdzS*6r&xlirMN~?Qu5?-FUcLgY1CT8n9(gYPy?ruzsyl`7c8UYbC|~7(onIo;FH4 zhPj@8%DBd{qjHtX@7+(EesaNl#(mYJ_GdkxCk=_`pX**yul)~3Nsro}H?Dcqe%k%C zN9`{dS9!E-t57cYgD$qXqGc2*qx5)sw5)*QWci&pvLqfO6TjM_hTR$MTy*FV#(z(D zZN>PnA2{-sR=-JfEYah$+KE@tFwiQA@qF|_v(UjIeB%qvGV2TS`4VQEHu*#Tq(mm0 z%&sIjvyuhtcj|-ZpE`a`&AO4rH*KAJa^Z@P?>PL^_*s=tZm2)doV|7Bb!X-+eremR z#~wa&ffaS$bM)v@QSMMzzRvviys)WyK)&piIpTq)k?Tj6904_pKuz`LeNU>Y zdecwB_d(9bzht%F-M4n;DbT^z5~fqD3{2~47MJA{J>x4Ab$Yzx$LxIja0k2jr268u zPpz$v>!T!gqXeT=Es{d^b_9Bhjum#K`~vPYbw#@&JewuNXi2p%;g;pItOdIfe45y$UFxnEYa-F3#-MnigMD;Cy=uNpA#?!~`w@AHcB<%33LA z3FSkYGVJ-An-QgpgE#dT-J9xW8AaVfihpq|5%)Tl+@^gPM$vHIF`oE&X%84$CQ1Ey z@|`?iu;Z2D?;MpDe@B&g%29<+{<|k&R|U-U0rq0uEk*LV1bq{YEj!U>!*V9-wul>J z@d%D6zk5n^Rghm^MnzijqUvdj_s@y5L+*SjrQ+l;H-dCcD2&TO1o)K!pV>oP*uJ&cJ^*oy0t&mxLww z>>Y~xuzSP_Vjp>$q9~teztsz3ACrpc(I?ntKOSnAvDc&o;Urd^Qyu^w9ygogwB13% z$kbwMbbV`qHKI)ZpfD}0VA#XoTL(p?6%y<~z`g~$$ZX0dT?;YRGoz0E_+)H^JYH;- z--Bl5_DO7Yej$X*<2TVg9V33vnkBM(9(d|=aRQ~EA(#=ezj#poQc{!)9N)i6j|gwe zx8wKE!VP%3O*V<83atdeenxWwJIQwDs8f`@kDDLB-IcgIO1~Q-+k~#s>Xd9HZ{GvW z1lub8M!Z$Qs8zX)6O^!6MmmmI-JeVfhlOpS3BrEXx7yiR!~Enwy*IACecXG5vA?tn z0ti6s<8Z#BlE+m=H&ykHBy_HtBAl%HQj*U0dj(Pmtw~6E`czw zk^UmilqXAy*+g70bx41a6nV12UrC|0bkbioX;hvEmDj<3UN&r97obR$!dLPI$*lZf z8lY?i%I3 zhwyuX(fXK?58+(YIvXZzXyqdBc=*nKlTlQZky>2L7N!*yrKJ}a%l-vv>4k;qX$8h7 z+T$-c*!NAvo6qihO!^3o_KFI7Zh5)lU6GkJY*F=|izVN#Gb`rbd+2dChe3 z$?Lzt-CvL^lK{Em9LKX}U~LT%?RYWk(Hoxs;Nb)B%Y)d&;p(X@HgS~tBwz(dUyHXZ zDO{Rt30YX?%cj>dzs0O?%_ZsU`tQE0f4oO2UFPa*xXjgFR8ktg{kGu=luDJp5jQJA zy~!?*m$W_oIXIyfm>5(d~N0zB^;!smGUqOp?efBJ@Ie~PL7S;QnaR21GfThUaP`qKlJYYNK7r-X-0^Yc%jdJd{NVjQxK0OuL7lC)e9xS9=> zoGW^T&zhS!xG3755D+=kwq(obdkPCvM_0zBW|_^~70Z(P;VWAJ$00g}7v-fK-w@Fz zq~0`ls=PEny~US$rrx*EtAF6SEnG2)2b6Q@lNX@5|A`VGDn7y+=(lF&XSxE23*|4x zB69?XNc!$Iwm3^|HAj5Vd^hgS#ocmqpjIFB-On}ZxKwlCzFWDz3y?n*Z!xa`w97iQ z0Qq0y?dAcV8t(w>JHpvHlKGhjxHVpY{5i_`chh(yxb%(eH2aTx@4az%p!Rh6TXC!M zQ*YAOShJaHyvpI4#*@D-m=+)aw>c znmVZ=pkGsH8a?%A>6q}IGDfQdEuQe`*44_GXmvG2iLuwkKM8-6x47w}OOoU7xSp2h-H*LR`*u~jQ}L+7QYmz1zSWE2;Z0yM|Ps}BcC z#c8P}C8=q}#wV9G>8S9EG7{W`tSRr4^hvkkN{xQyPr&YTlzB}V>0$S=L6!G^p)RdB z)S5T5wV!XXGO{o&BE8=~-iyl&huw$fD8}xksY(Ix4glY+X3^H&Qi!Ihi+B8%ti?66 z@&iMQl}vSR6x%b{(`XQ!imSjeu;6f>049O?v#udbTyTKT*XO$7GuDLN8;*gGKyZGWTB70O2Yfi3I}6jp!Jl(+gTvA} ze*z$tcPmd|1~Lx(;n|1aGIETpy=z3-R#=>#Q4}1XG&5V*jj?Sb>@~V>oG)!&mrzI+ zE~L0@IIHK1a`<#ZQ3|ZDv=uhxlwPCWh`dg-z4sdLpJ9s+E=o@?DXQ+h#;wE4ibK;1 z3)2b{-|JDI$RZ%Q2b6EQoz|>}OHw_h)@n5;i19Gc=@3r!5J@P+xB2gt>IL~3Lt2eb zmZpAIcG2h;H~b3d@bK7f4ivt?OMpz(c+FNXRQ3ja=EXAfxghC1@jm58jycUhdH5Bc zK8DyU*VqA~g?k><72$^L>eZAYgn0&*xm1 zy#-zIsje%~r-YkEY`I?EqRtCqJ5XBpKv4P%ZaZ|%0!@gx*A?1KHZ^obdezF`nbxrd z47+yOgNTk{2&hl0xfZ}@W>7?e{>XS@u9Qrkjb`H#p$W)#^J|nwfEz;^5@*^oFdPCf zR8Al6w`>%~6f3`h4>Jzi?c?JjaH(kj1L)hV}WAZa&5Bu0sA5{ zH&Vze^R+~TStQ9Kcn3;MObqbLO^Jw5vdaQ3zD=#sJ{`)Yv44EBFd)m%V$Dkp@`}h$ zH6ukwd4NTU6|D84KIw(QqvfRZ*ub!cCBp)nXO1$*EpH1PP#Tsrbk>BXYeE#k9wu1J z1I$_ZDZz@jccL5>;nk+JmbTw>1aX4e0AF9lTVy@~g9Z$+9+=UxC*RT}?K&8n>?2A2 z1BWCB1cyc^_PbdMnzDRR;^1&^ucU!Br>cC!$i70%j$z}kpIhq98Y5aVi-f_&LAgS9 zb&O!m_R3Y#heel7+G`#-bwd0g@0l!2@d>g=<%pxb{Ra3ZRS%4B6JL$nTvNVxT10$= z95SjU!WvO$ODW7B=#^Vw&B*lk9x;BHg2k2o>G2lD%a-h8tC>|BGr>0?TVZDF(7deW zV~2>$Hz+ZvU%YR>#JaHAzM~h!uIg91X($VcxB2zUom$a1%pU4Hc=_z85s!NLh~CLN zYwf9)z|tVUOkW{d@DB1CIx8})q9Uw+T3UZ=AIw-j|z`7`lZ({!h zYhX!Pu;ObQ5R@K}EilFsg9KYaa$oOgF{NxotTLc-s22+e3lgoV@sgMjo^5~Vo8KiO-QlAOC@?igP`t9N#MM6q9pSFm4Daj-QiG9WxR zKBgg4X!9PK86MS7v`BtQwt%#7>(D{r-j;-<0sZ_^3c`hu1aFH^Y(>j>>r z5b)wqX`68nU9;P`VuNPHb*?xlJjk)M+e7RCKs9hvZ?}OcIfIA_&a~Tda1WX+(+og> ziVxhjLA!A(0IFEX0O|CM4b_p$X%}XDm(IO@{IDG{Lf=Tyr|MMAz$7p4@WF|TmQM+i zZtj;D9U2^vJS4EcB>5!A9^550S@QR^%s5~jFkn!C4->r=U*CY*ay>_I+C7@O%mD1O4!fo4%@ ziyv5>PYbXm`AHVBUsQPJNbfcw zvmquvH#{IR${Ji8-^WV5ntFHStf5{(-steYnSMc~ftFNz?apLx z(Z}m4A1^OuI=YNxi5nDTsgS(9EU}7LO6Y?b-a~W!BEkj_6SIB2%j-keBndGIl7a~y zFZm1hnB}Z=04vI9zgHY~{)-l%ho6NLFKJA%0$Q!Ux^~7B4?bc(5?LPoP(T zGALk*k0mOsO_mj#pMOrEXwH=L(tH)cD^c|EU)#qE9)ytACrYt|B{1i2EoLV9_{66Q zA`?aC-QUM*@^yaD6{(!_^k>*9_8fbOy~X~*zF_}imjxNzKwtYG59j|45624E4!Reb zScXKRVGUyZQb@io>P3=2BAQT@-xe)8;h{NhK7#hs$qH+ej8uDV`k+Kuv`|9H^}mUU)IdV z7fTmaK2Ww>C|9QyL23V`*lg(xoi2~KY}*;xw4#zdqXso?p4O1k8e2STvYO1kTDEtl zxLUn%4cntGy@ujN)m^tB?o^Al)fn^Qe1aHr42E@D1}L~}c$v4o zd{*?^{;E>zoV@;@ZuQSbqJbcPrstucJ=YZXV0;j1=U%7{`8-+Cl(gE& z$oTlk$l5gFyYxEkdtLgZ-Ajfn*?r^6(3PwtJHeS8pOqCaoJ+`73kxPpC@5@f+=1O^ z%kbEkbmxa2AmY=<00cX`WcThR>R|TXiWTZWr_7%Yrg!zl@kNETic(uxG(PtdB9JDN z^O*P*+DxF;=uG^|5el}~(h_ifPdb8ou$cv^hNF-R+f8N!DVc|GGi zCOwQDca`Q0_l%M$)O9t}xQdu!y4)8rVawHH!oiJtOn8gjH@kI2SgGA!8aASpBC>-N QFL9GuLcBM-OEo$E7bYrfRsaA1 literal 0 HcmV?d00001 diff --git a/package/index.html b/package/index.html new file mode 100644 index 0000000..b324d59 --- /dev/null +++ b/package/index.html @@ -0,0 +1,153 @@ + + + + + + + + Document + + + + + + + + + \ No newline at end of file diff --git a/package/run_server.bat b/package/run_server.bat new file mode 100644 index 0000000..10f6bc1 --- /dev/null +++ b/package/run_server.bat @@ -0,0 +1 @@ +py.exe -m http.server 8080 \ No newline at end of file diff --git a/src/app/app.c b/src/app/app.c new file mode 100644 index 0000000..4a58c72 --- /dev/null +++ b/src/app/app.c @@ -0,0 +1,2 @@ +#include "app.gen.c" +#include "app_wasm.c" \ No newline at end of file diff --git a/src/app/app.gen.c b/src/app/app.gen.c new file mode 100644 index 0000000..cc552e9 --- /dev/null +++ b/src/app/app.gen.c @@ -0,0 +1,274 @@ + +type_t type__app_key_t = { type_kind_enum, s8_const_lit("app_key_t"), sizeof(app_key_t), + .members = (type_member_t[]){ + {.name = s8_const_lit("app_key_invalid"), .value = app_key_invalid}, + {.name = s8_const_lit("app_key_1"), .value = app_key_1}, + {.name = s8_const_lit("app_key_2"), .value = app_key_2}, + {.name = s8_const_lit("app_key_3"), .value = app_key_3}, + {.name = s8_const_lit("app_key_4"), .value = app_key_4}, + {.name = s8_const_lit("app_key_5"), .value = app_key_5}, + {.name = s8_const_lit("app_key_6"), .value = app_key_6}, + {.name = s8_const_lit("app_key_7"), .value = app_key_7}, + {.name = s8_const_lit("app_key_8"), .value = app_key_8}, + {.name = s8_const_lit("app_key_9"), .value = app_key_9}, + {.name = s8_const_lit("app_key_0"), .value = app_key_0}, + {.name = s8_const_lit("app_key_f1"), .value = app_key_f1}, + {.name = s8_const_lit("app_key_f2"), .value = app_key_f2}, + {.name = s8_const_lit("app_key_f3"), .value = app_key_f3}, + {.name = s8_const_lit("app_key_f4"), .value = app_key_f4}, + {.name = s8_const_lit("app_key_f5"), .value = app_key_f5}, + {.name = s8_const_lit("app_key_f6"), .value = app_key_f6}, + {.name = s8_const_lit("app_key_f7"), .value = app_key_f7}, + {.name = s8_const_lit("app_key_f8"), .value = app_key_f8}, + {.name = s8_const_lit("app_key_f9"), .value = app_key_f9}, + {.name = s8_const_lit("app_key_f10"), .value = app_key_f10}, + {.name = s8_const_lit("app_key_f11"), .value = app_key_f11}, + {.name = s8_const_lit("app_key_f12"), .value = app_key_f12}, + {.name = s8_const_lit("app_key_a"), .value = app_key_a}, + {.name = s8_const_lit("app_key_b"), .value = app_key_b}, + {.name = s8_const_lit("app_key_c"), .value = app_key_c}, + {.name = s8_const_lit("app_key_d"), .value = app_key_d}, + {.name = s8_const_lit("app_key_e"), .value = app_key_e}, + {.name = s8_const_lit("app_key_f"), .value = app_key_f}, + {.name = s8_const_lit("app_key_g"), .value = app_key_g}, + {.name = s8_const_lit("app_key_h"), .value = app_key_h}, + {.name = s8_const_lit("app_key_i"), .value = app_key_i}, + {.name = s8_const_lit("app_key_j"), .value = app_key_j}, + {.name = s8_const_lit("app_key_k"), .value = app_key_k}, + {.name = s8_const_lit("app_key_l"), .value = app_key_l}, + {.name = s8_const_lit("app_key_m"), .value = app_key_m}, + {.name = s8_const_lit("app_key_n"), .value = app_key_n}, + {.name = s8_const_lit("app_key_o"), .value = app_key_o}, + {.name = s8_const_lit("app_key_p"), .value = app_key_p}, + {.name = s8_const_lit("app_key_q"), .value = app_key_q}, + {.name = s8_const_lit("app_key_r"), .value = app_key_r}, + {.name = s8_const_lit("app_key_s"), .value = app_key_s}, + {.name = s8_const_lit("app_key__t"), .value = app_key__t}, + {.name = s8_const_lit("app_key_u"), .value = app_key_u}, + {.name = s8_const_lit("app_key_v"), .value = app_key_v}, + {.name = s8_const_lit("app_key_w"), .value = app_key_w}, + {.name = s8_const_lit("app_key_x"), .value = app_key_x}, + {.name = s8_const_lit("app_key_y"), .value = app_key_y}, + {.name = s8_const_lit("app_key_z"), .value = app_key_z}, + {.name = s8_const_lit("app_key_space"), .value = app_key_space}, + {.name = s8_const_lit("app_key_enter"), .value = app_key_enter}, + {.name = s8_const_lit("app_key_escape"), .value = app_key_escape}, + {.name = s8_const_lit("app_key_left"), .value = app_key_left}, + {.name = s8_const_lit("app_key_up"), .value = app_key_up}, + {.name = s8_const_lit("app_key_right"), .value = app_key_right}, + {.name = s8_const_lit("app_key_down"), .value = app_key_down}, + {.name = s8_const_lit("app_key_tab"), .value = app_key_tab}, + {.name = s8_const_lit("app_key_backspace"), .value = app_key_backspace}, + {.name = s8_const_lit("app_key_control"), .value = app_key_control}, + {.name = s8_const_lit("app_key_shift"), .value = app_key_shift}, + {.name = s8_const_lit("app_key_alt"), .value = app_key_alt}, + {.name = s8_const_lit("app_key_meta"), .value = app_key_meta}, + {.name = s8_const_lit("app_key_caps_lock"), .value = app_key_caps_lock}, + {.name = s8_const_lit("app_key_delete"), .value = app_key_delete}, + {.name = s8_const_lit("app_key_home"), .value = app_key_home}, + {.name = s8_const_lit("app_key_end"), .value = app_key_end}, + {.name = s8_const_lit("app_key_insert"), .value = app_key_insert}, + {.name = s8_const_lit("app_key_page_up"), .value = app_key_page_up}, + {.name = s8_const_lit("app_key_page_down"), .value = app_key_page_down}, + }, + .count = 69, +}; + +#if PLATFORM_WASM +typedef struct { app_key_t key; b32 filter_out; } wasm_key_map_t; +wasm_key_map_t wasm_map_key_string_to_app_key(s8_t key) { + if (0) {} + else if (s8_equal_ex(key, s8_lit("1"), s8_ignore_case)) return (wasm_key_map_t){app_key_1, 0}; + else if (s8_equal_ex(key, s8_lit("2"), s8_ignore_case)) return (wasm_key_map_t){app_key_2, 0}; + else if (s8_equal_ex(key, s8_lit("3"), s8_ignore_case)) return (wasm_key_map_t){app_key_3, 0}; + else if (s8_equal_ex(key, s8_lit("4"), s8_ignore_case)) return (wasm_key_map_t){app_key_4, 0}; + else if (s8_equal_ex(key, s8_lit("5"), s8_ignore_case)) return (wasm_key_map_t){app_key_5, 0}; + else if (s8_equal_ex(key, s8_lit("6"), s8_ignore_case)) return (wasm_key_map_t){app_key_6, 0}; + else if (s8_equal_ex(key, s8_lit("7"), s8_ignore_case)) return (wasm_key_map_t){app_key_7, 0}; + else if (s8_equal_ex(key, s8_lit("8"), s8_ignore_case)) return (wasm_key_map_t){app_key_8, 0}; + else if (s8_equal_ex(key, s8_lit("9"), s8_ignore_case)) return (wasm_key_map_t){app_key_9, 0}; + else if (s8_equal_ex(key, s8_lit("0"), s8_ignore_case)) return (wasm_key_map_t){app_key_0, 0}; + else if (s8_equal_ex(key, s8_lit("F1"), s8_ignore_case)) return (wasm_key_map_t){app_key_f1, 1}; + else if (s8_equal_ex(key, s8_lit("F2"), s8_ignore_case)) return (wasm_key_map_t){app_key_f2, 1}; + else if (s8_equal_ex(key, s8_lit("F3"), s8_ignore_case)) return (wasm_key_map_t){app_key_f3, 1}; + else if (s8_equal_ex(key, s8_lit("F4"), s8_ignore_case)) return (wasm_key_map_t){app_key_f4, 1}; + else if (s8_equal_ex(key, s8_lit("F5"), s8_ignore_case)) return (wasm_key_map_t){app_key_f5, 1}; + else if (s8_equal_ex(key, s8_lit("F6"), s8_ignore_case)) return (wasm_key_map_t){app_key_f6, 1}; + else if (s8_equal_ex(key, s8_lit("F7"), s8_ignore_case)) return (wasm_key_map_t){app_key_f7, 1}; + else if (s8_equal_ex(key, s8_lit("F8"), s8_ignore_case)) return (wasm_key_map_t){app_key_f8, 1}; + else if (s8_equal_ex(key, s8_lit("F9"), s8_ignore_case)) return (wasm_key_map_t){app_key_f9, 1}; + else if (s8_equal_ex(key, s8_lit("F10"), s8_ignore_case)) return (wasm_key_map_t){app_key_f10, 1}; + else if (s8_equal_ex(key, s8_lit("F11"), s8_ignore_case)) return (wasm_key_map_t){app_key_f11, 1}; + else if (s8_equal_ex(key, s8_lit("F12"), s8_ignore_case)) return (wasm_key_map_t){app_key_f12, 1}; + else if (s8_equal_ex(key, s8_lit("a"), s8_ignore_case)) return (wasm_key_map_t){app_key_a, 0}; + else if (s8_equal_ex(key, s8_lit("b"), s8_ignore_case)) return (wasm_key_map_t){app_key_b, 0}; + else if (s8_equal_ex(key, s8_lit("c"), s8_ignore_case)) return (wasm_key_map_t){app_key_c, 0}; + else if (s8_equal_ex(key, s8_lit("d"), s8_ignore_case)) return (wasm_key_map_t){app_key_d, 0}; + else if (s8_equal_ex(key, s8_lit("e"), s8_ignore_case)) return (wasm_key_map_t){app_key_e, 0}; + else if (s8_equal_ex(key, s8_lit("f"), s8_ignore_case)) return (wasm_key_map_t){app_key_f, 0}; + else if (s8_equal_ex(key, s8_lit("g"), s8_ignore_case)) return (wasm_key_map_t){app_key_g, 0}; + else if (s8_equal_ex(key, s8_lit("h"), s8_ignore_case)) return (wasm_key_map_t){app_key_h, 0}; + else if (s8_equal_ex(key, s8_lit("i"), s8_ignore_case)) return (wasm_key_map_t){app_key_i, 0}; + else if (s8_equal_ex(key, s8_lit("j"), s8_ignore_case)) return (wasm_key_map_t){app_key_j, 0}; + else if (s8_equal_ex(key, s8_lit("k"), s8_ignore_case)) return (wasm_key_map_t){app_key_k, 0}; + else if (s8_equal_ex(key, s8_lit("l"), s8_ignore_case)) return (wasm_key_map_t){app_key_l, 0}; + else if (s8_equal_ex(key, s8_lit("m"), s8_ignore_case)) return (wasm_key_map_t){app_key_m, 0}; + else if (s8_equal_ex(key, s8_lit("n"), s8_ignore_case)) return (wasm_key_map_t){app_key_n, 0}; + else if (s8_equal_ex(key, s8_lit("o"), s8_ignore_case)) return (wasm_key_map_t){app_key_o, 0}; + else if (s8_equal_ex(key, s8_lit("p"), s8_ignore_case)) return (wasm_key_map_t){app_key_p, 0}; + else if (s8_equal_ex(key, s8_lit("q"), s8_ignore_case)) return (wasm_key_map_t){app_key_q, 0}; + else if (s8_equal_ex(key, s8_lit("r"), s8_ignore_case)) return (wasm_key_map_t){app_key_r, 0}; + else if (s8_equal_ex(key, s8_lit("s"), s8_ignore_case)) return (wasm_key_map_t){app_key_s, 0}; + else if (s8_equal_ex(key, s8_lit("t"), s8_ignore_case)) return (wasm_key_map_t){app_key__t, 0}; + else if (s8_equal_ex(key, s8_lit("u"), s8_ignore_case)) return (wasm_key_map_t){app_key_u, 0}; + else if (s8_equal_ex(key, s8_lit("v"), s8_ignore_case)) return (wasm_key_map_t){app_key_v, 0}; + else if (s8_equal_ex(key, s8_lit("w"), s8_ignore_case)) return (wasm_key_map_t){app_key_w, 0}; + else if (s8_equal_ex(key, s8_lit("x"), s8_ignore_case)) return (wasm_key_map_t){app_key_x, 0}; + else if (s8_equal_ex(key, s8_lit("y"), s8_ignore_case)) return (wasm_key_map_t){app_key_y, 0}; + else if (s8_equal_ex(key, s8_lit("z"), s8_ignore_case)) return (wasm_key_map_t){app_key_z, 0}; + else if (s8_equal_ex(key, s8_lit(" "), s8_ignore_case)) return (wasm_key_map_t){app_key_space, 0}; + else if (s8_equal_ex(key, s8_lit("Enter"), s8_ignore_case)) return (wasm_key_map_t){app_key_enter, 1}; + else if (s8_equal_ex(key, s8_lit("Escape"), s8_ignore_case)) return (wasm_key_map_t){app_key_escape, 1}; + else if (s8_equal_ex(key, s8_lit("ArrowLeft"), s8_ignore_case)) return (wasm_key_map_t){app_key_left, 1}; + else if (s8_equal_ex(key, s8_lit("ArrowUp"), s8_ignore_case)) return (wasm_key_map_t){app_key_up, 1}; + else if (s8_equal_ex(key, s8_lit("ArrowRight"), s8_ignore_case)) return (wasm_key_map_t){app_key_right, 1}; + else if (s8_equal_ex(key, s8_lit("ArrowDown"), s8_ignore_case)) return (wasm_key_map_t){app_key_down, 1}; + else if (s8_equal_ex(key, s8_lit("Tab"), s8_ignore_case)) return (wasm_key_map_t){app_key_tab, 1}; + else if (s8_equal_ex(key, s8_lit("Backspace"), s8_ignore_case)) return (wasm_key_map_t){app_key_backspace, 1}; + else if (s8_equal_ex(key, s8_lit("Control"), s8_ignore_case)) return (wasm_key_map_t){app_key_control, 1}; + else if (s8_equal_ex(key, s8_lit("Shift"), s8_ignore_case)) return (wasm_key_map_t){app_key_shift, 1}; + else if (s8_equal_ex(key, s8_lit("Alt"), s8_ignore_case)) return (wasm_key_map_t){app_key_alt, 1}; + else if (s8_equal_ex(key, s8_lit("AltGraph"), s8_ignore_case)) return (wasm_key_map_t){app_key_alt, 1}; + else if (s8_equal_ex(key, s8_lit("Meta"), s8_ignore_case)) return (wasm_key_map_t){app_key_meta, 1}; + else if (s8_equal_ex(key, s8_lit("CapsLock"), s8_ignore_case)) return (wasm_key_map_t){app_key_caps_lock, 1}; + else if (s8_equal_ex(key, s8_lit("Delete"), s8_ignore_case)) return (wasm_key_map_t){app_key_delete, 1}; + else if (s8_equal_ex(key, s8_lit("Home"), s8_ignore_case)) return (wasm_key_map_t){app_key_home, 1}; + else if (s8_equal_ex(key, s8_lit("End"), s8_ignore_case)) return (wasm_key_map_t){app_key_end, 1}; + else if (s8_equal_ex(key, s8_lit("Insert"), s8_ignore_case)) return (wasm_key_map_t){app_key_insert, 1}; + else if (s8_equal_ex(key, s8_lit("PageUp"), s8_ignore_case)) return (wasm_key_map_t){app_key_page_up, 1}; + else if (s8_equal_ex(key, s8_lit("PageDown"), s8_ignore_case)) return (wasm_key_map_t){app_key_page_down, 1}; + return (wasm_key_map_t){0}; +} +#endif + +#if PLATFORM_WINDOWS +void w32_key_dispatch(WPARAM wparam, void (*handle_key)(app_key_t)) { + switch(wparam) { + case '1': handle_key(app_key_1); break; + case '2': handle_key(app_key_2); break; + case '3': handle_key(app_key_3); break; + case '4': handle_key(app_key_4); break; + case '5': handle_key(app_key_5); break; + case '6': handle_key(app_key_6); break; + case '7': handle_key(app_key_7); break; + case '8': handle_key(app_key_8); break; + case '9': handle_key(app_key_9); break; + case '0': handle_key(app_key_0); break; + case VK_F1: handle_key(app_key_f1); break; + case VK_F2: handle_key(app_key_f2); break; + case VK_F3: handle_key(app_key_f3); break; + case VK_F4: handle_key(app_key_f4); break; + case VK_F5: handle_key(app_key_f5); break; + case VK_F6: handle_key(app_key_f6); break; + case VK_F7: handle_key(app_key_f7); break; + case VK_F8: handle_key(app_key_f8); break; + case VK_F9: handle_key(app_key_f9); break; + case VK_F10: handle_key(app_key_f10); break; + case VK_F11: handle_key(app_key_f11); break; + case VK_F12: handle_key(app_key_f12); break; + case 'A': handle_key(app_key_a); break; + case 'B': handle_key(app_key_b); break; + case 'C': handle_key(app_key_c); break; + case 'D': handle_key(app_key_d); break; + case 'E': handle_key(app_key_e); break; + case 'F': handle_key(app_key_f); break; + case 'G': handle_key(app_key_g); break; + case 'H': handle_key(app_key_h); break; + case 'I': handle_key(app_key_i); break; + case 'J': handle_key(app_key_j); break; + case 'K': handle_key(app_key_k); break; + case 'L': handle_key(app_key_l); break; + case 'M': handle_key(app_key_m); break; + case 'N': handle_key(app_key_n); break; + case 'O': handle_key(app_key_o); break; + case 'P': handle_key(app_key_p); break; + case 'Q': handle_key(app_key_q); break; + case 'R': handle_key(app_key_r); break; + case 'S': handle_key(app_key_s); break; + case 'T': handle_key(app_key__t); break; + case 'U': handle_key(app_key_u); break; + case 'V': handle_key(app_key_v); break; + case 'W': handle_key(app_key_w); break; + case 'X': handle_key(app_key_x); break; + case 'Y': handle_key(app_key_y); break; + case 'Z': handle_key(app_key_z); break; + case VK_SPACE: handle_key(app_key_space); break; + case VK_RETURN: handle_key(app_key_enter); break; + case VK_ESCAPE: handle_key(app_key_escape); break; + case VK_LEFT: handle_key(app_key_left); break; + case VK_UP: handle_key(app_key_up); break; + case VK_RIGHT: handle_key(app_key_right); break; + case VK_DOWN: handle_key(app_key_down); break; + case VK_TAB: handle_key(app_key_tab); break; + case VK_BACK: handle_key(app_key_backspace); break; + case VK_CONTROL: handle_key(app_key_control); break; + case VK_SHIFT: handle_key(app_key_shift); break; + case VK_LMENU: handle_key(app_key_alt); break; + case VK_RMENU: handle_key(app_key_alt); break; + case VK_LWIN: handle_key(app_key_meta); break; + case VK_RWIN: handle_key(app_key_meta); break; + case VK_CAPITAL: handle_key(app_key_caps_lock); break; + case VK_DELETE: handle_key(app_key_delete); break; + case VK_HOME: handle_key(app_key_home); break; + case VK_END: handle_key(app_key_end); break; + case VK_NEXT: handle_key(app_key_insert); break; + case VK_INSERT: handle_key(app_key_page_up); break; + case VK_PRIOR: handle_key(app_key_page_down); break; + default: {} break; + } +} +#endif/*D:\dev\wasm\src/app/app.meta.c*/ +type_t type__app_mouse_button_t = { type_kind_enum, s8_const_lit("app_mouse_button_t"), sizeof(app_mouse_button_t), + .members = (type_member_t[]){ + {.name = s8_const_lit("app_mouse_button_null"), .value = app_mouse_button_null}, + {.name = s8_const_lit("app_mouse_button_left"), .value = app_mouse_button_left}, + {.name = s8_const_lit("app_mouse_button_middle"), .value = app_mouse_button_middle}, + {.name = s8_const_lit("app_mouse_button_right"), .value = app_mouse_button_right}, + {.name = s8_const_lit("app_mouse_button_count"), .value = app_mouse_button_count}, + }, + .count = 5, +}; +type_t type__app_event_kind_t = { type_kind_enum, s8_const_lit("app_event_kind_t"), sizeof(app_event_kind_t), + .members = (type_member_t[]){ + {.name = s8_const_lit("app_event_kind_invalid"), .value = app_event_kind_invalid}, + {.name = s8_const_lit("app_event_kind_update"), .value = app_event_kind_update}, + {.name = s8_const_lit("app_event_kind_text"), .value = app_event_kind_text}, + {.name = s8_const_lit("app_event_kind_key_down"), .value = app_event_kind_key_down}, + {.name = s8_const_lit("app_event_kind_key_up"), .value = app_event_kind_key_up}, + {.name = s8_const_lit("app_event_kind_mouse_down"), .value = app_event_kind_mouse_down}, + {.name = s8_const_lit("app_event_kind_mouse_up"), .value = app_event_kind_mouse_up}, + {.name = s8_const_lit("app_event_kind_mouse_wheel"), .value = app_event_kind_mouse_wheel}, + {.name = s8_const_lit("app_event_kind_mouse_move"), .value = app_event_kind_mouse_move}, + {.name = s8_const_lit("app_event_kind_count"), .value = app_event_kind_count}, + }, + .count = 10, +}; +type_t type__app_event_t = { type_kind_struct, s8_const_lit("app_event_t"), sizeof(app_event_t), + .members = (type_member_t[]){ + {.name = s8_const_lit("kind"), .type = &type__app_event_kind_t, .offset = offsetof(app_event_t, kind)}, + {.name = s8_const_lit("mouse_button"), .type = &type__app_mouse_button_t, .offset = offsetof(app_event_t, mouse_button)}, + {.name = s8_const_lit("key"), .type = &type__app_key_t, .offset = offsetof(app_event_t, key)}, + {.name = s8_const_lit("text"), .type = &type__s8_t, .offset = offsetof(app_event_t, text)}, + {.name = s8_const_lit("ctrl"), .type = &type__b8, .offset = offsetof(app_event_t, ctrl)}, + {.name = s8_const_lit("shift"), .type = &type__b8, .offset = offsetof(app_event_t, shift)}, + {.name = s8_const_lit("alt"), .type = &type__b8, .offset = offsetof(app_event_t, alt)}, + {.name = s8_const_lit("meta"), .type = &type__b8, .offset = offsetof(app_event_t, meta)}, + {.name = s8_const_lit("time"), .type = &type__f64, .offset = offsetof(app_event_t, time)}, + {.name = s8_const_lit("delta_time"), .type = &type__f64, .offset = offsetof(app_event_t, delta_time)}, + {.name = s8_const_lit("dpr"), .type = &type__f64, .offset = offsetof(app_event_t, dpr)}, + {.name = s8_const_lit("window_size"), .type = &type__v2f32_t, .offset = offsetof(app_event_t, window_size)}, + {.name = s8_const_lit("mouse_pos"), .type = &type__v2f32_t, .offset = offsetof(app_event_t, mouse_pos)}, + {.name = s8_const_lit("mouse_wheel_delta"), .type = &type__v3f32_t, .offset = offsetof(app_event_t, mouse_wheel_delta)}, + }, + .count = 14, +}; \ No newline at end of file diff --git a/src/app/app.gen.h b/src/app/app.gen.h new file mode 100644 index 0000000..3867c6b --- /dev/null +++ b/src/app/app.gen.h @@ -0,0 +1,110 @@ +typedef enum { +app_key_invalid, +app_key_1, +app_key_2, +app_key_3, +app_key_4, +app_key_5, +app_key_6, +app_key_7, +app_key_8, +app_key_9, +app_key_0, +app_key_f1, +app_key_f2, +app_key_f3, +app_key_f4, +app_key_f5, +app_key_f6, +app_key_f7, +app_key_f8, +app_key_f9, +app_key_f10, +app_key_f11, +app_key_f12, +app_key_a, +app_key_b, +app_key_c, +app_key_d, +app_key_e, +app_key_f, +app_key_g, +app_key_h, +app_key_i, +app_key_j, +app_key_k, +app_key_l, +app_key_m, +app_key_n, +app_key_o, +app_key_p, +app_key_q, +app_key_r, +app_key_s, +app_key__t, +app_key_u, +app_key_v, +app_key_w, +app_key_x, +app_key_y, +app_key_z, +app_key_space, +app_key_enter, +app_key_escape, +app_key_left, +app_key_up, +app_key_right, +app_key_down, +app_key_tab, +app_key_backspace, +app_key_control, +app_key_shift, +app_key_alt, +app_key_meta, +app_key_caps_lock, +app_key_delete, +app_key_home, +app_key_end, +app_key_insert, +app_key_page_up, +app_key_page_down, +app_key_count, +} app_key_t; +/*D:\dev\wasm\src/app/app.meta.c*/ +typedef enum { + app_mouse_button_null, + app_mouse_button_left, + app_mouse_button_middle, + app_mouse_button_right, + app_mouse_button_count, +} app_mouse_button_t; +typedef enum { + app_event_kind_invalid, + app_event_kind_update, + app_event_kind_text, + app_event_kind_key_down, + app_event_kind_key_up, + app_event_kind_mouse_down, + app_event_kind_mouse_up, + app_event_kind_mouse_wheel, + app_event_kind_mouse_move, + app_event_kind_count, +} app_event_kind_t; + +typedef struct app_event_t app_event_t; +struct app_event_t { + app_event_kind_t kind; + app_mouse_button_t mouse_button; + app_key_t key; + s8_t text; + b8 ctrl; + b8 shift; + b8 alt; + b8 meta; + f64 time; + f64 delta_time; + f64 dpr; + v2f32_t window_size; + v2f32_t mouse_pos; + v3f32_t mouse_wheel_delta; +}; \ No newline at end of file diff --git a/src/app/app.h b/src/app/app.h new file mode 100644 index 0000000..f3d2aa0 --- /dev/null +++ b/src/app/app.h @@ -0,0 +1,3 @@ +#include "app.gen.h" + + diff --git a/src/app/app.meta.c b/src/app/app.meta.c new file mode 100644 index 0000000..0bcca68 --- /dev/null +++ b/src/app/app.meta.c @@ -0,0 +1,189 @@ +void meta_app(ma_arena_t *arena) { + sb8_t *h = sb8_serial_begin(arena); + sb8_t *c = sb8_serial_begin(arena); + + + ast_t *keys = parse_table(arena, __FILE__, CODE( + // javascript filter out + | name | js1 | js2 | jf | windows1 | windows2 | + | invalid | XXX | XXX | 1 | XXX | XXX | + | 1 | 1 | XXX | 0 | `'1'` | XXX | + | 2 | 2 | XXX | 0 | `'2'` | XXX | + | 3 | 3 | XXX | 0 | `'3'` | XXX | + | 4 | 4 | XXX | 0 | `'4'` | XXX | + | 5 | 5 | XXX | 0 | `'5'` | XXX | + | 6 | 6 | XXX | 0 | `'6'` | XXX | + | 7 | 7 | XXX | 0 | `'7'` | XXX | + | 8 | 8 | XXX | 0 | `'8'` | XXX | + | 9 | 9 | XXX | 0 | `'9'` | XXX | + | 0 | 0 | XXX | 0 | `'0'` | XXX | + | f1 | F1 | XXX | 1 | VK_F1 | XXX | + | f2 | F2 | XXX | 1 | VK_F2 | XXX | + | f3 | F3 | XXX | 1 | VK_F3 | XXX | + | f4 | F4 | XXX | 1 | VK_F4 | XXX | + | f5 | F5 | XXX | 1 | VK_F5 | XXX | + | f6 | F6 | XXX | 1 | VK_F6 | XXX | + | f7 | F7 | XXX | 1 | VK_F7 | XXX | + | f8 | F8 | XXX | 1 | VK_F8 | XXX | + | f9 | F9 | XXX | 1 | VK_F9 | XXX | + | f10 | F10 | XXX | 1 | VK_F10 | XXX | + | f11 | F11 | XXX | 1 | VK_F11 | XXX | + | f12 | F12 | XXX | 1 | VK_F12 | XXX | + | a | a | XXX | 0 | `'A'` | XXX | + | b | b | XXX | 0 | `'B'` | XXX | + | c | c | XXX | 0 | `'C'` | XXX | + | d | d | XXX | 0 | `'D'` | XXX | + | e | e | XXX | 0 | `'E'` | XXX | + | f | f | XXX | 0 | `'F'` | XXX | + | g | g | XXX | 0 | `'G'` | XXX | + | h | h | XXX | 0 | `'H'` | XXX | + | i | i | XXX | 0 | `'I'` | XXX | + | j | j | XXX | 0 | `'J'` | XXX | + | k | k | XXX | 0 | `'K'` | XXX | + | l | l | XXX | 0 | `'L'` | XXX | + | m | m | XXX | 0 | `'M'` | XXX | + | n | n | XXX | 0 | `'N'` | XXX | + | o | o | XXX | 0 | `'O'` | XXX | + | p | p | XXX | 0 | `'P'` | XXX | + | q | q | XXX | 0 | `'Q'` | XXX | + | r | r | XXX | 0 | `'R'` | XXX | + | s | s | XXX | 0 | `'S'` | XXX | + | _t | t | XXX | 0 | `'T'` | XXX | + | u | u | XXX | 0 | `'U'` | XXX | + | v | v | XXX | 0 | `'V'` | XXX | + | w | w | XXX | 0 | `'W'` | XXX | + | x | x | XXX | 0 | `'X'` | XXX | + | y | y | XXX | 0 | `'Y'` | XXX | + | z | z | XXX | 0 | `'Z'` | XXX | + | space | ` ` | XXX | 0 | VK_SPACE | XXX | + | enter | Enter | XXX | 1 | VK_RETURN | XXX | + | escape | Escape | XXX | 1 | VK_ESCAPE | XXX | + | left | ArrowLeft | XXX | 1 | VK_LEFT | XXX | + | up | ArrowUp | XXX | 1 | VK_UP | XXX | + | right | ArrowRight | XXX | 1 | VK_RIGHT | XXX | + | down | ArrowDown | XXX | 1 | VK_DOWN | XXX | + | tab | Tab | XXX | 1 | VK_TAB | XXX | + | backspace | Backspace | XXX | 1 | VK_BACK | XXX | + | control | Control | XXX | 1 | VK_CONTROL | XXX | + | shift | Shift | XXX | 1 | VK_SHIFT | XXX | + | alt | Alt | AltGraph | 1 | VK_LMENU | VK_RMENU | + | meta | Meta | XXX | 1 | VK_LWIN | VK_RWIN | + | caps_lock | CapsLock | XXX | 1 | VK_CAPITAL | XXX | + | delete | Delete | XXX | 1 | VK_DELETE | XXX | + | home | Home | XXX | 1 | VK_HOME | XXX | + | end | End | XXX | 1 | VK_END | XXX | + | insert | Insert | XXX | 1 | VK_NEXT | XXX | + | page_up | PageUp | XXX | 1 | VK_INSERT | XXX | + | page_down | PageDown | XXX | 1 | VK_PRIOR | XXX | + )); + sb8_serial_table_enum(c, h, keys, s8_lit("app_key")); + + // Javascript + { + int name_idx = row_findi(keys->first, "name"); + int js1_idx = row_findi(keys->first, "js1"); + int js2_idx = row_findi(keys->first, "js2"); + int filter_out_idx = row_findi(keys->first, "jf"); + + + sb8_stmtf(c, "\n#if PLATFORM_WASM"); + sb8_stmtf(c, "typedef struct { app_key_t key; b32 filter_out; } wasm_key_map_t;"); + sb8_stmtf(c, "wasm_key_map_t wasm_map_key_string_to_app_key(s8_t key) {"); + c->indent += 1; + sb8_stmtf(c, "if (0) {}"); + for (ast_t *row = keys->first->next; row; row = row->next) { + s8_t name = row_geti(row, name_idx)->string; + i64 filter_out = row_geti(row, filter_out_idx)->integer; + assert(filter_out == 0 || filter_out == 1); + + s8_t js[] = {row_geti(row, js1_idx)->string, row_geti(row, js2_idx)->string}; + for (i32 i = 0; i < lengthof(js); i += 1) { + if (s8_equal(js[i], s8_lit("XXX"))) continue; + sb8_stmtf(c, "else if (s8_equal_ex(key, s8_lit(\"%S\"), s8_ignore_case)) return (wasm_key_map_t){app_key_%S, %d};", js[i], name, (int)filter_out); + } + } + sb8_stmtf(c, "return (wasm_key_map_t){0};"); + c->indent -= 1; + sb8_stmtf(c, "}"); + sb8_stmtf(c, "#endif"); + } + + // Windows + { + int name_idx = row_findi(keys->first, "name"); + int w1i = row_findi(keys->first, "windows1"); + int w2i = row_findi(keys->first, "windows2"); + + sb8_stmtf(c, "\n#if PLATFORM_WINDOWS"); + sb8_stmtf(c, "void w32_key_dispatch(WPARAM wparam, void (*handle_key)(app_key_t)) {"); + c->indent += 1; + sb8_stmtf(c, "switch(wparam) {"); + c->indent += 1; + for (ast_t *row = keys->first->next; row; row = row->next) { + s8_t name = row_geti(row, name_idx)->string; + s8_t w[] = {row_geti(row, w1i)->string, row_geti(row, w2i)->string}; + for (i32 i = 0; i < lengthof(w); i += 1) { + if (s8_equal(w[i], s8_lit("XXX"))) continue; + + sb8_stmtf(c, "case %.*s: handle_key(app_key_%.*s); break;", s8_fmtspec(w[i]), s8_fmtspec(name)); + } + } + sb8_stmtf(c, "default: {} break;"); + c->indent -= 1; + sb8_stmtf(c, "}"); + c->indent -= 1; + sb8_stmtf(c, "}"); + sb8_stmtf(c, "#endif"); + } + + { + ast_t *decls = parse_decls(arena, __FILE__, CODE( + typedef enum { + app_mouse_button_null, + app_mouse_button_left, + app_mouse_button_middle, + app_mouse_button_right, + app_mouse_button_count, + } app_mouse_button_t; + + typedef enum { + app_event_kind_invalid, + app_event_kind_update, + app_event_kind_text, + app_event_kind_key_down, + app_event_kind_key_up, + app_event_kind_mouse_down, + app_event_kind_mouse_up, + app_event_kind_mouse_wheel, + app_event_kind_mouse_move, + app_event_kind_count, + } app_event_kind_t; + + struct app_event_t { + app_event_kind_t kind; + + app_mouse_button_t mouse_button; + app_key_t key; + s8_t text; + b8 ctrl; + b8 shift; + b8 alt; + b8 meta; + + f64 time; + f64 delta_time; + + f64 dpr; + v2f32_t window_size; + v2f32_t mouse_pos; + v3f32_t mouse_wheel_delta; + }; + )); + + sb8_serial_ast_to_code(h, decls); + sb8_serial_ast_to_type_info(c, decls); + } + + os_write_file(gen_c(arena), sb8_merge(c)); + os_write_file(gen_h(arena), sb8_merge(h)); +} \ No newline at end of file diff --git a/src/app/app_wasm.c b/src/app/app_wasm.c new file mode 100644 index 0000000..ac31f5a --- /dev/null +++ b/src/app/app_wasm.c @@ -0,0 +1,212 @@ +#define WASM_EXPORT __attribute__((visibility("default"))) + +f64 wasm_parse_float(isize str, i32 len); +void wasm_write_to_console(isize str, i32 len); +void wasm_draw_text(isize str, i32 len, f64 x, f64 y, isize font_str, i32 font_len, i32 font_size, f32 r, f32 g, f32 b, f32 a); +void wasm_draw_rect(f64 x, f64 y, f64 w, f64 h, f32 r, f32 g, f32 b, f32 a); +f64 wasm_measure_text(isize str, i32 len, isize font_str, i32 font_len, i32 font_size); +f64 wasm_get_font_height(isize font_str, i32 font_len, i32 font_size); +void wasm_set_clip(f64 x, f64 y, f64 w, f64 h); + +void on_update(); +void app_update(app_event_t *events); + +extern char __heap_base; +ma_arena_t wasm_perm_arena; + +WASM_EXPORT char wasm_temp_buff1[128] = {[127] = 0x13}; +WASM_EXPORT i32 wasm_temp_buff1_len = 127; +WASM_EXPORT char wasm_temp_buff2[128] = {[127] = 0x13}; +WASM_EXPORT i32 wasm_temp_buff2_len = 127; + + +char *font_face = "fira"; +i32 font_face_len = 4; + +f64 wasm_dpr; +ma_arena_t *wasm_input_text_arena; +STACK(app_event_t, 64) wasm_events; +b32 wasm_event_failed_to_queue; +f64 wasm_last_time = 0; + +void write_to_console(char *string) { + int len = str_len(string); + wasm_write_to_console((isize)string, len); +} + +f64 s8_deserial_f64(s8_t string) { + return wasm_parse_float((isize)string.str, (i32)string.len); +} + +void clip(r2f64_t rect) { + wasm_set_clip(wasm_dpr * rect.min.x, wasm_dpr * rect.min.y, wasm_dpr * (rect.max.x - rect.min.x), wasm_dpr * (rect.max.y - rect.min.y)); +} + + +f64 get_font_height(void) { + return wasm_get_font_height((isize) font_face, font_face_len, 20*wasm_dpr) / wasm_dpr; +} + +f64 measure_text_ex(char *str, int len) { + return wasm_measure_text((isize)str, len, (isize) font_face, font_face_len, 20*wasm_dpr) / wasm_dpr; +} + +f64 measure_text(char *str) { + return measure_text_ex(str, str_len(str)); +} + +void draw_text(v2f64_t pos, v4f32_t color, char *str, int len) { + wasm_draw_text((isize)str, len, wasm_dpr * pos.x, wasm_dpr * pos.y, (isize) font_face, font_face_len, 20*wasm_dpr, color.r * 255.f, color.g * 255.f, color.b * 255.f, color.a); +} + +void draw_textf(v2f64_t pos, char *str, ...) { + char buff[1024]; + va_list args; + va_start(args, str); + int len = stbsp_vsnprintf(buff, sizeof(buff), str, args); + va_end(args); + + wasm_draw_text((isize)buff, len, wasm_dpr * pos.x, wasm_dpr * pos.y, (isize) font_face, font_face_len, 20*wasm_dpr, 0, 0, 0, 1); +} + +void draw_rect(r2f64_t rect, v4f32_t color) { + wasm_draw_rect(wasm_dpr * rect.min.x, wasm_dpr * rect.min.y, wasm_dpr * (rect.max.x - rect.min.x), wasm_dpr * (rect.max.y - rect.min.y), color.r * 255.f, color.g * 255.f, color.b * 255.f, color.a); +} + +void wasm_add_event(app_event_t event) { + if (wasm_events.len < lengthof(wasm_events.data)) { + STACK_PUSH(wasm_events, event); + } else if (wasm_event_failed_to_queue == false) { + wasm_event_failed_to_queue = true; + debugf("failed to queue event"); + } +} + +WASM_EXPORT void wasm_mouse_move(f64 x, f64 y, b32 ctrl, b32 shift, b32 alt, b32 meta) { + wasm_add_event((app_event_t){ + .kind = app_event_kind_mouse_move, + .mouse_pos = {x, y}, + .ctrl = ctrl, + .shift = shift, + .alt = alt, + .meta = meta, + }); +} + +WASM_EXPORT void wasm_mouse_down(f64 x, f64 y, i32 button, b32 ctrl, b32 shift, b32 alt, b32 meta) { + button += 1; + assert(button >= app_mouse_button_left && button <= app_mouse_button_right); + wasm_add_event((app_event_t){ + .kind = app_event_kind_mouse_down, + .mouse_pos = {x, y}, + .mouse_button = button, + .ctrl = ctrl, + .shift = shift, + .alt = alt, + .meta = meta, + }); +} + +WASM_EXPORT void wasm_mouse_up(f64 x, f64 y, i32 button, b32 ctrl, b32 shift, b32 alt, b32 meta) { + button += 1; + assert(button >= app_mouse_button_left && button <= app_mouse_button_right); + wasm_add_event((app_event_t){ + .kind = app_event_kind_mouse_up, + .mouse_pos = {x, y}, + .mouse_button = button, + .ctrl = ctrl, + .shift = shift, + .alt = alt, + .meta = meta, + }); +} + +WASM_EXPORT void wasm_mouse_wheel(f64 delta_x, f64 delta_y, f64 delta_z, b32 ctrl, b32 shift, b32 alt, b32 meta) { + wasm_add_event((app_event_t){ + .kind = app_event_kind_mouse_wheel, + .mouse_wheel_delta = {delta_x, delta_y, delta_z}, + .ctrl = ctrl, + .shift = shift, + .alt = alt, + .meta = meta, + }); +} + +WASM_EXPORT void wasm_key_down(char *key, b32 ctrl, b32 shift, b32 alt, b32 meta) { + assert(wasm_temp_buff1[127] == 0x13); // make sure we didn't overwrite memory in JS + assert(wasm_temp_buff2[127] == 0x13); + s8_t key8 = s8_from_char(key); + + wasm_key_map_t map = wasm_map_key_string_to_app_key(key8); + if (map.key != app_key_invalid) { + wasm_add_event((app_event_t){ + .kind = app_event_kind_key_down, + .key = map.key, + .ctrl = ctrl, + .shift = shift, + .alt = alt, + .meta = meta, + }); + } + + + if (map.filter_out) { + return; + } + + s8_t text = s8_copy(wasm_input_text_arena, key8); + wasm_add_event((app_event_t){ + .kind = app_event_kind_text, + .text = text, + .ctrl = ctrl, + .shift = shift, + .alt = alt, + .meta = meta, + }); +} + +WASM_EXPORT void wasm_key_up(char *key, b32 ctrl, b32 shift, b32 alt, b32 meta) { + assert(wasm_temp_buff1[127] == 0x13); // make sure we didn't overwrite memory in JS + assert(wasm_temp_buff2[127] == 0x13); + s8_t key8 = s8_from_char(key); + + wasm_key_map_t map = wasm_map_key_string_to_app_key(key8); + if (map.key != app_key_invalid) { + wasm_add_event((app_event_t){ + .kind = app_event_kind_key_down, + .key = map.key, + .ctrl = ctrl, + .shift = shift, + .alt = alt, + .meta = meta, + }); + } +} + +WASM_EXPORT void wasm_update(f64 time, f64 width, f64 height, f64 dpr) { + f64 delta_time = (time - wasm_last_time); + for (i32 i = 0; i < wasm_events.len; i += 1) { + wasm_events.data[i].dpr = dpr; + wasm_events.data[i].window_size = (v2f32_t){width / dpr, height / dpr}; + wasm_events.data[i].time = time; + wasm_events.data[i].delta_time = delta_time; + } + wasm_dpr = dpr; + + on_update(); + + wasm_events.len = 0; + wasm_last_time = time; + ma_set0(wasm_input_text_arena); +} + +WASM_EXPORT void wasm_init(void) { + isize page_size = kib(64); + isize page_count = __builtin_wasm_memory_size(0); + u8 *memory = (u8 *)&__heap_base; + usize memory_size = page_count * (page_size) - (isize)memory; + wasm_perm_arena.data = memory; + wasm_perm_arena.commit = wasm_perm_arena.reserve = memory_size; + wasm_input_text_arena = ma_push_arena(&wasm_perm_arena, kib(1)); + debugf("on_init, __builtin_wasm_memory_size(0) = %d(%d)", page_count, memory_size); +} \ No newline at end of file diff --git a/src/app/app_win32.c b/src/app/app_win32.c new file mode 100644 index 0000000..c1ba73c --- /dev/null +++ b/src/app/app_win32.c @@ -0,0 +1,132 @@ +#include "core/core.h" +#include "app.gen.h" + +#include "core/core.c" +#include "app.gen.c" + +#pragma comment(linker, "/subsystem:windows") +#pragma comment(lib, "gdi32.lib") +#pragma comment(lib, "user32.lib") +#pragma comment(lib, "winmm.lib") + + +b32 w32_good_scheduling = false; +WNDCLASSW w32_wc; +HWND w32_window_handle; +HDC w32_dc; + +void w32_on_key_down(app_key_t key) { +} + +void w32_on_key_up(app_key_t key) { +} + +LRESULT CALLBACK w32_window_proc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam) { + switch (msg) { + case WM_CLOSE: PostQuitMessage(0); break; + case WM_KEYUP: w32_key_dispatch(wparam, w32_on_key_up); break; + case WM_KEYDOWN: w32_key_dispatch(wparam, w32_on_key_down); break; + default: return DefWindowProcW(wnd, msg, wparam, lparam); + } + return 0; + +} + +int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { + typedef enum W32_PROCESS_DPI_AWARENESS { + W32_PROCESS_DPI_UNAWARE = 0, + W32_PROCESS_SYSTEM_DPI_AWARE = 1, + W32_PROCESS_PER_MONITOR_DPI_AWARE = 2 + } W32_PROCESS_DPI_AWARENESS; + + typedef unsigned MU_TimeBeginPeriod(unsigned); + typedef HRESULT MU_SetProcessDpiAwareness(W32_PROCESS_DPI_AWARENESS); + + HMODULE shcore = LoadLibraryA("Shcore.dll"); + if (shcore) { + MU_SetProcessDpiAwareness *set_dpi_awr = (MU_SetProcessDpiAwareness *)GetProcAddress(shcore, "SetProcessDpiAwareness"); + if (set_dpi_awr) { + HRESULT hr = set_dpi_awr(W32_PROCESS_PER_MONITOR_DPI_AWARE); + assert(SUCCEEDED(hr) && "Failed to set dpi awareness"); + } + } + + HMODULE winmm = LoadLibraryA("winmm.dll"); + if (winmm) { + MU_TimeBeginPeriod *timeBeginPeriod = (MU_TimeBeginPeriod *)GetProcAddress(winmm, "timeBeginPeriod"); + if (timeBeginPeriod) { + if (timeBeginPeriod(1) == 0) { + w32_good_scheduling = true; + } + } + } + + WNDCLASSW wc = {0}; + { + wc.lpfnWndProc = w32_window_proc; + wc.hInstance = GetModuleHandleW(NULL); + wc.lpszClassName = L"HelloClassName"; + wc.hCursor = LoadCursor(0, IDC_ARROW); + wc.hIcon = NULL; // LoadIcon(wc.hInstance, IDI_APPLICATION); + wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW; + ATOM result = RegisterClassW(&wc); + assert(result != 0); + w32_wc = wc; + } + + RECT window_rect = {0}; + { + window_rect.left = 0; + window_rect.right = 1280; + window_rect.bottom = 732; + window_rect.top = 12; + AdjustWindowRectEx(&window_rect, WS_OVERLAPPEDWINDOW, false, 0); + } + w32_window_handle = CreateWindowW(w32_wc.lpszClassName, L"Zzz... Window, hello!", WS_OVERLAPPEDWINDOW, window_rect.left, window_rect.top, window_rect.right - window_rect.left, window_rect.bottom - window_rect.top, NULL, NULL, hInstance, NULL); + assert(w32_window_handle); + + w32_dc = GetDC(w32_window_handle); + assert(w32_dc); + ShowWindow(w32_window_handle, SW_SHOW); + + // @todo: rebuild on resize + // Create a writable backbuffer bitmap + uint32_t *mem = 0; + BITMAPINFO bminfo = {0}; + { + bminfo.bmiHeader.biSize = sizeof(bminfo.bmiHeader); + bminfo.bmiHeader.biWidth = (LONG)1280; + bminfo.bmiHeader.biHeight = (LONG)720; + bminfo.bmiHeader.biPlanes = 1; + bminfo.bmiHeader.biBitCount = 32; + bminfo.bmiHeader.biCompression = BI_RGB; // AA RR GG BB + bminfo.bmiHeader.biXPelsPerMeter = 1; + bminfo.bmiHeader.biYPelsPerMeter = 1; + } + HBITMAP dib = CreateDIBSection(w32_dc, &bminfo, DIB_RGB_COLORS, (void **)&mem, 0, 0); + HDC dib_dc = CreateCompatibleDC(w32_dc); + + for (;;) { + MSG msg; + if (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { + if (msg.message == WM_QUIT) { + break; + } + TranslateMessage(&msg); + DispatchMessageW(&msg); + continue; + } + + for (int y = 0; y < 720; y++) { + for (int x = 0; x < 1280; x++) { + mem[x + y * 1280] = 0xFFFF0000; + } + } + + SelectObject(dib_dc, dib); + BitBlt(w32_dc, 0, 0, 1280, 720, dib_dc, 0, 0, SRCCOPY); + Sleep(10); + } + + return 0; +} \ No newline at end of file diff --git a/src/core/arena.c b/src/core/arena.c new file mode 100644 index 0000000..90565a9 --- /dev/null +++ b/src/core/arena.c @@ -0,0 +1,132 @@ +b32 is_pow2(usize x) { + b32 result = (((x) & ((x)-1)) == 0); + return result; +} + +usize get_align_offset(usize size, usize align) { + assert(is_pow2(align)); + if (align == 0) return 0; + + usize mask = align - 1; + usize val = size & mask; + if (val) { + val = align - val; + } + return val; +} + +usize align_up(usize size, usize align) { + usize result = size + get_align_offset(size, align); + return result; +} + +usize align_down(usize size, usize align) { + size += 1; // Make sure when align is 8 doesn't get rounded down to 0 + usize result = size - (align - get_align_offset(size, align)); + return result; +} + +void ma_init(ma_arena_t *arena, usize reserve) { + reserve = align_up(reserve, ma_page_size); + arena->align = ma_default_alignment; + arena->data = (u8 *)vmem_reserve(reserve); + if (arena->data) { + arena->reserve = reserve; + } +} + +ma_arena_t *ma_create(usize reserve) { + ma_arena_t *result = NULL; + + void *data = vmem_reserve(reserve); + if (!data) return result; + + b32 success = vmem_commit(data, ma_page_size); + if (!success) { + vmem_release(data); + return result; + } + + result = (ma_arena_t *)data; + result->data = (u8 *)data; + result->reserve = reserve; + result->commit = ma_page_size; + result->len = result->base_len = sizeof(ma_arena_t); + result->align = ma_default_alignment; + return result; +} + +void *ma_push_size_ex(ma_arena_t *arena, usize size) { + // base_len is used for bootstraping arenas, it denotes the + // space occupied by the arena. If len is smaller then base_len then + // we start to overwrite the arena itself - pure barbarism. + assert(arena->len >= arena->base_len); + + usize align_offset = 0; + if (arena->align) { + align_offset = get_align_offset((usize)arena->data + arena->len, arena->align); + } + usize size_with_alignment = size + align_offset; + usize new_len = arena->len + size_with_alignment; + if (new_len > arena->commit) { + if (arena->reserve == 0) { + ma_init(arena, ma_default_reserve_size); + } + usize new_len_aligned_to_page_size = align_up(new_len, ma_page_size); + usize to_commit = new_len_aligned_to_page_size - arena->commit; + usize to_commit_clamped = CLAMP_TOP(to_commit, arena->reserve); + if (to_commit_clamped > 0) { + b32 success = vmem_commit(arena->data + arena->commit, to_commit_clamped); + if (success) { + MA_ASAN_UNPOISON_MEMORY_REGION(arena->data + arena->commit, to_commit_clamped); + arena->commit += to_commit_clamped; + } + } + if (new_len > arena->commit) { + return NULL; + } + } + u8 *result = arena->data + arena->len + align_offset; + arena->len = new_len; + MA_ASAN_UNPOISON_MEMORY_REGION(result, size); + return (void *)result; +} + +void *ma_push_size(ma_arena_t *arena, usize size) { + void *result = ma_push_size_ex(arena, size); + if (result) memory_zero(result, size); + return result; +} + +ma_arena_t *ma_push_arena(ma_arena_t *allocator, usize size) { + ma_arena_t *result = ma_push_type(allocator, ma_arena_t); + result->data = (u8 *)ma_push_size(allocator, size); + result->reserve = size; + result->commit = size; + result->align = ma_default_alignment; + return result; +} + +void ma_destroy(ma_arena_t *arena) { + if (arena == NULL || arena->data == NULL) return; + b32 zero_memory = (u8 *)arena != arena->data; + vmem_release(arena->data); + if (zero_memory) memory_zero(arena, sizeof(ma_arena_t)); +} + +void ma_set_len(ma_arena_t *arena, usize pos) { + // base_len is used for bootstraping arenas, it denotes the + // space occupied by the arena. If len is smaller then base_len then + // we start to overwrite the arena itself - pure barbarism. + assert(arena->len >= arena->base_len); + + pos = CLAMP(pos, arena->base_len, arena->len); + usize size = arena->len - pos; + arena->len = pos; + MA_ASAN_POISON_MEMORY_REGION(arena->data + arena->len, size); +} + +void ma_pop(ma_arena_t *arena, usize size) { ma_set_len(arena, arena->len - size); } +ma_temp_t ma_begin_temp(ma_arena_t *arena) { return (ma_temp_t){arena, arena->len}; } +void ma_end_temp(ma_temp_t temp) { ma_set_len(temp.arena, temp.len); } +void ma_set0(ma_arena_t *arena) { ma_set_len(arena, 0); } diff --git a/src/core/core.c b/src/core/core.c new file mode 100644 index 0000000..961abc8 --- /dev/null +++ b/src/core/core.c @@ -0,0 +1,41 @@ +#include + +#if PLATFORM_WINDOWS +#define NOMINMAX +#define WIN32_LEAN_AND_MEAN +#include +#endif + +#if PLATFORM_ADDRESS_SANITIZER + #include +#endif + +#if !defined(ASAN_POISON_MEMORY_REGION) + #define MA_ASAN_POISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size)) + #define MA_ASAN_UNPOISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size)) +#else + #define MA_ASAN_POISON_MEMORY_REGION(addr, size) ASAN_POISON_MEMORY_REGION(addr, size) + #define MA_ASAN_UNPOISON_MEMORY_REGION(addr, size) ASAN_UNPOISON_MEMORY_REGION(addr, size) +#endif + +#if PLATFORM_CL +#include +#include +#include +#include +#endif + +#include "intrinsics.c" +#include "unicode.c" +#include "mathx.c" +#include "mathx.gen.c" +#include "arena.c" + +#define STB_SPRINTF_IMPLEMENTATION +#include "stb_sprintf.h" +#include "string.c" +#include "string8.c" +#include "scratch.c" +#include "log.c" +#include "lexer.c" +#include "type_info.c" \ No newline at end of file diff --git a/src/core/core.h b/src/core/core.h new file mode 100644 index 0000000..44ff12c --- /dev/null +++ b/src/core/core.h @@ -0,0 +1,5 @@ +#include "platform_defines.h" +#include +#include +#include "types.h" +#include "type_info.h" diff --git a/src/core/intrinsics.c b/src/core/intrinsics.c new file mode 100644 index 0000000..b40fae6 --- /dev/null +++ b/src/core/intrinsics.c @@ -0,0 +1,126 @@ +void memory_copy(void *dst, void *src, size_t n) { +#if PLATFORM_CLANG + __builtin_memcpy(dst, src, n); +#else + memcpy(dst, src, n); +#endif +} + +void memory_move(void *dest, const void *src, size_t n) { +#if PLATFORM_CLANG + __builtin_memmove(dest, src, n); +#else + memmove(dest, src, n); +#endif +} + +void memory_set(void *dst, int c, size_t size) { +#if PLATFORM_CLANG + __builtin_memset(dst, c, size); +#else + memset(dst, c, size); +#endif +} +void memory_zero(void *dst, usize size) { memory_set(dst, 0, size); } + + +#if PLATFORM_CLANG +f32 f32_sqrt(f32 x) { return __builtin_sqrtf(x); } +f64 f64_sqrt(f64 x) { return __builtin_sqrt(x); } +f32 f32_ceil(f32 x) { return __builtin_ceilf(x); } +f64 f64_ceil(f64 x) { return __builtin_ceil(x); } +f32 f32_floor(f32 x) { return __builtin_floorf(x); } +f64 f64_floor(f64 x) { return __builtin_floor(x); } +f32 f32_abs(f32 x) { return __builtin_fabsf(x); } +f64 f64_abs(f64 x) { return __builtin_fabs(x); } +f32 f32_round(f32 x) { return __builtin_roundf(x); } +f64 f64_round(f64 x) { return __builtin_round(x); } +f64 f64_mod(f64 a, f64 b) { return __builtin_fmod(a, b); } +f32 f32_mod(f32 a, f32 b) { return __builtin_fmodf(a, b); } +#else +f32 f32_sqrt(f32 x) { return sqrtf(x); } +f64 f64_sqrt(f64 x) { return sqrt(x); } +f32 f32_ceil(f32 x) { return ceilf(x); } +f64 f64_ceil(f64 x) { return ceil(x); } +f32 f32_floor(f32 x) { return floorf(x); } +f64 f64_floor(f64 x) { return floor(x); } +f32 f32_abs(f32 x) { return fabsf(x); } +f64 f64_abs(f64 x) { return fabs(x); } +f32 f32_round(f32 x) { return roundf(x); } +f64 f64_round(f64 x) { return round(x); } +f64 f64_mod(f64 a, f64 b) { return fmod(a, b); } +f32 f32_mod(f32 a, f32 b) { return fmodf(a, b); } +#endif + +#if PLATFORM_WINDOWS +void *vmem_reserve(size_t size) { + void *result = (uint8_t *)VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE); + return result; +} +b32 vmem_commit(void *p, size_t size) { + void *result = VirtualAlloc(p, size, MEM_COMMIT, PAGE_READWRITE); + return result ? true : false; +} +b32 vmem_release(void *p) { + BOOL result = VirtualFree(p, 0, MEM_RELEASE); + return result ? true : false; +} +b32 vmem_decommit(void *p, size_t size) { + BOOL result = VirtualFree(p, size, MEM_DECOMMIT); + return result ? true : false; +} +#else +void *vmem_reserve(size_t size) { return NULL; } +b32 vmem_commit(void *p, size_t size) { return false; } +b32 vmem_release(void *p) { return true; } +b32 vmem_decommit(void *p, size_t size) { return true; } +#endif + +#if PLATFORM_WASM +f64 s8_deserial_f64(s8_t string); +#else +f64 s8_deserial_f64(s8_t string) { return strtod(string.str, NULL); } +#endif + +#if PLATFORM_WINDOWS +void core_log_message(char *string) { + if (IsDebuggerPresent()) { + OutputDebugStringA(string); + } + printf("%s", string); + fflush(stdout); +} +void core_panic(void) { + if (core_desc.on_exit) core_desc.on_exit(); + ExitProcess(1); +} +#elif PLATFORM_WASM +void write_to_console(char *string); +void core_log_message(char *string) { + write_to_console(string); +} +void core_panic(void) { + if (core_desc.on_exit) core_desc.on_exit(); + debug_break(); +} +#else +void core_log_message(char *string) { + printf("%s"); + fflush(stdout); +} +void core_panic(void) { + if (core_desc.on_exit) core_desc.on_exit(); + debug_break(); +} +#endif + +void core_on_exit(void) { + debugf("program panicked! exiting..."); +} + +THREAD_LOCAL core_desc_t core_desc = { + .print = core_log_message, + .panic = core_panic, + .break_on_panic = true, +}; + diff --git a/src/core/lexer.c b/src/core/lexer.c new file mode 100644 index 0000000..c4a0de5 --- /dev/null +++ b/src/core/lexer.c @@ -0,0 +1,493 @@ +typedef enum lex_kind_t lex_kind_t; +enum lex_kind_t { + #define LEX_KIND_XLIST\ + X(eof, "end of file", "---")\ + X(int, "integer", "---")\ + X(real, "real", "---")\ + X(ident, "identifier", "---")\ + X(string, "string", "---")\ + X(comment, "comment", "---")\ + X(open_brace, "'{' open brace", "{")\ + X(close_brace, "'}' close brace", "}")\ + X(open_paren, "'(' open parenthesis", "(")\ + X(close_paren, "')' close parenthesis", ")")\ + X(open_bracket, "'[' open bracket", "[")\ + X(close_bracket, "']' close bracket", "]")\ + X(plus, "'+' plus", "+")\ + X(minus, "'-' minus", "-")\ + X(divide, "'/' division sign", "/")\ + X(multiply, "'*' multiplication sign", "*")\ + X(modulo, "'%' modulo", "%")\ + X(or, "'||' logical or", "||")\ + X(and, "'&&' logical and", "&&")\ + X(negation, "'!' logical negation", "!")\ + X(bit_negation, "'~' bit negation", "~")\ + X(bit_left_shift, "'<<' bit left shift", "<<")\ + X(bit_right_shift, "'>>' bit right shift", ">>")\ + X(bit_or, "'|' bit or", "|")\ + X(bit_and, "'&' bit and", "&")\ + X(bit_xor, "'^' bit xor", "^")\ + X(decrement, "'--' decrement", "--")\ + X(increment, "'++' increment", "++")\ + X(post_decrement, "'--' post decrement", "--")\ + X(post_increment, "'++' post increment", "++")\ + X(assign, "'=' assignment", "=")\ + X(divide_assign, "'/=' divide assignment", "/=")\ + X(multiply_assign, "'*=' multiply assignment", "*=")\ + X(plus_assign, "'+=' plus assignment", "+=")\ + X(minus_assign, "'-=' minus assignment", "-=")\ + X(modulo_assign, "'%=' modulo assignment", "%=")\ + X(bit_and_assign, "&=", "&=")\ + X(bit_or_assign, "'|=' bit or assignment", "|=")\ + X(bit_xor_assign, "'^=' bit xor assignment", "^=")\ + X(bit_left_shift_assign, "'<<=' bit left shift assignment", "<<=")\ + X(bit_right_shift_assign, "'>>=' bit right shift assignment", ">>=")\ + X(equals, "'==' equals sign", "==")\ + X(not_equals, "'!=' not equals sign", "!=")\ + X(lesser, "'<' lesser then", "<")\ + X(greater, "'>' greater then", ">")\ + X(lesser_or_equal, "'<=' lesser then or equal", "<=")\ + X(greater_or_equal, "'>=' greater then or equal", ">=")\ + X(comma, "',' comma", ",")\ + X(dot, "'.' dot", ".")\ + X(three_dots, "'...' three dots", "...")\ + X(semicolon, "';' semicolon", ";")\ + X(colon, "':' colon", ":")\ + X(arrow, "'->' arrow", "->")\ + X(question, "'?' question mark", "?")\ + + + #define X(KIND, STR, SIMPLE) lex_kind_##KIND, + LEX_KIND_XLIST + #undef X + + lex_kind_count, +}; + +typedef enum lex_suffix_t lex_suffix_t; +enum lex_suffix_t { + #define LEX_SUFFIX_XLIST X(none) X(f) X(d) X(u) X(ul) X(ull) X(l) X(ll) + #define X(KIND) lex_suffix_##KIND, + LEX_SUFFIX_XLIST + #undef X + + lex_suffix_count, +}; + +typedef struct lex_t lex_t; +struct lex_t { + lex_kind_t kind; + lex_suffix_t suffix; + + union { + struct {char *str; i64 len;}; + s8_t s8; + }; + + int line; + int column; + char *file_name; + + union { + u64 integer; + f64 real; + char *error; + }; +}; + +typedef struct lexer_t lexer_t; +struct lexer_t { + char *at; + char *file_name; + int line; + int column; +}; + +typedef struct lex_array_t lex_array_t; +struct lex_array_t { + lex_t *data; + int len; +}; + +void lex_panicf(lex_t *token, const char *str, ...) { + ma_temp_t scratch = ma_begin_scratch(); + S8_FMT(scratch.arena, str, str8); + panicf("%s(%d:%d): error: %S", token->file_name, token->line, token->column, str8); + ma_end_scratch(scratch); +} + +lexer_t lex_make(char *begin, char *file_name) { + lexer_t result = {.at = begin, .file_name = file_name}; + return result; +} + +void lex_advance(lexer_t *lex) { + if (lex->at[0] == 0) return; + if (lex->at[0] == '\n') { lex->column = 0; lex->line += 1; } + lex->column += 1; + lex->at += 1; +} + +b32 lex_match(lexer_t *lex, char c) { + if (lex->at[0] == c) { + lex_advance(lex); + return true; + } + return false; +} + +void lex_eat_whitespace(lexer_t *lex) { + while (char_is_whitespace(lex->at[0])) lex_advance(lex); +} + +u64 lex_map_char_to_int(char c) { + switch (c) { + case '0': return 0; break; + case '1': return 1; break; + case '2': return 2; break; + case '3': return 3; break; + case '4': return 4; break; + case '5': return 5; break; + case '6': return 6; break; + case '7': return 7; break; + case '8': return 8; break; + case '9': return 9; break; + case 'a': + case 'A': return 10; break; + case 'b': + case 'B': return 11; break; + case 'c': + case 'C': return 12; break; + case 'd': + case 'D': return 13; break; + case 'e': + case 'E': return 14; break; + case 'f': + case 'F': return 15; break; + default: return 255; + } +} + +u64 lex_deserial_u64(char *string, i64 len, u64 base) { + assert(base >= 2 && base <= 16); + u64 acc = 0; + for (i64 i = 0; i < len; i++) { + u64 num = lex_map_char_to_int(string[i]); + if (num >= base) { + panicf("invalid number"); + break; + } + acc *= base; + acc += num; + } + return acc; +} + +void lex_eat_number(lexer_t *lex, lex_t *token) { + token->kind = lex_kind_int; + for (;;) { + if (char_is_digit(lex->at[0])) { + lex_advance(lex); + continue; + } + + if (lex_match(lex, '.')) { + if (token->kind == lex_kind_real) { + lex_panicf(token, "multiple '.' periods in floating point number literal"); + } + token->kind = lex_kind_real; + continue; + } + + break; + } + + if (lex_match(lex, 'f')) { + token->kind = lex_kind_real; + token->suffix = lex_suffix_f; + } else if (lex_match(lex, 'd')) { + token->kind = lex_kind_real; + token->suffix = lex_suffix_d; + } else if (token->kind == lex_kind_int && ((lex->at[0] == 'u' && lex->at[1] == 'l' && lex->at[2] == 'l') || (lex->at[0] == 'U' && lex->at[1] == 'L' && lex->at[2] == 'L'))) { + token->suffix = lex_suffix_ull; + lex_advance(lex); lex_advance(lex); lex_advance(lex); + } else if (token->kind == lex_kind_int && ((lex->at[0] == 'u' && lex->at[1] == 'l') || (lex->at[0] == 'U' && lex->at[1] == 'L'))) { + token->suffix = lex_suffix_ul; + lex_advance(lex); lex_advance(lex); + } else if (token->kind == lex_kind_int && (lex->at[0] == 'l' || lex->at[0] == 'L')) { + token->suffix = lex_suffix_l; + lex_advance(lex); + } else if (token->kind == lex_kind_int && ((lex->at[0] == 'l' && lex->at[1] == 'l') || (lex->at[0] == 'L' && lex->at[1] == 'L'))) { + token->suffix = lex_suffix_ll; + lex_advance(lex); lex_advance(lex); + } +} + +void lex_eat_until(lexer_t *lex, char c) { + while (lex->at[0] != c && lex->at[0] != 0) lex_advance(lex); +} + +void lex_eat_string(lexer_t *lex, lex_t *token) { + token->kind = lex_kind_string; + for (;;) { + if (lex_match(lex, token->str[0])) { + break; + } + + if (lex->at[0] == 0) { + lex_panicf(token, "unclosed string"); + } + + lex_advance(lex); + } +} + +#define LEX_CASE3(C1, K1, C2, K2, C3, K3)\ +case C1: {\ + token->kind = K1;\ + if (lex_match(lex, C2)) {\ + lex_advance(lex);\ + token->kind = K2;\ + } else if (lex_match(lex, C3)) {\ + lex_advance(lex);\ + token->kind = K3;\ + }\ +} break + +void lex_token_ex(lexer_t *lex, lex_t *token) { + lex_eat_whitespace(lex); + *token = (lex_t){.str = lex->at, .file_name = lex->file_name, .line = lex->line, .column = lex->column}; + lex_advance(lex); + + switch (token->str[0]) { + case '\0': token->kind = lex_kind_eof; break; + case '{': token->kind = lex_kind_open_brace; break; + case '}': token->kind = lex_kind_close_brace; break; + case '(': token->kind = lex_kind_open_paren; break; + case ')': token->kind = lex_kind_close_paren; break; + case '[': token->kind = lex_kind_open_bracket; break; + case ']': token->kind = lex_kind_close_bracket; break; + case '~': token->kind = lex_kind_bit_negation; break; + case ';': token->kind = lex_kind_semicolon; break; + case ':': token->kind = lex_kind_colon; break; + case ',': token->kind = lex_kind_comma; break; + case '"': lex_eat_string(lex, token); break; + case '`': lex_eat_string(lex, token); break; + case '\'': lex_eat_string(lex, token); break; + + case '.': { + token->kind = lex_kind_dot; + if (lex->at[0] == '.' && lex->at[1] == '.') { + lex_advance(lex); + lex_advance(lex); + token->kind = lex_kind_three_dots; + } + } break; + + case '/': { + token->kind = lex_kind_divide; + if (lex_match(lex, '/')) { + token->kind = lex_kind_comment; + lex_eat_until(lex, '\n'); + } else if (lex_match(lex, '*')) { + token->kind = lex_kind_comment; + for (;;) { + if (lex->at[0] == '*' && lex->at[1] == '/') { + break; + } + if (lex->at[0] == 0) { + lex_panicf(token, "Unclosed block comment"); + return; + } + lex_advance(lex); + } + lex_advance(lex); + lex_advance(lex); + } else if (lex_match(lex, '=')) { + token->kind = lex_kind_divide_assign; + lex_advance(lex); + } + } break; + + LEX_CASE3('^', lex_kind_bit_xor, '=', lex_kind_bit_xor_assign, /*ignored option*/'=', lex_kind_bit_xor_assign); + LEX_CASE3('=', lex_kind_assign, '=', lex_kind_equals, /*ignored option*/'=', lex_kind_equals); + LEX_CASE3('!', lex_kind_negation, '=', lex_kind_not_equals, /*ignored option*/'=', lex_kind_not_equals); + LEX_CASE3('%', lex_kind_modulo, '=', lex_kind_modulo_assign, /*ignored option*/'=', lex_kind_modulo_assign); + LEX_CASE3('*', lex_kind_multiply, '=', lex_kind_multiply_assign, /*ignored option*/'=', lex_kind_multiply_assign); + LEX_CASE3('+', lex_kind_plus, '+', lex_kind_increment, '=', lex_kind_plus_assign); + LEX_CASE3('-', lex_kind_minus, '-', lex_kind_decrement, '=', lex_kind_minus_assign); + LEX_CASE3('&', lex_kind_bit_and, '&', lex_kind_and, '=', lex_kind_bit_and_assign); + LEX_CASE3('|', lex_kind_bit_or, '|', lex_kind_or, '=', lex_kind_bit_or_assign); + + case '>': { + token->kind = lex_kind_greater; + if (lex_match(lex, '=')) { + token->kind = lex_kind_greater_or_equal; + } else if (lex_match(lex, '>')) { + token->kind = lex_kind_bit_right_shift; + if (lex_match(lex, '=')) { + token->kind = lex_kind_bit_right_shift_assign; + } + } + } break; + + case '<': { + token->kind = lex_kind_lesser; + if (lex_match(lex, '=')) { + token->kind = lex_kind_lesser_or_equal; + } + else if (lex_match(lex, '<')) { + token->kind = lex_kind_bit_left_shift; + if (lex_match(lex, '=')) { + token->kind = lex_kind_bit_left_shift_assign; + } + } + } break; + + case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '0': { + lex_eat_number(lex, token); + } break; + + case 'A': case 'a': case 'B': case 'b': case 'C': case 'c': + case 'D': case 'd': case 'E': case 'e': case 'F': case 'f': + case 'G': case 'g': case 'H': case 'h': case 'I': case 'i': + case 'J': case 'j': case 'K': case 'k': case 'L': case 'l': + case 'M': case 'm': case 'N': case 'n': case 'O': case 'o': + case 'P': case 'p': case 'Q': case 'q': case 'R': case 'r': + case 'S': case 's': case 'T': case 't': case 'U': case 'u': + case 'V': case 'v': case 'W': case 'w': case 'X': case 'x': + case 'Y': case 'y': case 'Z': case 'z': case '_': { + token->kind = lex_kind_ident; + while (char_is_alphanumeric(lex->at[0]) || lex->at[0] == '_') lex_advance(lex); + } break; + + default: { + lex_panicf(token, "found invalid character in the token stream (%d)", token->str[0]); + } + } + + token->len = (int)(lex->at - token->str); + + if (token->kind == lex_kind_int) { + token->integer = lex_deserial_u64(token->str, token->len, 10); + } else if (token->kind == lex_kind_real) { + token->real = s8_deserial_f64(token->s8); + } else if (token->kind == lex_kind_string) { + token->str += 1; + token->len -= 2; + } +} + +lex_t lex_token(lexer_t *lex) { + lex_t result = {0}; + lex_token_ex(lex, &result); + return result; +} + +// @todo: use s8_t instead +lex_array_t lex_tokens(ma_arena_t *arena, char *file_name, char *stream) { + usize align = arena->align; + arena->align = 0; + + lex_array_t token_array = {0}; + lexer_t l = lex_make(stream, file_name); + for (;;) { + lex_t *token = ma_push_type(arena, lex_t); + if (token_array.data == NULL) { + token_array.data = token; + } + token_array.len += 1; + + do { + lex_token_ex(&l, token); + } while (token->kind == lex_kind_comment); + if (token->kind == lex_kind_eof) break; + } + + arena->align = align; + return token_array; +} + +s8_t global_lex_kind_simple_strings[] = { +#define X(KIND, STR, SIMPLE) s8_const_lit(SIMPLE), + LEX_KIND_XLIST +#undef X +}; + +s8_t s8_serial_simple_lex_kind_t(lex_kind_t kind) { + assert(kind >= 0 && kind < lex_kind_count); + return global_lex_kind_simple_strings[kind]; +} + +s8_t global_lex_kind_strings[] = { +#define X(KIND, STR, SIMPLE) s8_const_lit(STR), + LEX_KIND_XLIST +#undef X +}; + +s8_t s8_serial_lex_kind_t(lex_kind_t kind) { + assert(kind >= 0 && kind < lex_kind_count); + return global_lex_kind_strings[kind]; +} + +type_member_t members__lex_kind_t[] = { +#define X(KIND, STR, SIMPLE) {.name = s8_const_lit("lex_kind_" #KIND), .value = lex_kind_##KIND}, + LEX_KIND_XLIST +#undef X +}; +DEFINE_ENUM(lex_kind_t); + +type_member_t members__lex_suffix_t[] = { +#define X(KIND) {.name = s8_const_lit("lex_suffix_" #KIND), .value = lex_suffix_##KIND}, + LEX_SUFFIX_XLIST +#undef X +}; +DEFINE_ENUM(lex_suffix_t); + +// +// +typedef struct parser_t parser_t; +struct parser_t { + ma_arena_t *arena; + lex_t *at; +}; + +#define parser_make(ARENA, TOKEN) &(parser_t){.arena = ARENA, .at = TOKEN} + +lex_t *parser_next(parser_t *par) { + lex_t *result = par->at; + if (result->kind != lex_kind_eof) par->at += 1; + return result; +} + +lex_t *parser_match(parser_t *par, lex_kind_t kind) { + if (par->at->kind == kind) { + return parser_next(par); + } else { + return NULL; + } +} + +lex_t *parser_matchi(parser_t *par, s8_t str) { + if (par->at->kind == lex_kind_ident && s8_equal(par->at->s8, str)) { + return parser_next(par); + } else { + return NULL; + } +} + +lex_t *parser_expect(parser_t *par, lex_kind_t kind) { + lex_t *token = parser_match(par, kind); + if (!token) lex_panicf(par->at, "expected token kind: %S, got instead: %S", s8_serial_lex_kind_t(kind), s8_serial_lex_kind_t(par->at->kind)); + return token; +} + +void parser_eat_until(parser_t *par, lex_kind_t kind) { + while (par->at->kind != kind && par->at->kind != lex_kind_eof) { + parser_next(par); + } +} +void parser_eat_including(parser_t *par, lex_kind_t kind) { + parser_eat_until(par, kind); + parser_next(par); +} \ No newline at end of file diff --git a/src/core/log.c b/src/core/log.c new file mode 100644 index 0000000..690def4 --- /dev/null +++ b/src/core/log.c @@ -0,0 +1,27 @@ +void panicf(const char *_str, ...) { + ma_temp_t scratch = ma_begin_scratch(); + S8_FMT(scratch.arena, _str, str); + core_desc.print(str.str); + core_desc.print("\n"); + if (core_desc.break_on_panic) { + debug_break(); + } else { + core_desc.panic(); + } + ma_end_scratch(scratch); +} + +void debugexf(const char *_str, ...) { + ma_temp_t scratch = ma_begin_scratch(); + S8_FMT(scratch.arena, _str, str); + core_desc.print(str.str); + ma_end_scratch(scratch); +} + +void debugf(const char *_str, ...) { + ma_temp_t scratch = ma_begin_scratch(); + S8_FMT(scratch.arena, _str, str); + core_desc.print(str.str); + core_desc.print("\n"); + ma_end_scratch(scratch); +} \ No newline at end of file diff --git a/src/core/math_gen.py b/src/core/math_gen.py new file mode 100644 index 0000000..0a4ef07 --- /dev/null +++ b/src/core/math_gen.py @@ -0,0 +1,291 @@ +import sys + +op_idx = 0 +opname_idx = 1 +symbols = [ + ["+", "add"], + ["-", "sub"], + ["*", "mul"], + ["/", "div"], +] + +basic_types = [ + "f32", + "f64", + "i32", + "i64", +] + +name_idx = 0 +member_idx = 1 + +vec_types = [ + ["v2", ["x", "y"]], + ["v3", ["x", "y", "z"]], + ["v4", ["x", "y", "z", "w"]], +] + +rect_types = [ + ["r2", ["x0", "y0", "x1", "y1"]], + ["r3", ["x0", "y0", "z0", "x1", "y1", "z1"]], +] + +struct_types = vec_types + rect_types + + +# fd = open("mathx.gen.h", "w") +# sys.stdout = fd + +# print("// auto generated using:", __file__) + +# for bt in basic_types: +# s = """ +# typedef union v2f64_t v2f64_t; +# union v2f64_t { +# struct { f64 x, y; }; +# f64 e[2]; +# }; + +# typedef union v3f64_t v3f64_t; +# union v3f64_t { +# struct { f64 x, y, z; }; +# struct { v2f64_t xy; }; +# struct { f64 _x0; v2f64_t zw; }; +# struct { f64 r, g, b; }; +# struct { f64 h, s, l; }; +# f64 e[3]; +# }; + +# typedef union v4f64_t v4f64_t; +# union v4f64_t { +# struct { f64 x, y, z, w; }; +# struct { v2f64_t xy; v2f64_t zw; }; +# struct { v3f64_t xyz; }; +# struct { f64 _x0; f64 yzw; }; +# struct { f64 r, g, b, a; }; +# struct { f64 h, s, l, _a; }; +# f64 e[4]; +# }; + +# typedef union r1f64_t r1f64_t; +# union r1f64_t { +# struct { f64 min, max; }; +# struct { f64 x0, x1; }; +# f64 e[2]; +# }; + +# typedef union r2f64_t r2f64_t; +# union r2f64_t { +# struct { v2f64_t min, max; }; +# struct { f64 x0, y0, x1, y1; }; +# v4f64_t e4; +# f64 e[4]; +# }; + +# typedef union r3f64_t r3f64_t; +# union r3f64_t { +# struct { v3f64_t min, max; }; +# struct { f64 x0, y0, z0, x1, y1, z1; }; +# f64 e[6]; +# }; +# +# +# type_t type__v2f64_t = { type_kind_struct, s8_const_lit("v2f64_t"), sizeof(v2f64_t), .count = 2, +# .members = (type_member_t[]){ +# {s8_const_lit("x"), &type__f64, .offset = offsetof(v2f64_t, x)}, +# {s8_const_lit("y"), &type__f64, .offset = offsetof(v2f64_t, y)}, +# } +# }; + +# type_t type__v3f64_t = { type_kind_struct, s8_const_lit("v3f64_t"), sizeof(v3f64_t), .count = 3, +# .members = (type_member_t[]){ +# {s8_const_lit("x"), &type__f64, .offset = offsetof(v3f64_t, x)}, +# {s8_const_lit("y"), &type__f64, .offset = offsetof(v3f64_t, y)}, +# {s8_const_lit("z"), &type__f64, .offset = offsetof(v3f64_t, z)}, +# } +# }; + +# type_t type__v4f64_t = { type_kind_struct, s8_const_lit("v4f64_t"), sizeof(v4f64_t), .count = 4, +# .members = (type_member_t[]){ +# {s8_const_lit("x"), &type__f64, .offset = offsetof(v4f64_t, x)}, +# {s8_const_lit("y"), &type__f64, .offset = offsetof(v4f64_t, y)}, +# {s8_const_lit("z"), &type__f64, .offset = offsetof(v4f64_t, z)}, +# {s8_const_lit("w"), &type__f64, .offset = offsetof(v4f64_t, w)}, +# } +# }; + +# type_t type__r3f64_t = { type_kind_struct, s8_const_lit("r3f64_t"), sizeof(r3f64_t), .count = 6, +# .members = (type_member_t[]){ +# {s8_const_lit("x0"), &type__f64, .offset = offsetof(r3f64_t, x0)}, +# {s8_const_lit("y0"), &type__f64, .offset = offsetof(r3f64_t, y0)}, +# {s8_const_lit("x0"), &type__f64, .offset = offsetof(r3f64_t, z0)}, +# {s8_const_lit("x1"), &type__f64, .offset = offsetof(r3f64_t, x1)}, +# {s8_const_lit("y1"), &type__f64, .offset = offsetof(r3f64_t, y1)}, +# {s8_const_lit("x1"), &type__f64, .offset = offsetof(r3f64_t, z1)}, +# } +# }; + +# type_member_t members__r2f64_t[] = { +# {s8_const_lit("x0"), &type__f64, .offset = offsetof(r2f64_t, x0)}, +# {s8_const_lit("y0"), &type__f64, .offset = offsetof(r2f64_t, y0)}, +# {s8_const_lit("x1"), &type__f64, .offset = offsetof(r2f64_t, x1)}, +# {s8_const_lit("y1"), &type__f64, .offset = offsetof(r2f64_t, y1)}, +# }; +# DEFINE_STRUCT(r2f64_t); + +# type_member_t members__r1f64_t[] = { +# {s8_const_lit("x0"), &type__f64, .offset = offsetof(r1f64_t, x0)}, +# {s8_const_lit("x1"), &type__f64, .offset = offsetof(r1f64_t, x1)}, +# }; +# DEFINE_STRUCT(r1f64_t); +# """ +# s = s.replace("f64", bt) +# print(s) + + +fd = open("mathx.gen.c", "w") +sys.stdout = fd + +print("// auto generated using:", __file__) + +for basic_type in basic_types: + for st in struct_types: + type = f"{st[name_idx]}{basic_type}" + typedef = f"{type}_t" + members = st[member_idx] + + def members_as_func_params(members, basic_type): + result = "" + for i, m in enumerate(members): + result += f"{basic_type} {m}" + if i != len(members) - 1: + result += ", " + return result + + print(f"""{type}_t {type}({members_as_func_params(members, basic_type)}) {{ return ({type}_t){{ {members_as_func_params(members, "")} }}; }}""") + + for sym in symbols: + op = sym[op_idx] + opname = sym[opname_idx] + + + def op_str(members, op, skip_right = False): + result = "" + for i in range(len(members)): + mem = members[i] + if skip_right: + result += f"a.{mem} {op} b" + else: + result += f"a.{mem} {op} b.{mem}" + + if i != len(members) - 1: + result += ", " + return result + + + + print(f"{type}_t {type}_{opname}({type}_t a, {type}_t b) {{ return ({type}_t){{ {op_str(members, op)} }}; }}") + print(f"{type}_t {type}_{opname}s({type}_t a, {basic_type} b) {{ return ({type}_t){{ {op_str(members, op, skip_right = True)} }}; }}") + +for basic_type in basic_types: + for i in range(len(rect_types)): + vec_type = vec_types[i] + rect_type = rect_types[i] + + vec_name = vec_type[name_idx] + basic_type + vec_members = vec_type[member_idx] + rect_name = rect_type[name_idx] + basic_type + rect_members = rect_type[member_idx] + + print(f"{rect_name}_t {rect_name}_mindim({vec_name}_t pos, {vec_name}_t size) {{ return ({rect_name}_t){{ pos, {vec_name}_add(pos, size) }}; }}") + print(f"{rect_name}_t {rect_name}_center_halfdim({vec_name}_t center, {vec_name}_t halfdim) {{ return ({rect_name}_t){{ {vec_name}_sub(center, halfdim), {vec_name}_add(center, halfdim) }}; }}") + +for basic_type in basic_types: + s = """ +r2f64_t r2f64_cut_left(r2f64_t *r, f64 value) { + f64 minx = r->min.x; + r->min.x = MIN(r->min.x + value, r->max.x); + return (r2f64_t){ .min = {.x = minx, .y = r->min.y}, .max = {.x = r->min.x, .y =r->max.y} }; +} +r2f64_t r2f64_cut_right(r2f64_t *r, f64 value) { + f64 maxx = r->max.x; + r->max.x = MAX(r->min.x, r->max.x - value); + return (r2f64_t){ .min = {.x = r->max.x, .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} }; +} +r2f64_t r2f64_cut_top(r2f64_t *r, f64 value) { /* Y is down */ + f64 miny = r->min.y; + r->min.y = MIN(r->max.y, r->min.y + value); + return (r2f64_t){ .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = r->min.y} }; +} +r2f64_t r2f64_cut_bottom(r2f64_t *r, f64 value) { /* Y is down */ + f64 maxy = r->max.y; + r->max.y = MAX(r->min.y, r->max.y - value); + return (r2f64_t){ .min = {.x = r->min.x, .y = r->max.y}, .max = {.x = r->max.x, .y = maxy} }; +} + +// get past left +r2f64_t r2f64_getp_left(const r2f64_t *rect, f64 value) { + r2f64_t result = r2f64(rect->min.x - value, rect->min.y, rect->min.x, rect->max.y); + return result; +} +r2f64_t r2f64_getp_right(const r2f64_t *rect, f64 value) { + r2f64_t result = r2f64(rect->max.x, rect->min.y, rect->max.x + value, rect->max.y); + return result; +} +r2f64_t r2f64_getp_bottom(const r2f64_t *rect, f64 value) { + r2f64_t result = r2f64(rect->min.x, rect->max.y, rect->max.x, rect->max.y + value); + return result; +} +r2f64_t r2f64_getp_top(const r2f64_t *rect, f64 value) { + r2f64_t result = r2f64(rect->min.x, rect->min.y - value, rect->max.x, rect->min.y); + return result; +} + +r2f64_t r2f64_get_left(const r2f64_t *r, f64 value) { + f64 minx = r->min.x; + r2f64_t result = (r2f64_t){ .min = {.x = minx, .y = r->min.y}, .max = {.x = MIN(r->min.x + value, r->max.x), .y =r->max.y} }; + return result; +} +r2f64_t r2f64_get_right(const r2f64_t *r, f64 value) { + f64 maxx = r->max.x; + r2f64_t result = (r2f64_t){ .min = {.x = MAX(r->min.x, r->max.x - value), .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} }; + return result; +} +r2f64_t r2f64_get_top(const r2f64_t *r, f64 value) { /* Y is down */ + f64 miny = r->min.y; + r2f64_t result = (r2f64_t){ .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = MIN(r->max.y, r->min.y + value)} }; + return result; +} +r2f64_t r2f64_get_bottom(const r2f64_t *r, f64 value) { /* Y is down */ + f64 maxy = r->max.y; + r2f64_t result = (r2f64_t){ .min = {.x = r->min.x, .y = MAX(r->min.y, r->max.y - value)}, .max = {.x = r->max.x, .y = maxy} }; + return result; +} + +r2f64_t r2f64_shrink(r2f64_t rect, v2f64_t value) { return (r2f64_t){ v2f64_add(rect.min, value), v2f64_sub(rect.max, value) }; } +v2f64_t r2f64_get_size(r2f64_t r) { return (v2f64_t){r.max.x - r.min.x, r.max.y - r.min.y}; } +v2f64_t r2f64_get_mid(r2f64_t r) { return v2f64_add(r.min, v2f64_divs(r2f64_get_size(r), 2)); } +b32 r2f64_contains(r2f64_t rec, v2f64_t point) { return (point.x >= rec.min.x) && (point.x < rec.max.x) && (point.y >= rec.min.y) && (point.y < rec.max.y); } + """ + s = s.replace("r2f64", f"r2{basic_type}") + s = s.replace("f64", basic_type) + s = s.replace("v2f64", f"v2{basic_type}") + print(s) + +for at in basic_types: + for bt in basic_types: + if at == bt: continue + for st in struct_types: + atype = f"{st[name_idx]}{at}" + btype = f"{st[name_idx]}{bt}" + members = st[member_idx] + + def cast_members(members, type): + result = "" + for i in range(len(members)): + result += f"({type})v.{members[i]}" + if i != len(members) - 1: + result += ", " + return result + + print(f"{btype}_t {atype}_to_{btype}({atype}_t v) {{ return ({btype}_t){{ {cast_members(members, bt)} }}; }}") \ No newline at end of file diff --git a/src/core/mathx.c b/src/core/mathx.c new file mode 100644 index 0000000..daa7fac --- /dev/null +++ b/src/core/mathx.c @@ -0,0 +1,520 @@ +#define F64_PI (3.1415926535897932384626433832795028841971693994) +#define F64_DEG2RAD (F64_PI / 180.0); // @Usage: degree * DEG2RAD = radians; +#define F64_RAD2DEG (180.0 / F64_PI); +#define F32_PI ((f32)F64_PI) + +#define rgba_macro_const(r, g, b, a) { r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f } + +v4f32_t primary_color_global = rgba_macro_const(245, 238, 230, 255); +v4f32_t secondary_color_global = rgba_macro_const(255, 248, 227, 255); +v4f32_t accent1_color_global = rgba_macro_const(243, 215, 202, 255); +v4f32_t accent2_color_global = rgba_macro_const(230, 164, 180, 255); + +v4f32_t white_color_global = rgba_macro_const(255, 255, 255, 255); +v4f32_t black_color_global = rgba_macro_const(0, 0, 0, 255); + +/* + * Converts an RGB color value to HSL. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * also https://gist.github.com/ciembor/1494530 + * alpha is ignored + */ +v3f32_t v3f32_rgb_to_hsl(v3f32_t color) { + v3f32_t result; + f32 max = MAX(MAX(color.r, color.g), color.b); + f32 min = MIN(MIN(color.r, color.g), color.b); + + result.h = result.s = result.l = (max + min) / 2.f; + + if (max == min) { + result.h = result.s = 0.f; // achromatic + } else { + f32 d = max - min; + result.s = (result.l > 0.5f) ? d / (2.f - max - min) : d / (max + min); + + if (max == color.r) { + result.h = (color.g - color.b) / d + (color.g < color.b ? 6.f : 0.f); + } else if (max == color.g) { + result.h = (color.b - color.r) / d + 2.f; + } else if (max == color.b) { + result.h = (color.r - color.g) / d + 4.f; + } + + result.h /= 6.f; + } + + return result; +} + +/* + * Converts an HUE to r, g or b. + * returns f32 in the set [0, 1]. + */ +f32 f32_hue_to_rgb(f32 p, f32 q, f32 t) { + if (t < 0.f) + t += 1.f; + if (t > 1.f) + t -= 1.f; + if (t < 1.f / 6.f) + return p + (q - p) * 6.f * t; + if (t < 1.f / 2.f) + return q; + if (t < 2.f / 3.f) + return p + (q - p) * (2.f / 3.f - t) * 6.f; + return p; +} + +v3f32_t v3f32_hsl_to_rgb(v3f32_t color) { + v3f32_t result; + + if (0 == color.s) { + result.r = result.g = result.b = color.l; // achromatic + } else { + f32 q = color.l < 0.5f ? color.l * (1.f + color.s) + : color.l + color.s - color.l * color.s; + f32 p = 2.f * color.l - q; + result.r = f32_hue_to_rgb(p, q, color.h + 1.f / 3.f); + result.g = f32_hue_to_rgb(p, q, color.h); + result.b = f32_hue_to_rgb(p, q, color.h - 1.f / 3.f); + } + return result; +} + +v4f32_t v4f32_rgba_to_hsla(v4f32_t rgba) { + v3f32_t result = v3f32_rgb_to_hsl(rgba.xyz); + return (v4f32_t){result.r, result.g, result.b, rgba.a}; +} + +v4f32_t v4f32_hsl_to_rgb(v4f32_t color) { + v3f32_t result = v3f32_hsl_to_rgb(color.xyz); + return (v4f32_t){result.r, result.g, result.b, color.a}; +} + +/* +https://git.musl-libc.org/cgit/musl/tree/src/math +https://gitlab.com/nakst/essence/-/blob/master/shared/math.cpp +*/ + + + +f64 f64_lerp(f64 a, f64 b, f64 t) { return (1 - t) * a + t * b; } +f64 f64_smooth_step(f64 t) { return t*t*(3-2*t); } +f64 f64_ease_in_quint(f64 x) { return x * x * x * x * x; } +f64 f64_ease_in_cubic(f64 x) { return x * x * x; } +f64 f64_ping_pong(f64 x) { return 1.0 - f64_abs(1.0 - f64_mod(x,2)); } + +f32 f32_lerp(f32 a, f32 b, f32 t) { return (1 - t) * a + t * b; } +f32 f32_smooth_step(f32 t) { return t*t*(3-2*t); } +f32 f32_ease_in_quint(f32 x) { return x * x * x * x * x; } +f32 f32_ease_in_cubic(f32 x) { return x * x * x; } +f32 f32_ping_pong(f32 x) { return 1.0f - f32_abs(1.0f - f32_mod(x,2)); } + +v4f32_t v4f32_lerp(v4f32_t a, v4f32_t b, f32 t) { + return (v4f32_t){f32_lerp(a.x, a.y, t), f32_lerp(a.y, b.y, t), f32_lerp(a.z, b.z, t), f32_lerp(a.w, b.w, t)}; +} + +#if PLATFORM_CL +#pragma warning(disable: 4116) +#endif + +#define U64ToF64(x) (((union { f64 d; u64 i; }) { .i = (x) }).d) +#define U32ToF32(x) (((union { f32 f; u32 i; }) { .i = (x) }).f) + +f64 _Sine(f64 x) { + // Calculates sin(x) for x in [0, pi/4]. + + f64 x2 = x * x; + + return x * (U64ToF64(0x3FF0000000000000) + x2 * (U64ToF64(0xBFC5555555555540) + x2 * (U64ToF64(0x3F8111111110ED80) + x2 * (U64ToF64(0xBF2A01A019AE6000) + + x2 * (U64ToF64(0x3EC71DE349280000) + x2 * (U64ToF64(0xBE5AE5DC48000000) + x2 * U64ToF64(0x3DE5D68200000000))))))); +} + +f32 _SineFloat(f32 x) { + // Calculates sin(x) for x in [0, pi/4]. + + f32 x2 = x * x; + + return x * (U32ToF32(0x3F800000) + x2 * (U32ToF32(0xBE2AAAA0) + x2 * (U32ToF32(0x3C0882C0) + x2 * U32ToF32(0xB94C6000)))); +} + +f64 _ArcSine(f64 x) { + // Calculates arcsin(x) for x in [0, 0.5]. + + f64 x2 = x * x; + + return x * (U64ToF64(0x3FEFFFFFFFFFFFE6) + x2 * (U64ToF64(0x3FC555555555FE00) + x2 * (U64ToF64(0x3FB333333292DF90) + x2 * (U64ToF64(0x3FA6DB6DFD3693A0) + + x2 * (U64ToF64(0x3F9F1C608DE51900) + x2 * (U64ToF64(0x3F96EA0659B9A080) + x2 * (U64ToF64(0x3F91B4ABF1029100) + + x2 * (U64ToF64(0x3F8DA8DAF31ECD00) + x2 * (U64ToF64(0x3F81C01FD5000C00) + x2 * (U64ToF64(0x3F94BDA038CF6B00) + + x2 * (U64ToF64(0xBF8E849CA75B1E00) + x2 * U64ToF64(0x3FA146C2D37F2C60)))))))))))); +} + +f32 _ArcSineFloat(f32 x) { + // Calculates arcsin(x) for x in [0, 0.5]. + + f32 x2 = x * x; + + return x * (U32ToF32(0x3F800004) + x2 * (U32ToF32(0x3E2AA130) + x2 * (U32ToF32(0x3D9B2C28) + x2 * (U32ToF32(0x3D1C1800) + x2 * U32ToF32(0x3D5A97C0))))); +} + +f64 _ArcTangent(f64 x) { + // Calculates arctan(x) for x in [0, 0.5]. + + f64 x2 = x * x; + + return x * (U64ToF64(0x3FEFFFFFFFFFFFF8) + x2 * (U64ToF64(0xBFD5555555553B44) + x2 * (U64ToF64(0x3FC9999999803988) + x2 * (U64ToF64(0xBFC249248C882E80) + + x2 * (U64ToF64(0x3FBC71C5A4E4C220) + x2 * (U64ToF64(0xBFB745B3B75243F0) + x2 * (U64ToF64(0x3FB3AFAE9A2939E0) + + x2 * (U64ToF64(0xBFB1030C4A4A1B90) + x2 * (U64ToF64(0x3FAD6F65C35579A0) + x2 * (U64ToF64(0xBFA805BCFDAFEDC0) + + x2 * (U64ToF64(0x3F9FC6B5E115F2C0) + x2 * U64ToF64(0xBF87DCA5AB25BF80)))))))))))); +} + +f32 _ArcTangentFloat(f32 x) { + // Calculates arctan(x) for x in [0, 0.5]. + + f32 x2 = x * x; + + return x * (U32ToF32(0x3F7FFFF8) + x2 * (U32ToF32(0xBEAAA53C) + x2 * (U32ToF32(0x3E4BC990) + x2 * (U32ToF32(0xBE084A60) + x2 * U32ToF32(0x3D8864B0))))); +} + +f64 _Cosine(f64 x) { + // Calculates cos(x) for x in [0, pi/4]. + + f64 x2 = x * x; + + return U64ToF64(0x3FF0000000000000) + x2 * (U64ToF64(0xBFDFFFFFFFFFFFA0) + x2 * (U64ToF64(0x3FA555555554F7C0) + x2 * (U64ToF64(0xBF56C16C16475C00) + + x2 * (U64ToF64(0x3EFA019F87490000) + x2 * (U64ToF64(0xBE927DF66B000000) + x2 * U64ToF64(0x3E21B949E0000000)))))); +} + +f32 _CosineFloat(f32 x) { + // Calculates cos(x) for x in [0, pi/4]. + + f32 x2 = x * x; + + return U32ToF32(0x3F800000) + x2 * (U32ToF32(0xBEFFFFDA) + x2 * (U32ToF32(0x3D2A9F60) + x2 * U32ToF32(0xBAB22C00))); +} + +f64 _Tangent(f64 x) { + // Calculates tan(x) for x in [0, pi/4]. + + f64 x2 = x * x; + + return x * (U64ToF64(0x3FEFFFFFFFFFFFE8) + x2 * (U64ToF64(0x3FD5555555558000) + x2 * (U64ToF64(0x3FC1111110FACF90) + x2 * (U64ToF64(0x3FABA1BA266BFD20) + + x2 * (U64ToF64(0x3F9664F30E56E580) + x2 * (U64ToF64(0x3F822703B08BDC00) + x2 * (U64ToF64(0x3F6D698D2E4A4C00) + + x2 * (U64ToF64(0x3F57FF4F23EA4400) + x2 * (U64ToF64(0x3F424F3BEC845800) + x2 * (U64ToF64(0x3F34C78CA9F61000) + + x2 * (U64ToF64(0xBF042089F8510000) + x2 * (U64ToF64(0x3F29D7372D3A8000) + x2 * (U64ToF64(0xBF19D1C5EF6F0000) + + x2 * (U64ToF64(0x3F0980BDF11E8000))))))))))))))); +} + +f32 _TangentFloat(f32 x) { + // Calculates tan(x) for x in [0, pi/4]. + + f32 x2 = x * x; + + return x * (U32ToF32(0x3F800001) + x2 * (U32ToF32(0x3EAAA9AA) + x2 * (U32ToF32(0x3E08ABA8) + x2 * (U32ToF32(0x3D58EC90) + + x2 * (U32ToF32(0x3CD24840) + x2 * (U32ToF32(0x3AC3CA00) + x2 * U32ToF32(0x3C272F00))))))); +} + + +f64 f64_sin(f64 x) { + b32 negate = false; + + // x in -infty, infty + + if (x < 0) { + x = -x; + negate = true; + } + + // x in 0, infty + + x -= 2 * F64_PI * f64_floor(x / (2 * F64_PI)); + + // x in 0, 2*pi + + if (x < F64_PI / 2) { + } else if (x < F64_PI) { + x = F64_PI - x; + } else if (x < 3 * F64_PI / 2) { + x = x - F64_PI; + negate = !negate; + } else { + x = F64_PI * 2 - x; + negate = !negate; + } + + // x in 0, pi/2 + + f64 y = x < F64_PI / 4 ? _Sine(x) : _Cosine(F64_PI / 2 - x); + return negate ? -y : y; +} + + +f32 f32_sin(f32 x) { + b32 negate = false; + + // x in -infty, infty + + if (x < 0) { + x = -x; + negate = true; + } + + // x in 0, infty + + x -= 2 * F32_PI * f32_floor(x / (2 * F32_PI)); + + // x in 0, 2*pi + + if (x < F32_PI / 2) { + } else if (x < F32_PI) { + x = F32_PI - x; + } else if (x < 3 * F32_PI / 2) { + x = x - F32_PI; + negate = !negate; + } else { + x = F32_PI * 2 - x; + negate = !negate; + } + + // x in 0, pi/2 + + f32 y = x < F32_PI / 4 ? _SineFloat(x) : _CosineFloat(F32_PI / 2 - x); + return negate ? -y : y; +} + +f64 f64_cos(f64 x) { + b32 negate = false; + + // x in -infty, infty + + if (x < 0) { + x = -x; + } + + // x in 0, infty + + x -= 2 * F64_PI * f64_floor(x / (2 * F64_PI)); + + // x in 0, 2*pi + + if (x < F64_PI / 2) { + } else if (x < F64_PI) { + x = F64_PI - x; + negate = !negate; + } else if (x < 3 * F64_PI / 2) { + x = x - F64_PI; + negate = !negate; + } else { + x = F64_PI * 2 - x; + } + + // x in 0, pi/2 + + f64 y = x < F64_PI / 4 ? _Cosine(x) : _Sine(F64_PI / 2 - x); + return negate ? -y : y; +} + +f32 f32_cos(f32 x) { + b32 negate = false; + + // x in -infty, infty + + if (x < 0) { + x = -x; + } + + // x in 0, infty + + x -= 2 * F32_PI * f32_floor(x / (2 * F32_PI)); + + // x in 0, 2*pi + + if (x < F32_PI / 2) { + } else if (x < F32_PI) { + x = F32_PI - x; + negate = !negate; + } else if (x < 3 * F32_PI / 2) { + x = x - F32_PI; + negate = !negate; + } else { + x = F32_PI * 2 - x; + } + + // x in 0, pi/2 + + f32 y = x < F32_PI / 4 ? _CosineFloat(x) : _SineFloat(F32_PI / 2 - x); + return negate ? -y : y; +} + +f64 f64_tan(f64 x) { + b32 negate = false; + + // x in -infty, infty + + if (x < 0) { + x = -x; + negate = !negate; + } + + // x in 0, infty + + x -= F64_PI * f64_floor(x / F64_PI); + + // x in 0, pi + + if (x > F64_PI / 2) { + x = F64_PI - x; + negate = !negate; + } + + // x in 0, pi/2 + + f64 y = x < F64_PI / 4 ? _Tangent(x) : (1.0 / _Tangent(F64_PI / 2 - x)); + return negate ? -y : y; +} + +f32 f32_tan(f32 x) { + b32 negate = false; + + // x in -infty, infty + + if (x < 0) { + x = -x; + negate = !negate; + } + + // x in 0, infty + + x -= F32_PI * f32_floor(x / F32_PI); + + // x in 0, pi + + if (x > F32_PI / 2) { + x = F32_PI - x; + negate = !negate; + } + + // x in 0, pi/2 + + f32 y = x < F32_PI / 4 ? _TangentFloat(x) : (1.0f / _TangentFloat(F32_PI / 2 - x)); + return negate ? -y : y; +} + +f64 f64_asin(f64 x) { + b32 negate = false; + + if (x < 0) { + x = -x; + negate = true; + } + + f64 y; + + if (x < 0.5) { + y = _ArcSine(x); + } else { + y = F64_PI / 2 - 2 * _ArcSine(f64_sqrt(0.5 - 0.5 * x)); + } + + return negate ? -y : y; +} + +f32 f32_asin(f32 x) { + b32 negate = false; + + if (x < 0) { + x = -x; + negate = true; + } + + f32 y; + + if (x < 0.5) { + y = _ArcSineFloat(x); + } else { + y = F32_PI / 2 - 2 * _ArcSineFloat(f32_sqrt(0.5f - 0.5f * x)); + } + + return negate ? -y : y; +} + +f64 f64_acos(f64 x) { + return f64_asin(-x) + F64_PI / 2; +} + +f32 f32_acos(f32 x) { + return f32_asin(-x) + F32_PI / 2; +} + +f64 f64_atan(f64 x) { + b32 negate = false; + + if (x < 0) { + x = -x; + negate = true; + } + + b32 reciprocalTaken = false; + + if (x > 1) { + x = 1 / x; + reciprocalTaken = true; + } + + f64 y; + + if (x < 0.5) { + y = _ArcTangent(x); + } else { + y = 0.463647609000806116 + _ArcTangent((2 * x - 1) / (2 + x)); + } + + if (reciprocalTaken) { + y = F64_PI / 2 - y; + } + + return negate ? -y : y; +} + +f32 f32_atan(f32 x) { + b32 negate = false; + + if (x < 0) { + x = -x; + negate = true; + } + + b32 reciprocalTaken = false; + + if (x > 1) { + x = 1 / x; + reciprocalTaken = true; + } + + f32 y; + + if (x < 0.5f) { + y = _ArcTangentFloat(x); + } else { + y = 0.463647609000806116f + _ArcTangentFloat((2 * x - 1) / (2 + x)); + } + + if (reciprocalTaken) { + y = F32_PI / 2 - y; + } + + return negate ? -y : y; +} + +f64 f64_atan2(f64 y, f64 x) { + if (x == 0) return y > 0 ? F64_PI / 2 : -F64_PI / 2; + else if (x > 0) return f64_atan(y / x); + else if (y >= 0) return F64_PI + f64_atan(y / x); + else return -F64_PI + f64_atan(y / x); +} + +f32 f32_atan2(f32 y, f32 x) { + if (x == 0) return y > 0 ? F32_PI / 2 : -F32_PI / 2; + else if (x > 0) return f32_atan(y / x); + else if (y >= 0) return F32_PI + f32_atan(y / x); + else return -F32_PI + f32_atan(y / x); +} diff --git a/src/core/mathx.gen.c b/src/core/mathx.gen.c new file mode 100644 index 0000000..ca25f46 --- /dev/null +++ b/src/core/mathx.gen.c @@ -0,0 +1,519 @@ +// auto generated using: D:\dev\wasm\src\core\math_gen.py +v2f32_t v2f32(f32 x, f32 y) { return (v2f32_t){ x, y }; } +v2f32_t v2f32_add(v2f32_t a, v2f32_t b) { return (v2f32_t){ a.x + b.x, a.y + b.y }; } +v2f32_t v2f32_adds(v2f32_t a, f32 b) { return (v2f32_t){ a.x + b, a.y + b }; } +v2f32_t v2f32_sub(v2f32_t a, v2f32_t b) { return (v2f32_t){ a.x - b.x, a.y - b.y }; } +v2f32_t v2f32_subs(v2f32_t a, f32 b) { return (v2f32_t){ a.x - b, a.y - b }; } +v2f32_t v2f32_mul(v2f32_t a, v2f32_t b) { return (v2f32_t){ a.x * b.x, a.y * b.y }; } +v2f32_t v2f32_muls(v2f32_t a, f32 b) { return (v2f32_t){ a.x * b, a.y * b }; } +v2f32_t v2f32_div(v2f32_t a, v2f32_t b) { return (v2f32_t){ a.x / b.x, a.y / b.y }; } +v2f32_t v2f32_divs(v2f32_t a, f32 b) { return (v2f32_t){ a.x / b, a.y / b }; } +v3f32_t v3f32(f32 x, f32 y, f32 z) { return (v3f32_t){ x, y, z }; } +v3f32_t v3f32_add(v3f32_t a, v3f32_t b) { return (v3f32_t){ a.x + b.x, a.y + b.y, a.z + b.z }; } +v3f32_t v3f32_adds(v3f32_t a, f32 b) { return (v3f32_t){ a.x + b, a.y + b, a.z + b }; } +v3f32_t v3f32_sub(v3f32_t a, v3f32_t b) { return (v3f32_t){ a.x - b.x, a.y - b.y, a.z - b.z }; } +v3f32_t v3f32_subs(v3f32_t a, f32 b) { return (v3f32_t){ a.x - b, a.y - b, a.z - b }; } +v3f32_t v3f32_mul(v3f32_t a, v3f32_t b) { return (v3f32_t){ a.x * b.x, a.y * b.y, a.z * b.z }; } +v3f32_t v3f32_muls(v3f32_t a, f32 b) { return (v3f32_t){ a.x * b, a.y * b, a.z * b }; } +v3f32_t v3f32_div(v3f32_t a, v3f32_t b) { return (v3f32_t){ a.x / b.x, a.y / b.y, a.z / b.z }; } +v3f32_t v3f32_divs(v3f32_t a, f32 b) { return (v3f32_t){ a.x / b, a.y / b, a.z / b }; } +v4f32_t v4f32(f32 x, f32 y, f32 z, f32 w) { return (v4f32_t){ x, y, z, w }; } +v4f32_t v4f32_add(v4f32_t a, v4f32_t b) { return (v4f32_t){ a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w }; } +v4f32_t v4f32_adds(v4f32_t a, f32 b) { return (v4f32_t){ a.x + b, a.y + b, a.z + b, a.w + b }; } +v4f32_t v4f32_sub(v4f32_t a, v4f32_t b) { return (v4f32_t){ a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w }; } +v4f32_t v4f32_subs(v4f32_t a, f32 b) { return (v4f32_t){ a.x - b, a.y - b, a.z - b, a.w - b }; } +v4f32_t v4f32_mul(v4f32_t a, v4f32_t b) { return (v4f32_t){ a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w }; } +v4f32_t v4f32_muls(v4f32_t a, f32 b) { return (v4f32_t){ a.x * b, a.y * b, a.z * b, a.w * b }; } +v4f32_t v4f32_div(v4f32_t a, v4f32_t b) { return (v4f32_t){ a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w }; } +v4f32_t v4f32_divs(v4f32_t a, f32 b) { return (v4f32_t){ a.x / b, a.y / b, a.z / b, a.w / b }; } +r2f32_t r2f32(f32 x0, f32 y0, f32 x1, f32 y1) { return (r2f32_t){ x0, y0, x1, y1 }; } +r2f32_t r2f32_add(r2f32_t a, r2f32_t b) { return (r2f32_t){ a.x0 + b.x0, a.y0 + b.y0, a.x1 + b.x1, a.y1 + b.y1 }; } +r2f32_t r2f32_adds(r2f32_t a, f32 b) { return (r2f32_t){ a.x0 + b, a.y0 + b, a.x1 + b, a.y1 + b }; } +r2f32_t r2f32_sub(r2f32_t a, r2f32_t b) { return (r2f32_t){ a.x0 - b.x0, a.y0 - b.y0, a.x1 - b.x1, a.y1 - b.y1 }; } +r2f32_t r2f32_subs(r2f32_t a, f32 b) { return (r2f32_t){ a.x0 - b, a.y0 - b, a.x1 - b, a.y1 - b }; } +r2f32_t r2f32_mul(r2f32_t a, r2f32_t b) { return (r2f32_t){ a.x0 * b.x0, a.y0 * b.y0, a.x1 * b.x1, a.y1 * b.y1 }; } +r2f32_t r2f32_muls(r2f32_t a, f32 b) { return (r2f32_t){ a.x0 * b, a.y0 * b, a.x1 * b, a.y1 * b }; } +r2f32_t r2f32_div(r2f32_t a, r2f32_t b) { return (r2f32_t){ a.x0 / b.x0, a.y0 / b.y0, a.x1 / b.x1, a.y1 / b.y1 }; } +r2f32_t r2f32_divs(r2f32_t a, f32 b) { return (r2f32_t){ a.x0 / b, a.y0 / b, a.x1 / b, a.y1 / b }; } +r3f32_t r3f32(f32 x0, f32 y0, f32 z0, f32 x1, f32 y1, f32 z1) { return (r3f32_t){ x0, y0, z0, x1, y1, z1 }; } +r3f32_t r3f32_add(r3f32_t a, r3f32_t b) { return (r3f32_t){ a.x0 + b.x0, a.y0 + b.y0, a.z0 + b.z0, a.x1 + b.x1, a.y1 + b.y1, a.z1 + b.z1 }; } +r3f32_t r3f32_adds(r3f32_t a, f32 b) { return (r3f32_t){ a.x0 + b, a.y0 + b, a.z0 + b, a.x1 + b, a.y1 + b, a.z1 + b }; } +r3f32_t r3f32_sub(r3f32_t a, r3f32_t b) { return (r3f32_t){ a.x0 - b.x0, a.y0 - b.y0, a.z0 - b.z0, a.x1 - b.x1, a.y1 - b.y1, a.z1 - b.z1 }; } +r3f32_t r3f32_subs(r3f32_t a, f32 b) { return (r3f32_t){ a.x0 - b, a.y0 - b, a.z0 - b, a.x1 - b, a.y1 - b, a.z1 - b }; } +r3f32_t r3f32_mul(r3f32_t a, r3f32_t b) { return (r3f32_t){ a.x0 * b.x0, a.y0 * b.y0, a.z0 * b.z0, a.x1 * b.x1, a.y1 * b.y1, a.z1 * b.z1 }; } +r3f32_t r3f32_muls(r3f32_t a, f32 b) { return (r3f32_t){ a.x0 * b, a.y0 * b, a.z0 * b, a.x1 * b, a.y1 * b, a.z1 * b }; } +r3f32_t r3f32_div(r3f32_t a, r3f32_t b) { return (r3f32_t){ a.x0 / b.x0, a.y0 / b.y0, a.z0 / b.z0, a.x1 / b.x1, a.y1 / b.y1, a.z1 / b.z1 }; } +r3f32_t r3f32_divs(r3f32_t a, f32 b) { return (r3f32_t){ a.x0 / b, a.y0 / b, a.z0 / b, a.x1 / b, a.y1 / b, a.z1 / b }; } +v2f64_t v2f64(f64 x, f64 y) { return (v2f64_t){ x, y }; } +v2f64_t v2f64_add(v2f64_t a, v2f64_t b) { return (v2f64_t){ a.x + b.x, a.y + b.y }; } +v2f64_t v2f64_adds(v2f64_t a, f64 b) { return (v2f64_t){ a.x + b, a.y + b }; } +v2f64_t v2f64_sub(v2f64_t a, v2f64_t b) { return (v2f64_t){ a.x - b.x, a.y - b.y }; } +v2f64_t v2f64_subs(v2f64_t a, f64 b) { return (v2f64_t){ a.x - b, a.y - b }; } +v2f64_t v2f64_mul(v2f64_t a, v2f64_t b) { return (v2f64_t){ a.x * b.x, a.y * b.y }; } +v2f64_t v2f64_muls(v2f64_t a, f64 b) { return (v2f64_t){ a.x * b, a.y * b }; } +v2f64_t v2f64_div(v2f64_t a, v2f64_t b) { return (v2f64_t){ a.x / b.x, a.y / b.y }; } +v2f64_t v2f64_divs(v2f64_t a, f64 b) { return (v2f64_t){ a.x / b, a.y / b }; } +v3f64_t v3f64(f64 x, f64 y, f64 z) { return (v3f64_t){ x, y, z }; } +v3f64_t v3f64_add(v3f64_t a, v3f64_t b) { return (v3f64_t){ a.x + b.x, a.y + b.y, a.z + b.z }; } +v3f64_t v3f64_adds(v3f64_t a, f64 b) { return (v3f64_t){ a.x + b, a.y + b, a.z + b }; } +v3f64_t v3f64_sub(v3f64_t a, v3f64_t b) { return (v3f64_t){ a.x - b.x, a.y - b.y, a.z - b.z }; } +v3f64_t v3f64_subs(v3f64_t a, f64 b) { return (v3f64_t){ a.x - b, a.y - b, a.z - b }; } +v3f64_t v3f64_mul(v3f64_t a, v3f64_t b) { return (v3f64_t){ a.x * b.x, a.y * b.y, a.z * b.z }; } +v3f64_t v3f64_muls(v3f64_t a, f64 b) { return (v3f64_t){ a.x * b, a.y * b, a.z * b }; } +v3f64_t v3f64_div(v3f64_t a, v3f64_t b) { return (v3f64_t){ a.x / b.x, a.y / b.y, a.z / b.z }; } +v3f64_t v3f64_divs(v3f64_t a, f64 b) { return (v3f64_t){ a.x / b, a.y / b, a.z / b }; } +v4f64_t v4f64(f64 x, f64 y, f64 z, f64 w) { return (v4f64_t){ x, y, z, w }; } +v4f64_t v4f64_add(v4f64_t a, v4f64_t b) { return (v4f64_t){ a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w }; } +v4f64_t v4f64_adds(v4f64_t a, f64 b) { return (v4f64_t){ a.x + b, a.y + b, a.z + b, a.w + b }; } +v4f64_t v4f64_sub(v4f64_t a, v4f64_t b) { return (v4f64_t){ a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w }; } +v4f64_t v4f64_subs(v4f64_t a, f64 b) { return (v4f64_t){ a.x - b, a.y - b, a.z - b, a.w - b }; } +v4f64_t v4f64_mul(v4f64_t a, v4f64_t b) { return (v4f64_t){ a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w }; } +v4f64_t v4f64_muls(v4f64_t a, f64 b) { return (v4f64_t){ a.x * b, a.y * b, a.z * b, a.w * b }; } +v4f64_t v4f64_div(v4f64_t a, v4f64_t b) { return (v4f64_t){ a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w }; } +v4f64_t v4f64_divs(v4f64_t a, f64 b) { return (v4f64_t){ a.x / b, a.y / b, a.z / b, a.w / b }; } +r2f64_t r2f64(f64 x0, f64 y0, f64 x1, f64 y1) { return (r2f64_t){ x0, y0, x1, y1 }; } +r2f64_t r2f64_add(r2f64_t a, r2f64_t b) { return (r2f64_t){ a.x0 + b.x0, a.y0 + b.y0, a.x1 + b.x1, a.y1 + b.y1 }; } +r2f64_t r2f64_adds(r2f64_t a, f64 b) { return (r2f64_t){ a.x0 + b, a.y0 + b, a.x1 + b, a.y1 + b }; } +r2f64_t r2f64_sub(r2f64_t a, r2f64_t b) { return (r2f64_t){ a.x0 - b.x0, a.y0 - b.y0, a.x1 - b.x1, a.y1 - b.y1 }; } +r2f64_t r2f64_subs(r2f64_t a, f64 b) { return (r2f64_t){ a.x0 - b, a.y0 - b, a.x1 - b, a.y1 - b }; } +r2f64_t r2f64_mul(r2f64_t a, r2f64_t b) { return (r2f64_t){ a.x0 * b.x0, a.y0 * b.y0, a.x1 * b.x1, a.y1 * b.y1 }; } +r2f64_t r2f64_muls(r2f64_t a, f64 b) { return (r2f64_t){ a.x0 * b, a.y0 * b, a.x1 * b, a.y1 * b }; } +r2f64_t r2f64_div(r2f64_t a, r2f64_t b) { return (r2f64_t){ a.x0 / b.x0, a.y0 / b.y0, a.x1 / b.x1, a.y1 / b.y1 }; } +r2f64_t r2f64_divs(r2f64_t a, f64 b) { return (r2f64_t){ a.x0 / b, a.y0 / b, a.x1 / b, a.y1 / b }; } +r3f64_t r3f64(f64 x0, f64 y0, f64 z0, f64 x1, f64 y1, f64 z1) { return (r3f64_t){ x0, y0, z0, x1, y1, z1 }; } +r3f64_t r3f64_add(r3f64_t a, r3f64_t b) { return (r3f64_t){ a.x0 + b.x0, a.y0 + b.y0, a.z0 + b.z0, a.x1 + b.x1, a.y1 + b.y1, a.z1 + b.z1 }; } +r3f64_t r3f64_adds(r3f64_t a, f64 b) { return (r3f64_t){ a.x0 + b, a.y0 + b, a.z0 + b, a.x1 + b, a.y1 + b, a.z1 + b }; } +r3f64_t r3f64_sub(r3f64_t a, r3f64_t b) { return (r3f64_t){ a.x0 - b.x0, a.y0 - b.y0, a.z0 - b.z0, a.x1 - b.x1, a.y1 - b.y1, a.z1 - b.z1 }; } +r3f64_t r3f64_subs(r3f64_t a, f64 b) { return (r3f64_t){ a.x0 - b, a.y0 - b, a.z0 - b, a.x1 - b, a.y1 - b, a.z1 - b }; } +r3f64_t r3f64_mul(r3f64_t a, r3f64_t b) { return (r3f64_t){ a.x0 * b.x0, a.y0 * b.y0, a.z0 * b.z0, a.x1 * b.x1, a.y1 * b.y1, a.z1 * b.z1 }; } +r3f64_t r3f64_muls(r3f64_t a, f64 b) { return (r3f64_t){ a.x0 * b, a.y0 * b, a.z0 * b, a.x1 * b, a.y1 * b, a.z1 * b }; } +r3f64_t r3f64_div(r3f64_t a, r3f64_t b) { return (r3f64_t){ a.x0 / b.x0, a.y0 / b.y0, a.z0 / b.z0, a.x1 / b.x1, a.y1 / b.y1, a.z1 / b.z1 }; } +r3f64_t r3f64_divs(r3f64_t a, f64 b) { return (r3f64_t){ a.x0 / b, a.y0 / b, a.z0 / b, a.x1 / b, a.y1 / b, a.z1 / b }; } +v2i32_t v2i32(i32 x, i32 y) { return (v2i32_t){ x, y }; } +v2i32_t v2i32_add(v2i32_t a, v2i32_t b) { return (v2i32_t){ a.x + b.x, a.y + b.y }; } +v2i32_t v2i32_adds(v2i32_t a, i32 b) { return (v2i32_t){ a.x + b, a.y + b }; } +v2i32_t v2i32_sub(v2i32_t a, v2i32_t b) { return (v2i32_t){ a.x - b.x, a.y - b.y }; } +v2i32_t v2i32_subs(v2i32_t a, i32 b) { return (v2i32_t){ a.x - b, a.y - b }; } +v2i32_t v2i32_mul(v2i32_t a, v2i32_t b) { return (v2i32_t){ a.x * b.x, a.y * b.y }; } +v2i32_t v2i32_muls(v2i32_t a, i32 b) { return (v2i32_t){ a.x * b, a.y * b }; } +v2i32_t v2i32_div(v2i32_t a, v2i32_t b) { return (v2i32_t){ a.x / b.x, a.y / b.y }; } +v2i32_t v2i32_divs(v2i32_t a, i32 b) { return (v2i32_t){ a.x / b, a.y / b }; } +v3i32_t v3i32(i32 x, i32 y, i32 z) { return (v3i32_t){ x, y, z }; } +v3i32_t v3i32_add(v3i32_t a, v3i32_t b) { return (v3i32_t){ a.x + b.x, a.y + b.y, a.z + b.z }; } +v3i32_t v3i32_adds(v3i32_t a, i32 b) { return (v3i32_t){ a.x + b, a.y + b, a.z + b }; } +v3i32_t v3i32_sub(v3i32_t a, v3i32_t b) { return (v3i32_t){ a.x - b.x, a.y - b.y, a.z - b.z }; } +v3i32_t v3i32_subs(v3i32_t a, i32 b) { return (v3i32_t){ a.x - b, a.y - b, a.z - b }; } +v3i32_t v3i32_mul(v3i32_t a, v3i32_t b) { return (v3i32_t){ a.x * b.x, a.y * b.y, a.z * b.z }; } +v3i32_t v3i32_muls(v3i32_t a, i32 b) { return (v3i32_t){ a.x * b, a.y * b, a.z * b }; } +v3i32_t v3i32_div(v3i32_t a, v3i32_t b) { return (v3i32_t){ a.x / b.x, a.y / b.y, a.z / b.z }; } +v3i32_t v3i32_divs(v3i32_t a, i32 b) { return (v3i32_t){ a.x / b, a.y / b, a.z / b }; } +v4i32_t v4i32(i32 x, i32 y, i32 z, i32 w) { return (v4i32_t){ x, y, z, w }; } +v4i32_t v4i32_add(v4i32_t a, v4i32_t b) { return (v4i32_t){ a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w }; } +v4i32_t v4i32_adds(v4i32_t a, i32 b) { return (v4i32_t){ a.x + b, a.y + b, a.z + b, a.w + b }; } +v4i32_t v4i32_sub(v4i32_t a, v4i32_t b) { return (v4i32_t){ a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w }; } +v4i32_t v4i32_subs(v4i32_t a, i32 b) { return (v4i32_t){ a.x - b, a.y - b, a.z - b, a.w - b }; } +v4i32_t v4i32_mul(v4i32_t a, v4i32_t b) { return (v4i32_t){ a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w }; } +v4i32_t v4i32_muls(v4i32_t a, i32 b) { return (v4i32_t){ a.x * b, a.y * b, a.z * b, a.w * b }; } +v4i32_t v4i32_div(v4i32_t a, v4i32_t b) { return (v4i32_t){ a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w }; } +v4i32_t v4i32_divs(v4i32_t a, i32 b) { return (v4i32_t){ a.x / b, a.y / b, a.z / b, a.w / b }; } +r2i32_t r2i32(i32 x0, i32 y0, i32 x1, i32 y1) { return (r2i32_t){ x0, y0, x1, y1 }; } +r2i32_t r2i32_add(r2i32_t a, r2i32_t b) { return (r2i32_t){ a.x0 + b.x0, a.y0 + b.y0, a.x1 + b.x1, a.y1 + b.y1 }; } +r2i32_t r2i32_adds(r2i32_t a, i32 b) { return (r2i32_t){ a.x0 + b, a.y0 + b, a.x1 + b, a.y1 + b }; } +r2i32_t r2i32_sub(r2i32_t a, r2i32_t b) { return (r2i32_t){ a.x0 - b.x0, a.y0 - b.y0, a.x1 - b.x1, a.y1 - b.y1 }; } +r2i32_t r2i32_subs(r2i32_t a, i32 b) { return (r2i32_t){ a.x0 - b, a.y0 - b, a.x1 - b, a.y1 - b }; } +r2i32_t r2i32_mul(r2i32_t a, r2i32_t b) { return (r2i32_t){ a.x0 * b.x0, a.y0 * b.y0, a.x1 * b.x1, a.y1 * b.y1 }; } +r2i32_t r2i32_muls(r2i32_t a, i32 b) { return (r2i32_t){ a.x0 * b, a.y0 * b, a.x1 * b, a.y1 * b }; } +r2i32_t r2i32_div(r2i32_t a, r2i32_t b) { return (r2i32_t){ a.x0 / b.x0, a.y0 / b.y0, a.x1 / b.x1, a.y1 / b.y1 }; } +r2i32_t r2i32_divs(r2i32_t a, i32 b) { return (r2i32_t){ a.x0 / b, a.y0 / b, a.x1 / b, a.y1 / b }; } +r3i32_t r3i32(i32 x0, i32 y0, i32 z0, i32 x1, i32 y1, i32 z1) { return (r3i32_t){ x0, y0, z0, x1, y1, z1 }; } +r3i32_t r3i32_add(r3i32_t a, r3i32_t b) { return (r3i32_t){ a.x0 + b.x0, a.y0 + b.y0, a.z0 + b.z0, a.x1 + b.x1, a.y1 + b.y1, a.z1 + b.z1 }; } +r3i32_t r3i32_adds(r3i32_t a, i32 b) { return (r3i32_t){ a.x0 + b, a.y0 + b, a.z0 + b, a.x1 + b, a.y1 + b, a.z1 + b }; } +r3i32_t r3i32_sub(r3i32_t a, r3i32_t b) { return (r3i32_t){ a.x0 - b.x0, a.y0 - b.y0, a.z0 - b.z0, a.x1 - b.x1, a.y1 - b.y1, a.z1 - b.z1 }; } +r3i32_t r3i32_subs(r3i32_t a, i32 b) { return (r3i32_t){ a.x0 - b, a.y0 - b, a.z0 - b, a.x1 - b, a.y1 - b, a.z1 - b }; } +r3i32_t r3i32_mul(r3i32_t a, r3i32_t b) { return (r3i32_t){ a.x0 * b.x0, a.y0 * b.y0, a.z0 * b.z0, a.x1 * b.x1, a.y1 * b.y1, a.z1 * b.z1 }; } +r3i32_t r3i32_muls(r3i32_t a, i32 b) { return (r3i32_t){ a.x0 * b, a.y0 * b, a.z0 * b, a.x1 * b, a.y1 * b, a.z1 * b }; } +r3i32_t r3i32_div(r3i32_t a, r3i32_t b) { return (r3i32_t){ a.x0 / b.x0, a.y0 / b.y0, a.z0 / b.z0, a.x1 / b.x1, a.y1 / b.y1, a.z1 / b.z1 }; } +r3i32_t r3i32_divs(r3i32_t a, i32 b) { return (r3i32_t){ a.x0 / b, a.y0 / b, a.z0 / b, a.x1 / b, a.y1 / b, a.z1 / b }; } +v2i64_t v2i64(i64 x, i64 y) { return (v2i64_t){ x, y }; } +v2i64_t v2i64_add(v2i64_t a, v2i64_t b) { return (v2i64_t){ a.x + b.x, a.y + b.y }; } +v2i64_t v2i64_adds(v2i64_t a, i64 b) { return (v2i64_t){ a.x + b, a.y + b }; } +v2i64_t v2i64_sub(v2i64_t a, v2i64_t b) { return (v2i64_t){ a.x - b.x, a.y - b.y }; } +v2i64_t v2i64_subs(v2i64_t a, i64 b) { return (v2i64_t){ a.x - b, a.y - b }; } +v2i64_t v2i64_mul(v2i64_t a, v2i64_t b) { return (v2i64_t){ a.x * b.x, a.y * b.y }; } +v2i64_t v2i64_muls(v2i64_t a, i64 b) { return (v2i64_t){ a.x * b, a.y * b }; } +v2i64_t v2i64_div(v2i64_t a, v2i64_t b) { return (v2i64_t){ a.x / b.x, a.y / b.y }; } +v2i64_t v2i64_divs(v2i64_t a, i64 b) { return (v2i64_t){ a.x / b, a.y / b }; } +v3i64_t v3i64(i64 x, i64 y, i64 z) { return (v3i64_t){ x, y, z }; } +v3i64_t v3i64_add(v3i64_t a, v3i64_t b) { return (v3i64_t){ a.x + b.x, a.y + b.y, a.z + b.z }; } +v3i64_t v3i64_adds(v3i64_t a, i64 b) { return (v3i64_t){ a.x + b, a.y + b, a.z + b }; } +v3i64_t v3i64_sub(v3i64_t a, v3i64_t b) { return (v3i64_t){ a.x - b.x, a.y - b.y, a.z - b.z }; } +v3i64_t v3i64_subs(v3i64_t a, i64 b) { return (v3i64_t){ a.x - b, a.y - b, a.z - b }; } +v3i64_t v3i64_mul(v3i64_t a, v3i64_t b) { return (v3i64_t){ a.x * b.x, a.y * b.y, a.z * b.z }; } +v3i64_t v3i64_muls(v3i64_t a, i64 b) { return (v3i64_t){ a.x * b, a.y * b, a.z * b }; } +v3i64_t v3i64_div(v3i64_t a, v3i64_t b) { return (v3i64_t){ a.x / b.x, a.y / b.y, a.z / b.z }; } +v3i64_t v3i64_divs(v3i64_t a, i64 b) { return (v3i64_t){ a.x / b, a.y / b, a.z / b }; } +v4i64_t v4i64(i64 x, i64 y, i64 z, i64 w) { return (v4i64_t){ x, y, z, w }; } +v4i64_t v4i64_add(v4i64_t a, v4i64_t b) { return (v4i64_t){ a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w }; } +v4i64_t v4i64_adds(v4i64_t a, i64 b) { return (v4i64_t){ a.x + b, a.y + b, a.z + b, a.w + b }; } +v4i64_t v4i64_sub(v4i64_t a, v4i64_t b) { return (v4i64_t){ a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w }; } +v4i64_t v4i64_subs(v4i64_t a, i64 b) { return (v4i64_t){ a.x - b, a.y - b, a.z - b, a.w - b }; } +v4i64_t v4i64_mul(v4i64_t a, v4i64_t b) { return (v4i64_t){ a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w }; } +v4i64_t v4i64_muls(v4i64_t a, i64 b) { return (v4i64_t){ a.x * b, a.y * b, a.z * b, a.w * b }; } +v4i64_t v4i64_div(v4i64_t a, v4i64_t b) { return (v4i64_t){ a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w }; } +v4i64_t v4i64_divs(v4i64_t a, i64 b) { return (v4i64_t){ a.x / b, a.y / b, a.z / b, a.w / b }; } +r2i64_t r2i64(i64 x0, i64 y0, i64 x1, i64 y1) { return (r2i64_t){ x0, y0, x1, y1 }; } +r2i64_t r2i64_add(r2i64_t a, r2i64_t b) { return (r2i64_t){ a.x0 + b.x0, a.y0 + b.y0, a.x1 + b.x1, a.y1 + b.y1 }; } +r2i64_t r2i64_adds(r2i64_t a, i64 b) { return (r2i64_t){ a.x0 + b, a.y0 + b, a.x1 + b, a.y1 + b }; } +r2i64_t r2i64_sub(r2i64_t a, r2i64_t b) { return (r2i64_t){ a.x0 - b.x0, a.y0 - b.y0, a.x1 - b.x1, a.y1 - b.y1 }; } +r2i64_t r2i64_subs(r2i64_t a, i64 b) { return (r2i64_t){ a.x0 - b, a.y0 - b, a.x1 - b, a.y1 - b }; } +r2i64_t r2i64_mul(r2i64_t a, r2i64_t b) { return (r2i64_t){ a.x0 * b.x0, a.y0 * b.y0, a.x1 * b.x1, a.y1 * b.y1 }; } +r2i64_t r2i64_muls(r2i64_t a, i64 b) { return (r2i64_t){ a.x0 * b, a.y0 * b, a.x1 * b, a.y1 * b }; } +r2i64_t r2i64_div(r2i64_t a, r2i64_t b) { return (r2i64_t){ a.x0 / b.x0, a.y0 / b.y0, a.x1 / b.x1, a.y1 / b.y1 }; } +r2i64_t r2i64_divs(r2i64_t a, i64 b) { return (r2i64_t){ a.x0 / b, a.y0 / b, a.x1 / b, a.y1 / b }; } +r3i64_t r3i64(i64 x0, i64 y0, i64 z0, i64 x1, i64 y1, i64 z1) { return (r3i64_t){ x0, y0, z0, x1, y1, z1 }; } +r3i64_t r3i64_add(r3i64_t a, r3i64_t b) { return (r3i64_t){ a.x0 + b.x0, a.y0 + b.y0, a.z0 + b.z0, a.x1 + b.x1, a.y1 + b.y1, a.z1 + b.z1 }; } +r3i64_t r3i64_adds(r3i64_t a, i64 b) { return (r3i64_t){ a.x0 + b, a.y0 + b, a.z0 + b, a.x1 + b, a.y1 + b, a.z1 + b }; } +r3i64_t r3i64_sub(r3i64_t a, r3i64_t b) { return (r3i64_t){ a.x0 - b.x0, a.y0 - b.y0, a.z0 - b.z0, a.x1 - b.x1, a.y1 - b.y1, a.z1 - b.z1 }; } +r3i64_t r3i64_subs(r3i64_t a, i64 b) { return (r3i64_t){ a.x0 - b, a.y0 - b, a.z0 - b, a.x1 - b, a.y1 - b, a.z1 - b }; } +r3i64_t r3i64_mul(r3i64_t a, r3i64_t b) { return (r3i64_t){ a.x0 * b.x0, a.y0 * b.y0, a.z0 * b.z0, a.x1 * b.x1, a.y1 * b.y1, a.z1 * b.z1 }; } +r3i64_t r3i64_muls(r3i64_t a, i64 b) { return (r3i64_t){ a.x0 * b, a.y0 * b, a.z0 * b, a.x1 * b, a.y1 * b, a.z1 * b }; } +r3i64_t r3i64_div(r3i64_t a, r3i64_t b) { return (r3i64_t){ a.x0 / b.x0, a.y0 / b.y0, a.z0 / b.z0, a.x1 / b.x1, a.y1 / b.y1, a.z1 / b.z1 }; } +r3i64_t r3i64_divs(r3i64_t a, i64 b) { return (r3i64_t){ a.x0 / b, a.y0 / b, a.z0 / b, a.x1 / b, a.y1 / b, a.z1 / b }; } +r2f32_t r2f32_mindim(v2f32_t pos, v2f32_t size) { return (r2f32_t){ pos, v2f32_add(pos, size) }; } +r2f32_t r2f32_center_halfdim(v2f32_t center, v2f32_t halfdim) { return (r2f32_t){ v2f32_sub(center, halfdim), v2f32_add(center, halfdim) }; } +r3f32_t r3f32_mindim(v3f32_t pos, v3f32_t size) { return (r3f32_t){ pos, v3f32_add(pos, size) }; } +r3f32_t r3f32_center_halfdim(v3f32_t center, v3f32_t halfdim) { return (r3f32_t){ v3f32_sub(center, halfdim), v3f32_add(center, halfdim) }; } +r2f64_t r2f64_mindim(v2f64_t pos, v2f64_t size) { return (r2f64_t){ pos, v2f64_add(pos, size) }; } +r2f64_t r2f64_center_halfdim(v2f64_t center, v2f64_t halfdim) { return (r2f64_t){ v2f64_sub(center, halfdim), v2f64_add(center, halfdim) }; } +r3f64_t r3f64_mindim(v3f64_t pos, v3f64_t size) { return (r3f64_t){ pos, v3f64_add(pos, size) }; } +r3f64_t r3f64_center_halfdim(v3f64_t center, v3f64_t halfdim) { return (r3f64_t){ v3f64_sub(center, halfdim), v3f64_add(center, halfdim) }; } +r2i32_t r2i32_mindim(v2i32_t pos, v2i32_t size) { return (r2i32_t){ pos, v2i32_add(pos, size) }; } +r2i32_t r2i32_center_halfdim(v2i32_t center, v2i32_t halfdim) { return (r2i32_t){ v2i32_sub(center, halfdim), v2i32_add(center, halfdim) }; } +r3i32_t r3i32_mindim(v3i32_t pos, v3i32_t size) { return (r3i32_t){ pos, v3i32_add(pos, size) }; } +r3i32_t r3i32_center_halfdim(v3i32_t center, v3i32_t halfdim) { return (r3i32_t){ v3i32_sub(center, halfdim), v3i32_add(center, halfdim) }; } +r2i64_t r2i64_mindim(v2i64_t pos, v2i64_t size) { return (r2i64_t){ pos, v2i64_add(pos, size) }; } +r2i64_t r2i64_center_halfdim(v2i64_t center, v2i64_t halfdim) { return (r2i64_t){ v2i64_sub(center, halfdim), v2i64_add(center, halfdim) }; } +r3i64_t r3i64_mindim(v3i64_t pos, v3i64_t size) { return (r3i64_t){ pos, v3i64_add(pos, size) }; } +r3i64_t r3i64_center_halfdim(v3i64_t center, v3i64_t halfdim) { return (r3i64_t){ v3i64_sub(center, halfdim), v3i64_add(center, halfdim) }; } + +r2f32_t r2f32_cut_left(r2f32_t *r, f32 value) { + f32 minx = r->min.x; + r->min.x = MIN(r->min.x + value, r->max.x); + return (r2f32_t){ .min = {.x = minx, .y = r->min.y}, .max = {.x = r->min.x, .y =r->max.y} }; +} +r2f32_t r2f32_cut_right(r2f32_t *r, f32 value) { + f32 maxx = r->max.x; + r->max.x = MAX(r->min.x, r->max.x - value); + return (r2f32_t){ .min = {.x = r->max.x, .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} }; +} +r2f32_t r2f32_cut_top(r2f32_t *r, f32 value) { /* Y is down */ + f32 miny = r->min.y; + r->min.y = MIN(r->max.y, r->min.y + value); + return (r2f32_t){ .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = r->min.y} }; +} +r2f32_t r2f32_cut_bottom(r2f32_t *r, f32 value) { /* Y is down */ + f32 maxy = r->max.y; + r->max.y = MAX(r->min.y, r->max.y - value); + return (r2f32_t){ .min = {.x = r->min.x, .y = r->max.y}, .max = {.x = r->max.x, .y = maxy} }; +} + +// get past left +r2f32_t r2f32_getp_left(const r2f32_t *rect, f32 value) { + r2f32_t result = r2f32(rect->min.x - value, rect->min.y, rect->min.x, rect->max.y); + return result; +} +r2f32_t r2f32_getp_right(const r2f32_t *rect, f32 value) { + r2f32_t result = r2f32(rect->max.x, rect->min.y, rect->max.x + value, rect->max.y); + return result; +} +r2f32_t r2f32_getp_bottom(const r2f32_t *rect, f32 value) { + r2f32_t result = r2f32(rect->min.x, rect->max.y, rect->max.x, rect->max.y + value); + return result; +} +r2f32_t r2f32_getp_top(const r2f32_t *rect, f32 value) { + r2f32_t result = r2f32(rect->min.x, rect->min.y - value, rect->max.x, rect->min.y); + return result; +} + +r2f32_t r2f32_get_left(const r2f32_t *r, f32 value) { + f32 minx = r->min.x; + r2f32_t result = (r2f32_t){ .min = {.x = minx, .y = r->min.y}, .max = {.x = MIN(r->min.x + value, r->max.x), .y =r->max.y} }; + return result; +} +r2f32_t r2f32_get_right(const r2f32_t *r, f32 value) { + f32 maxx = r->max.x; + r2f32_t result = (r2f32_t){ .min = {.x = MAX(r->min.x, r->max.x - value), .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} }; + return result; +} +r2f32_t r2f32_get_top(const r2f32_t *r, f32 value) { /* Y is down */ + f32 miny = r->min.y; + r2f32_t result = (r2f32_t){ .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = MIN(r->max.y, r->min.y + value)} }; + return result; +} +r2f32_t r2f32_get_bottom(const r2f32_t *r, f32 value) { /* Y is down */ + f32 maxy = r->max.y; + r2f32_t result = (r2f32_t){ .min = {.x = r->min.x, .y = MAX(r->min.y, r->max.y - value)}, .max = {.x = r->max.x, .y = maxy} }; + return result; +} + +r2f32_t r2f32_shrink(r2f32_t rect, v2f32_t value) { return (r2f32_t){ v2f32_add(rect.min, value), v2f32_sub(rect.max, value) }; } +v2f32_t r2f32_get_size(r2f32_t r) { return (v2f32_t){r.max.x - r.min.x, r.max.y - r.min.y}; } +v2f32_t r2f32_get_mid(r2f32_t r) { return v2f32_add(r.min, v2f32_divs(r2f32_get_size(r), 2)); } +b32 r2f32_contains(r2f32_t rec, v2f32_t point) { return (point.x >= rec.min.x) && (point.x < rec.max.x) && (point.y >= rec.min.y) && (point.y < rec.max.y); } + +r2f64_t r2f64_cut_left(r2f64_t *r, f64 value) { + f64 minx = r->min.x; + r->min.x = MIN(r->min.x + value, r->max.x); + return (r2f64_t){ .min = {.x = minx, .y = r->min.y}, .max = {.x = r->min.x, .y =r->max.y} }; +} +r2f64_t r2f64_cut_right(r2f64_t *r, f64 value) { + f64 maxx = r->max.x; + r->max.x = MAX(r->min.x, r->max.x - value); + return (r2f64_t){ .min = {.x = r->max.x, .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} }; +} +r2f64_t r2f64_cut_top(r2f64_t *r, f64 value) { /* Y is down */ + f64 miny = r->min.y; + r->min.y = MIN(r->max.y, r->min.y + value); + return (r2f64_t){ .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = r->min.y} }; +} +r2f64_t r2f64_cut_bottom(r2f64_t *r, f64 value) { /* Y is down */ + f64 maxy = r->max.y; + r->max.y = MAX(r->min.y, r->max.y - value); + return (r2f64_t){ .min = {.x = r->min.x, .y = r->max.y}, .max = {.x = r->max.x, .y = maxy} }; +} + +// get past left +r2f64_t r2f64_getp_left(const r2f64_t *rect, f64 value) { + r2f64_t result = r2f64(rect->min.x - value, rect->min.y, rect->min.x, rect->max.y); + return result; +} +r2f64_t r2f64_getp_right(const r2f64_t *rect, f64 value) { + r2f64_t result = r2f64(rect->max.x, rect->min.y, rect->max.x + value, rect->max.y); + return result; +} +r2f64_t r2f64_getp_bottom(const r2f64_t *rect, f64 value) { + r2f64_t result = r2f64(rect->min.x, rect->max.y, rect->max.x, rect->max.y + value); + return result; +} +r2f64_t r2f64_getp_top(const r2f64_t *rect, f64 value) { + r2f64_t result = r2f64(rect->min.x, rect->min.y - value, rect->max.x, rect->min.y); + return result; +} + +r2f64_t r2f64_get_left(const r2f64_t *r, f64 value) { + f64 minx = r->min.x; + r2f64_t result = (r2f64_t){ .min = {.x = minx, .y = r->min.y}, .max = {.x = MIN(r->min.x + value, r->max.x), .y =r->max.y} }; + return result; +} +r2f64_t r2f64_get_right(const r2f64_t *r, f64 value) { + f64 maxx = r->max.x; + r2f64_t result = (r2f64_t){ .min = {.x = MAX(r->min.x, r->max.x - value), .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} }; + return result; +} +r2f64_t r2f64_get_top(const r2f64_t *r, f64 value) { /* Y is down */ + f64 miny = r->min.y; + r2f64_t result = (r2f64_t){ .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = MIN(r->max.y, r->min.y + value)} }; + return result; +} +r2f64_t r2f64_get_bottom(const r2f64_t *r, f64 value) { /* Y is down */ + f64 maxy = r->max.y; + r2f64_t result = (r2f64_t){ .min = {.x = r->min.x, .y = MAX(r->min.y, r->max.y - value)}, .max = {.x = r->max.x, .y = maxy} }; + return result; +} + +r2f64_t r2f64_shrink(r2f64_t rect, v2f64_t value) { return (r2f64_t){ v2f64_add(rect.min, value), v2f64_sub(rect.max, value) }; } +v2f64_t r2f64_get_size(r2f64_t r) { return (v2f64_t){r.max.x - r.min.x, r.max.y - r.min.y}; } +v2f64_t r2f64_get_mid(r2f64_t r) { return v2f64_add(r.min, v2f64_divs(r2f64_get_size(r), 2)); } +b32 r2f64_contains(r2f64_t rec, v2f64_t point) { return (point.x >= rec.min.x) && (point.x < rec.max.x) && (point.y >= rec.min.y) && (point.y < rec.max.y); } + + +r2i32_t r2i32_cut_left(r2i32_t *r, i32 value) { + i32 minx = r->min.x; + r->min.x = MIN(r->min.x + value, r->max.x); + return (r2i32_t){ .min = {.x = minx, .y = r->min.y}, .max = {.x = r->min.x, .y =r->max.y} }; +} +r2i32_t r2i32_cut_right(r2i32_t *r, i32 value) { + i32 maxx = r->max.x; + r->max.x = MAX(r->min.x, r->max.x - value); + return (r2i32_t){ .min = {.x = r->max.x, .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} }; +} +r2i32_t r2i32_cut_top(r2i32_t *r, i32 value) { /* Y is down */ + i32 miny = r->min.y; + r->min.y = MIN(r->max.y, r->min.y + value); + return (r2i32_t){ .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = r->min.y} }; +} +r2i32_t r2i32_cut_bottom(r2i32_t *r, i32 value) { /* Y is down */ + i32 maxy = r->max.y; + r->max.y = MAX(r->min.y, r->max.y - value); + return (r2i32_t){ .min = {.x = r->min.x, .y = r->max.y}, .max = {.x = r->max.x, .y = maxy} }; +} + +// get past left +r2i32_t r2i32_getp_left(const r2i32_t *rect, i32 value) { + r2i32_t result = r2i32(rect->min.x - value, rect->min.y, rect->min.x, rect->max.y); + return result; +} +r2i32_t r2i32_getp_right(const r2i32_t *rect, i32 value) { + r2i32_t result = r2i32(rect->max.x, rect->min.y, rect->max.x + value, rect->max.y); + return result; +} +r2i32_t r2i32_getp_bottom(const r2i32_t *rect, i32 value) { + r2i32_t result = r2i32(rect->min.x, rect->max.y, rect->max.x, rect->max.y + value); + return result; +} +r2i32_t r2i32_getp_top(const r2i32_t *rect, i32 value) { + r2i32_t result = r2i32(rect->min.x, rect->min.y - value, rect->max.x, rect->min.y); + return result; +} + +r2i32_t r2i32_get_left(const r2i32_t *r, i32 value) { + i32 minx = r->min.x; + r2i32_t result = (r2i32_t){ .min = {.x = minx, .y = r->min.y}, .max = {.x = MIN(r->min.x + value, r->max.x), .y =r->max.y} }; + return result; +} +r2i32_t r2i32_get_right(const r2i32_t *r, i32 value) { + i32 maxx = r->max.x; + r2i32_t result = (r2i32_t){ .min = {.x = MAX(r->min.x, r->max.x - value), .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} }; + return result; +} +r2i32_t r2i32_get_top(const r2i32_t *r, i32 value) { /* Y is down */ + i32 miny = r->min.y; + r2i32_t result = (r2i32_t){ .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = MIN(r->max.y, r->min.y + value)} }; + return result; +} +r2i32_t r2i32_get_bottom(const r2i32_t *r, i32 value) { /* Y is down */ + i32 maxy = r->max.y; + r2i32_t result = (r2i32_t){ .min = {.x = r->min.x, .y = MAX(r->min.y, r->max.y - value)}, .max = {.x = r->max.x, .y = maxy} }; + return result; +} + +r2i32_t r2i32_shrink(r2i32_t rect, v2i32_t value) { return (r2i32_t){ v2i32_add(rect.min, value), v2i32_sub(rect.max, value) }; } +v2i32_t r2i32_get_size(r2i32_t r) { return (v2i32_t){r.max.x - r.min.x, r.max.y - r.min.y}; } +v2i32_t r2i32_get_mid(r2i32_t r) { return v2i32_add(r.min, v2i32_divs(r2i32_get_size(r), 2)); } +b32 r2i32_contains(r2i32_t rec, v2i32_t point) { return (point.x >= rec.min.x) && (point.x < rec.max.x) && (point.y >= rec.min.y) && (point.y < rec.max.y); } + +r2i64_t r2i64_cut_left(r2i64_t *r, i64 value) { + i64 minx = r->min.x; + r->min.x = MIN(r->min.x + value, r->max.x); + return (r2i64_t){ .min = {.x = minx, .y = r->min.y}, .max = {.x = r->min.x, .y =r->max.y} }; +} +r2i64_t r2i64_cut_right(r2i64_t *r, i64 value) { + i64 maxx = r->max.x; + r->max.x = MAX(r->min.x, r->max.x - value); + return (r2i64_t){ .min = {.x = r->max.x, .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} }; +} +r2i64_t r2i64_cut_top(r2i64_t *r, i64 value) { /* Y is down */ + i64 miny = r->min.y; + r->min.y = MIN(r->max.y, r->min.y + value); + return (r2i64_t){ .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = r->min.y} }; +} +r2i64_t r2i64_cut_bottom(r2i64_t *r, i64 value) { /* Y is down */ + i64 maxy = r->max.y; + r->max.y = MAX(r->min.y, r->max.y - value); + return (r2i64_t){ .min = {.x = r->min.x, .y = r->max.y}, .max = {.x = r->max.x, .y = maxy} }; +} + +// get past left +r2i64_t r2i64_getp_left(const r2i64_t *rect, i64 value) { + r2i64_t result = r2i64(rect->min.x - value, rect->min.y, rect->min.x, rect->max.y); + return result; +} +r2i64_t r2i64_getp_right(const r2i64_t *rect, i64 value) { + r2i64_t result = r2i64(rect->max.x, rect->min.y, rect->max.x + value, rect->max.y); + return result; +} +r2i64_t r2i64_getp_bottom(const r2i64_t *rect, i64 value) { + r2i64_t result = r2i64(rect->min.x, rect->max.y, rect->max.x, rect->max.y + value); + return result; +} +r2i64_t r2i64_getp_top(const r2i64_t *rect, i64 value) { + r2i64_t result = r2i64(rect->min.x, rect->min.y - value, rect->max.x, rect->min.y); + return result; +} + +r2i64_t r2i64_get_left(const r2i64_t *r, i64 value) { + i64 minx = r->min.x; + r2i64_t result = (r2i64_t){ .min = {.x = minx, .y = r->min.y}, .max = {.x = MIN(r->min.x + value, r->max.x), .y =r->max.y} }; + return result; +} +r2i64_t r2i64_get_right(const r2i64_t *r, i64 value) { + i64 maxx = r->max.x; + r2i64_t result = (r2i64_t){ .min = {.x = MAX(r->min.x, r->max.x - value), .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} }; + return result; +} +r2i64_t r2i64_get_top(const r2i64_t *r, i64 value) { /* Y is down */ + i64 miny = r->min.y; + r2i64_t result = (r2i64_t){ .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = MIN(r->max.y, r->min.y + value)} }; + return result; +} +r2i64_t r2i64_get_bottom(const r2i64_t *r, i64 value) { /* Y is down */ + i64 maxy = r->max.y; + r2i64_t result = (r2i64_t){ .min = {.x = r->min.x, .y = MAX(r->min.y, r->max.y - value)}, .max = {.x = r->max.x, .y = maxy} }; + return result; +} + +r2i64_t r2i64_shrink(r2i64_t rect, v2i64_t value) { return (r2i64_t){ v2i64_add(rect.min, value), v2i64_sub(rect.max, value) }; } +v2i64_t r2i64_get_size(r2i64_t r) { return (v2i64_t){r.max.x - r.min.x, r.max.y - r.min.y}; } +v2i64_t r2i64_get_mid(r2i64_t r) { return v2i64_add(r.min, v2i64_divs(r2i64_get_size(r), 2)); } +b32 r2i64_contains(r2i64_t rec, v2i64_t point) { return (point.x >= rec.min.x) && (point.x < rec.max.x) && (point.y >= rec.min.y) && (point.y < rec.max.y); } + +v2f64_t v2f32_to_v2f64(v2f32_t v) { return (v2f64_t){ (f64)v.x, (f64)v.y }; } +v3f64_t v3f32_to_v3f64(v3f32_t v) { return (v3f64_t){ (f64)v.x, (f64)v.y, (f64)v.z }; } +v4f64_t v4f32_to_v4f64(v4f32_t v) { return (v4f64_t){ (f64)v.x, (f64)v.y, (f64)v.z, (f64)v.w }; } +r2f64_t r2f32_to_r2f64(r2f32_t v) { return (r2f64_t){ (f64)v.x0, (f64)v.y0, (f64)v.x1, (f64)v.y1 }; } +r3f64_t r3f32_to_r3f64(r3f32_t v) { return (r3f64_t){ (f64)v.x0, (f64)v.y0, (f64)v.z0, (f64)v.x1, (f64)v.y1, (f64)v.z1 }; } +v2i32_t v2f32_to_v2i32(v2f32_t v) { return (v2i32_t){ (i32)v.x, (i32)v.y }; } +v3i32_t v3f32_to_v3i32(v3f32_t v) { return (v3i32_t){ (i32)v.x, (i32)v.y, (i32)v.z }; } +v4i32_t v4f32_to_v4i32(v4f32_t v) { return (v4i32_t){ (i32)v.x, (i32)v.y, (i32)v.z, (i32)v.w }; } +r2i32_t r2f32_to_r2i32(r2f32_t v) { return (r2i32_t){ (i32)v.x0, (i32)v.y0, (i32)v.x1, (i32)v.y1 }; } +r3i32_t r3f32_to_r3i32(r3f32_t v) { return (r3i32_t){ (i32)v.x0, (i32)v.y0, (i32)v.z0, (i32)v.x1, (i32)v.y1, (i32)v.z1 }; } +v2i64_t v2f32_to_v2i64(v2f32_t v) { return (v2i64_t){ (i64)v.x, (i64)v.y }; } +v3i64_t v3f32_to_v3i64(v3f32_t v) { return (v3i64_t){ (i64)v.x, (i64)v.y, (i64)v.z }; } +v4i64_t v4f32_to_v4i64(v4f32_t v) { return (v4i64_t){ (i64)v.x, (i64)v.y, (i64)v.z, (i64)v.w }; } +r2i64_t r2f32_to_r2i64(r2f32_t v) { return (r2i64_t){ (i64)v.x0, (i64)v.y0, (i64)v.x1, (i64)v.y1 }; } +r3i64_t r3f32_to_r3i64(r3f32_t v) { return (r3i64_t){ (i64)v.x0, (i64)v.y0, (i64)v.z0, (i64)v.x1, (i64)v.y1, (i64)v.z1 }; } +v2f32_t v2f64_to_v2f32(v2f64_t v) { return (v2f32_t){ (f32)v.x, (f32)v.y }; } +v3f32_t v3f64_to_v3f32(v3f64_t v) { return (v3f32_t){ (f32)v.x, (f32)v.y, (f32)v.z }; } +v4f32_t v4f64_to_v4f32(v4f64_t v) { return (v4f32_t){ (f32)v.x, (f32)v.y, (f32)v.z, (f32)v.w }; } +r2f32_t r2f64_to_r2f32(r2f64_t v) { return (r2f32_t){ (f32)v.x0, (f32)v.y0, (f32)v.x1, (f32)v.y1 }; } +r3f32_t r3f64_to_r3f32(r3f64_t v) { return (r3f32_t){ (f32)v.x0, (f32)v.y0, (f32)v.z0, (f32)v.x1, (f32)v.y1, (f32)v.z1 }; } +v2i32_t v2f64_to_v2i32(v2f64_t v) { return (v2i32_t){ (i32)v.x, (i32)v.y }; } +v3i32_t v3f64_to_v3i32(v3f64_t v) { return (v3i32_t){ (i32)v.x, (i32)v.y, (i32)v.z }; } +v4i32_t v4f64_to_v4i32(v4f64_t v) { return (v4i32_t){ (i32)v.x, (i32)v.y, (i32)v.z, (i32)v.w }; } +r2i32_t r2f64_to_r2i32(r2f64_t v) { return (r2i32_t){ (i32)v.x0, (i32)v.y0, (i32)v.x1, (i32)v.y1 }; } +r3i32_t r3f64_to_r3i32(r3f64_t v) { return (r3i32_t){ (i32)v.x0, (i32)v.y0, (i32)v.z0, (i32)v.x1, (i32)v.y1, (i32)v.z1 }; } +v2i64_t v2f64_to_v2i64(v2f64_t v) { return (v2i64_t){ (i64)v.x, (i64)v.y }; } +v3i64_t v3f64_to_v3i64(v3f64_t v) { return (v3i64_t){ (i64)v.x, (i64)v.y, (i64)v.z }; } +v4i64_t v4f64_to_v4i64(v4f64_t v) { return (v4i64_t){ (i64)v.x, (i64)v.y, (i64)v.z, (i64)v.w }; } +r2i64_t r2f64_to_r2i64(r2f64_t v) { return (r2i64_t){ (i64)v.x0, (i64)v.y0, (i64)v.x1, (i64)v.y1 }; } +r3i64_t r3f64_to_r3i64(r3f64_t v) { return (r3i64_t){ (i64)v.x0, (i64)v.y0, (i64)v.z0, (i64)v.x1, (i64)v.y1, (i64)v.z1 }; } +v2f32_t v2i32_to_v2f32(v2i32_t v) { return (v2f32_t){ (f32)v.x, (f32)v.y }; } +v3f32_t v3i32_to_v3f32(v3i32_t v) { return (v3f32_t){ (f32)v.x, (f32)v.y, (f32)v.z }; } +v4f32_t v4i32_to_v4f32(v4i32_t v) { return (v4f32_t){ (f32)v.x, (f32)v.y, (f32)v.z, (f32)v.w }; } +r2f32_t r2i32_to_r2f32(r2i32_t v) { return (r2f32_t){ (f32)v.x0, (f32)v.y0, (f32)v.x1, (f32)v.y1 }; } +r3f32_t r3i32_to_r3f32(r3i32_t v) { return (r3f32_t){ (f32)v.x0, (f32)v.y0, (f32)v.z0, (f32)v.x1, (f32)v.y1, (f32)v.z1 }; } +v2f64_t v2i32_to_v2f64(v2i32_t v) { return (v2f64_t){ (f64)v.x, (f64)v.y }; } +v3f64_t v3i32_to_v3f64(v3i32_t v) { return (v3f64_t){ (f64)v.x, (f64)v.y, (f64)v.z }; } +v4f64_t v4i32_to_v4f64(v4i32_t v) { return (v4f64_t){ (f64)v.x, (f64)v.y, (f64)v.z, (f64)v.w }; } +r2f64_t r2i32_to_r2f64(r2i32_t v) { return (r2f64_t){ (f64)v.x0, (f64)v.y0, (f64)v.x1, (f64)v.y1 }; } +r3f64_t r3i32_to_r3f64(r3i32_t v) { return (r3f64_t){ (f64)v.x0, (f64)v.y0, (f64)v.z0, (f64)v.x1, (f64)v.y1, (f64)v.z1 }; } +v2i64_t v2i32_to_v2i64(v2i32_t v) { return (v2i64_t){ (i64)v.x, (i64)v.y }; } +v3i64_t v3i32_to_v3i64(v3i32_t v) { return (v3i64_t){ (i64)v.x, (i64)v.y, (i64)v.z }; } +v4i64_t v4i32_to_v4i64(v4i32_t v) { return (v4i64_t){ (i64)v.x, (i64)v.y, (i64)v.z, (i64)v.w }; } +r2i64_t r2i32_to_r2i64(r2i32_t v) { return (r2i64_t){ (i64)v.x0, (i64)v.y0, (i64)v.x1, (i64)v.y1 }; } +r3i64_t r3i32_to_r3i64(r3i32_t v) { return (r3i64_t){ (i64)v.x0, (i64)v.y0, (i64)v.z0, (i64)v.x1, (i64)v.y1, (i64)v.z1 }; } +v2f32_t v2i64_to_v2f32(v2i64_t v) { return (v2f32_t){ (f32)v.x, (f32)v.y }; } +v3f32_t v3i64_to_v3f32(v3i64_t v) { return (v3f32_t){ (f32)v.x, (f32)v.y, (f32)v.z }; } +v4f32_t v4i64_to_v4f32(v4i64_t v) { return (v4f32_t){ (f32)v.x, (f32)v.y, (f32)v.z, (f32)v.w }; } +r2f32_t r2i64_to_r2f32(r2i64_t v) { return (r2f32_t){ (f32)v.x0, (f32)v.y0, (f32)v.x1, (f32)v.y1 }; } +r3f32_t r3i64_to_r3f32(r3i64_t v) { return (r3f32_t){ (f32)v.x0, (f32)v.y0, (f32)v.z0, (f32)v.x1, (f32)v.y1, (f32)v.z1 }; } +v2f64_t v2i64_to_v2f64(v2i64_t v) { return (v2f64_t){ (f64)v.x, (f64)v.y }; } +v3f64_t v3i64_to_v3f64(v3i64_t v) { return (v3f64_t){ (f64)v.x, (f64)v.y, (f64)v.z }; } +v4f64_t v4i64_to_v4f64(v4i64_t v) { return (v4f64_t){ (f64)v.x, (f64)v.y, (f64)v.z, (f64)v.w }; } +r2f64_t r2i64_to_r2f64(r2i64_t v) { return (r2f64_t){ (f64)v.x0, (f64)v.y0, (f64)v.x1, (f64)v.y1 }; } +r3f64_t r3i64_to_r3f64(r3i64_t v) { return (r3f64_t){ (f64)v.x0, (f64)v.y0, (f64)v.z0, (f64)v.x1, (f64)v.y1, (f64)v.z1 }; } +v2i32_t v2i64_to_v2i32(v2i64_t v) { return (v2i32_t){ (i32)v.x, (i32)v.y }; } +v3i32_t v3i64_to_v3i32(v3i64_t v) { return (v3i32_t){ (i32)v.x, (i32)v.y, (i32)v.z }; } +v4i32_t v4i64_to_v4i32(v4i64_t v) { return (v4i32_t){ (i32)v.x, (i32)v.y, (i32)v.z, (i32)v.w }; } +r2i32_t r2i64_to_r2i32(r2i64_t v) { return (r2i32_t){ (i32)v.x0, (i32)v.y0, (i32)v.x1, (i32)v.y1 }; } +r3i32_t r3i64_to_r3i32(r3i64_t v) { return (r3i32_t){ (i32)v.x0, (i32)v.y0, (i32)v.z0, (i32)v.x1, (i32)v.y1, (i32)v.z1 }; } diff --git a/src/core/platform_defines.h b/src/core/platform_defines.h new file mode 100644 index 0000000..35bb87a --- /dev/null +++ b/src/core/platform_defines.h @@ -0,0 +1,69 @@ +#ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS +#endif + +#if defined(__APPLE__) && defined(__MACH__) + #define PLATFORM_MAC_OS 1 + #define PLATFORM_POSIX 1 +#elif defined(_WIN32) + #define PLATFORM_WINDOWS 1 +#elif defined(__linux__) + #define PLATFORM_POSIX 1 + #define PLATFORM_LINUX 1 +#elif defined(__wasm) + #define PLATFORM_WASM 1 +#endif + +#if defined(__clang__) + #define PLATFORM_CLANG 1 +#elif defined(__GNUC__) || defined(__GNUG__) + #define PLATFORM_GCC 1 +#elif defined(_MSC_VER) + #define PLATFORM_CL 1 +#elif defined(__TINYC__) + #define PLATFORM_TCC 1 +#endif + +#ifndef PLATFORM_WASM +#define PLATFORM_WASM 0 +#endif + +#ifndef PLATFORM_WINDOWS +#define PLATFORM_WINDOWS 0 +#endif + +#ifndef PLATFORM_LINUX +#define PLATFORM_LINUX 0 +#endif + +#ifndef PLATFORM_POSIX +#define PLATFORM_POSIX 0 +#endif + +#ifndef PLATFORM_MAC_OS +#define PLATFORM_MAC_OS 0 +#endif + +#ifndef PLATFORM_CLANG +#define PLATFORM_CLANG 0 +#endif + +#ifndef PLATFORM_GCC +#define PLATFORM_GCC 0 +#endif + +#ifndef PLATFORM_CL +#define PLATFORM_CL 0 +#endif + +#ifndef PLATFORM_TCC +#define PLATFORM_TCC 0 +#endif + +#ifndef PLATFORM_ASSERT +#define PLATFORM_ASSERT 1 +#endif + +#ifndef PLATFORM_ADDRESS_SANITIZER +#define PLATFORM_ADDRESS_SANITIZER 0 +#endif diff --git a/src/core/scratch.c b/src/core/scratch.c new file mode 100644 index 0000000..2b0ed0f --- /dev/null +++ b/src/core/scratch.c @@ -0,0 +1,36 @@ +ma_temp_t ma_begin_scratch_ex(ma_arena_t **conflicts, int conflict_count) { + ma_arena_t *unoccupied = 0; + for (int i = 0; i < lengthof(core_desc.scratch); i += 1) { + ma_arena_t *from_pool = core_desc.scratch + i; + unoccupied = from_pool; + for (int conflict_i = 0; conflict_i < conflict_count; conflict_i += 1) { + ma_arena_t *from_conflict = conflicts[conflict_i]; + if (from_pool == from_conflict) { + unoccupied = 0; + break; + } + } + + if (unoccupied) { + break; + } + } + + assertf(unoccupied, "Failed to get free scratch memory, this is a fatal error, this shouldnt happen"); + ma_temp_t result = ma_begin_temp(unoccupied); + return result; +} + +ma_temp_t ma_begin_scratch(void) { + ma_temp_t result = ma_begin_temp(core_desc.scratch + 0); + return result; +} + +ma_temp_t ma_begin_scratch1(ma_arena_t *conflict) { + ma_arena_t *conflicts[] = {conflict}; + return ma_begin_scratch_ex(conflicts, 1); +} + +#define ma_end_scratch(x) ma_end_temp(x) +#define ma_temp_scope(name, InArena) for (ma_temp_t name = ma_begin_temp(InArena); name.arena; (ma_end_temp(name), name.arena = 0)) +#define ma_scratch_scope(x) for (ma_temp_t x = ma_begin_scratch(); x.arena; (ma_end_temp(x), x.arena = 0)) \ No newline at end of file diff --git a/src/core/stb_sprintf.h b/src/core/stb_sprintf.h new file mode 100644 index 0000000..4cc302a --- /dev/null +++ b/src/core/stb_sprintf.h @@ -0,0 +1,1961 @@ +// stb_sprintf - v1.10 - public domain snprintf() implementation +// originally by Jeff Roberts / RAD Game Tools, 2015/10/20 +// http://github.com/nothings/stb +// +// allowed types: sc uidBboXx p AaGgEef n +// lengths : hh h ll j z t I64 I32 I +// +// Contributors: +// Fabian "ryg" Giesen (reformatting) +// github:aganm (attribute format) +// +// Contributors (bugfixes): +// github:d26435 +// github:trex78 +// github:account-login +// Jari Komppa (SI suffixes) +// Rohit Nirmal +// Marcin Wojdyr +// Leonard Ritter +// Stefano Zanotti +// Adam Allison +// Arvid Gerstmann +// Markus Kolb +// +// LICENSE: +// +// See end of file for license information. + +#ifndef STB_SPRINTF_H_INCLUDE +#define STB_SPRINTF_H_INCLUDE + +/* +Single file sprintf replacement. + +Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20. +Hereby placed in public domain. + +This is a full sprintf replacement that supports everything that +the C runtime sprintfs support, including float/double, 64-bit integers, +hex floats, field parameters (%*.*d stuff), length reads backs, etc. + +Why would you need this if sprintf already exists? Well, first off, +it's *much* faster (see below). It's also much smaller than the CRT +versions code-space-wise. We've also added some simple improvements +that are super handy (commas in thousands, callbacks at buffer full, +for example). Finally, the format strings for MSVC and GCC differ +for 64-bit integers (among other small things), so this lets you use +the same format strings in cross platform code. + +It uses the standard single file trick of being both the header file +and the source itself. If you just include it normally, you just get +the header file function definitions. To get the code, you include +it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first. + +It only uses va_args macros from the C runtime to do it's work. It +does cast doubles to S64s and shifts and divides U64s, which does +drag in CRT code on most platforms. + +It compiles to roughly 8K with float support, and 4K without. +As a comparison, when using MSVC static libs, calling sprintf drags +in 16K. + +API: +==== +int stbsp_sprintf( char * buf, char const * fmt, ... ) +int stbsp_snprintf( char * buf, int count, char const * fmt, ... ) + Convert an arg list into a buffer. stbsp_snprintf always returns + a zero-terminated string (unlike regular snprintf). + +int stbsp_vsprintf( char * buf, char const * fmt, va_list va ) +int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va ) + Convert a va_list arg list into a buffer. stbsp_vsnprintf always returns + a zero-terminated string (unlike regular snprintf). + +int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char +const * fmt, va_list va ) typedef char * STBSP_SPRINTFCB( char const * buf, void +* user, int len ); Convert into a buffer, calling back every STB_SPRINTF_MIN +chars. Your callback can then copy the chars out, print them or whatever. This +function is actually the workhorse for everything else. The buffer you pass in +must hold at least STB_SPRINTF_MIN characters. + // you return the next buffer to use or 0 to stop converting + +void stbsp_set_separators( char comma, char period ) + Set the comma and period characters to use. + +FLOATS/DOUBLES: +=============== +This code uses a internal float->ascii conversion method that uses +doubles with error correction (double-doubles, for ~105 bits of +precision). This conversion is round-trip perfect - that is, an atof +of the values output here will give you the bit-exact double back. + +One difference is that our insignificant digits will be different than +with MSVC or GCC (but they don't match each other either). We also +don't attempt to find the minimum length matching float (pre-MSVC15 +doesn't either). + +If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT +and you'll save 4K of code space. + +64-BIT INTS: +============ +This library also supports 64-bit integers and you can use MSVC style or +GCC style indicators (%I64d or %lld). It supports the C99 specifiers +for size_t and ptr_diff_t (%jd %zd) as well. + +EXTRAS: +======= +Like some GCCs, for integers and floats, you can use a ' (single quote) +specifier and commas will be inserted on the thousands: "%'d" on 12345 +would print 12,345. + +For integers and floats, you can use a "$" specifier and the number +will be converted to float and then divided to get kilo, mega, giga or +tera and then printed, so "%$d" 1000 is "1.0 k", "%$.2d" 2536000 is +"2.53 M", etc. For byte values, use two $:s, like "%$$d" to turn +2536000 to "2.42 Mi". If you prefer JEDEC suffixes to SI ones, use three +$:s: "%$$$d" -> "2.42 M". To remove the space between the number and the +suffix, add "_" specifier: "%_$d" -> "2.53M". + +In addition to octal and hexadecimal conversions, you can print +integers in binary: "%b" for 256 would print 100. + +PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC): +=================================================================== +"%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC) +"%24d" across all 32-bit ints (4.5x/4.2x faster) +"%x" across all 32-bit ints (4.5x/3.8x faster) +"%08x" across all 32-bit ints (4.3x/3.8x faster) +"%f" across e-10 to e+10 floats (7.3x/6.0x faster) +"%e" across e-10 to e+10 floats (8.1x/6.0x faster) +"%g" across e-10 to e+10 floats (10.0x/7.1x faster) +"%f" for values near e-300 (7.9x/6.5x faster) +"%f" for values near e+300 (10.0x/9.1x faster) +"%e" for values near e-300 (10.1x/7.0x faster) +"%e" for values near e+300 (9.2x/6.0x faster) +"%.320f" for values near e-300 (12.6x/11.2x faster) +"%a" for random values (8.6x/4.3x faster) +"%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster) +"%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster) +"%s%s%s" for 64 char strings (7.1x/7.3x faster) +"...512 char string..." ( 35.0x/32.5x faster!) +*/ + +#if defined(__clang__) +#if defined(__has_feature) && defined(__has_attribute) +#if __has_feature(address_sanitizer) +#if __has_attribute(__no_sanitize__) +#define STBSP__ASAN __attribute__((__no_sanitize__("address"))) +#elif __has_attribute(__no_sanitize_address__) +#define STBSP__ASAN __attribute__((__no_sanitize_address__)) +#elif __has_attribute(__no_address_safety_analysis__) +#define STBSP__ASAN __attribute__((__no_address_safety_analysis__)) +#endif +#endif +#endif +#elif defined(__GNUC__) && \ + (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) +#if defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__ +#define STBSP__ASAN __attribute__((__no_sanitize_address__)) +#endif +#endif + +#ifndef STBSP__ASAN +#define STBSP__ASAN +#endif + +#ifdef STB_SPRINTF_STATIC +#define STBSP__PUBLICDEC static +#define STBSP__PUBLICDEF static STBSP__ASAN +#else +#ifdef __cplusplus +#define STBSP__PUBLICDEC extern "C" +#define STBSP__PUBLICDEF extern "C" STBSP__ASAN +#else +#define STBSP__PUBLICDEC extern +#define STBSP__PUBLICDEF STBSP__ASAN +#endif +#endif + +#if defined(__has_attribute) +#if __has_attribute(format) +#define STBSP__ATTRIBUTE_FORMAT(fmt, va) \ + __attribute__((format(printf, fmt, va))) +#endif +#endif + +#ifndef STBSP__ATTRIBUTE_FORMAT +#define STBSP__ATTRIBUTE_FORMAT(fmt, va) +#endif + +#ifdef _MSC_VER +#define STBSP__NOTUSED(v) (void)(v) +#else +#define STBSP__NOTUSED(v) (void)sizeof(v) +#endif + +#include // for va_arg(), va_list() +#include // size_t, ptrdiff_t + +#ifndef STB_SPRINTF_MIN +#define STB_SPRINTF_MIN 512 // how many characters per callback +#endif +typedef char *STBSP_SPRINTFCB(const char *buf, void *user, int len); + +#ifndef STB_SPRINTF_DECORATE +#define STB_SPRINTF_DECORATE(name) \ + stbsp_##name // define this before including if you want to change the names +#endif + +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, + va_list va); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, + char const *fmt, + va_list va); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, + ...) + STBSP__ATTRIBUTE_FORMAT(2, 3); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, + char const *fmt, ...) + STBSP__ATTRIBUTE_FORMAT(3, 4); + +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, + void *user, char *buf, + char const *fmt, + va_list va); +STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, + char period); + +#endif // STB_SPRINTF_H_INCLUDE + +#ifdef STB_SPRINTF_IMPLEMENTATION + +#define stbsp__uint32 unsigned int +#define stbsp__int32 signed int + +#ifdef _MSC_VER +#define stbsp__uint64 unsigned __int64 +#define stbsp__int64 signed __int64 +#else +#define stbsp__uint64 unsigned long long +#define stbsp__int64 signed long long +#endif +#define stbsp__uint16 unsigned short + +#ifndef stbsp__uintptr +#if defined(__ppc64__) || defined(__powerpc64__) || defined(__aarch64__) || \ + defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) || \ + defined(__s390x__) +#define stbsp__uintptr stbsp__uint64 +#else +#define stbsp__uintptr stbsp__uint32 +#endif +#endif + +#ifndef STB_SPRINTF_MSVC_MODE // used for MSVC2013 and earlier (MSVC2015 matches + // GCC) +#if defined(_MSC_VER) && (_MSC_VER < 1900) +#define STB_SPRINTF_MSVC_MODE +#endif +#endif + +#ifdef STB_SPRINTF_NOUNALIGNED // define this before inclusion to force + // stbsp_sprintf to always use aligned accesses +#define STBSP__UNALIGNED(code) +#else +#define STBSP__UNALIGNED(code) code +#endif + +#ifndef STB_SPRINTF_NOFLOAT +// internal float utility functions +static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, + char *out, stbsp__int32 *decimal_pos, + double value, stbsp__uint32 frac_digits); +static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, + double value); +#define STBSP__SPECIAL 0x7000 +#endif + +static char stbsp__period = '.'; +static char stbsp__comma = ','; +static struct { + short temp; // force next field to be 2-byte aligned + char pair[201]; +} stbsp__digitpair = {0, "00010203040506070809101112131415161718192021222324" + "25262728293031323334353637383940414243444546474849" + "50515253545556575859606162636465666768697071727374" + "75767778798081828384858687888990919293949596979899"}; + +STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char pcomma, + char pperiod) { + stbsp__period = pperiod; + stbsp__comma = pcomma; +} + +#define STBSP__LEFTJUST 1 +#define STBSP__LEADINGPLUS 2 +#define STBSP__LEADINGSPACE 4 +#define STBSP__LEADING_0X 8 +#define STBSP__LEADINGZERO 16 +#define STBSP__INTMAX 32 +#define STBSP__TRIPLET_COMMA 64 +#define STBSP__NEGATIVE 128 +#define STBSP__METRIC_SUFFIX 256 +#define STBSP__HALFWIDTH 512 +#define STBSP__METRIC_NOSPACE 1024 +#define STBSP__METRIC_1024 2048 +#define STBSP__METRIC_JEDEC 4096 + +static void stbsp__lead_sign(stbsp__uint32 fl, char *sign) { + sign[0] = 0; + if (fl & STBSP__NEGATIVE) { + sign[0] = 1; + sign[1] = '-'; + } else if (fl & STBSP__LEADINGSPACE) { + sign[0] = 1; + sign[1] = ' '; + } else if (fl & STBSP__LEADINGPLUS) { + sign[0] = 1; + sign[1] = '+'; + } +} + +static STBSP__ASAN stbsp__uint32 stbsp__strlen_limited(char const *s, + stbsp__uint32 limit) { + char const *sn = s; + + // get up to 4-byte alignment + for (;;) { + if (((stbsp__uintptr)sn & 3) == 0) + break; + + if (!limit || *sn == 0) + return (stbsp__uint32)(sn - s); + + ++sn; + --limit; + } + + // scan over 4 bytes at a time to find terminating 0 + // this will intentionally scan up to 3 bytes past the end of buffers, + // but becase it works 4B aligned, it will never cross page boundaries + // (hence the STBSP__ASAN markup; the over-read here is intentional + // and harmless) + while (limit >= 4) { + stbsp__uint32 v = *(stbsp__uint32 *)sn; + // bit hack to find if there's a 0 byte in there + if ((v - 0x01010101) & (~v) & 0x80808080UL) + break; + + sn += 4; + limit -= 4; + } + + // handle the last few characters to find actual size + while (limit && *sn) { + ++sn; + --limit; + } + + return (stbsp__uint32)(sn - s); +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, + void *user, char *buf, + char const *fmt, + va_list va) { + static char hex[] = "0123456789abcdefxp"; + static char hexu[] = "0123456789ABCDEFXP"; + char *bf; + char const *f; + int tlen = 0; + + bf = buf; + f = fmt; + for (;;) { + stbsp__int32 fw, pr, tz; + stbsp__uint32 fl; + + // macros for the callback buffer stuff +#define stbsp__chk_cb_bufL(bytes) \ + { \ + int len = (int)(bf - buf); \ + if ((len + (bytes)) >= STB_SPRINTF_MIN) { \ + tlen += len; \ + if (0 == (bf = buf = callback(buf, user, len))) \ + goto done; \ + } \ + } +#define stbsp__chk_cb_buf(bytes) \ + { \ + if (callback) { \ + stbsp__chk_cb_bufL(bytes); \ + } \ + } +#define stbsp__flush_cb() \ + { \ + stbsp__chk_cb_bufL(STB_SPRINTF_MIN - 1); \ + } // flush if there is even one byte in the buffer +#define stbsp__cb_buf_clamp(cl, v) \ + cl = v; \ + if (callback) { \ + int lg = STB_SPRINTF_MIN - (int)(bf - buf); \ + if (cl > lg) \ + cl = lg; \ + } + + // fast copy everything up to the next % (or end of string) + for (;;) { + while (((stbsp__uintptr)f) & 3) { + schk1: + if (f[0] == '%') + goto scandd; + schk2: + if (f[0] == 0) + goto endfmt; + stbsp__chk_cb_buf(1); + *bf++ = f[0]; + ++f; + } + for (;;) { + // Check if the next 4 bytes contain %(0x25) or end of string. + // Using the 'hasless' trick: + // https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord + stbsp__uint32 v, c; + v = *(stbsp__uint32 *)f; + c = (~v) & 0x80808080; + if (((v ^ 0x25252525) - 0x01010101) & c) + goto schk1; + if ((v - 0x01010101) & c) + goto schk2; + if (callback) + if ((STB_SPRINTF_MIN - (int)(bf - buf)) < 4) + goto schk1; +#ifdef STB_SPRINTF_NOUNALIGNED + if (((stbsp__uintptr)bf) & 3) { + bf[0] = f[0]; + bf[1] = f[1]; + bf[2] = f[2]; + bf[3] = f[3]; + } else +#endif + { + *(stbsp__uint32 *)bf = v; + } + bf += 4; + f += 4; + } + } + scandd: + + ++f; + + // ok, we have a percent, read the modifiers first + fw = 0; + pr = -1; + fl = 0; + tz = 0; + + // flags + for (;;) { + switch (f[0]) { + // if we have left justify + case '-': + fl |= STBSP__LEFTJUST; + ++f; + continue; + // if we have leading plus + case '+': + fl |= STBSP__LEADINGPLUS; + ++f; + continue; + // if we have leading space + case ' ': + fl |= STBSP__LEADINGSPACE; + ++f; + continue; + // if we have leading 0x + case '#': + fl |= STBSP__LEADING_0X; + ++f; + continue; + // if we have thousand commas + case '\'': + fl |= STBSP__TRIPLET_COMMA; + ++f; + continue; + // if we have kilo marker (none->kilo->kibi->jedec) + case '$': + if (fl & STBSP__METRIC_SUFFIX) { + if (fl & STBSP__METRIC_1024) { + fl |= STBSP__METRIC_JEDEC; + } else { + fl |= STBSP__METRIC_1024; + } + } else { + fl |= STBSP__METRIC_SUFFIX; + } + ++f; + continue; + // if we don't want space between metric suffix and number + case '_': + fl |= STBSP__METRIC_NOSPACE; + ++f; + continue; + // if we have leading zero + case '0': + fl |= STBSP__LEADINGZERO; + ++f; + goto flags_done; + default: + goto flags_done; + } + } + flags_done: + + // get the field width + if (f[0] == '*') { + fw = va_arg(va, stbsp__uint32); + ++f; + } else { + while ((f[0] >= '0') && (f[0] <= '9')) { + fw = fw * 10 + f[0] - '0'; + f++; + } + } + // get the precision + if (f[0] == '.') { + ++f; + if (f[0] == '*') { + pr = va_arg(va, stbsp__uint32); + ++f; + } else { + pr = 0; + while ((f[0] >= '0') && (f[0] <= '9')) { + pr = pr * 10 + f[0] - '0'; + f++; + } + } + } + + // handle integer size overrides + switch (f[0]) { + // are we halfwidth? + case 'h': + fl |= STBSP__HALFWIDTH; + ++f; + if (f[0] == 'h') + ++f; // QUARTERWIDTH + break; + // are we 64-bit (unix style) + case 'l': + fl |= ((sizeof(long) == 8) ? STBSP__INTMAX : 0); + ++f; + if (f[0] == 'l') { + fl |= STBSP__INTMAX; + ++f; + } + break; + // are we 64-bit on intmax? (c99) + case 'j': + fl |= (sizeof(size_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + // are we 64-bit on size_t or ptrdiff_t? (c99) + case 'z': + fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + case 't': + fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + // are we 64-bit (msft style) + case 'I': + if ((f[1] == '6') && (f[2] == '4')) { + fl |= STBSP__INTMAX; + f += 3; + } else if ((f[1] == '3') && (f[2] == '2')) { + f += 3; + } else { + fl |= ((sizeof(void *) == 8) ? STBSP__INTMAX : 0); + ++f; + } + break; + default: + break; + } + + // handle each replacement + switch (f[0]) { +#define STBSP__NUMSZ 512 // big enough for e308 (with commas) or e-307 + char num[STBSP__NUMSZ]; + char lead[8]; + char tail[8]; + char *s; + char const *h; + stbsp__uint32 l, n, cs; + stbsp__uint64 n64; +#ifndef STB_SPRINTF_NOFLOAT + double fv; +#endif + stbsp__int32 dp; + char const *sn; + struct STB_STRING { char *str; int64_t len; }; + struct STB_STRING str; + + case 'S': + str = va_arg(va, struct STB_STRING); + if (str.str == 0 && str.len != 0) { + str.str = (char *)"null"; + str.len = 4; + } + pr = (int)str.len; + s = (char *)str.str; + l = stbsp__strlen_limited(s, (pr >= 0) ? pr : ~0u); + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + goto scopy; + + case 's': + // get the string + s = va_arg(va, char *); + if (s == 0) + s = (char *)"null"; + // get the length, limited to desired precision + // always limit to ~0u chars since our counts are 32b + l = stbsp__strlen_limited(s, (pr >= 0) ? pr : ~0u); + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + // copy the string in + goto scopy; + + case 'c': // char + // get the character + s = num + STBSP__NUMSZ - 1; + *s = (char)va_arg(va, int); + l = 1; + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + goto scopy; + + case 'n': // weird write-bytes specifier + { + int *d = va_arg(va, int *); + *d = tlen + (int)(bf - buf); + } break; + +#ifdef STB_SPRINTF_NOFLOAT + case 'A': // float + case 'a': // hex float + case 'G': // float + case 'g': // float + case 'E': // float + case 'e': // float + case 'f': // float + va_arg(va, double); // eat it + s = (char *)"No float"; + l = 8; + lead[0] = 0; + tail[0] = 0; + pr = 0; + cs = 0; + STBSP__NOTUSED(dp); + goto scopy; +#else + case 'A': // hex float + case 'a': // hex float + h = (f[0] == 'A') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_parts((stbsp__int64 *)&n64, &dp, fv)) + fl |= STBSP__NEGATIVE; + + s = num + 64; + + stbsp__lead_sign(fl, lead); + + if (dp == -1023) + dp = (n64) ? -1022 : 0; + else + n64 |= (((stbsp__uint64)1) << 52); + n64 <<= (64 - 56); + if (pr < 15) + n64 += ((((stbsp__uint64)8) << 56) >> (pr * 4)); + // add leading chars + +#ifdef STB_SPRINTF_MSVC_MODE + *s++ = '0'; + *s++ = 'x'; +#else + lead[1 + lead[0]] = '0'; + lead[2 + lead[0]] = 'x'; + lead[0] += 2; +#endif + *s++ = h[(n64 >> 60) & 15]; + n64 <<= 4; + if (pr) + *s++ = stbsp__period; + sn = s; + + // print the bits + n = pr; + if (n > 13) + n = 13; + if (pr > (stbsp__int32)n) + tz = pr - n; + pr = 0; + while (n--) { + *s++ = h[(n64 >> 60) & 15]; + n64 <<= 4; + } + + // print the expo + tail[1] = h[17]; + if (dp < 0) { + tail[2] = '-'; + dp = -dp; + } else + tail[2] = '+'; + n = (dp >= 1000) ? 6 : ((dp >= 100) ? 5 : ((dp >= 10) ? 4 : 3)); + tail[0] = (char)n; + for (;;) { + tail[n] = '0' + dp % 10; + if (n <= 3) + break; + --n; + dp /= 10; + } + + dp = (int)(s - sn); + l = (int)(s - (num + 64)); + s = num + 64; + cs = 1 + (3 << 24); + goto scopy; + + case 'G': // float + case 'g': // float + h = (f[0] == 'G') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; + else if (pr == 0) + pr = 1; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, (pr - 1) | 0x80000000)) + fl |= STBSP__NEGATIVE; + + // clamp the precision and delete extra zeros after clamp + n = pr; + if (l > (stbsp__uint32)pr) + l = pr; + while ((l > 1) && (pr) && (sn[l - 1] == '0')) { + --pr; + --l; + } + + // should we use %e + if ((dp <= -4) || (dp > (stbsp__int32)n)) { + if (pr > (stbsp__int32)l) + pr = l - 1; + else if (pr) + --pr; // when using %e, there is one digit before the decimal + goto doexpfromg; + } + // this is the insane action to get the pr to match %g semantics for %f + if (dp > 0) { + pr = (dp < (stbsp__int32)l) ? l - dp : 0; + } else { + pr = -dp + ((pr > (stbsp__int32)l) ? (stbsp__int32)l : pr); + } + goto dofloatfromg; + + case 'E': // float + case 'e': // float + h = (f[0] == 'E') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr | 0x80000000)) + fl |= STBSP__NEGATIVE; + doexpfromg: + tail[0] = 0; + stbsp__lead_sign(fl, lead); + if (dp == STBSP__SPECIAL) { + s = (char *)sn; + cs = 0; + pr = 0; + goto scopy; + } + s = num + 64; + // handle leading chars + *s++ = sn[0]; + + if (pr) + *s++ = stbsp__period; + + // handle after decimal + if ((l - 1) > (stbsp__uint32)pr) + l = pr + 1; + for (n = 1; n < l; n++) + *s++ = sn[n]; + // trailing zeros + tz = pr - (l - 1); + pr = 0; + // dump expo + tail[1] = h[0xe]; + dp -= 1; + if (dp < 0) { + tail[2] = '-'; + dp = -dp; + } else + tail[2] = '+'; +#ifdef STB_SPRINTF_MSVC_MODE + n = 5; +#else + n = (dp >= 100) ? 5 : 4; +#endif + tail[0] = (char)n; + for (;;) { + tail[n] = '0' + dp % 10; + if (n <= 3) + break; + --n; + dp /= 10; + } + cs = 1 + (3 << 24); // how many tens + goto flt_lead; + + case 'f': // float + fv = va_arg(va, double); + doafloat: + // do kilos + if (fl & STBSP__METRIC_SUFFIX) { + double divisor; + divisor = 1000.0f; + if (fl & STBSP__METRIC_1024) + divisor = 1024.0; + while (fl < 0x4000000) { + if ((fv < divisor) && (fv > -divisor)) + break; + fv /= divisor; + fl += 0x1000000; + } + } + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr)) + fl |= STBSP__NEGATIVE; + dofloatfromg: + tail[0] = 0; + stbsp__lead_sign(fl, lead); + if (dp == STBSP__SPECIAL) { + s = (char *)sn; + cs = 0; + pr = 0; + goto scopy; + } + s = num + 64; + + // handle the three decimal varieties + if (dp <= 0) { + stbsp__int32 i; + // handle 0.000*000xxxx + *s++ = '0'; + if (pr) + *s++ = stbsp__period; + n = -dp; + if ((stbsp__int32)n > pr) + n = pr; + i = n; + while (i) { + if ((((stbsp__uintptr)s) & 3) == 0) + break; + *s++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)s = 0x30303030; + s += 4; + i -= 4; + } + while (i) { + *s++ = '0'; + --i; + } + if ((stbsp__int32)(l + n) > pr) + l = pr - n; + i = l; + while (i) { + *s++ = *sn++; + --i; + } + tz = pr - (n + l); + cs = 1 + (3 << 24); // how many tens did we write (for commas below) + } else { + cs = (fl & STBSP__TRIPLET_COMMA) ? ((600 - (stbsp__uint32)dp) % 3) : 0; + if ((stbsp__uint32)dp >= l) { + // handle xxxx000*000.0 + n = 0; + for (;;) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } else { + *s++ = sn[n]; + ++n; + if (n >= l) + break; + } + } + if (n < (stbsp__uint32)dp) { + n = dp - n; + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + while (n) { + if ((((stbsp__uintptr)s) & 3) == 0) + break; + *s++ = '0'; + --n; + } + while (n >= 4) { + *(stbsp__uint32 *)s = 0x30303030; + s += 4; + n -= 4; + } + } + while (n) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } else { + *s++ = '0'; + --n; + } + } + } + cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens + if (pr) { + *s++ = stbsp__period; + tz = pr; + } + } else { + // handle xxxxx.xxxx000*000 + n = 0; + for (;;) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } else { + *s++ = sn[n]; + ++n; + if (n >= (stbsp__uint32)dp) + break; + } + } + cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens + if (pr) + *s++ = stbsp__period; + if ((l - dp) > (stbsp__uint32)pr) + l = pr + dp; + while (n < l) { + *s++ = sn[n]; + ++n; + } + tz = pr - (l - dp); + } + } + pr = 0; + + // handle k,m,g,t + if (fl & STBSP__METRIC_SUFFIX) { + char idx; + idx = 1; + if (fl & STBSP__METRIC_NOSPACE) + idx = 0; + tail[0] = idx; + tail[1] = ' '; + { + if (fl >> 24) { // SI kilo is 'k', JEDEC and SI kibits are 'K'. + if (fl & STBSP__METRIC_1024) + tail[idx + 1] = "_KMGT"[fl >> 24]; + else + tail[idx + 1] = "_kMGT"[fl >> 24]; + idx++; + // If printing kibits and not in jedec, add the 'i'. + if (fl & STBSP__METRIC_1024 && !(fl & STBSP__METRIC_JEDEC)) { + tail[idx + 1] = 'i'; + idx++; + } + tail[0] = idx; + } + } + }; + + flt_lead: + // get the length that we copied + l = (stbsp__uint32)(s - (num + 64)); + s = num + 64; + goto scopy; +#endif + + case 'B': // upper binary + case 'b': // lower binary + h = (f[0] == 'B') ? hexu : hex; + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 2; + lead[1] = '0'; + lead[2] = h[0xb]; + } + l = (8 << 4) | (1 << 8); + goto radixnum; + + case 'o': // octal + h = hexu; + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 1; + lead[1] = '0'; + } + l = (3 << 4) | (3 << 8); + goto radixnum; + + case 'p': // pointer + fl |= (sizeof(void *) == 8) ? STBSP__INTMAX : 0; + pr = sizeof(void *) * 2; + fl &= ~STBSP__LEADINGZERO; // 'p' only prints the pointer with zeros + // fall through - to X + + case 'X': // upper hex + case 'x': // lower hex + h = (f[0] == 'X') ? hexu : hex; + l = (4 << 4) | (4 << 8); + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 2; + lead[1] = '0'; + lead[2] = h[16]; + } + radixnum: + // get the number + if (fl & STBSP__INTMAX) + n64 = va_arg(va, stbsp__uint64); + else + n64 = va_arg(va, stbsp__uint32); + + s = num + STBSP__NUMSZ; + dp = 0; + // clear tail, and clear leading if value is zero + tail[0] = 0; + if (n64 == 0) { + lead[0] = 0; + if (pr == 0) { + l = 0; + cs = 0; + goto scopy; + } + } + // convert to string + for (;;) { + *--s = h[n64 & ((1 << (l >> 8)) - 1)]; + n64 >>= (l >> 8); + if (!((n64) || ((stbsp__int32)((num + STBSP__NUMSZ) - s) < pr))) + break; + if (fl & STBSP__TRIPLET_COMMA) { + ++l; + if ((l & 15) == ((l >> 4) & 15)) { + l &= ~15; + *--s = stbsp__comma; + } + } + }; + // get the tens and the comma pos + cs = + (stbsp__uint32)((num + STBSP__NUMSZ) - s) + ((((l >> 4) & 15)) << 24); + // get the length that we copied + l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); + // copy it + goto scopy; + + case 'u': // unsigned + case 'i': + case 'd': // integer + // get the integer and abs it + if (fl & STBSP__INTMAX) { + stbsp__int64 i64 = va_arg(va, stbsp__int64); + n64 = (stbsp__uint64)i64; + if ((f[0] != 'u') && (i64 < 0)) { + n64 = (stbsp__uint64)-i64; + fl |= STBSP__NEGATIVE; + } + } else { + stbsp__int32 i = va_arg(va, stbsp__int32); + n64 = (stbsp__uint32)i; + if ((f[0] != 'u') && (i < 0)) { + n64 = (stbsp__uint32)-i; + fl |= STBSP__NEGATIVE; + } + } + +#ifndef STB_SPRINTF_NOFLOAT + if (fl & STBSP__METRIC_SUFFIX) { + if (n64 < 1024) + pr = 0; + else if (pr == -1) + pr = 1; + fv = (double)(stbsp__int64)n64; + goto doafloat; + } +#endif + + // convert to string + s = num + STBSP__NUMSZ; + l = 0; + + for (;;) { + // do in 32-bit chunks (avoid lots of 64-bit divides even with constant + // denominators) + char *o = s - 8; + if (n64 >= 100000000) { + n = (stbsp__uint32)(n64 % 100000000); + n64 /= 100000000; + } else { + n = (stbsp__uint32)n64; + n64 = 0; + } + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + do { + s -= 2; + *(stbsp__uint16 *)s = + *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; + n /= 100; + } while (n); + } + while (n) { + if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { + l = 0; + *--s = stbsp__comma; + --o; + } else { + *--s = (char)(n % 10) + '0'; + n /= 10; + } + } + if (n64 == 0) { + if ((s[0] == '0') && (s != (num + STBSP__NUMSZ))) + ++s; + break; + } + while (s != o) + if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { + l = 0; + *--s = stbsp__comma; + --o; + } else { + *--s = '0'; + } + } + + tail[0] = 0; + stbsp__lead_sign(fl, lead); + + // get the length that we copied + l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); + if (l == 0) { + *--s = '0'; + l = 1; + } + cs = l + (3 << 24); + if (pr < 0) + pr = 0; + + scopy: + // get fw=leading/trailing space, pr=leading zeros + if (pr < (stbsp__int32)l) + pr = l; + n = pr + lead[0] + tail[0] + tz; + if (fw < (stbsp__int32)n) + fw = n; + fw -= n; + pr -= l; + + // handle right justify and leading zeros + if ((fl & STBSP__LEFTJUST) == 0) { + if (fl & STBSP__LEADINGZERO) // if leading zeros, everything is in pr + { + pr = (fw > pr) ? fw : pr; + fw = 0; + } else { + fl &= ~STBSP__TRIPLET_COMMA; // if no leading zeros, then no commas + } + } + + // copy the spaces and/or zeros + if (fw + pr) { + stbsp__int32 i; + stbsp__uint32 c; + + // copy leading spaces (or when doing %8.4d stuff) + if ((fl & STBSP__LEFTJUST) == 0) + while (fw > 0) { + stbsp__cb_buf_clamp(i, fw); + fw -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = ' '; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x20202020; + bf += 4; + i -= 4; + } + while (i) { + *bf++ = ' '; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy leader + sn = lead + 1; + while (lead[0]) { + stbsp__cb_buf_clamp(i, lead[0]); + lead[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy leading zeros + c = cs >> 24; + cs &= 0xffffff; + cs = (fl & STBSP__TRIPLET_COMMA) + ? ((stbsp__uint32)(c - ((pr + cs) % (c + 1)))) + : 0; + while (pr > 0) { + stbsp__cb_buf_clamp(i, pr); + pr -= i; + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x30303030; + bf += 4; + i -= 4; + } + } + while (i) { + if ((fl & STBSP__TRIPLET_COMMA) && (cs++ == c)) { + cs = 0; + *bf++ = stbsp__comma; + } else + *bf++ = '0'; + --i; + } + stbsp__chk_cb_buf(1); + } + } + + // copy leader if there is still one + sn = lead + 1; + while (lead[0]) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, lead[0]); + lead[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy the string + n = l; + while (n) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, n); + n -= i; + STBSP__UNALIGNED(while (i >= 4) { + *(stbsp__uint32 volatile *)bf = *(stbsp__uint32 volatile *)s; + bf += 4; + s += 4; + i -= 4; + }) + while (i) { + *bf++ = *s++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy trailing zeros + while (tz) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, tz); + tz -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x30303030; + bf += 4; + i -= 4; + } + while (i) { + *bf++ = '0'; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy tail if there is one + sn = tail + 1; + while (tail[0]) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, tail[0]); + tail[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // handle the left justify + if (fl & STBSP__LEFTJUST) + if (fw > 0) { + while (fw) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, fw); + fw -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = ' '; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x20202020; + bf += 4; + i -= 4; + } + while (i--) + *bf++ = ' '; + stbsp__chk_cb_buf(1); + } + } + break; + + default: // unknown, just copy code + s = num + STBSP__NUMSZ - 1; + *s = f[0]; + l = 1; + fw = fl = 0; + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + goto scopy; + } + ++f; + } +endfmt: + + if (!callback) + *bf = 0; + else + stbsp__flush_cb(); + +done: + return tlen + (int)(bf - buf); +} + +// cleanup +#undef STBSP__LEFTJUST +#undef STBSP__LEADINGPLUS +#undef STBSP__LEADINGSPACE +#undef STBSP__LEADING_0X +#undef STBSP__LEADINGZERO +#undef STBSP__INTMAX +#undef STBSP__TRIPLET_COMMA +#undef STBSP__NEGATIVE +#undef STBSP__METRIC_SUFFIX +#undef STBSP__NUMSZ +#undef stbsp__chk_cb_bufL +#undef stbsp__chk_cb_buf +#undef stbsp__flush_cb +#undef stbsp__cb_buf_clamp + +// ============================================================================ +// wrapper functions + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, + ...) { + int result; + va_list va; + va_start(va, fmt); + result = STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); + va_end(va); + return result; +} + +typedef struct stbsp__context { + char *buf; + int count; + int length; + char tmp[STB_SPRINTF_MIN]; +} stbsp__context; + +static char *stbsp__clamp_callback(const char *buf, void *user, int len) { + stbsp__context *c = (stbsp__context *)user; + c->length += len; + + if (len > c->count) + len = c->count; + + if (len) { + if (buf != c->buf) { + const char *s, *se; + char *d; + d = c->buf; + s = buf; + se = buf + len; + do { + *d++ = *s++; + } while (s < se); + } + c->buf += len; + c->count -= len; + } + + if (c->count <= 0) + return c->tmp; + return (c->count >= STB_SPRINTF_MIN) + ? c->buf + : c->tmp; // go direct into buffer if you can +} + +static char *stbsp__count_clamp_callback(const char *buf, void *user, int len) { + stbsp__context *c = (stbsp__context *)user; + (void)sizeof(buf); + + c->length += len; + return c->tmp; // go direct into buffer if you can +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, + char const *fmt, + va_list va) { + stbsp__context c; + + if ((count == 0) && !buf) { + c.length = 0; + + STB_SPRINTF_DECORATE(vsprintfcb) + (stbsp__count_clamp_callback, &c, c.tmp, fmt, va); + } else { + int l; + + c.buf = buf; + c.count = count; + c.length = 0; + + STB_SPRINTF_DECORATE(vsprintfcb) + (stbsp__clamp_callback, &c, stbsp__clamp_callback(0, &c, 0), fmt, va); + + // zero-terminate + l = (int)(c.buf - buf); + if (l >= count) // should never be greater, only equal (or less) than count + l = count - 1; + buf[l] = 0; + } + + return c.length; +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, + char const *fmt, ...) { + int result; + va_list va; + va_start(va, fmt); + + result = STB_SPRINTF_DECORATE(vsnprintf)(buf, count, fmt, va); + va_end(va); + + return result; +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, + va_list va) { + return STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); +} + +// ======================================================================= +// low level float utility functions + +#ifndef STB_SPRINTF_NOFLOAT + +// copies d to bits w/ strict aliasing (this compiles to nothing on /Ox) +#define STBSP__COPYFP(dest, src) \ + { \ + int cn; \ + for (cn = 0; cn < 8; cn++) \ + ((char *)&dest)[cn] = ((char *)&src)[cn]; \ + } + +// get float info +static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, + double value) { + double d; + stbsp__int64 b = 0; + + // load value and round at the frac_digits + d = value; + + STBSP__COPYFP(b, d); + + *bits = b & ((((stbsp__uint64)1) << 52) - 1); + *expo = (stbsp__int32)(((b >> 52) & 2047) - 1023); + + return (stbsp__int32)((stbsp__uint64)b >> 63); +} + +static double const stbsp__bot[23] = { + 1e+000, 1e+001, 1e+002, 1e+003, 1e+004, 1e+005, 1e+006, 1e+007, + 1e+008, 1e+009, 1e+010, 1e+011, 1e+012, 1e+013, 1e+014, 1e+015, + 1e+016, 1e+017, 1e+018, 1e+019, 1e+020, 1e+021, 1e+022}; +static double const stbsp__negbot[22] = { + 1e-001, 1e-002, 1e-003, 1e-004, 1e-005, 1e-006, 1e-007, 1e-008, + 1e-009, 1e-010, 1e-011, 1e-012, 1e-013, 1e-014, 1e-015, 1e-016, + 1e-017, 1e-018, 1e-019, 1e-020, 1e-021, 1e-022}; +static double const stbsp__negboterr[22] = { + -5.551115123125783e-018, -2.0816681711721684e-019, + -2.0816681711721686e-020, -4.7921736023859299e-021, + -8.1803053914031305e-022, 4.5251888174113741e-023, + 4.5251888174113739e-024, -2.0922560830128471e-025, + -6.2281591457779853e-026, -3.6432197315497743e-027, + 6.0503030718060191e-028, 2.0113352370744385e-029, + -3.0373745563400371e-030, 1.1806906454401013e-032, + -7.7705399876661076e-032, 2.0902213275965398e-033, + -7.1542424054621921e-034, -7.1542424054621926e-035, + 2.4754073164739869e-036, 5.4846728545790429e-037, + 9.2462547772103625e-038, -4.8596774326570872e-039}; +static double const stbsp__top[13] = {1e+023, 1e+046, 1e+069, 1e+092, 1e+115, + 1e+138, 1e+161, 1e+184, 1e+207, 1e+230, + 1e+253, 1e+276, 1e+299}; +static double const stbsp__negtop[13] = {1e-023, 1e-046, 1e-069, 1e-092, 1e-115, + 1e-138, 1e-161, 1e-184, 1e-207, 1e-230, + 1e-253, 1e-276, 1e-299}; +static double const stbsp__toperr[13] = {8388608, + 6.8601809640529717e+028, + -7.253143638152921e+052, + -4.3377296974619174e+075, + -1.5559416129466825e+098, + -3.2841562489204913e+121, + -3.7745893248228135e+144, + -1.7356668416969134e+167, + -3.8893577551088374e+190, + -9.9566444326005119e+213, + 6.3641293062232429e+236, + -5.2069140800249813e+259, + -5.2504760255204387e+282}; +static double const stbsp__negtoperr[13] = { + 3.9565301985100693e-040, -2.299904345391321e-063, + 3.6506201437945798e-086, 1.1875228833981544e-109, + -5.0644902316928607e-132, -6.7156837247865426e-155, + -2.812077463003139e-178, -5.7778912386589953e-201, + 7.4997100559334532e-224, -4.6439668915134491e-247, + -6.3691100762962136e-270, -9.436808465446358e-293, + 8.0970921678014997e-317}; + +#if defined(_MSC_VER) && (_MSC_VER <= 1200) +static stbsp__uint64 const stbsp__powten[20] = {1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000, + 100000000000, + 1000000000000, + 10000000000000, + 100000000000000, + 1000000000000000, + 10000000000000000, + 100000000000000000, + 1000000000000000000, + 10000000000000000000U}; +#define stbsp__tento19th ((stbsp__uint64)1000000000000000000) +#else +static stbsp__uint64 const stbsp__powten[20] = {1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000ULL, + 100000000000ULL, + 1000000000000ULL, + 10000000000000ULL, + 100000000000000ULL, + 1000000000000000ULL, + 10000000000000000ULL, + 100000000000000000ULL, + 1000000000000000000ULL, + 10000000000000000000ULL}; +#define stbsp__tento19th (1000000000000000000ULL) +#endif + +#define stbsp__ddmulthi(oh, ol, xh, yh) \ + { \ + double ahi = 0, alo, bhi = 0, blo; \ + stbsp__int64 bt; \ + oh = xh * yh; \ + STBSP__COPYFP(bt, xh); \ + bt &= ((~(stbsp__uint64)0) << 27); \ + STBSP__COPYFP(ahi, bt); \ + alo = xh - ahi; \ + STBSP__COPYFP(bt, yh); \ + bt &= ((~(stbsp__uint64)0) << 27); \ + STBSP__COPYFP(bhi, bt); \ + blo = yh - bhi; \ + ol = ((ahi * bhi - oh) + ahi * blo + alo * bhi) + alo * blo; \ + } + +#define stbsp__ddtoS64(ob, xh, xl) \ + { \ + double ahi = 0, alo, vh, t; \ + ob = (stbsp__int64)xh; \ + vh = (double)ob; \ + ahi = (xh - vh); \ + t = (ahi - xh); \ + alo = (xh - (ahi - t)) - (vh + t); \ + ob += (stbsp__int64)(ahi + alo + xl); \ + } + +#define stbsp__ddrenorm(oh, ol) \ + { \ + double s; \ + s = oh + ol; \ + ol = ol - (s - oh); \ + oh = s; \ + } + +#define stbsp__ddmultlo(oh, ol, xh, xl, yh, yl) ol = ol + (xh * yl + xl * yh); + +#define stbsp__ddmultlos(oh, ol, xh, yl) ol = ol + (xh * yl); + +static void +stbsp__raise_to_power10(double *ohi, double *olo, double d, + stbsp__int32 power) // power can be -323 to +350 +{ + double ph, pl; + if ((power >= 0) && (power <= 22)) { + stbsp__ddmulthi(ph, pl, d, stbsp__bot[power]); + } else { + stbsp__int32 e, et, eb; + double p2h, p2l; + + e = power; + if (power < 0) + e = -e; + et = (e * 0x2c9) >> 14; /* %23 */ + if (et > 13) + et = 13; + eb = e - (et * 23); + + ph = d; + pl = 0.0; + if (power < 0) { + if (eb) { + --eb; + stbsp__ddmulthi(ph, pl, d, stbsp__negbot[eb]); + stbsp__ddmultlos(ph, pl, d, stbsp__negboterr[eb]); + } + if (et) { + stbsp__ddrenorm(ph, pl); + --et; + stbsp__ddmulthi(p2h, p2l, ph, stbsp__negtop[et]); + stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__negtop[et], + stbsp__negtoperr[et]); + ph = p2h; + pl = p2l; + } + } else { + if (eb) { + e = eb; + if (eb > 22) + eb = 22; + e -= eb; + stbsp__ddmulthi(ph, pl, d, stbsp__bot[eb]); + if (e) { + stbsp__ddrenorm(ph, pl); + stbsp__ddmulthi(p2h, p2l, ph, stbsp__bot[e]); + stbsp__ddmultlos(p2h, p2l, stbsp__bot[e], pl); + ph = p2h; + pl = p2l; + } + } + if (et) { + stbsp__ddrenorm(ph, pl); + --et; + stbsp__ddmulthi(p2h, p2l, ph, stbsp__top[et]); + stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__top[et], stbsp__toperr[et]); + ph = p2h; + pl = p2l; + } + } + } + stbsp__ddrenorm(ph, pl); + *ohi = ph; + *olo = pl; +} + +// given a float value, returns the significant bits in bits, and the position +// of the +// decimal point in decimal_pos. +/-INF and NAN are specified by special +// values returned in the decimal_pos parameter. +// frac_digits is absolute normally, but if you want from first significant +// digits (got %g and %e), or in 0x80000000 +static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, + char *out, stbsp__int32 *decimal_pos, + double value, + stbsp__uint32 frac_digits) { + double d; + stbsp__int64 bits = 0; + stbsp__int32 expo, e, ng, tens; + + d = value; + STBSP__COPYFP(bits, d); + expo = (stbsp__int32)((bits >> 52) & 2047); + ng = (stbsp__int32)((stbsp__uint64)bits >> 63); + if (ng) + d = -d; + + if (expo == 2047) // is nan or inf? + { + *start = (bits & ((((stbsp__uint64)1) << 52) - 1)) ? "NaN" : "Inf"; + *decimal_pos = STBSP__SPECIAL; + *len = 3; + return ng; + } + + if (expo == 0) // is zero or denormal + { + if (((stbsp__uint64)bits << 1) == 0) // do zero + { + *decimal_pos = 1; + *start = out; + out[0] = '0'; + *len = 1; + return ng; + } + // find the right expo for denormals + { + stbsp__int64 v = ((stbsp__uint64)1) << 51; + while ((bits & v) == 0) { + --expo; + v >>= 1; + } + } + } + + // find the decimal exponent as well as the decimal bits of the value + { + double ph, pl; + + // log10 estimate - very specifically tweaked to hit or undershoot by no + // more than 1 of log10 of all expos 1..2046 + tens = expo - 1023; + tens = (tens < 0) ? ((tens * 617) / 2048) : (((tens * 1233) / 4096) + 1); + + // move the significant bits into position and stick them into an int + stbsp__raise_to_power10(&ph, &pl, d, 18 - tens); + + // get full as much precision from double-double as possible + stbsp__ddtoS64(bits, ph, pl); + + // check if we undershot + if (((stbsp__uint64)bits) >= stbsp__tento19th) + ++tens; + } + + // now do the rounding in integer land + frac_digits = (frac_digits & 0x80000000) ? ((frac_digits & 0x7ffffff) + 1) + : (tens + frac_digits); + if ((frac_digits < 24)) { + stbsp__uint32 dg = 1; + if ((stbsp__uint64)bits >= stbsp__powten[9]) + dg = 10; + while ((stbsp__uint64)bits >= stbsp__powten[dg]) { + ++dg; + if (dg == 20) + goto noround; + } + if (frac_digits < dg) { + stbsp__uint64 r; + // add 0.5 at the right position and round + e = dg - frac_digits; + if ((stbsp__uint32)e >= 24) + goto noround; + r = stbsp__powten[e]; + bits = bits + (r / 2); + if ((stbsp__uint64)bits >= stbsp__powten[dg]) + ++tens; + bits /= r; + } + noround:; + } + + // kill long trailing runs of zeros + if (bits) { + stbsp__uint32 n; + for (;;) { + if (bits <= 0xffffffff) + break; + if (bits % 1000) + goto donez; + bits /= 1000; + } + n = (stbsp__uint32)bits; + while ((n % 1000) == 0) + n /= 1000; + bits = n; + donez:; + } + + // convert to string + out += 64; + e = 0; + for (;;) { + stbsp__uint32 n; + char *o = out - 8; + // do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, + // constant denomiators be damned) + if (bits >= 100000000) { + n = (stbsp__uint32)(bits % 100000000); + bits /= 100000000; + } else { + n = (stbsp__uint32)bits; + bits = 0; + } + while (n) { + out -= 2; + *(stbsp__uint16 *)out = + *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; + n /= 100; + e += 2; + } + if (bits == 0) { + if ((e) && (out[0] == '0')) { + ++out; + --e; + } + break; + } + while (out != o) { + *--out = '0'; + ++e; + } + } + + *decimal_pos = tens; + *start = out; + *len = e; + return ng; +} + +#undef stbsp__ddmulthi +#undef stbsp__ddrenorm +#undef stbsp__ddmultlo +#undef stbsp__ddmultlos +#undef STBSP__SPECIAL +#undef STBSP__COPYFP + +#endif // STB_SPRINTF_NOFLOAT + +// clean up +#undef stbsp__uint16 +#undef stbsp__uint32 +#undef stbsp__int32 +#undef stbsp__uint64 +#undef stbsp__int64 +#undef STBSP__UNALIGNED + +#endif // STB_SPRINTF_IMPLEMENTATION + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/src/core/string.c b/src/core/string.c new file mode 100644 index 0000000..53b1fb9 --- /dev/null +++ b/src/core/string.c @@ -0,0 +1,59 @@ +#define STR_FMT(buff, str) \ + va_list args1; \ + va_start(args1, str); \ + int len = stbsp_vsnprintf(buff, sizeof(buff), str, args1); \ + va_end(args1) + +int str_len(char *str) { + int i = 0; + while (str[i]) i += 1; + return i; +} + +b32 s8_equal(s8_t a, s8_t b); +s8_t s8_from_char(char *string); +b32 str_eq(char *a, char *b) { + return s8_equal(s8_from_char(a), s8_from_char(b)); +} + +char char_to_lower_case(char a) { + if (a >= 'A' && a <= 'Z') a += 32; + return a; +} + +char char_to_upper_case(char a) { + if (a >= 'a' && a <= 'z') a -= 32; + return a; +} + +b32 char_is_whitespace(char w) { + b32 result = w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r'; + return result; +} + +b32 char_is_alphabetic(char a) { + b32 result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z'); + return result; +} + +b32 char_is_ident(char a) { + b32 result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z') || a == '_'; + return result; +} + +b32 char_is_digit(char a) { + b32 result = a >= '0' && a <= '9'; + return result; +} + +b32 char_is_alphanumeric(char a) { + b32 result = char_is_digit(a) || char_is_alphabetic(a); + return result; +} + +i64 wstr_len(wchar_t *string) { + i64 len = 0; + while (*string++ != 0) + len++; + return len; +} \ No newline at end of file diff --git a/src/core/string8.c b/src/core/string8.c new file mode 100644 index 0000000..11ef337 --- /dev/null +++ b/src/core/string8.c @@ -0,0 +1,454 @@ +#define s8_vsnprintf stbsp_vsnprintf + +#define S8_FMT(ma, str, result) \ + va_list args1; \ + va_start(args1, str); \ + s8_t result = s8_vfmt(ma, str, args1); \ + va_end(args1) + + +s8_t s8_range(char *begin, char *end) { + assert(end >= begin); + intptr_t size = (intptr_t)end - (intptr_t)begin; + s8_t result = {begin, size}; + return result; +} + +b32 s8_equal_ex(s8_t a, s8_t b, b32 ignore_case) { + if (a.len != b.len) return false; + for (int64_t i = 0; i < a.len; i++) { + char A = a.str[i]; + char B = b.str[i]; + if (ignore_case) { + A = char_to_lower_case(A); + B = char_to_lower_case(B); + } + if (A != B) + return false; + } + return true; +} +b32 s8_equal(s8_t a, s8_t b) {return s8_equal_ex(a, b, false);} + +s8_t s8_get_postfix(s8_t string, int64_t len) { + len = CLAMP_TOP(len, string.len); + int64_t remain_len = string.len - len; + s8_t result = s8(string.str + remain_len, len); + return result; +} + +s8_t s8_get_prefix(s8_t string, int64_t len) { + len = CLAMP_TOP(len, string.len); + s8_t result = s8(string.str, len); + return result; +} + +s8_t s8_chop(s8_t string, int64_t len) { + len = CLAMP_TOP(len, string.len); + s8_t result = s8(string.str, string.len - len); + return result; +} + +s8_t s8_skip(s8_t string, int64_t len) { + len = CLAMP_TOP(len, string.len); + int64_t remain = string.len - len; + s8_t result = s8(string.str + len, remain); + return result; +} + +b32 s8_ends_with(s8_t a, s8_t end, b32 ignore_case) { + s8_t a_end = s8_get_postfix(a, end.len); + b32 result = s8_equal_ex(end, a_end, ignore_case); + return result; +} + +b32 s8_starts_with(s8_t a, s8_t start, b32 ignore_case) { + s8_t a_start = s8_get_prefix(a, start.len); + b32 result = s8_equal_ex(start, a_start, ignore_case); + return result; +} + +void s8_normalize_path_unsafe(s8_t s) { + for (int64_t i = 0; i < s.len; i++) { + if (s.str[i] == '\\') + s.str[i] = '/'; + } +} + +b32 s8_is_pointer_inside(s8_t string, char *p) { + uintptr_t pointer = (uintptr_t)p; + uintptr_t start = (uintptr_t)string.str; + uintptr_t stop = start + (uintptr_t)string.len; + b32 result = pointer >= start && pointer < stop; + return result; +} + +s8_t s8_skip_to_p(s8_t string, char *p) { + if (s8_is_pointer_inside(string, p)) { + s8_t result = s8(p, p - string.str); + return result; + } + return string; +} + +s8_t s8_skip_past(s8_t string, s8_t a) { + if (s8_is_pointer_inside(string, a.str)) { + s8_t on_p = s8(a.str, a.str - string.str); + s8_t result = s8_skip(on_p, a.len); + return result; + } + return string; +} + +s8_t s8_slice(s8_t string, int64_t first_index, int64_t one_past_last_index) { + if (one_past_last_index < 0) one_past_last_index = string.len + one_past_last_index + 1; + if (first_index < 0) first_index = string.len + first_index; + assert(first_index < one_past_last_index && "s8_slice, first_index is bigger then one_past_last_index"); + assert(string.len > 0 && "Slicing string of length 0! Might be an error!"); + s8_t result = string; + if (string.len > 0) { + if (one_past_last_index > first_index) { + first_index = CLAMP_TOP(first_index, string.len - 1); + one_past_last_index = CLAMP_TOP(one_past_last_index, string.len); + result.str += first_index; + result.len = one_past_last_index - first_index; + } + else { + result.len = 0; + } + } + return result; +} + +s8_t s8_trim(s8_t string) { + if (string.len == 0) + return string; + + int64_t whitespace_begin = 0; + for (; whitespace_begin < string.len; whitespace_begin++) { + if (!char_is_whitespace(string.str[whitespace_begin])) { + break; + } + } + + int64_t whitespace_end = string.len; + for (; whitespace_end != whitespace_begin; whitespace_end--) { + if (!char_is_whitespace(string.str[whitespace_end - 1])) { + break; + } + } + + if (whitespace_begin == whitespace_end) { + string.len = 0; + } + else { + string = s8_slice(string, whitespace_begin, whitespace_end); + } + + return string; +} + +s8_t s8_trim_end(s8_t string) { + int64_t whitespace_end = string.len; + for (; whitespace_end != 0; whitespace_end--) { + if (!char_is_whitespace(string.str[whitespace_end - 1])) { + break; + } + } + + s8_t result = s8_get_prefix(string, whitespace_end); + return result; +} + +typedef int s8_seek_t; +enum { + s8_seek_none = 0, + s8_seek_ignore_case = 1, + s8_seek_match_find_last = 2, +}; + +b32 s8_seek(s8_t string, s8_t find, s8_seek_t flags, int64_t *index_out) { + b32 ignore_case = flags & s8_seek_ignore_case ? true : false; + b32 result = false; + if (flags & s8_seek_match_find_last) { + for (int64_t i = string.len; i != 0; i--) { + int64_t index = i - 1; + s8_t substring = s8_slice(string, index, index + find.len); + if (s8_equal_ex(substring, find, ignore_case)) { + if (index_out) + *index_out = index; + result = true; + break; + } + } + } + else { + for (int64_t i = 0; i < string.len; i++) { + s8_t substring = s8_slice(string, i, i + find.len); + if (s8_equal_ex(substring, find, ignore_case)) { + if (index_out) + *index_out = i; + result = true; + break; + } + } + } + + return result; +} + +int64_t s8_find(s8_t string, s8_t find, s8_seek_t flag) { + int64_t result = -1; + s8_seek(string, find, flag, &result); + return result; +} + +s8_t s8_chop_last_slash(s8_t s) { + s8_t result = s; + s8_seek(s, s8_lit("/"), s8_seek_match_find_last, &result.len); + return result; +} + +s8_t s8_chop_last_period(s8_t s) { + s8_t result = s; + s8_seek(s, s8_lit("."), s8_seek_match_find_last, &result.len); + return result; +} + +s8_t s8_skip_to_last_slash(s8_t s) { + int64_t pos; + s8_t result = s; + if (s8_seek(s, s8_lit("/"), s8_seek_match_find_last, &pos)) { + result = s8_skip(result, pos + 1); + } + return result; +} + +s8_t s8_skip_to_last_period(s8_t s) { + int64_t pos; + s8_t result = s; + if (s8_seek(s, s8_lit("."), s8_seek_match_find_last, &pos)) { + result = s8_skip(result, pos + 1); + } + return result; +} + +s8_t s8_from_char(char *string) { + s8_t result; + result.str = (char *)string; + result.len = str_len(string); + return result; +} + +s8_t s8_get_name_no_ext(s8_t s) { + return s8_skip_to_last_slash(s8_chop_last_period(s)); +} + +s8_t s8_copy(ma_arena_t *ma, s8_t string) { + char *copy = (char *)ma_push_size(ma, sizeof(char) * (string.len + 1)); + if (copy) { + memory_copy(copy, string.str, string.len); + copy[string.len] = 0; + s8_t result = s8(copy, string.len); + return result; + } + return (s8_t){0}; +} + +s8_t s8_copy_char(ma_arena_t *ma, char *s) { + int64_t len = str_len(s); + char *copy = (char *)ma_push_size(ma, sizeof(char) * (len + 1)); + memory_copy(copy, s, len); + copy[len] = 0; + s8_t result = s8(copy, len); + return result; +} + +s8_t s8_normalize_path(ma_arena_t *ma, s8_t s) { + s8_t copy = s8_copy(ma, s); + for (int64_t i = 0; i < copy.len; i++) { + if (copy.str[i] == '\\') + copy.str[i] = '/'; + } + return copy; +} + +s8_t s8_to_lower_case(ma_arena_t *ma, s8_t s) { + s8_t copy = s8_copy(ma, s); + for (int64_t i = 0; i < copy.len; i++) { + copy.str[i] = char_to_lower_case(copy.str[i]); + } + return copy; +} + +s8_t s8_to_upper_case(ma_arena_t *ma, s8_t s) { + s8_t copy = s8_copy(ma, s); + for (int64_t i = 0; i < copy.len; i++) { + copy.str[i] = char_to_upper_case(copy.str[i]); + } + return copy; +} + +s8_t s8_vfmt(ma_arena_t *ma, const char *str, va_list args1) { + va_list args2; + va_copy(args2, args1); + int64_t len = s8_vsnprintf(0, 0, str, args2); + va_end(args2); + + char *result = (char *)ma_push_size(ma, sizeof(char) * (len + 1)); + s8_vsnprintf(result, (int)(len + 1), str, args1); + s8_t res = s8(result, len); + return res; +} + +s8_t s8_fmt(ma_arena_t *ma, const char *str, ...) { + S8_FMT(ma, str, result); + return result; +} + +// +// sb8_t +// +sb8_node_t *sb8_node(ma_arena_t *ma, s8_t str) { + sb8_node_t *node = ma_push_type(ma, sb8_node_t); + node->s = str; + return node; +} + +sb8_node_t *sb8_append(sb8_t *list, s8_t string) { + sb8_node_t *node = sb8_node(list->arena, string); + SLLQ_APPEND(list->first, list->last, node); + return node; +} + +s8_t sb8_printf(sb8_t *sb, const char *str, ...) { + S8_FMT(sb->arena, str, result); + sb8_append(sb, result); + return result; +} + +void sb8_indent(sb8_t *sb) { + sb8_printf(sb, "\n%.*s", sb->indent*4, " "); +} + +s8_t sb8_stmtf(sb8_t *sb, const char *str, ...) { + S8_FMT(sb->arena, str, result); + sb8_indent(sb); + sb8_append(sb, result); + return result; +} + +int64_t sb8_char_size(sb8_t *sb) { + int64_t result = 0; + for (sb8_node_t *it = sb->first; it; it = it->next) { + result += it->len; + } + return result; +} + +s8_t sb8_merge(sb8_t *sb) { + int64_t size = sb8_char_size(sb) + 1; + char *str = ma_push_size(sb->arena, size); + s8_t result = {str, 0}; + for (sb8_node_t *it = sb->first; it; it = it->next) { + memory_copy(result.str + result.len, it->str, it->len); + result.len += it->len; + } + result.str[result.len] = 0; + return result; +} + +typedef int s8_split_t; +enum { + s8_split_none = 0, + s8_split_ignore_case = 1, + s8_split_inclusive = 2, +}; + +sb8_t s8_split(ma_arena_t *ma, s8_t string, s8_t find, s8_split_t flags) { + sb8_t result = (sb8_t){ma}; + int64_t index = 0; + + s8_seek_t find_flag = flags & s8_split_ignore_case ? s8_seek_ignore_case : s8_seek_none; + while (s8_seek(string, find, find_flag, &index)) { + s8_t before_match = s8(string.str, index); + sb8_append(&result, before_match); + if (flags & s8_split_inclusive) { + s8_t match = s8(string.str + index, find.len); + sb8_append(&result, match); + } + string = s8_skip(string, index + find.len); + } + if (string.len) sb8_append(&result, string); + return result; +} + +s16_t s16_from_s8(ma_arena_t *ma, s8_t string) { + u16 *buffer = ma_push_array(ma, u16, string.len + 1); + i64 len = wstr_from_str(buffer, string.len + 1, string.str, string.len); + assert(len < string.len); + return (s16_t){buffer,len}; +} + +s8_t s8_from_s16(ma_arena_t *ma, s16_t string) { + i64 buffer_size = (string.len + 1) * 2; + char *buffer = ma_push_array(ma, char, buffer_size); + i64 len = str_from_wstr(buffer, buffer_size, string.str, string.len); + assert(len < buffer_size); + return (s8_t){buffer,len}; +} +#if 0 + +s8_t S8_MergeWithSeparator(ma_arena_t *ma, S8_List list, s8_t separator) { + if (list.node_count == 0) return s8_makeEmpty(); + if (list.char_count == 0) return s8_makeEmpty(); + + int64_t base_size = (list.char_count + 1); + int64_t sep_size = (list.node_count - 1) * separator.len; + int64_t size = base_size + sep_size; + char *buff = (char *)ma_push_size(ma, sizeof(char) * (size + 1)); + s8_t string = s8(buff, 0); + for (S8_Node *it = list.first; it; it = it->next) { + assert(string.len + it->string.len <= size); + memory_copy(string.str + string.len, it->string.str, it->string.len); + string.len += it->string.len; + if (it != list.last) { + memory_copy(string.str + string.len, separator.str, separator.len); + string.len += separator.len; + } + } + assert(string.len == size - 1); + string.str[size] = 0; + return string; +} + +s8_t S8_Merge(ma_arena_t *ma, S8_List list) { + return S8_MergeWithSeparator(ma, list, s8_lit("")); +} + +s8_t S8_ReplaceAll(ma_arena_t *ma, s8_t string, s8_t replace, s8_t with, b32 ignore_case) { + s8_split_flag split_flag = ignore_case ? S8_SplitFlag_IgnoreCase : S8_SplitFlag_None; + S8_List list = S8_Split(ma, string, replace, split_flag | S8_SplitFlag_SplitInclusive); + for (S8_Node *it = list.first; it; it = it->next) { + if (s8_equal_ex(it->string, replace, ignore_case)) { + S8_ReplaceNodeString(&list, it, with); + } + } + s8_t result = S8_Merge(ma, list); + return result; +} + +S8_List S8_FindAll(ma_arena_t *ma, s8_t string, s8_t find, b32 ignore_case) { // @untested + S8_List result = s8_makeEmptyList(); + int64_t index = 0; + + s8_seek find_flag = ignore_case ? s8_seek_ignore_case : 0; + while (s8_seek(string, find, find_flag, &index)) { + s8_t match = s8(string.str + index, find.len); + S8_AddNode(ma, &result, match); + string = s8_skip(string, index + find.len); + } + return result; +} + +#endif \ No newline at end of file diff --git a/src/core/type_info.c b/src/core/type_info.c new file mode 100644 index 0000000..7c4c56a --- /dev/null +++ b/src/core/type_info.c @@ -0,0 +1,403 @@ +s8_t ti_enum_value_to_name(type_t *type, i64 value) { + assert(type->kind == type_kind_enum); + for (i32 i = 0; i < type->count; i += 1) { + type_member_t *it = type->members + i; + if (value == it->value) { + return it->name; + } + } + return s8_lit("invalid"); +} + +i64 ti_enum_name_to_value(type_t *type, s8_t name) { + assert(type->kind == type_kind_enum); + for (i32 i = 0; i < type->count; i += 1) { + type_member_t *it = type->members + i; + if (s8_equal(it->name, name)) { + return it->value; + } + } + return -1; +} + +type_member_t *ti_get_member(type_t *type, s8_t name) { + for (i32 i = 0; i < type->count; i += 1) { + type_member_t *it = type->members + i; + if (s8_equal(it->name, name)) { + return it; + } + } + return NULL; +} + +void sb8_serial_data(sb8_t *sb, void *p, type_t *type) { + assert(type->kind != type_kind_invalid); + + switch(type->kind) { + case type_kind_i8: { + i8 n = *(i8 *)p; + sb8_printf(sb, "%d", n); + return; + } break; + case type_kind_i16: { + i16 n = *(i16 *)p; + sb8_printf(sb, "%d", n); + return; + } break; + case type_kind_i32: { + i32 n = *(i32 *)p; + sb8_printf(sb, "%d", n); + return; + } break; + case type_kind_i64: { + i64 n = *(i64 *)p; + sb8_printf(sb, "%lld", (long long)n); + return; + } break; + case type_kind_u8: { + u8 n = *(u8 *)p; + sb8_printf(sb, "%u", n); + return; + } break; + case type_kind_u16: { + u16 n = *(u16 *)p; + sb8_printf(sb, "%u", n); + return; + } break; + case type_kind_u32: { + u32 n = *(u32 *)p; + sb8_printf(sb, "%u", n); + return; + } break; + case type_kind_u64: { + u64 n = *(u64 *)p; + sb8_printf(sb, "%llu", (unsigned long long)n); + return; + } break; + case type_kind_b8: { + b8 n = *(b8 *)p; + sb8_printf(sb, "%d", n); + return; + } break; + case type_kind_b16: { + b16 n = *(b16 *)p; + sb8_printf(sb, "%d", n); + return; + } break; + case type_kind_b32: { + b32 n = *(b32 *)p; + sb8_printf(sb, "%d", n); + return; + } break; + case type_kind_b64: { + b64 n = *(b64 *)p; + sb8_printf(sb, "%lld", (long long)n); + return; + } break; + case type_kind_f32: { + f32 n = *(f32 *)p; + sb8_printf(sb, "%f", n); + return; + } break; + case type_kind_f64: { + f64 n = *(f64 *)p; + sb8_printf(sb, "%f", n); + return; + } break; + case type_kind_isize: { + isize n = *(isize *)p; + sb8_printf(sb, "%lld", (long long)n); + return; + } break; + case type_kind_usize: { + usize n = *(usize *)p; + sb8_printf(sb, "%llu", (unsigned long long)n); + return; + } break; + case type_kind_int: { + int n = *(int *)p; + sb8_printf(sb, "%d", n); + return; + } break; + case type_kind_char: { + char n = *(char *)p; + sb8_printf(sb, "%c", n); + return; + } break; + } + + if (type == &type__s8_t) { + s8_t n = *(s8_t *)p; + sb8_printf(sb, "\"%S\"", n); + return; + } + + + if (type->kind == type_kind_pointer) { + sb8_printf(sb, "0x%x", p); + return; + } + + if (type->kind == type_kind_enum) { + i64 value = 0; + if (type->size == 1) { + value = *(i8 *)p; + } else if (type->size == 2) { + value = *(i16 *)p; + } else if (type->size == 4) { + value = *(i32 *)p; + } else if (type->size == 8) { + value = *(i64 *)p; + } else { + panicf("invalid size of enum: %d", type->size); + } + s8_t s = ti_enum_value_to_name(type, value); + sb8_append(sb, s); + return; + } + + if (type->kind == type_kind_array) { + u8 *p8 = (u8 *)p; + sb8_printf(sb, "{"); + sb->indent += 1; + for (i32 i = 0; i < type->count; i += 1) { + type_t *base = type->base; + u8 *mem_p = p8 + base->size * i; + + sb8_indent(sb); + sb8_serial_data(sb, mem_p, base); + sb8_printf(sb, ","); + } + sb->indent -= 1; + sb8_stmtf(sb, "}"); + return; + } + + if (type->kind == type_kind_struct) { + u8 *p8 = (u8 *)p; + + sb8_printf(sb, "{"); + sb->indent += 1; + for (i32 i = 0; i < type->count; i += 1) { + type_member_t *mem = type->members + i; + u8 *mem_p = p8 + mem->offset; + sb8_indent(sb); + sb8_printf(sb, "%S: ", mem->name); + sb8_serial_data(sb, mem_p, mem->type); + sb8_printf(sb, ","); + } + sb->indent -= 1; + sb8_stmtf(sb, "}"); + return; + } + + panicf("can't serialize: unhandled type"); +} + +s8_t s8_serial_data(ma_arena_t *arena, void *p, type_t *type) { + sb8_t *sb = sb8_serial_begin(arena); + sb8_serial_data(sb, p, type); + s8_t string = sb8_serial_end(sb); + return string; +} + +i64 parser__match_i64(parser_t *par) { + i64 minus = 1; + if (parser_match(par, lex_kind_minus)) { + minus = -1; + } + lex_t *token = parser_expect(par, lex_kind_int); + i64 result = (i64)token->integer * minus; + return result; +} + +void s8_deserial_data_ex(ma_arena_t *arena, parser_t *par, void *p, type_t *type) { + switch(type->kind) { + case type_kind_i8: { + i8 *n = (i8 *)p; + n[0] = (i8)parser__match_i64(par); + return; + } break; + case type_kind_i16: { + i16 *n = (i16 *)p; + n[0] = (i16)parser__match_i64(par); + return; + } break; + case type_kind_i32: { + i32 *n = (i32 *)p; + n[0] = (i32)parser__match_i64(par); + return; + } break; + case type_kind_i64: { + i64 *n = (i64 *)p; + n[0] = (i64)parser__match_i64(par); + return; + } break; + case type_kind_b8: { + b8 *n = (b8 *)p; + n[0] = (b8)parser__match_i64(par); + return; + } break; + case type_kind_b16: { + b16 *n = (b16 *)p; + n[0] = (b16)parser__match_i64(par); + return; + } break; + case type_kind_b32: { + b32 *n = (b32 *)p; + n[0] = (b32)parser__match_i64(par); + return; + } break; + case type_kind_b64: { + b64 *n = (b64 *)p; + n[0] = (b64)parser__match_i64(par); + return; + } break; + case type_kind_u8: { + lex_t *token = parser_expect(par, lex_kind_int); + u8 *n = (u8 *)p; + n[0] = (u8)token->integer; + return; + } break; + case type_kind_u16: { + lex_t *token = parser_expect(par, lex_kind_int); + u16 *n = (u16 *)p; + n[0] = (u16)token->integer; + return; + } break; + case type_kind_u32: { + lex_t *token = parser_expect(par, lex_kind_int); + u32 *n = (u32 *)p; + n[0] = (u32)token->integer; + return; + } break; + case type_kind_u64: { + lex_t *token = parser_expect(par, lex_kind_int); + u64 *n = (u64 *)p; + n[0] = (u64)token->integer; + return; + } break; + case type_kind_f32: { + lex_t *token = parser_expect(par, lex_kind_real); + f32 *n = (f32 *)p; + n[0] = (f32)token->real; + return; + } break; + case type_kind_f64: { + lex_t *token = parser_expect(par, lex_kind_real); + f64 *n = (f64 *)p; + n[0] = token->real; + return; + } break; + case type_kind_isize: { + isize *n = (isize *)p; + n[0] = (isize)parser__match_i64(par); + return; + } break; + case type_kind_usize: { + lex_t *token = parser_expect(par, lex_kind_int); + usize *n = (usize *)p; + n[0] = (usize)token->integer; + return; + } break; + case type_kind_int: { + int *n = (int *)p; + n[0] = (int)parser__match_i64(par); + return; + } break; + case type_kind_char: { + char *n = (char *)p; + n[0] = (char)parser__match_i64(par); + return; + } break; + } + + if (type == &type__s8_t) { + lex_t *token = parser_expect(par, lex_kind_string); + s8_t *n = (s8_t *)p; + n[0] = s8_copy(arena, token->s8); + return; + } + + if (type->kind == type_kind_enum) { + lex_t *token = parser_expect(par, lex_kind_ident); + i64 value = ti_enum_name_to_value(type, token->s8); + if (value == -1) { + panicf("invalid enum value: %S", token->s8); + } + if (type->size == 1) { + *(i8 *)p = (i8)value; + } else if (type->size == 2) { + *(i16 *)p = (i16)value; + } else if (type->size == 4) { + *(i32 *)p = (i32)value; + } else if (type->size == 8) { + *(i64 *)p = (i64)value; + } else { + panicf("invalid size of enum: %d", type->size); + } + return; + } + + if (type->kind == type_kind_struct) { + u8 *p8 = (u8 *)p; + +#if 0 + parser_expect(par, lex_kind_open_brace); + while (!parser_match(par, lex_kind_close_brace)) { + lex_t *ident = parser_expect(par, lex_kind_ident); + parser_expect(par, lex_kind_colon); + + type_member_t *mem = ti_get_member(type, ident->s8); + if (mem) { + u8 *mem_p = p8 + mem->offset; + s8_deserial_data_ex(arena, par, mem_p, mem->type); + } else { + debugf("deserial - skipping field: %S", ident->s8); + parser_eat_until(par, lex_kind_comma); + } + + parser_expect(par, lex_kind_comma); + } + +#else // strict + parser_expect(par, lex_kind_open_brace); + for (i32 i = 0; i < type->count; i += 1) { + type_member_t *mem = type->members + i; + u8 *mem_p = p8 + mem->offset; + + lex_t *ident = parser_expect(par, lex_kind_ident); + parser_expect(par, lex_kind_colon); + assert(s8_equal(ident->s8, mem->name)); + + s8_deserial_data_ex(arena, par, mem_p, mem->type); + parser_expect(par, lex_kind_comma); + } + parser_expect(par, lex_kind_close_brace); +#endif + } + + if (type->kind == type_kind_array) { + u8 *p8 = (u8 *)p; + parser_expect(par, lex_kind_open_brace); + for (i32 i = 0; i < type->count; i += 1) { + type_t *base = type->base; + u8 *mem_p = p8 + base->size * i; + + s8_deserial_data_ex(arena, par, mem_p, base); + parser_expect(par, lex_kind_comma); + } + parser_expect(par, lex_kind_close_brace); + } +} + +#define s8_deserial_data(ARENA, DATA, TYPE) (TYPE *)s8__deserial_data(ARENA, DATA, &type__##TYPE) +void *s8__deserial_data(ma_arena_t *arena, s8_t data, type_t *type) { + void *p = ma_push_size(arena, type->size); + ma_temp_t scratch = ma_begin_scratch(); + lex_array_t tokens = lex_tokens(scratch.arena, "data serializing", data.str); + parser_t *par = parser_make(scratch.arena, tokens.data); + s8_deserial_data_ex(arena, par, p, type); + ma_end_scratch(scratch); + return p; +} \ No newline at end of file diff --git a/src/core/type_info.h b/src/core/type_info.h new file mode 100644 index 0000000..0ad68a1 --- /dev/null +++ b/src/core/type_info.h @@ -0,0 +1,314 @@ +typedef int type_kind_t; +enum { + type_kind_invalid, + + type_kind_i8, + type_kind_i16, + type_kind_i32, + type_kind_i64, + type_kind_b8, + type_kind_b16, + type_kind_b32, + type_kind_b64, + type_kind_u8, + type_kind_u16, + type_kind_u32, + type_kind_u64, + type_kind_f32, + type_kind_f64, + type_kind_isize, + type_kind_usize, + type_kind_int, + type_kind_char, + + type_kind_count, + + type_kind_pointer, + type_kind_array, + type_kind_struct, + type_kind_union, + type_kind_enum, +}; + +typedef struct type_member_t type_member_t; +typedef struct type_t type_t; +struct type_t { + type_kind_t kind; + s8_t name; + i32 size; + i32 count; + type_member_t *members; + type_t *base; +}; + +struct type_member_t { + s8_t name; + type_t *type; + i64 value; + u64 offset; +}; + +#define type(X) &type__##X +#define DEFINE_ENUM(x) type_t type__##x = {type_kind_enum, s8_const_lit(#x), sizeof(x), .members = members__##x, .count = lengthof(members__##x)} +#define DEFINE_STRUCT(x) type_t type__##x = {type_kind_struct, s8_const_lit(#x), sizeof(x), .members = members__##x, .count = lengthof(members__##x)} +#define POINTER(x) (type_t){type_kind_pointer, s8_const_lit(#x "*"), sizeof(void *), .base = &type__##x} + +s8_t ti_enum_value_to_name(type_t *type, i64 value); +i64 ti_enum_name_to_value(type_t *type, s8_t name); +type_member_t *ti_get_member(type_t *type, s8_t name); + +type_t type__i8 = {type_kind_i8, s8_const_lit("i8"), sizeof(i8)}; +type_t type__i16 = {type_kind_i16, s8_const_lit("i16"), sizeof(i16)}; +type_t type__i32 = {type_kind_i32, s8_const_lit("i32"), sizeof(i32)}; +type_t type__i64 = {type_kind_i64, s8_const_lit("i64"), sizeof(i64)}; +type_t type__u8 = {type_kind_u8, s8_const_lit("u8"), sizeof(u8)}; +type_t type__u16 = {type_kind_u16, s8_const_lit("u16"), sizeof(u16)}; +type_t type__u32 = {type_kind_u32, s8_const_lit("u32"), sizeof(u32)}; +type_t type__u64 = {type_kind_u64, s8_const_lit("u64"), sizeof(u64)}; +type_t type__b8 = {type_kind_b8, s8_const_lit("b8"), sizeof(b8)}; +type_t type__b16 = {type_kind_b16, s8_const_lit("b16"), sizeof(b16)}; +type_t type__b32 = {type_kind_b32, s8_const_lit("b32"), sizeof(b32)}; +type_t type__b64 = {type_kind_b64, s8_const_lit("b64"), sizeof(b64)}; +type_t type__f32 = {type_kind_f32, s8_const_lit("f32"), sizeof(f32)}; +type_t type__f64 = {type_kind_f64, s8_const_lit("f64"), sizeof(f64)}; +type_t type__isize = {type_kind_isize, s8_const_lit("isize"), sizeof(isize)}; +type_t type__usize = {type_kind_usize, s8_const_lit("usize"), sizeof(usize)}; +type_t type__int = {type_kind_int, s8_const_lit("int"), sizeof(int)}; +type_t type__char = {type_kind_char, s8_const_lit("char"), sizeof(char)}; + +type_t type__s8_t = { type_kind_struct, s8_const_lit("s8_t"), sizeof(s8_t), .count = 2, + .members = (type_member_t[]){ + {s8_const_lit("str"), &POINTER(char), .offset = offsetof(s8_t, str)}, + {s8_const_lit("len"), &type__i64, .offset = offsetof(s8_t, len)}, + } +}; + +type_t type__s16_t = { type_kind_struct, s8_const_lit("s16_t"), sizeof(s16_t), .count = 2, + .members = (type_member_t[]){ + {s8_const_lit("str"), &POINTER(u16), .offset = offsetof(s16_t, str)}, + {s8_const_lit("len"), &type__i64, .offset = offsetof(s16_t, len)}, + } +}; + +type_t type__s32_t = { type_kind_struct, s8_const_lit("s32_t"), sizeof(s32_t), .count = 2, + .members = (type_member_t[]){ + {s8_const_lit("str"), &POINTER(u32), .offset = offsetof(s32_t, str)}, + {s8_const_lit("len"), &type__i64, .offset = offsetof(s32_t, len)}, + } +}; + +type_t type__ma_arena_t = { type_kind_struct, s8_const_lit("ma_arena_t"), sizeof(ma_arena_t), .count = 6, + .members = (type_member_t[]){ + {s8_const_lit("data"), &POINTER(u8), .offset = offsetof(ma_arena_t, data)}, + {s8_const_lit("len"), &type__usize, .offset = offsetof(ma_arena_t, len)}, + {s8_const_lit("base_len"), &type__usize, .offset = offsetof(ma_arena_t, base_len)}, + {s8_const_lit("reserve"), &type__usize, .offset = offsetof(ma_arena_t, reserve)}, + {s8_const_lit("commit"), &type__usize, .offset = offsetof(ma_arena_t, commit)}, + {s8_const_lit("align"), &type__usize, .offset = offsetof(ma_arena_t, align)}, + } +}; + +type_t type__ma_temp_t = { type_kind_struct, s8_const_lit("ma_temp_t"), sizeof(ma_temp_t), .count = 2, + .members = (type_member_t[]){ + {s8_const_lit("arena"), &POINTER(ma_arena_t), .offset = offsetof(ma_temp_t, arena)}, + {s8_const_lit("len"), &type__usize, .offset = offsetof(ma_temp_t, len)}, + } +}; + + +type_t type__v2f32_t = { type_kind_struct, s8_const_lit("v2f32_t"), sizeof(v2f32_t), .count = 2, + .members = (type_member_t[]){ + {s8_const_lit("x"), &type__f32, .offset = offsetof(v2f32_t, x)}, + {s8_const_lit("y"), &type__f32, .offset = offsetof(v2f32_t, y)}, + } +}; + +type_t type__v3f32_t = { type_kind_struct, s8_const_lit("v3f32_t"), sizeof(v3f32_t), .count = 3, + .members = (type_member_t[]){ + {s8_const_lit("x"), &type__f32, .offset = offsetof(v3f32_t, x)}, + {s8_const_lit("y"), &type__f32, .offset = offsetof(v3f32_t, y)}, + {s8_const_lit("z"), &type__f32, .offset = offsetof(v3f32_t, z)}, + } +}; + +type_t type__v4f32_t = { type_kind_struct, s8_const_lit("v4f32_t"), sizeof(v4f32_t), .count = 4, + .members = (type_member_t[]){ + {s8_const_lit("x"), &type__f32, .offset = offsetof(v4f32_t, x)}, + {s8_const_lit("y"), &type__f32, .offset = offsetof(v4f32_t, y)}, + {s8_const_lit("z"), &type__f32, .offset = offsetof(v4f32_t, z)}, + {s8_const_lit("w"), &type__f32, .offset = offsetof(v4f32_t, w)}, + } +}; + +type_t type__r3f32_t = { type_kind_struct, s8_const_lit("r3f32_t"), sizeof(r3f32_t), .count = 6, + .members = (type_member_t[]){ + {s8_const_lit("x0"), &type__f32, .offset = offsetof(r3f32_t, x0)}, + {s8_const_lit("y0"), &type__f32, .offset = offsetof(r3f32_t, y0)}, + {s8_const_lit("x0"), &type__f32, .offset = offsetof(r3f32_t, z0)}, + {s8_const_lit("x1"), &type__f32, .offset = offsetof(r3f32_t, x1)}, + {s8_const_lit("y1"), &type__f32, .offset = offsetof(r3f32_t, y1)}, + {s8_const_lit("x1"), &type__f32, .offset = offsetof(r3f32_t, z1)}, + } +}; + +type_member_t members__r2f32_t[] = { + {s8_const_lit("x0"), &type__f32, .offset = offsetof(r2f32_t, x0)}, + {s8_const_lit("y0"), &type__f32, .offset = offsetof(r2f32_t, y0)}, + {s8_const_lit("x1"), &type__f32, .offset = offsetof(r2f32_t, x1)}, + {s8_const_lit("y1"), &type__f32, .offset = offsetof(r2f32_t, y1)}, +}; +DEFINE_STRUCT(r2f32_t); + +type_member_t members__r1f32_t[] = { + {s8_const_lit("x0"), &type__f32, .offset = offsetof(r1f32_t, x0)}, + {s8_const_lit("x1"), &type__f32, .offset = offsetof(r1f32_t, x1)}, +}; +DEFINE_STRUCT(r1f32_t); + +type_t type__v2f64_t = { type_kind_struct, s8_const_lit("v2f64_t"), sizeof(v2f64_t), .count = 2, + .members = (type_member_t[]){ + {s8_const_lit("x"), &type__f64, .offset = offsetof(v2f64_t, x)}, + {s8_const_lit("y"), &type__f64, .offset = offsetof(v2f64_t, y)}, + } +}; + +type_t type__v3f64_t = { type_kind_struct, s8_const_lit("v3f64_t"), sizeof(v3f64_t), .count = 3, + .members = (type_member_t[]){ + {s8_const_lit("x"), &type__f64, .offset = offsetof(v3f64_t, x)}, + {s8_const_lit("y"), &type__f64, .offset = offsetof(v3f64_t, y)}, + {s8_const_lit("z"), &type__f64, .offset = offsetof(v3f64_t, z)}, + } +}; + +type_t type__v4f64_t = { type_kind_struct, s8_const_lit("v4f64_t"), sizeof(v4f64_t), .count = 4, + .members = (type_member_t[]){ + {s8_const_lit("x"), &type__f64, .offset = offsetof(v4f64_t, x)}, + {s8_const_lit("y"), &type__f64, .offset = offsetof(v4f64_t, y)}, + {s8_const_lit("z"), &type__f64, .offset = offsetof(v4f64_t, z)}, + {s8_const_lit("w"), &type__f64, .offset = offsetof(v4f64_t, w)}, + } +}; + +type_t type__r3f64_t = { type_kind_struct, s8_const_lit("r3f64_t"), sizeof(r3f64_t), .count = 6, + .members = (type_member_t[]){ + {s8_const_lit("x0"), &type__f64, .offset = offsetof(r3f64_t, x0)}, + {s8_const_lit("y0"), &type__f64, .offset = offsetof(r3f64_t, y0)}, + {s8_const_lit("x0"), &type__f64, .offset = offsetof(r3f64_t, z0)}, + {s8_const_lit("x1"), &type__f64, .offset = offsetof(r3f64_t, x1)}, + {s8_const_lit("y1"), &type__f64, .offset = offsetof(r3f64_t, y1)}, + {s8_const_lit("x1"), &type__f64, .offset = offsetof(r3f64_t, z1)}, + } +}; + +type_member_t members__r2f64_t[] = { + {s8_const_lit("x0"), &type__f64, .offset = offsetof(r2f64_t, x0)}, + {s8_const_lit("y0"), &type__f64, .offset = offsetof(r2f64_t, y0)}, + {s8_const_lit("x1"), &type__f64, .offset = offsetof(r2f64_t, x1)}, + {s8_const_lit("y1"), &type__f64, .offset = offsetof(r2f64_t, y1)}, +}; +DEFINE_STRUCT(r2f64_t); + +type_member_t members__r1f64_t[] = { + {s8_const_lit("x0"), &type__f64, .offset = offsetof(r1f64_t, x0)}, + {s8_const_lit("x1"), &type__f64, .offset = offsetof(r1f64_t, x1)}, +}; +DEFINE_STRUCT(r1f64_t); + +type_t type__v2i32_t = { type_kind_struct, s8_const_lit("v2i32_t"), sizeof(v2i32_t), .count = 2, + .members = (type_member_t[]){ + {s8_const_lit("x"), &type__i32, .offset = offsetof(v2i32_t, x)}, + {s8_const_lit("y"), &type__i32, .offset = offsetof(v2i32_t, y)}, + } +}; + +type_t type__v3i32_t = { type_kind_struct, s8_const_lit("v3i32_t"), sizeof(v3i32_t), .count = 3, + .members = (type_member_t[]){ + {s8_const_lit("x"), &type__i32, .offset = offsetof(v3i32_t, x)}, + {s8_const_lit("y"), &type__i32, .offset = offsetof(v3i32_t, y)}, + {s8_const_lit("z"), &type__i32, .offset = offsetof(v3i32_t, z)}, + } +}; + +type_t type__v4i32_t = { type_kind_struct, s8_const_lit("v4i32_t"), sizeof(v4i32_t), .count = 4, + .members = (type_member_t[]){ + {s8_const_lit("x"), &type__i32, .offset = offsetof(v4i32_t, x)}, + {s8_const_lit("y"), &type__i32, .offset = offsetof(v4i32_t, y)}, + {s8_const_lit("z"), &type__i32, .offset = offsetof(v4i32_t, z)}, + {s8_const_lit("w"), &type__i32, .offset = offsetof(v4i32_t, w)}, + } +}; + +type_t type__r3i32_t = { type_kind_struct, s8_const_lit("r3i32_t"), sizeof(r3i32_t), .count = 6, + .members = (type_member_t[]){ + {s8_const_lit("x0"), &type__i32, .offset = offsetof(r3i32_t, x0)}, + {s8_const_lit("y0"), &type__i32, .offset = offsetof(r3i32_t, y0)}, + {s8_const_lit("x0"), &type__i32, .offset = offsetof(r3i32_t, z0)}, + {s8_const_lit("x1"), &type__i32, .offset = offsetof(r3i32_t, x1)}, + {s8_const_lit("y1"), &type__i32, .offset = offsetof(r3i32_t, y1)}, + {s8_const_lit("x1"), &type__i32, .offset = offsetof(r3i32_t, z1)}, + } +}; + +type_member_t members__r2i32_t[] = { + {s8_const_lit("x0"), &type__i32, .offset = offsetof(r2i32_t, x0)}, + {s8_const_lit("y0"), &type__i32, .offset = offsetof(r2i32_t, y0)}, + {s8_const_lit("x1"), &type__i32, .offset = offsetof(r2i32_t, x1)}, + {s8_const_lit("y1"), &type__i32, .offset = offsetof(r2i32_t, y1)}, +}; +DEFINE_STRUCT(r2i32_t); + +type_member_t members__r1i32_t[] = { + {s8_const_lit("x0"), &type__i32, .offset = offsetof(r1i32_t, x0)}, + {s8_const_lit("x1"), &type__i32, .offset = offsetof(r1i32_t, x1)}, +}; +DEFINE_STRUCT(r1i32_t); + + +type_t type__v2i64_t = { type_kind_struct, s8_const_lit("v2i64_t"), sizeof(v2i64_t), .count = 2, + .members = (type_member_t[]){ + {s8_const_lit("x"), &type__i64, .offset = offsetof(v2i64_t, x)}, + {s8_const_lit("y"), &type__i64, .offset = offsetof(v2i64_t, y)}, + } +}; + +type_t type__v3i64_t = { type_kind_struct, s8_const_lit("v3i64_t"), sizeof(v3i64_t), .count = 3, + .members = (type_member_t[]){ + {s8_const_lit("x"), &type__i64, .offset = offsetof(v3i64_t, x)}, + {s8_const_lit("y"), &type__i64, .offset = offsetof(v3i64_t, y)}, + {s8_const_lit("z"), &type__i64, .offset = offsetof(v3i64_t, z)}, + } +}; + +type_t type__v4i64_t = { type_kind_struct, s8_const_lit("v4i64_t"), sizeof(v4i64_t), .count = 4, + .members = (type_member_t[]){ + {s8_const_lit("x"), &type__i64, .offset = offsetof(v4i64_t, x)}, + {s8_const_lit("y"), &type__i64, .offset = offsetof(v4i64_t, y)}, + {s8_const_lit("z"), &type__i64, .offset = offsetof(v4i64_t, z)}, + {s8_const_lit("w"), &type__i64, .offset = offsetof(v4i64_t, w)}, + } +}; + +type_t type__r3i64_t = { type_kind_struct, s8_const_lit("r3i64_t"), sizeof(r3i64_t), .count = 6, + .members = (type_member_t[]){ + {s8_const_lit("x0"), &type__i64, .offset = offsetof(r3i64_t, x0)}, + {s8_const_lit("y0"), &type__i64, .offset = offsetof(r3i64_t, y0)}, + {s8_const_lit("x0"), &type__i64, .offset = offsetof(r3i64_t, z0)}, + {s8_const_lit("x1"), &type__i64, .offset = offsetof(r3i64_t, x1)}, + {s8_const_lit("y1"), &type__i64, .offset = offsetof(r3i64_t, y1)}, + {s8_const_lit("x1"), &type__i64, .offset = offsetof(r3i64_t, z1)}, + } +}; + +type_member_t members__r2i64_t[] = { + {s8_const_lit("x0"), &type__i64, .offset = offsetof(r2i64_t, x0)}, + {s8_const_lit("y0"), &type__i64, .offset = offsetof(r2i64_t, y0)}, + {s8_const_lit("x1"), &type__i64, .offset = offsetof(r2i64_t, x1)}, + {s8_const_lit("y1"), &type__i64, .offset = offsetof(r2i64_t, y1)}, +}; +DEFINE_STRUCT(r2i64_t); + +type_member_t members__r1i64_t[] = { + {s8_const_lit("x0"), &type__i64, .offset = offsetof(r1i64_t, x0)}, + {s8_const_lit("x1"), &type__i64, .offset = offsetof(r1i64_t, x1)}, +}; +DEFINE_STRUCT(r1i64_t); \ No newline at end of file diff --git a/src/core/types.h b/src/core/types.h new file mode 100644 index 0000000..28d7515 --- /dev/null +++ b/src/core/types.h @@ -0,0 +1,538 @@ +typedef uintptr_t usize; +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; + +typedef intptr_t isize; +typedef int64_t i64; +typedef int32_t i32; +typedef int16_t i16; +typedef int8_t i8; + +typedef int64_t b64; +typedef int32_t b32; +typedef int16_t b16; +typedef int8_t b8; + +typedef float f32; +typedef double f64; + +#ifndef true +#define true 1 +#endif +#ifndef false +#define false 0 +#endif + +#define MIN(x,y) ((x) > (y) ? (y) : (x)) +#define MAX(x,y) ((x) > (y) ? (x) : (y)) + +#define CLAMP_TOP(A,X) MIN(A,X) +#define CLAMP_BOT(X,B) MAX(X,B) +#define CLAMP(x,a,b) (((x)<(a))?(a):((x)>(b))?(b):(x)) + +#define set_bit(x) (1ULL << (x)) +#define lengthof(x) (sizeof((x))/sizeof((x)[0])) +#ifndef offsetof +#define offsetof(st, m) ((usize)&(((st *)0)->m)) +#endif + +#define kib(x) (1024ULL * (x##ULL)) +#define mib(x) (1024ULL * kib(x)) +#define gib(x) (1024ULL * mib(x)) + +#define DEFER_LOOP(begin, end) for (int PASTE(_i_, __LINE__) = (begin, 0); !PASTE(_i_, __LINE__); PASTE(_i_, __LINE__) += (end, 1)) +#define STACK(type, size) struct { type data[size]; i32 len; } +#define STACK_PUSH(stack, ...) (assert((stack).len < lengthof((stack).data)), (stack).data[(stack).len++] = __VA_ARGS__) +#define STACK_POP(stack) (assert((stack).len > 0), (stack).data[--(stack).len]) +#define STACK_LAST(stack) (assert((stack).len > 0), (stack).data + ((stack).len-1)) + +#define STRINGIFY_(S) #S +#define STRINGIFY(S) STRINGIFY_(S) +#define PASTE_(a, b) a##b +#define PASTE(a, b) PASTE_(a, b) +#define SWAP(t, a, b) do { t PASTE(temp__, __LINE__) = a; a = b; b = PASTE(temp__, __LINE__); } while(0) +#define CODE(...) #__VA_ARGS__ +#define S8_CODE(...) s8_lit(#__VA_ARGS__) + +#if PLATFORM_CL +#define FILE_AND_LINE __FILE__"("STRINGIFY(__LINE__)")" +#else +#define FILE_AND_LINE __FILE__":"STRINGIFY(__LINE__) +#endif + +#if PLATFORM_CL +#define debug__break() __debugbreak() +#else +#define debug__break() __builtin_trap() +#endif +#define debug_break() (debug__break(), 0) + +#if PLATFORM_ASSERT +#define assert(x) (!(x) && debug_break()) + +#define assertf(x, ...) do {\ + debugf(__VA_ARGS__);\ + assert(x);\ +} while(0) + +#else +#define assert(x) ((void)(x)) +#define assertf(x,...) ((void)(x)) +#endif + +#if PLATFORM_WASM + #define THREAD_LOCAL +#elif PLATFORM_GCC | PLATFORM_CLANG + #define THREAD_LOCAL __thread +#elif PLATFORM_CL + #define THREAD_LOCAL __declspec(thread) +#else + #define THREAD_LOCAL _Thread_local +#endif + +// Single linked list Queue +#define SLLQ_APPEND_MOD(f, l, n, next) \ + do { \ + assert((n)->next == NULL); \ + if ((f) == 0) { \ + (f) = (l) = (n); \ + } else { \ + (l) = (l)->next = (n); \ + } \ + } while (0) +#define SLLQ_APPEND(f, l, n) SLLQ_APPEND_MOD(f, l, n, next) + +#define SLLQ_PREPEND_MOD(f, l, n, next) \ + do { \ + assert((n)->next == NULL); \ + if ((f) == 0) { \ + (f) = (l) = (n); \ + } else { \ + (n)->next = (f); \ + (f) = (n); \ + } \ + } while (0) +#define SLLQ_PREPEND(f, l, n) SLLQ_PREPEND_MOD(f, l, n, next) + +#define SLLQ_REMOVE_FIRST_MOD(f, l, next) \ + do { \ + if ((f) == (l)) { \ + (f) = (l) = 0; \ + } else { \ + (f) = (f)->next; \ + } \ + } while (0) +#define SLLQ_REMOVE_FIRST(f, l) SLLQ_REMOVE_FIRST_MOD(f, l, next) + +// Singly linked list stack +#define SLLS_PUSH_MOD(stack_base, new_stack_base, next) \ + do { \ + (new_stack_base)->next = (stack_base); \ + (stack_base) = (new_stack_base); \ + } while (0) +#define SLLS_PUSH(stack_base, new_stack_base) \ + SLLS_PUSH_MOD(stack_base, new_stack_base, next) + +#define SLLS_POP_AND_STORE(stack_base, out_node) \ + do { \ + if (stack_base) { \ + (out_node) = (stack_base); \ + (stack_base) = (stack_base)->next; \ + (out_node)->next = 0; \ + } \ + } while (0) + +// Doubly linked list Queue +#define DLLQ_APPEND_MOD(f, l, node, next, prev) \ + do { \ + assert((node)->next == NULL); \ + assert((node)->prev == NULL); \ + if ((f) == 0) { \ + (f) = (l) = (node); \ + } else { \ + (l)->next = (node); \ + (node)->prev = (l); \ + (l) = (node); \ + } \ + } while (0) +#define DLLQ_APPEND(f, l, node) DLLQ_APPEND_MOD(f, l, node, next, prev) + +#define DLLQ_PREPEND_MOD(f, l, node, next, prev) \ + do { \ + assert((node)->next == NULL); \ + assert((node)->prev == NULL); \ + if ((f) == 0) { \ + (f) = (l) = (node); \ + } else { \ + (node)->next = (f); \ + (f)->prev = (node); \ + (f) = (node); \ + } \ + } while (0) +#define DLLQ_PREPEND(f, l, node) DLLQ_PREPEND_MOD(f, l, node, next, prev) + +#define DLLQ_CONTAINS(f, l, n, next, prev) for ( + +#define DLLQ_REMOVE_MOD(first, last, node, next, prev) \ + do { \ + if ((first) == (last)) { \ + assert((node) == (first)); \ + (first) = (last) = 0; \ + } else if ((last) == (node)) { \ + (last) = (last)->prev; \ + (last)->next = 0; \ + } else if ((first) == (node)) { \ + (first) = (first)->next; \ + (first)->prev = 0; \ + } else { \ + (node)->prev->next = (node)->next; \ + (node)->next->prev = (node)->prev; \ + } \ + if (node) { \ + (node)->prev = 0; \ + (node)->next = 0; \ + } \ + } while (0) +#define DLLQ_REMOVE(first, last, node) DLLQ_REMOVE_MOD(first, last, node, next, prev) + +// Doubly linked list Stack +#define DLLS_ADD_MOD(first, node, next, prev) \ + do { \ + assert((node)->next == NULL); \ + assert((node)->prev == NULL); \ + (node)->next = (first); \ + if ((first)) \ + (first)->prev = (node); \ + (first) = (node); \ + } while (0) +#define DLLS_ADD(first, node) DLLS_ADD_MOD(first, node, next, prev) +#define DLLS_REMOVE_MOD(first, node, next, prev) \ + do { \ + if ((node) == (first)) { \ + (first) = (first)->next; \ + if ((first)) \ + (first)->prev = 0; \ + } else { \ + (node)->prev->next = (node)->next; \ + if ((node)->next) \ + (node)->next->prev = (node)->prev; \ + } \ + if (node) { \ + (node)->prev = 0; \ + (node)->next = 0; \ + } \ + } while (0) +#define DLLS_REMOVE(first, node) DLLS_REMOVE_MOD(first, node, next, prev) + +const usize ma_page_size = 4096; +const usize ma_default_alignment = sizeof(void *); +const usize ma_default_reserve_size = mib(256); + +typedef struct ma_arena_t ma_arena_t; +struct ma_arena_t { + u8 *data; + usize len; + usize base_len; // to prevent self deleting the arena + usize reserve; + usize commit; + usize align; +}; + +typedef struct ma_temp_t ma_temp_t; +struct ma_temp_t { + ma_arena_t *arena; + usize len; +}; + +void ma_init(ma_arena_t *arena, usize reserve); +ma_arena_t *ma_create(usize reserve); +void ma_destroy(ma_arena_t *arena); + +void *ma_push_size_ex(ma_arena_t *arena, usize size); +void *ma_push_size(ma_arena_t *arena, usize size); +ma_arena_t *ma_push_arena(ma_arena_t *allocator, usize size); + +#define ma_push_type(arena, Type) (Type *)ma_push_size((arena), sizeof(Type)) +#define ma_push_array(arena, Type, count) (Type *)ma_push_size((arena), sizeof(Type) * (count)) + +void ma_set_len(ma_arena_t *arena, usize pos); +void ma_pop(ma_arena_t *arena, usize size); +void ma_set0(ma_arena_t *arena); + +ma_temp_t ma_begin_temp(ma_arena_t *arena); +void ma_end_temp(ma_temp_t temp); + +typedef struct s8_t s8_t; +struct s8_t { + char *str; + int64_t len; +}; + +typedef struct s16_t s16_t; +struct s16_t { + u16 *str; + i64 len; +}; + +typedef struct s32_t s32_t; +struct s32_t { + u32 *str; + i64 len; +}; + +typedef struct sb8_node_t sb8_node_t; +struct sb8_node_t { + sb8_node_t *next; + union { + struct { char *str; int64_t len; }; + s8_t s; + }; +}; + +typedef struct sb8_t sb8_t; +struct sb8_t { + ma_arena_t *arena; + sb8_node_t *first; + sb8_node_t *last; + + int indent; +}; + +i64 wstr_len(wchar_t *string); +int str_len(char *str); + +char char_to_lower_case(char a); +char char_to_upper_case(char a); +b32 char_is_whitespace(char w); +b32 char_is_alphabetic(char a); +b32 char_is_ident(char a); +b32 char_is_digit(char a); +b32 char_is_alphanumeric(char a); + +b32 s8_equal(s8_t a, s8_t b); + +enum { s8_ignore_case = 1 }; + +#define s8(str,len) (s8_t){str, len} +#define s8_nil() (s8_t){0} +#define s8_lit(string) (s8_t){(char *)string, sizeof(string) - 1} +#define s8_const_lit(string) { string, sizeof(string) - 1 } +#define s8_fmtspec(string) (int)(string).len, (string).str + +#define sb8_serial_begin(arena) &(sb8_t){.arena = arena} +#define sb8_serial_end(sb) sb8_merge(sb) + +typedef struct core_desc_t core_desc_t; +struct core_desc_t { + void (*print)(char *string); + void (*panic)(void); + void (*on_exit)(void); + + ma_arena_t scratch[3]; + b32 break_on_panic; +}; +extern THREAD_LOCAL core_desc_t core_desc; +void debugf(const char *string, ...); +void panicf(const char *string, ...); + + +typedef union v2f32_t v2f32_t; +union v2f32_t { + struct { f32 x, y; }; + f32 e[2]; +}; + +typedef union v3f32_t v3f32_t; +union v3f32_t { + struct { f32 x, y, z; }; + struct { v2f32_t xy; }; + struct { f32 _x0; v2f32_t zw; }; + struct { f32 r, g, b; }; + struct { f32 h, s, l; }; + f32 e[3]; +}; + +typedef union v4f32_t v4f32_t; +union v4f32_t { + struct { f32 x, y, z, w; }; + struct { v2f32_t xy; v2f32_t zw; }; + struct { v3f32_t xyz; }; + struct { f32 _x0; f32 yzw; }; + struct { f32 r, g, b, a; }; + struct { f32 h, s, l, _a; }; + f32 e[4]; +}; + +typedef union r1f32_t r1f32_t; +union r1f32_t { + struct { f32 min, max; }; + struct { f32 x0, x1; }; + f32 e[2]; +}; + +typedef union r2f32_t r2f32_t; +union r2f32_t { + struct { v2f32_t min, max; }; + struct { f32 x0, y0, x1, y1; }; + v4f32_t e4; + f32 e[4]; +}; + +typedef union r3f32_t r3f32_t; +union r3f32_t { + struct { v3f32_t min, max; }; + struct { f32 x0, y0, z0, x1, y1, z1; }; + f32 e[6]; +}; + + +typedef union v2f64_t v2f64_t; +union v2f64_t { + struct { f64 x, y; }; + f64 e[2]; +}; + +typedef union v3f64_t v3f64_t; +union v3f64_t { + struct { f64 x, y, z; }; + struct { v2f64_t xy; }; + struct { f64 _x0; v2f64_t zw; }; + struct { f64 r, g, b; }; + struct { f64 h, s, l; }; + f64 e[3]; +}; + +typedef union v4f64_t v4f64_t; +union v4f64_t { + struct { f64 x, y, z, w; }; + struct { v2f64_t xy; v2f64_t zw; }; + struct { v3f64_t xyz; }; + struct { f64 _x0; f64 yzw; }; + struct { f64 r, g, b, a; }; + struct { f64 h, s, l, _a; }; + f64 e[4]; +}; + +typedef union r1f64_t r1f64_t; +union r1f64_t { + struct { f64 min, max; }; + struct { f64 x0, x1; }; + f64 e[2]; +}; + +typedef union r2f64_t r2f64_t; +union r2f64_t { + struct { v2f64_t min, max; }; + struct { f64 x0, y0, x1, y1; }; + v4f64_t e4; + f64 e[4]; +}; + +typedef union r3f64_t r3f64_t; +union r3f64_t { + struct { v3f64_t min, max; }; + struct { f64 x0, y0, z0, x1, y1, z1; }; + f64 e[6]; +}; + + +typedef union v2i32_t v2i32_t; +union v2i32_t { + struct { i32 x, y; }; + i32 e[2]; +}; + +typedef union v3i32_t v3i32_t; +union v3i32_t { + struct { i32 x, y, z; }; + struct { v2i32_t xy; }; + struct { i32 _x0; v2i32_t zw; }; + struct { i32 r, g, b; }; + struct { i32 h, s, l; }; + i32 e[3]; +}; + +typedef union v4i32_t v4i32_t; +union v4i32_t { + struct { i32 x, y, z, w; }; + struct { v2i32_t xy; v2i32_t zw; }; + struct { v3i32_t xyz; }; + struct { i32 _x0; i32 yzw; }; + struct { i32 r, g, b, a; }; + struct { i32 h, s, l, _a; }; + i32 e[4]; +}; + +typedef union r1i32_t r1i32_t; +union r1i32_t { + struct { i32 min, max; }; + struct { i32 x0, x1; }; + i32 e[2]; +}; + +typedef union r2i32_t r2i32_t; +union r2i32_t { + struct { v2i32_t min, max; }; + struct { i32 x0, y0, x1, y1; }; + v4i32_t e4; + i32 e[4]; +}; + +typedef union r3i32_t r3i32_t; +union r3i32_t { + struct { v3i32_t min, max; }; + struct { i32 x0, y0, z0, x1, y1, z1; }; + i32 e[6]; +}; + + +typedef union v2i64_t v2i64_t; +union v2i64_t { + struct { i64 x, y; }; + i64 e[2]; +}; + +typedef union v3i64_t v3i64_t; +union v3i64_t { + struct { i64 x, y, z; }; + struct { v2i64_t xy; }; + struct { i64 _x0; v2i64_t zw; }; + struct { i64 r, g, b; }; + struct { i64 h, s, l; }; + i64 e[3]; +}; + +typedef union v4i64_t v4i64_t; +union v4i64_t { + struct { i64 x, y, z, w; }; + struct { v2i64_t xy; v2i64_t zw; }; + struct { v3i64_t xyz; }; + struct { i64 _x0; i64 yzw; }; + struct { i64 r, g, b, a; }; + struct { i64 h, s, l, _a; }; + i64 e[4]; +}; + +typedef union r1i64_t r1i64_t; +union r1i64_t { + struct { i64 min, max; }; + struct { i64 x0, x1; }; + i64 e[2]; +}; + +typedef union r2i64_t r2i64_t; +union r2i64_t { + struct { v2i64_t min, max; }; + struct { i64 x0, y0, x1, y1; }; + v4i64_t e4; + i64 e[4]; +}; + +typedef union r3i64_t r3i64_t; +union r3i64_t { + struct { v3i64_t min, max; }; + struct { i64 x0, y0, z0, x1, y1, z1; }; + i64 e[6]; +}; diff --git a/src/core/unicode.c b/src/core/unicode.c new file mode 100644 index 0000000..367ddc6 --- /dev/null +++ b/src/core/unicode.c @@ -0,0 +1,226 @@ +typedef struct utf32_result_t utf32_result_t; +struct utf32_result_t { + uint32_t out_str; + int advance; + int error; +}; + +typedef struct utf16_result_t utf16_result_t; +struct utf16_result_t { + uint16_t out_str[2]; + int len; + int error; +}; + +typedef struct utf8_result_t utf8_result_t; +struct utf8_result_t { + uint8_t out_str[4]; + int len; + int error; +}; + +typedef struct utf8_iter_t utf8_iter_t; +struct utf8_iter_t { + char *str; + int len; + int utf8_codepoint_byte_size; + int i; + uint32_t item; +}; + +utf32_result_t utf16_to_utf32(uint16_t *c, int max_advance) { + utf32_result_t result = {0}; + if (max_advance >= 1) { + result.advance = 1; + result.out_str = c[0]; + if (c[0] >= 0xD800 && c[0] <= 0xDBFF && c[1] >= 0xDC00 && c[1] <= 0xDFFF) { + if (max_advance >= 2) { + result.out_str = 0x10000; + result.out_str += (uint32_t)(c[0] & 0x03FF) << 10u | (c[1] & 0x03FF); + result.advance = 2; + } + else + result.error = 2; + } + } + else { + result.error = 1; + } + return result; +} + +utf8_result_t utf32_to_utf8(uint32_t codepoint) { + utf8_result_t result = {0}; + + if (codepoint <= 0x7F) { + result.len = 1; + result.out_str[0] = (char)codepoint; + } + else if (codepoint <= 0x7FF) { + result.len = 2; + result.out_str[0] = 0xc0 | (0x1f & (codepoint >> 6)); + result.out_str[1] = 0x80 | (0x3f & codepoint); + } + else if (codepoint <= 0xFFFF) { // 16 bit word + result.len = 3; + result.out_str[0] = 0xe0 | (0xf & (codepoint >> 12)); // 4 bits + result.out_str[1] = 0x80 | (0x3f & (codepoint >> 6)); // 6 bits + result.out_str[2] = 0x80 | (0x3f & codepoint); // 6 bits + } + else if (codepoint <= 0x10FFFF) { // 21 bit word + result.len = 4; + result.out_str[0] = 0xf0 | (0x7 & (codepoint >> 18)); // 3 bits + result.out_str[1] = 0x80 | (0x3f & (codepoint >> 12)); // 6 bits + result.out_str[2] = 0x80 | (0x3f & (codepoint >> 6)); // 6 bits + result.out_str[3] = 0x80 | (0x3f & codepoint); // 6 bits + } + else { + result.error = 1; + } + + return result; +} + +utf32_result_t utf8_to_utf32(char *c, int max_advance) { + utf32_result_t result = {0}; + + if ((c[0] & 0x80) == 0) { // Check if leftmost zero of first byte is unset + if (max_advance >= 1) { + result.out_str = c[0]; + result.advance = 1; + } + else result.error = 1; + } + + else if ((c[0] & 0xe0) == 0xc0) { + if ((c[1] & 0xc0) == 0x80) { // Continuation byte required + if (max_advance >= 2) { + result.out_str = (uint32_t)(c[0] & 0x1f) << 6u | (c[1] & 0x3f); + result.advance = 2; + } + else result.error = 2; + } + else result.error = 2; + } + + else if ((c[0] & 0xf0) == 0xe0) { + if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80) { // Two continuation bytes required + if (max_advance >= 3) { + result.out_str = (uint32_t)(c[0] & 0xf) << 12u | (uint32_t)(c[1] & 0x3f) << 6u | (c[2] & 0x3f); + result.advance = 3; + } + else result.error = 3; + } + else result.error = 3; + } + + else if ((c[0] & 0xf8) == 0xf0) { + if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80 && (c[3] & 0xc0) == 0x80) { // Three continuation bytes required + if (max_advance >= 4) { + result.out_str = (uint32_t)(c[0] & 0xf) << 18u | (uint32_t)(c[1] & 0x3f) << 12u | (uint32_t)(c[2] & 0x3f) << 6u | (uint32_t)(c[3] & 0x3f); + result.advance = 4; + } + else result.error = 4; + } + else result.error = 4; + } + else result.error = 4; + + return result; +} + +utf16_result_t utf32_to_utf16(uint32_t codepoint) { + utf16_result_t result = {0}; + if (codepoint < 0x10000) { + result.out_str[0] = (uint16_t)codepoint; + result.out_str[1] = 0; + result.len = 1; + } + else if (codepoint <= 0x10FFFF) { + uint32_t code = (codepoint - 0x10000); + result.out_str[0] = (uint16_t)(0xD800 | (code >> 10)); + result.out_str[1] = (uint16_t)(0xDC00 | (code & 0x3FF)); + result.len = 2; + } + else { + result.error = 1; + } + + return result; +} + +#define UTF__HANDLE_DECODE_ERROR(question_mark) \ + { \ + if (outlen < buffer_size - 1) buffer[outlen++] = (question_mark); \ + break; \ + } + +int64_t str_from_wstr(char *buffer, int64_t buffer_size, uint16_t *in, int64_t inlen) { + int64_t outlen = 0; + for (int64_t i = 0; i < inlen && in[i];) { + utf32_result_t decode = utf16_to_utf32((uint16_t *)(in + i), (int)(inlen - i)); + if (!decode.error) { + i += decode.advance; + utf8_result_t encode = utf32_to_utf8(decode.out_str); + if (!encode.error) { + for (int64_t j = 0; j < encode.len; j++) { + if (outlen < buffer_size - 1) { + buffer[outlen++] = encode.out_str[j]; + } + } + } + else UTF__HANDLE_DECODE_ERROR('?'); + } + else UTF__HANDLE_DECODE_ERROR('?'); + } + + buffer[outlen] = 0; + return outlen; +} + +int64_t wstr_from_str(uint16_t *buffer, int64_t buffer_size, char *in, int64_t inlen) { + int64_t outlen = 0; + for (int64_t i = 0; i < inlen;) { + utf32_result_t decode = utf8_to_utf32(in + i, (int)(inlen - i)); + if (!decode.error) { + i += decode.advance; + utf16_result_t encode = utf32_to_utf16(decode.out_str); + if (!encode.error) { + for (int64_t j = 0; j < encode.len; j++) { + if (outlen < buffer_size - 1) { + buffer[outlen++] = encode.out_str[j]; + } + } + } + else UTF__HANDLE_DECODE_ERROR(0x003f); + } + else UTF__HANDLE_DECODE_ERROR(0x003f); + } + + buffer[outlen] = 0; + return outlen; +} + +void utf8_advance(utf8_iter_t *iter) { + iter->i += iter->utf8_codepoint_byte_size; + utf32_result_t r = utf8_to_utf32(iter->str + iter->i, iter->len - iter->i); + if (r.error) { + iter->item = 0; + return; + } + + iter->utf8_codepoint_byte_size = r.advance; + iter->item = r.out_str; +} + +utf8_iter_t utf8_iterate_ex(char *str, int len) { + utf8_iter_t result = {str, len}; + if (len) utf8_advance(&result); + return result; +} + +utf8_iter_t utf8_iterate(char *str) { + int length = 0; + while (str[length]) length += 1; + return utf8_iterate_ex(str, length); +} \ No newline at end of file diff --git a/src/core_test/core_test_entry.c b/src/core_test/core_test_entry.c new file mode 100644 index 0000000..799c6ed --- /dev/null +++ b/src/core_test/core_test_entry.c @@ -0,0 +1,110 @@ +#include "core/core.h" +#include "core/core.c" + +void test_s8(void) { + ma_arena_t *arena = ma_create(ma_default_reserve_size); + + { + ma_temp_t temp = ma_begin_temp(arena); + sb8_t *sb = &(sb8_t){arena}; + + s8_t memes = s8_lit("memes"); + sb8_printf(sb, "%S", memes); + assert(sb->first == sb->last); + assert(sb->first->len == 5); + assert(s8_equal(sb->first->s, memes)); + + sb8_printf(sb, "%S", s8_lit("things are going fine")); + s8_t string = sb8_merge(sb); + assert(s8_equal(string, s8_lit("memesthings are going fine"))); + + ma_end_temp(temp); + } + + { + s8_t str = s8_lit("thing|another|"); + sb8_t sb = s8_split(arena, str, s8_lit("|"), s8_split_none); + + assert(s8_equal(sb.first->s, s8_lit("thing"))); + assert(s8_equal(sb.first->next->s, s8_lit("another"))); + assert(sb.first->next->next == NULL); + } + + { + s8_t str = s8_lit("thing|another|"); + sb8_t sb = s8_split(arena, str, s8_lit("|"), s8_split_inclusive); + + assert(s8_equal(sb.first->s, s8_lit("thing"))); + assert(s8_equal(sb.first->next->s, s8_lit("|"))); + assert(s8_equal(sb.first->next->next->s, s8_lit("another"))); + assert(s8_equal(sb.first->next->next->next->s, s8_lit("|"))); + assert(sb.first->next->next->next->next == NULL); + } + + { + s8_t str = s8_lit("aabaaBaa"); + sb8_t sb = s8_split(arena, str, s8_lit("b"), s8_split_inclusive | s8_split_ignore_case); + + assert(s8_equal(sb.first->s, s8_lit("aa"))); + assert(s8_equal(sb.first->next->s, s8_lit("b"))); + assert(s8_equal(sb.first->next->next->s, s8_lit("aa"))); + assert(s8_equal(sb.first->next->next->next->s, s8_lit("B"))); + assert(s8_equal(sb.first->next->next->next->next->s, s8_lit("aa"))); + assert(sb.first->next->next->next->next->next == NULL); + } + + { + s8_t str = s8_lit("aabaaBaa"); + sb8_t sb = s8_split(arena, str, s8_lit("b"), s8_split_inclusive); + + assert(s8_equal(sb.first->s, s8_lit("aa"))); + assert(s8_equal(sb.first->next->s, s8_lit("b"))); + assert(s8_equal(sb.first->next->next->s, s8_lit("aaBaa"))); + } + + { + s8_t s = s8_lit("0123456789"); + assert(s8_equal(s8_slice(s, 0, 4), s8_lit("0123"))); + assert(s8_equal(s8_slice(s, -2, -1), s8_lit("89"))); + assert(s8_equal(s8_slice(s, -2, 10), s8_lit("89"))); + assert(s8_equal(s8_slice(s, 8, 10), s8_lit("89"))); + } + + { + s8_t s = s8_lit(" a \n"); + s = s8_trim(s); + assert(s8_equal(s, s8_lit("a"))); + } + + { + s8_t s = s8_lit("C:/memes/the_thing.c"); + s8_t ss = s8_get_name_no_ext(s); + assert(s8_equal(ss, s8_lit("the_thing"))); + } + + { + s8_t s = s8_fmt(arena, "%d%Sv%s", 32, s8_lit("|"), ">"); + assert(s8_equal(s, s8_lit("32|v>"))); + } + + ma_destroy(arena); +} + +#include + +int main(int argc, char **argv) { + printf("PLATFORM_WASM = %d\n", PLATFORM_WASM); + printf("PLATFORM_WINDOWS = %d\n", PLATFORM_WINDOWS); + printf("PLATFORM_LINUX = %d\n", PLATFORM_LINUX); + printf("PLATFORM_POSIX = %d\n", PLATFORM_POSIX); + printf("PLATFORM_MAC_OS = %d\n", PLATFORM_MAC_OS); + printf("PLATFORM_CLANG = %d\n", PLATFORM_CLANG); + printf("PLATFORM_GCC = %d\n", PLATFORM_GCC); + printf("PLATFORM_CL = %d\n", PLATFORM_CL); + printf("PLATFORM_TCC = %d\n", PLATFORM_TCC); + printf("PLATFORM_ASSERT = %d\n", PLATFORM_ASSERT); + + test_s8(); + + printf("all done!\n"); +} \ No newline at end of file diff --git a/src/meta/build_tool.c b/src/meta/build_tool.c new file mode 100644 index 0000000..1b42795 --- /dev/null +++ b/src/meta/build_tool.c @@ -0,0 +1,5187 @@ +/* +@echo off + +if not exist build\build_tool.exe ( + mkdir build + pushd build + cl ..\core\build_tool.c -Fe:build_tool.exe -Zi -FC -nologo + popd +) + +build\build_tool.exe + +///////////// +#define BUILD_TOOL_LIB +#include "core/build_tool.c" + +int main(int argc, char **argv) { + return 0; +} +*/ + +#ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS +#endif + +#ifndef FIRST_CORE_HEADER +#define FIRST_CORE_HEADER + + #if defined(__APPLE__) && defined(__MACH__) + #define OS_MAC 1 + #elif defined(_WIN32) + #define OS_WINDOWS 1 + #elif defined(__linux__) + #define OS_POSIX 1 + #define OS_LINUX 1 + #elif OS_WASM + #else + #error Unsupported platform + #endif + + #if defined(__clang__) + #define COMPILER_CLANG 1 + #elif defined(__GNUC__) || defined(__GNUG__) + #define COMPILER_GCC 1 + #elif defined(_MSC_VER) + #define COMPILER_MSVC 1 + #elif defined(__TINYC__) + #define COMPILER_TCC 1 + #else + #error Unsupported compiler + #endif + + #ifndef OS_MAC + #define OS_MAC 0 + #endif + + #ifndef OS_WINDOWS + #define OS_WINDOWS 0 + #endif + + #ifndef OS_LINUX + #define OS_LINUX 0 + #endif + + #ifndef OS_POSIX + #define OS_POSIX 0 + #endif + + #ifndef COMPILER_MSVC + #define COMPILER_MSVC 0 + #endif + + #ifndef COMPILER_CLANG + #define COMPILER_CLANG 0 + #endif + + #ifndef COMPILER_GCC + #define COMPILER_GCC 0 + #endif + + #ifndef COMPILER_TCC + #define COMPILER_TCC 0 + #endif + + #if COMPILER_MSVC + #define FORCE_INLINE __forceinline + #elif COMPILER_GCC || COMPILER_CLANG + #define FORCE_INLINE __attribute__((always_inline)) inline + #else + #define FORCE_INLINE inline + #endif + + #if OS_MAC + #define IF_MAC(x) x + #else + #define IF_MAC(x) + #endif + + #if OS_WINDOWS + #define IF_WINDOWS(x) x + #define IF_WINDOWS_ELSE(x, y) x + #else + #define IF_WINDOWS(x) + #define IF_WINDOWS_ELSE(x, y) y + #endif + + #if OS_LINUX + #define IF_LINUX(x) x + #define IF_LINUX_ELSE(x, y) x + #else + #define IF_LINUX(x) + #define IF_LINUX_ELSE(x, y) y + #endif + + #if OS_WINDOWS + #define OS_NAME "windows" + #elif OS_LINUX + #define OS_NAME "linux" + #elif OS_MAC + #define OS_NAME "mac_os" + #elif OS_WASM + #define OS_NAME "wasm" + #else + #error couldnt figure out OS + #endif + + #ifndef THREAD_LOCAL + #if defined(__cplusplus) && __cplusplus >= 201103L + #define THREAD_LOCAL thread_local + #elif defined(__GNUC__) + #define THREAD_LOCAL __thread + #elif defined(_MSC_VER) + #define THREAD_LOCAL __declspec(thread) + #elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) + #define THREAD_LOCAL _Thread_local + #elif defined(__TINYC__) + #define THREAD_LOCAL _Thread_local + #else + #error Couldnt figure out thread local, needs to be provided manually + #endif + #endif +#endif + + +#ifndef FIRST_IO_HEADER + #define FIRST_IO_HEADER + #include + +typedef enum IO_ErrorResult { + IO_ErrorResult_Continue, + IO_ErrorResult_Break, + IO_ErrorResult_Exit, +} IO_ErrorResult; + + #if defined(_MSC_VER) + #define IO_DebugBreak() (__debugbreak(), 0) + #else + #define IO_DebugBreak() (__builtin_trap(), 0) + #endif + +typedef void IO_MessageHandler(int kind, const char *file, int line, char *str, int len); +extern THREAD_LOCAL void (*IO_User_OutputMessage)(int kind, const char *file, int line, char *str, int len); + + #define IO__STRINGIFY(x) #x + #define IO__TOSTRING(x) IO__STRINGIFY(x) + #define IO_LINE IO__TOSTRING(__LINE__) + + #define IO_Assert(x) !(x) && IO__FatalError((__FILE__ "(" IO_LINE "): " \ + "error: " #x "\n")) && \ + IO_DebugBreak() + #define IO_FatalErrorf(...) \ + do { \ + bool result = IO__FatalErrorf(__FILE__, __LINE__, __VA_ARGS__); \ + if (result) IO_DebugBreak(); \ + } while (0) + #define IO_FatalError(...) \ + do { \ + bool result = IO__FatalError(__FILE__ "(" IO_LINE "): error - " __VA_ARGS__); \ + if (result) IO_DebugBreak(); \ + } while (0) + #define IO_Assertf(x, ...) \ + do { \ + if (!(x)) { \ + bool result = IO__FatalErrorf(__FILE__, __LINE__, __VA_ARGS__); \ + if (result) IO_DebugBreak(); \ + } \ + } while (0) + + #define IO_InvalidElseIf(c) \ + else if (c) { \ + IO_InvalidCodepath(); \ + } + #define IO_InvalidElse() \ + else { \ + IO_InvalidCodepath(); \ + } + #define IO_InvalidCodepath() IO_FatalError("This codepath is invalid") + #define IO_InvalidDefaultCase() \ + default: { \ + IO_FatalError("Entered invalid switch statement case"); \ + } + #define IO_Todo() IO_FatalError("This codepath is not implemented yet") + +bool IO__FatalErrorf(const char *file, int line, const char *msg, ...); +void IO__Printf(int kind, const char *file, int line, const char *msg, ...); +bool IO__FatalError(const char *msg); +void IO_Print(int kind, const char *file, int line, char *msg, int len); +void IO_OutputMessage(char *str, int len); +IO_ErrorResult IO_OutputError(char *str, int len); +void IO_Exit(int error_code); +bool IO_IsDebuggerPresent(void); + +static const int IO_KindPrintf = 1; +static const int IO_KindWarningf = 2; + + #define IO_Printf(...) IO__Printf(IO_KindPrintf, __FILE__, __LINE__, __VA_ARGS__) + #define IO_Warningf(...) IO__Printf(IO_KindWarningf, __FILE__, __LINE__, __VA_ARGS__) +#endif + +#include + +#ifndef IO_SNPRINTF + #include + #define IO_SNPRINTF snprintf +#endif + +#ifndef IO_VSNPRINTF + #include + #define IO_VSNPRINTF vsnprintf +#endif + +#ifndef IO_ALLOCATE + #include + #define IO_ALLOCATE(x) malloc(x) + #define IO_FREE(x) free(x) +#endif + +static int IO_Strlen(char *string) { + int len = 0; + while (*string++ != 0) len++; + return len; +} + +THREAD_LOCAL void (*IO_User_OutputMessage)(int kind, const char *file, int line, char *str, int len); + +bool IO__FatalErrorf(const char *file, int line, const char *msg, ...) { + va_list args1; + va_list args2; + char buff[2048]; + + va_start(args1, msg); + va_copy(args2, args1); + int size = IO_VSNPRINTF(buff, sizeof(buff), msg, args2); + va_end(args2); + + char *new_buffer = 0; + char *user_message = buff; + if (size >= sizeof(buff)) { + size += 4; + new_buffer = (char *)IO_ALLOCATE(size); + IO_VSNPRINTF(new_buffer, size, msg, args1); + user_message = new_buffer; + } + va_end(args1); + + IO_ErrorResult ret = IO_ErrorResult_Continue; + { + char buff2[2048]; + char *result = buff2; + char *b = 0; + int size2 = IO_SNPRINTF(buff2, sizeof(buff2), "%s(%d): error: %s \n", file, line, user_message); + if (size2 >= sizeof(buff2)) { + size2 += 4; + b = (char *)IO_ALLOCATE(size2); + size2 = IO_SNPRINTF(b, size2, "%s(%d): error: %s \n", file, line, user_message); + result = b; + } + + ret = IO_OutputError(result, size2); + if (ret == IO_ErrorResult_Exit) { + IO_Exit(1); + } + + if (b) { + IO_FREE(b); + } + } + + if (new_buffer) { + IO_FREE(new_buffer); + } + + return ret == IO_ErrorResult_Break; +} + +void IO__Printf(int kind, const char *file, int line, const char *msg, ...) { + // First try to use a static buffer. That can fail because the message + // can be bigger then the buffer. Allocate enough memory to fit in that + // case. + va_list args1; + va_list args2; + char buff[2048]; + + va_start(args1, msg); + va_copy(args2, args1); + int size = IO_VSNPRINTF(buff, sizeof(buff), msg, args2); + va_end(args2); + + char *new_buffer = 0; + char *result = buff; + if (size >= sizeof(buff)) { + size += 4; + new_buffer = (char *)IO_ALLOCATE(size); + IO_VSNPRINTF(new_buffer, size, msg, args1); + result = new_buffer; + } + va_end(args1); + + if (IO_User_OutputMessage) { + IO_User_OutputMessage(kind, file, line, result, size); + } else { + IO_OutputMessage(result, size); + } + + if (new_buffer) { + IO_FREE(new_buffer); + } +} + +bool IO__FatalError(const char *msg) { + int len = IO_Strlen((char *)msg); + IO_ErrorResult result = IO_OutputError((char *)msg, len); + if (result == IO_ErrorResult_Exit) { + IO_Exit(1); + } + return result == IO_ErrorResult_Break; +} + +void IO_Print(int kind, const char *file, int line, char *msg, int len) { + if (IO_User_OutputMessage) { + IO_User_OutputMessage(kind, file, line, msg, len); + } else { + IO_OutputMessage(msg, len); + } +} +#ifdef _WIN32 + #ifndef NOMINMAX + #define NOMINMAX + #endif + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #include + + #pragma comment(lib, "user32") + + #include + +bool IO_IsDebuggerPresent(void) { + return IsDebuggerPresent(); +} + +void IO_OutputMessage(char *str, int len) { + if (IsDebuggerPresent()) { + OutputDebugStringA(str); + } + printf("%.*s", len, str); + fflush(stdout); +} + +IO_ErrorResult IO_OutputError(char *str, int len) { + IO_ErrorResult result = IO_ErrorResult_Continue; + IO_OutputMessage(str, len); + + char *msg = str; + if (str[len] != 0) { + msg = (char *)IO_ALLOCATE(len + 1); + for (int i = 0; i < len; i += 1) msg[i] = str[i]; + msg[len] = 0; + } + + OutputDebugStringA(msg); + if (!IsDebuggerPresent()) { + + // Limit size of error output message + char tmp = 0; + if (len > 4096) { + tmp = str[4096]; + str[4096] = 0; + } + + MessageBoxA(0, msg, "Error!", 0); + + if (tmp != 0) { + str[4096] = tmp; + } + + result = IO_ErrorResult_Exit; + } else { + result = IO_ErrorResult_Break; + } + + if (msg != str) { + IO_FREE(msg); + } + + return result; +} + +void IO_Exit(int error_code) { + ExitProcess(error_code); +} +#elif __linux__ || __unix__ || __APPLE__ + #include +IO_ErrorResult IO_OutputError(char *str, int len) { + fprintf(stderr, "%.*s", len, str); + return IO_ErrorResult_Exit; +} + +void IO_OutputMessage(char *str, int len) { + fprintf(stdout, "%.*s", len, str); +} + +void IO_Exit(int error_code) { + exit(error_code); +} + +bool IO_IsDebuggerPresent(void) { + return false; +} +#else +IO_ErrorResult IO_OutputError(char *str, int len) { + return IO_ErrorResult_Exit; +} + +void IO_OutputMessage(char *str, int len) { +} + +void IO_Exit(int error_code) { +} + +bool IO_IsDebuggerPresent(void) { + return false; +} + +#endif // LIBC + +#ifndef MA_HEADER + #define MA_HEADER + #include + #include + #include + + #define MA_KIB(x) ((x##ull) * 1024ull) + #define MA_MIB(x) (MA_KIB(x) * 1024ull) + #define MA_GIB(x) (MA_MIB(x) * 1024ull) + #define MA_TIB(x) (MA_GIB(x) * 1024ull) + +typedef struct MV_Memory MV_Memory; +typedef struct MA_Temp MA_Temp; +typedef struct MA_Arena MA_Arena; +typedef struct MA_SourceLoc MA_SourceLoc; + + #ifndef MA_DEFAULT_RESERVE_SIZE + #define MA_DEFAULT_RESERVE_SIZE MA_GIB(1) + #endif + + #ifndef MA_DEFAULT_ALIGNMENT + #define MA_DEFAULT_ALIGNMENT 8 + #endif + + #ifndef MA_COMMIT_ADD_SIZE + #define MA_COMMIT_ADD_SIZE MA_MIB(4) + #endif + + #ifndef MA_ZERO_IS_INITIALIZATION + #define MA_ZERO_IS_INITIALIZATION 1 + #endif + + #ifndef MA_MemoryZero + #include + #define MA_MemoryZero(p, size) memset(p, 0, size) + #endif + + #ifndef MA_MemoryCopy + #include + #define MA_MemoryCopy(dst, src, size) memcpy(dst, src, size); + #endif + +struct MV_Memory { + size_t commit; + size_t reserve; + uint8_t *data; +}; + +struct MA_Arena { + MV_Memory memory; + int alignment; + size_t len; + size_t base_len; // When popping to 0 this is the minimum "len" value + // It's so that Bootstrapped arena won't delete itself when Reseting. +}; + +struct MA_Temp { + MA_Arena *arena; + size_t pos; +}; + +struct MA_SourceLoc { + const char *file; + int line; +}; + +extern THREAD_LOCAL MA_SourceLoc MA_SavedSourceLoc; + #define MA_SaveSourceLoc() MA_SaveSourceLocEx(__FILE__, __LINE__) +void MA_SaveSourceLocEx(const char *file, int line); + + #define MA_PushSize(a, size) MA__PushSize(a, size) + #define MA_PushSizeNonZeroed(a, size) MA__PushSizeNonZeroed(a, size) + #define MA_PushCopy(a, p, size) MA__PushCopy(a, p, size) + #define MA_PushStringCopy(a, p, size) MA__PushStringCopy(a, p, size) + + #define MA_PushArrayNonZeroed(a, T, c) (T *)MA__PushSizeNonZeroed(a, sizeof(T) * (c)) + #define MA_PushStructNonZeroed(a, T) (T *)MA__PushSizeNonZeroed(a, sizeof(T)) + #define MA_PushStruct(a, T) (T *)MA__PushSize(a, sizeof(T)) + #define MA_PushArray(a, T, c) (T *)MA__PushSize(a, sizeof(T) * (c)) + #define MA_PushStructCopy(a, T, p) (T *)MA__PushCopy(a, (p), sizeof(T)) + +// clang-format off +void MA_InitEx(MA_Arena *a, size_t reserve); +void MA_Init(MA_Arena *a); +MA_Arena MA_Create(); +void MA_MakeSureInitialized(MA_Arena *a); +void MA_InitFromBuffer(MA_Arena *arena, void *buffer, size_t size); +MA_Arena MA_MakeFromBuffer(void *buffer, size_t size); +MA_Arena * MA_Bootstrap(void); +MA_Arena MA_PushArena(MA_Arena *arena, size_t size); +MA_Arena * MA_PushArenaP(MA_Arena *arena, size_t size); + +void * MA__PushSizeNonZeroed(MA_Arena *a, size_t size); +void * MA__PushSize(MA_Arena *arena, size_t size); +char * MA__PushStringCopy(MA_Arena *arena, char *p, size_t size); +void * MA__PushCopy(MA_Arena *arena, void *p, size_t size); +MA_Temp MA_BeginTemp(MA_Arena *arena); +void MA_EndTemp(MA_Temp checkpoint); + +void MA_PopToPos(MA_Arena *arena, size_t pos); +void MA_PopSize(MA_Arena *arena, size_t size); +void MA_DeallocateArena(MA_Arena *arena); +void MA_Reset(MA_Arena *arena); + +size_t MA_GetAlignOffset(size_t size, size_t align); +size_t MA_AlignUp(size_t size, size_t align); +size_t MA_AlignDown(size_t size, size_t align); + +bool MA_IsPointerInside(MA_Arena *arena, void *p); +void MA_SetAlignment(MA_Arena *arena, int alignment); +uint8_t * MA_GetTop(MA_Arena *a); + +MV_Memory MV_Reserve(size_t size); +bool MV_Commit(MV_Memory *m, size_t commit); +void MV_Deallocate(MV_Memory *m); +bool MV_DecommitPos(MV_Memory *m, size_t pos); +// clang-format on + +extern THREAD_LOCAL MA_Arena MA_ScratchArenaPool[4]; + #define MA_CheckpointScope(name, InArena) for (MA_Temp name = MA_BeginTemp(InArena); name.arena; (MA_EndTemp(name), name.arena = 0)) + #define MA_ScratchScope(x) for (MA_Temp x = MA_GetScratch(); x.arena; (MA_ReleaseScratch(x), x.arena = 0)) + #define MA_ReleaseScratch MA_EndTemp +MA_Temp MA_GetScratchEx(MA_Arena **conflicts, int conflict_count); +MA_Temp MA_GetScratch(void); +MA_Temp MA_GetScratch1(MA_Arena *conflict); + + #if defined(__cplusplus) +struct MA_Scratch { + MA_Temp checkpoint; + MA_Scratch() { this->checkpoint = MA_GetScratch(); } + MA_Scratch(MA_Temp conflict) { this->checkpoint = MA_GetScratch1(conflict.arena); } + MA_Scratch(MA_Temp c1, MA_Temp c2) { + MA_Arena *conflicts[] = {c1.arena, c2.arena}; + this->checkpoint = MA_GetScratchEx(conflicts, 2); + } + ~MA_Scratch() { MA_EndTemp(checkpoint); } + operator MA_Arena *() { return checkpoint.arena; } + + private: // @Note: Disable copy constructors, cause its error prone + MA_Scratch(MA_Scratch &arena); + MA_Scratch(MA_Scratch &arena, MA_Scratch &a2); +}; + #endif // __cplusplus + + #define MA_IS_POW2(x) (((x) & ((x)-1)) == 0) + #define MA_MIN(x, y) ((x) <= (y) ? (x) : (y)) + #define MA_MAX(x, y) ((x) >= (y) ? (x) : (y)) + #define MA_Lengthof(x) ((int64_t)((sizeof(x) / sizeof((x)[0])))) + + #define MA_CLAMP_TOP(x, max) ((x) >= (max) ? (max) : (x)) + #define MA_CLAMP_BOT(x, min) ((x) <= (min) ? (min) : (x)) + #define MA_CLAMP(x, min, max) ((x) >= (max) ? (max) : (x) <= (min) ? (min) \ + : (x)) + +#endif // MA_HEADER + +#define MA_Assertf(x, ...) IO_Assertf(x, __VA_ARGS__) + #ifndef MA_Assertf + #include + #define MA_Assertf(x, ...) assert(x) +#endif + +#ifndef MA_StaticFunc + #if defined(__GNUC__) || defined(__clang__) + #define MA_StaticFunc __attribute__((unused)) static + #else + #define MA_StaticFunc static + #endif +#endif + +#if defined(MA_USE_ADDRESS_SANITIZER) + #include +#endif + +#if !defined(ASAN_POISON_MEMORY_REGION) + #define MA_ASAN_POISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size)) + #define MA_ASAN_UNPOISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size)) +#else + #define MA_ASAN_POISON_MEMORY_REGION(addr, size) ASAN_POISON_MEMORY_REGION(addr, size) + #define MA_ASAN_UNPOISON_MEMORY_REGION(addr, size) ASAN_UNPOISON_MEMORY_REGION(addr, size) +#endif + +THREAD_LOCAL MA_SourceLoc MA_SavedSourceLoc; +void MA_SaveSourceLocEx(const char *file, int line) { + MA_SavedSourceLoc.file = file; + MA_SavedSourceLoc.line = line; +} + +size_t MA_GetAlignOffset(size_t size, size_t align) { + size_t mask = align - 1; + size_t val = size & mask; + if (val) { + val = align - val; + } + return val; +} + +size_t MA_AlignUp(size_t size, size_t align) { + size_t result = size + MA_GetAlignOffset(size, align); + return result; +} + +size_t MA_AlignDown(size_t size, size_t align) { + size += 1; // Make sure when align is 8 doesn't get rounded down to 0 + size_t result = size - (align - MA_GetAlignOffset(size, align)); + return result; +} + +MA_StaticFunc uint8_t *MV__AdvanceCommit(MV_Memory *m, size_t *commit_size, size_t page_size) { + size_t aligned_up_commit = MA_AlignUp(*commit_size, page_size); + size_t to_be_total_commited_size = aligned_up_commit + m->commit; + size_t to_be_total_commited_size_clamped_to_reserve = MA_CLAMP_TOP(to_be_total_commited_size, m->reserve); + size_t adjusted_to_boundary_commit = to_be_total_commited_size_clamped_to_reserve - m->commit; + MA_Assertf(adjusted_to_boundary_commit, "Reached the virtual memory reserved boundary"); + *commit_size = adjusted_to_boundary_commit; + + if (adjusted_to_boundary_commit == 0) { + return 0; + } + uint8_t *result = m->data + m->commit; + return result; +} + +void MA_PopToPos(MA_Arena *arena, size_t pos) { + MA_Assertf(arena->len >= arena->base_len, "Bug: arena->len shouldn't ever be smaller then arena->base_len"); + pos = MA_CLAMP(pos, arena->base_len, arena->len); + size_t size = arena->len - pos; + arena->len = pos; + MA_ASAN_POISON_MEMORY_REGION(arena->memory.data + arena->len, size); +} + +void MA_PopSize(MA_Arena *arena, size_t size) { + MA_PopToPos(arena, arena->len - size); +} + +void MA_DeallocateArena(MA_Arena *arena) { + MV_Deallocate(&arena->memory); +} + +void MA_Reset(MA_Arena *arena) { + MA_PopToPos(arena, 0); +} + +MA_StaticFunc size_t MA__AlignLen(MA_Arena *a) { + size_t align_offset = a->alignment ? MA_GetAlignOffset((uintptr_t)a->memory.data + (uintptr_t)a->len, a->alignment) : 0; + size_t aligned = a->len + align_offset; + return aligned; +} + +void MA_SetAlignment(MA_Arena *arena, int alignment) { + arena->alignment = alignment; +} + +uint8_t *MA_GetTop(MA_Arena *a) { + MA_Assertf(a->memory.data, "Arena needs to be inited, there is no top to get!"); + return a->memory.data + a->len; +} + +void *MA__PushSizeNonZeroed(MA_Arena *a, size_t size) { + size_t align_offset = a->alignment ? MA_GetAlignOffset((uintptr_t)a->memory.data + (uintptr_t)a->len, a->alignment) : 0; + size_t aligned_len = a->len + align_offset; + size_t size_with_alignment = size + align_offset; + + if (a->len + size_with_alignment > a->memory.commit) { + if (a->memory.reserve == 0) { +#if MA_ZERO_IS_INITIALIZATION + MA_Init(a); +#else + MA_Assertf(0, "Pushing on uninitialized arena with zero initialization turned off"); +#endif + } + bool result = MV_Commit(&a->memory, size_with_alignment + MA_COMMIT_ADD_SIZE); + MA_Assertf(result, "%s(%d): Failed to commit memory more memory! reserve: %zu commit: %zu len: %zu size_with_alignment: %zu", MA_SavedSourceLoc.file, MA_SavedSourceLoc.line, a->memory.reserve, a->memory.commit, a->len, size_with_alignment); + (void)result; + } + + uint8_t *result = a->memory.data + aligned_len; + a->len += size_with_alignment; + MA_Assertf(a->len <= a->memory.commit, "%s(%d): Reached commit boundary! reserve: %zu commit: %zu len: %zu base_len: %zu alignment: %d size_with_alignment: %zu", MA_SavedSourceLoc.file, MA_SavedSourceLoc.line, a->memory.reserve, a->memory.commit, a->len, a->base_len, a->alignment, size_with_alignment); + MA_ASAN_UNPOISON_MEMORY_REGION(result, size); + return (void *)result; +} + +void *MA__PushSize(MA_Arena *arena, size_t size) { + void *result = MA__PushSizeNonZeroed(arena, size); + MA_MemoryZero(result, size); + return result; +} + +char *MA__PushStringCopy(MA_Arena *arena, char *p, size_t size) { + char *copy_buffer = (char *)MA__PushSizeNonZeroed(arena, size + 1); + MA_MemoryCopy(copy_buffer, p, size); + copy_buffer[size] = 0; + return copy_buffer; +} + +void *MA__PushCopy(MA_Arena *arena, void *p, size_t size) { + void *copy_buffer = MA__PushSizeNonZeroed(arena, size); + MA_MemoryCopy(copy_buffer, p, size); + return copy_buffer; +} + +MA_Arena MA_PushArena(MA_Arena *arena, size_t size) { + MA_Arena result; + MA_MemoryZero(&result, sizeof(result)); + result.memory.data = MA_PushArrayNonZeroed(arena, uint8_t, size); + result.memory.commit = size; + result.memory.reserve = size; + result.alignment = arena->alignment; + return result; +} + +MA_Arena *MA_PushArenaP(MA_Arena *arena, size_t size) { + MA_Arena *result = MA_PushStruct(arena, MA_Arena); + *result = MA_PushArena(arena, size); + return result; +} + +void MA_InitEx(MA_Arena *a, size_t reserve) { + a->memory = MV_Reserve(reserve); + MA_ASAN_POISON_MEMORY_REGION(a->memory.data, a->memory.reserve); + a->alignment = MA_DEFAULT_ALIGNMENT; +} + +void MA_Init(MA_Arena *a) { + MA_InitEx(a, MA_DEFAULT_RESERVE_SIZE); +} + +void MA_MakeSureInitialized(MA_Arena *a) { + if (a->memory.data == 0) { + MA_Init(a); + } +} + +MA_Arena *MA_Bootstrap(void) { + MA_Arena bootstrap_arena = {0}; + MA_Arena *arena = MA_PushStruct(&bootstrap_arena, MA_Arena); + *arena = bootstrap_arena; + arena->base_len = arena->len; + return arena; +} + +void MA_InitFromBuffer(MA_Arena *arena, void *buffer, size_t size) { + arena->memory.data = (uint8_t *)buffer; + arena->memory.commit = size; + arena->memory.reserve = size; + arena->alignment = MA_DEFAULT_ALIGNMENT; + MA_ASAN_POISON_MEMORY_REGION(arena->memory.data, arena->memory.reserve); +} + +MA_Arena MA_MakeFromBuffer(void *buffer, size_t size) { + MA_Arena arena; + MA_MemoryZero(&arena, sizeof(arena)); + MA_InitFromBuffer(&arena, buffer, size); + return arena; +} + +MA_Arena MA_Create() { + MA_Arena arena = {0}; + MA_Init(&arena); + return arena; +} + +bool MA_IsPointerInside(MA_Arena *arena, void *p) { + uintptr_t pointer = (uintptr_t)p; + uintptr_t start = (uintptr_t)arena->memory.data; + uintptr_t stop = start + (uintptr_t)arena->len; + bool result = pointer >= start && pointer < stop; + return result; +} + +MA_Temp MA_BeginTemp(MA_Arena *arena) { + MA_Temp result; + result.pos = arena->len; + result.arena = arena; + return result; +} + +void MA_EndTemp(MA_Temp checkpoint) { + MA_PopToPos(checkpoint.arena, checkpoint.pos); +} + +THREAD_LOCAL MA_Arena MA_ScratchArenaPool[4]; + +MA_Temp MA_GetScratchEx(MA_Arena **conflicts, int conflict_count) { + MA_Arena *unoccupied = 0; + for (int i = 0; i < MA_Lengthof(MA_ScratchArenaPool); i += 1) { + MA_Arena *from_pool = MA_ScratchArenaPool + i; + unoccupied = from_pool; + for (int conflict_i = 0; conflict_i < conflict_count; conflict_i += 1) { + MA_Arena *from_conflict = conflicts[conflict_i]; + if (from_pool == from_conflict) { + unoccupied = 0; + break; + } + } + + if (unoccupied) { + break; + } + } + + MA_Assertf(unoccupied, "Failed to get free scratch memory, this is a fatal error, this shouldnt happen"); + MA_Temp result = MA_BeginTemp(unoccupied); + return result; +} + +MA_Temp MA_GetScratch(void) { + MA_Temp result = MA_BeginTemp(MA_ScratchArenaPool); + return result; +} + +MA_Temp MA_GetScratch1(MA_Arena *conflict) { + MA_Arena *conflicts[] = {conflict}; + return MA_GetScratchEx(conflicts, 1); +} + +#ifdef _WIN32 + #ifndef NOMINMAX + #define NOMINMAX + #endif + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #include + +const size_t MV__WIN32_PAGE_SIZE = 4096; + +MV_Memory MV_Reserve(size_t size) { + MV_Memory result; + MA_MemoryZero(&result, sizeof(result)); + size_t adjusted_size = MA_AlignUp(size, MV__WIN32_PAGE_SIZE); + result.data = (uint8_t *)VirtualAlloc(0, adjusted_size, MEM_RESERVE, PAGE_READWRITE); + MA_Assertf(result.data, "Failed to reserve virtual memory"); + result.reserve = adjusted_size; + return result; +} + +bool MV_Commit(MV_Memory *m, size_t commit) { + uint8_t *pointer = MV__AdvanceCommit(m, &commit, MV__WIN32_PAGE_SIZE); + if (pointer) { + void *result = VirtualAlloc(pointer, commit, MEM_COMMIT, PAGE_READWRITE); + MA_Assertf(result, "Failed to commit more memory"); + if (result) { + m->commit += commit; + return true; + } + } + return false; +} + +void MV_Deallocate(MV_Memory *m) { + BOOL result = VirtualFree(m->data, 0, MEM_RELEASE); + MA_Assertf(result != 0, "Failed to release MV_Memory"); +} + +bool MV_DecommitPos(MV_Memory *m, size_t pos) { + size_t aligned = MA_AlignDown(pos, MV__WIN32_PAGE_SIZE); + size_t adjusted_pos = MA_CLAMP_TOP(aligned, m->commit); + size_t size_to_decommit = m->commit - adjusted_pos; + if (size_to_decommit) { + uint8_t *base_address = m->data + adjusted_pos; + BOOL result = VirtualFree(base_address, size_to_decommit, MEM_DECOMMIT); + if (result) { + m->commit -= size_to_decommit; + return true; + } + } + return false; +} + +#elif __unix__ || __linux__ || __APPLE__ + #include + #define MV__UNIX_PAGE_SIZE 4096 +MV_Memory MV_Reserve(size_t size) { + MV_Memory result = {}; + size_t size_aligned = MA_AlignUp(size, MV__UNIX_PAGE_SIZE); + result.data = (uint8_t *)mmap(0, size_aligned, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + MA_Assertf(result.data, "Failed to reserve memory using mmap!!"); + if (result.data) { + result.reserve = size_aligned; + } + return result; +} + +bool MV_Commit(MV_Memory *m, size_t commit) { + uint8_t *pointer = MV__AdvanceCommit(m, &commit, MV__UNIX_PAGE_SIZE); + if (pointer) { + int mprotect_result = mprotect(pointer, commit, PROT_READ | PROT_WRITE); + MA_Assertf(mprotect_result == 0, "Failed to commit more memory using mmap"); + if (mprotect_result == 0) { + m->commit += commit; + return true; + } + } + return false; +} + +void MV_Deallocate(MV_Memory *m) { + int result = munmap(m->data, m->reserve); + MA_Assertf(result == 0, "Failed to release virtual memory using munmap"); +} +#else +MV_Memory MV_Reserve(size_t size) { + MV_Memory result = {0}; + return result; +} + +bool MV_Commit(MV_Memory *m, size_t commit) { + return false; +} + +void MV_Deallocate(MV_Memory *m) { +} + +#endif + +#ifndef FIRST_UTF_HEADER + #define FIRST_UTF_HEADER + #define UTF_HEADER + #include +typedef struct UTF32_Result UTF32_Result; +typedef struct UTF8_Result UTF8_Result; +typedef struct UTF16_Result UTF16_Result; +typedef struct UTF8_Iter UTF8_Iter; + + #ifndef UTF_API + #ifdef __cplusplus + #define UTF_API extern "C" + #else + #define UTF_API + #endif + #endif + +struct UTF32_Result { + uint32_t out_str; + int advance; + int error; +}; + +struct UTF8_Result { + uint8_t out_str[4]; + int len; + int error; +}; + +struct UTF16_Result { + uint16_t out_str[2]; + int len; + int error; +}; + +struct UTF8_Iter { + char *str; + int len; + int utf8_codepoint_byte_size; + int i; + uint32_t item; +}; + +UTF_API UTF32_Result UTF_ConvertUTF16ToUTF32(uint16_t *c, int max_advance); +UTF_API UTF8_Result UTF_ConvertUTF32ToUTF8(uint32_t codepoint); +UTF_API UTF32_Result UTF_ConvertUTF8ToUTF32(char *c, int max_advance); +UTF_API UTF16_Result UTF_ConvertUTF32ToUTF16(uint32_t codepoint); +UTF_API int64_t UTF_CreateCharFromWidechar(char *buffer, int64_t buffer_size, wchar_t *in, int64_t inlen); +UTF_API int64_t UTF_CreateWidecharFromChar(wchar_t *buffer, int64_t buffer_size, char *in, int64_t inlen); +UTF_API void UTF8_Advance(UTF8_Iter *iter); +UTF_API UTF8_Iter UTF8_IterateEx(char *str, int len); +UTF_API UTF8_Iter UTF8_Iterate(char *str); + + #define UTF8_For(name, str, len) for (UTF8_Iter name = UTF8_IterateEx(str, (int)len); name.item; UTF8_Advance(&name)) +#endif + + +#ifndef UTF__MemoryZero + #include + #define UTF__MemoryZero(p, size) memset(p, 0, size) +#endif + +UTF_API UTF32_Result UTF_ConvertUTF16ToUTF32(uint16_t *c, int max_advance) { + UTF32_Result result; + UTF__MemoryZero(&result, sizeof(result)); + if (max_advance >= 1) { + result.advance = 1; + result.out_str = c[0]; + if (c[0] >= 0xD800 && c[0] <= 0xDBFF && c[1] >= 0xDC00 && c[1] <= 0xDFFF) { + if (max_advance >= 2) { + result.out_str = 0x10000; + result.out_str += (uint32_t)(c[0] & 0x03FF) << 10u | (c[1] & 0x03FF); + result.advance = 2; + } + else + result.error = 2; + } + } + else { + result.error = 1; + } + return result; +} + +UTF_API UTF8_Result UTF_ConvertUTF32ToUTF8(uint32_t codepoint) { + UTF8_Result result; + UTF__MemoryZero(&result, sizeof(result)); + + if (codepoint <= 0x7F) { + result.len = 1; + result.out_str[0] = (char)codepoint; + } + else if (codepoint <= 0x7FF) { + result.len = 2; + result.out_str[0] = 0xc0 | (0x1f & (codepoint >> 6)); + result.out_str[1] = 0x80 | (0x3f & codepoint); + } + else if (codepoint <= 0xFFFF) { // 16 bit word + result.len = 3; + result.out_str[0] = 0xe0 | (0xf & (codepoint >> 12)); // 4 bits + result.out_str[1] = 0x80 | (0x3f & (codepoint >> 6)); // 6 bits + result.out_str[2] = 0x80 | (0x3f & codepoint); // 6 bits + } + else if (codepoint <= 0x10FFFF) { // 21 bit word + result.len = 4; + result.out_str[0] = 0xf0 | (0x7 & (codepoint >> 18)); // 3 bits + result.out_str[1] = 0x80 | (0x3f & (codepoint >> 12)); // 6 bits + result.out_str[2] = 0x80 | (0x3f & (codepoint >> 6)); // 6 bits + result.out_str[3] = 0x80 | (0x3f & codepoint); // 6 bits + } + else { + result.error = 1; + } + + return result; +} + +UTF_API UTF32_Result UTF_ConvertUTF8ToUTF32(char *c, int max_advance) { + UTF32_Result result; + UTF__MemoryZero(&result, sizeof(result)); + + if ((c[0] & 0x80) == 0) { // Check if leftmost zero of first byte is unset + if (max_advance >= 1) { + result.out_str = c[0]; + result.advance = 1; + } + else result.error = 1; + } + + else if ((c[0] & 0xe0) == 0xc0) { + if ((c[1] & 0xc0) == 0x80) { // Continuation byte required + if (max_advance >= 2) { + result.out_str = (uint32_t)(c[0] & 0x1f) << 6u | (c[1] & 0x3f); + result.advance = 2; + } + else result.error = 2; + } + else result.error = 2; + } + + else if ((c[0] & 0xf0) == 0xe0) { + if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80) { // Two continuation bytes required + if (max_advance >= 3) { + result.out_str = (uint32_t)(c[0] & 0xf) << 12u | (uint32_t)(c[1] & 0x3f) << 6u | (c[2] & 0x3f); + result.advance = 3; + } + else result.error = 3; + } + else result.error = 3; + } + + else if ((c[0] & 0xf8) == 0xf0) { + if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80 && (c[3] & 0xc0) == 0x80) { // Three continuation bytes required + if (max_advance >= 4) { + result.out_str = (uint32_t)(c[0] & 0xf) << 18u | (uint32_t)(c[1] & 0x3f) << 12u | (uint32_t)(c[2] & 0x3f) << 6u | (uint32_t)(c[3] & 0x3f); + result.advance = 4; + } + else result.error = 4; + } + else result.error = 4; + } + else result.error = 4; + + return result; +} + +UTF_API UTF16_Result UTF_ConvertUTF32ToUTF16(uint32_t codepoint) { + UTF16_Result result; + UTF__MemoryZero(&result, sizeof(result)); + if (codepoint < 0x10000) { + result.out_str[0] = (uint16_t)codepoint; + result.out_str[1] = 0; + result.len = 1; + } + else if (codepoint <= 0x10FFFF) { + uint32_t code = (codepoint - 0x10000); + result.out_str[0] = (uint16_t)(0xD800 | (code >> 10)); + result.out_str[1] = (uint16_t)(0xDC00 | (code & 0x3FF)); + result.len = 2; + } + else { + result.error = 1; + } + + return result; +} + +#define UTF__HANDLE_DECODE_ERROR(question_mark) \ + { \ + if (outlen < buffer_size - 1) buffer[outlen++] = (question_mark); \ + break; \ + } + +UTF_API int64_t UTF_CreateCharFromWidechar(char *buffer, int64_t buffer_size, wchar_t *in, int64_t inlen) { + int64_t outlen = 0; + for (int64_t i = 0; i < inlen && in[i];) { + UTF32_Result decode = UTF_ConvertUTF16ToUTF32((uint16_t *)(in + i), (int)(inlen - i)); + if (!decode.error) { + i += decode.advance; + UTF8_Result encode = UTF_ConvertUTF32ToUTF8(decode.out_str); + if (!encode.error) { + for (int64_t j = 0; j < encode.len; j++) { + if (outlen < buffer_size - 1) { + buffer[outlen++] = encode.out_str[j]; + } + } + } + else UTF__HANDLE_DECODE_ERROR('?'); + } + else UTF__HANDLE_DECODE_ERROR('?'); + } + + buffer[outlen] = 0; + return outlen; +} + +UTF_API int64_t UTF_CreateWidecharFromChar(wchar_t *buffer, int64_t buffer_size, char *in, int64_t inlen) { + int64_t outlen = 0; + for (int64_t i = 0; i < inlen;) { + UTF32_Result decode = UTF_ConvertUTF8ToUTF32(in + i, (int)(inlen - i)); + if (!decode.error) { + i += decode.advance; + UTF16_Result encode = UTF_ConvertUTF32ToUTF16(decode.out_str); + if (!encode.error) { + for (int64_t j = 0; j < encode.len; j++) { + if (outlen < buffer_size - 1) { + buffer[outlen++] = encode.out_str[j]; + } + } + } + else UTF__HANDLE_DECODE_ERROR(0x003f); + } + else UTF__HANDLE_DECODE_ERROR(0x003f); + } + + buffer[outlen] = 0; + return outlen; +} + +UTF_API void UTF8_Advance(UTF8_Iter *iter) { + iter->i += iter->utf8_codepoint_byte_size; + UTF32_Result r = UTF_ConvertUTF8ToUTF32(iter->str + iter->i, iter->len - iter->i); + if (r.error) { + iter->item = 0; + return; + } + + iter->utf8_codepoint_byte_size = r.advance; + iter->item = r.out_str; +} + +UTF_API UTF8_Iter UTF8_IterateEx(char *str, int len) { + UTF8_Iter result; + UTF__MemoryZero(&result, sizeof(result)); + result.str = str; + result.len = len; + if (len) UTF8_Advance(&result); + return result; +} + +UTF_API UTF8_Iter UTF8_Iterate(char *str) { + int length = 0; + while (str[length]) length += 1; + return UTF8_IterateEx(str, length); +} + + +#ifndef FIRST_S8_STRING + #define FIRST_S8_STRING + #include + #include + #include + + #ifndef S8_API + #define S8_API + #endif + + #ifdef __cplusplus + #define S8_IF_CPP(x) x + #else + #define S8_IF_CPP(x) + #endif + + #ifndef S8_Allocator +struct MA_Arena; + #define S8_Allocator MA_Arena * + #endif + +typedef struct S8_Node S8_Node; +typedef struct S8_List S8_List; +S8_API int64_t S8_Length(char *string); + +#ifndef S8_String +typedef struct S8_String S8_String; +struct S8_String { + char *str; + int64_t len; + #if defined(__cplusplus) + S8_String() = default; + S8_String(char *s) : str(s), len(S8_Length(s)) {} + S8_String(char *s, int64_t l) : str(s), len(l) {} + S8_String(const char *s) : str((char *)s), len(S8_Length((char *)s)) {} + S8_String(const char *s, int64_t l) : str((char *)s), len(l) {} + #if defined(FIRST_UTF_HEADER) + struct Iter { + UTF8_Iter i; + + Iter &operator++() { + UTF8_Advance(&i); + return *this; + } + + friend bool operator!=(const Iter &a, const Iter &b) { return a.i.item != b.i.item; } + UTF8_Iter &operator*() { return i; } + }; + + Iter begin() { return {UTF8_IterateEx(str, (int)len)}; } + Iter end() { return {}; } + #endif // FIRST_UTF_HEADER + #endif // __cplusplus +}; +#endif + +struct S8_Node { + S8_Node *next; + S8_String string; +}; + +struct S8_List { + int64_t node_count; + int64_t char_count; + S8_Node *first; + S8_Node *last; + + #if defined(__cplusplus) + struct Iter { + S8_Node *it; + + Iter &operator++() { + it = it->next; + return *this; + } + + friend bool operator!=(const Iter &a, const Iter &b) { return a.it != b.it; } + S8_String &operator*() { return it->string; } + }; + + Iter begin() { return {first}; } + Iter end() { return {0}; } + #endif +}; + +typedef struct S16_String { + wchar_t *str; + int64_t len; +} S16_String; + +typedef int S8_FindFlag; +enum { + S8_FindFlag_None = 0, + S8_FindFlag_IgnoreCase = 1, + S8_FindFlag_MatchFindLast = 2, +}; + +typedef int S8_SplitFlag; +enum { + S8_SplitFlag_None = 0, + S8_SplitFlag_IgnoreCase = 1, + S8_SplitFlag_SplitInclusive = 2, +}; + +static const bool S8_IgnoreCase = true; + + #if defined(__has_attribute) + #if __has_attribute(format) + #define S8__PrintfFormat(fmt, va) __attribute__((format(printf, fmt, va))) + #endif + #endif + + #ifndef S8__PrintfFormat + #define S8__PrintfFormat(fmt, va) + #endif + + #define S8_Lit(string) S8_Make((char *)string, sizeof(string) - 1) + #define S8_ConstLit(string) \ + { string, sizeof(string) - 1 } + #define S8_Expand(string) (int)(string).len, (string).str + + #define S8_FORMAT(allocator, str, result) \ + va_list args1; \ + va_start(args1, str); \ + S8_String result = S8_FormatV(allocator, str, args1); \ + va_end(args1) + + #define S8_For(it, x) for (S8_Node *it = (x).first; it; it = it->next) + +S8_API char CHAR_ToLowerCase(char a); +S8_API char CHAR_ToUpperCase(char a); +S8_API bool CHAR_IsWhitespace(char w); +S8_API bool CHAR_IsAlphabetic(char a); +S8_API bool CHAR_IsIdent(char a); +S8_API bool CHAR_IsDigit(char a); +S8_API bool CHAR_IsAlphanumeric(char a); +S8_API bool S8_AreEqual(S8_String a, S8_String b, unsigned ignore_case S8_IF_CPP(= false)); +S8_API bool S8_EndsWith(S8_String a, S8_String end, unsigned ignore_case S8_IF_CPP(= false)); +S8_API bool S8_StartsWith(S8_String a, S8_String start, unsigned ignore_case S8_IF_CPP(= false)); +S8_API S8_String S8_Make(char *str, int64_t len); +S8_API S8_String S8_Copy(S8_Allocator allocator, S8_String string); +S8_API S8_String S8_CopyChar(S8_Allocator allocator, char *s); +S8_API S8_String S8_NormalizePath(S8_Allocator allocator, S8_String s); +S8_API void S8_NormalizePathUnsafe(S8_String s); // make sure there is no way string is const etc. +S8_API S8_String S8_Chop(S8_String string, int64_t len); +S8_API S8_String S8_Skip(S8_String string, int64_t len); +S8_API S8_String S8_GetPostfix(S8_String string, int64_t len); +S8_API S8_String S8_GetPrefix(S8_String string, int64_t len); +S8_API S8_String S8_Slice(S8_String string, int64_t first_index, int64_t one_past_last_index); +S8_API S8_String S8_Trim(S8_String string); +S8_API S8_String S8_TrimEnd(S8_String string); +S8_API S8_String S8_ToLowerCase(S8_Allocator allocator, S8_String s); +S8_API S8_String S8_ToUpperCase(S8_Allocator allocator, S8_String s); +S8_API bool S8_Seek(S8_String string, S8_String find, S8_FindFlag flags S8_IF_CPP(= S8_FindFlag_None), int64_t *index_out S8_IF_CPP(= 0)); +S8_API int64_t S8_Find(S8_String string, S8_String find, S8_FindFlag flags S8_IF_CPP(= S8_FindFlag_None)); +S8_API S8_String S8_ChopLastSlash(S8_String s); +S8_API S8_String S8_ChopLastPeriod(S8_String s); +S8_API S8_String S8_SkipToLastSlash(S8_String s); +S8_API S8_String S8_SkipToLastPeriod(S8_String s); +S8_API S8_String S8_GetNameNoExt(S8_String s); +S8_API bool S8_IsPointerInside(S8_String string, char *p); +S8_API S8_String S8_SkipToP(S8_String string, char *p); +S8_API S8_String S8_SkipPast(S8_String string, S8_String a); +S8_API int64_t S8_WideLength(wchar_t *string); +S8_API S8_String S8_MakeFromChar(char *string); +S8_API S8_String S8_MakeEmpty(void); +S8_API S8_List S8_MakeEmptyList(void); +S8_API S8_String S8_FormatV(S8_Allocator allocator, const char *str, va_list args1); +S8_API S8_String S8_Format(S8_Allocator allocator, const char *str, ...) S8__PrintfFormat(2, 3); + +S8_API S8_List S8_Split(S8_Allocator allocator, S8_String string, S8_String find, S8_SplitFlag flags S8_IF_CPP(= S8_SplitFlag_None)); +S8_API S8_String S8_MergeWithSeparator(S8_Allocator allocator, S8_List list, S8_String separator S8_IF_CPP(= S8_Lit(" "))); +S8_API S8_String S8_Merge(S8_Allocator allocator, S8_List list); +S8_API S8_String S8_ReplaceAll(S8_Allocator allocator, S8_String string, S8_String replace, S8_String with, bool ignore_case S8_IF_CPP(= false)); +S8_API S8_List S8_FindAll(S8_Allocator allocator, S8_String string, S8_String find, bool ignore_case S8_IF_CPP(= false)); + +S8_API S8_Node *S8_CreateNode(S8_Allocator allocator, S8_String string); +S8_API void S8_ReplaceNodeString(S8_List *list, S8_Node *node, S8_String new_string); +S8_API void S8_AddExistingNode(S8_List *list, S8_Node *node); +S8_API void S8_AddArray(S8_Allocator allocator, S8_List *list, char **array, int count); +S8_API void S8_AddArrayWithPrefix(S8_Allocator allocator, S8_List *list, char *prefix, char **array, int count); +S8_API S8_List S8_MakeList(S8_Allocator allocator, S8_String a); +S8_API S8_List S8_CopyList(S8_Allocator allocator, S8_List a); +S8_API S8_List S8_ConcatLists(S8_Allocator allocator, S8_List a, S8_List b); +S8_API S8_Node *S8_AddNode(S8_Allocator allocator, S8_List *list, S8_String string); +S8_API S8_Node *S8_Add(S8_Allocator allocator, S8_List *list, S8_String string); +S8_API S8_String S8_AddF(S8_Allocator allocator, S8_List *list, const char *str, ...) S8__PrintfFormat(3, 4); + +S8_API S16_String S8_ToWidecharEx(S8_Allocator allocator, S8_String string); +S8_API wchar_t *S8_ToWidechar(S8_Allocator allocator, S8_String string); +S8_API S8_String S8_FromWidecharEx(S8_Allocator allocator, wchar_t *wstring, int64_t wsize); +S8_API S8_String S8_FromWidechar(S8_Allocator allocator, wchar_t *wstring); + + #if defined(__cplusplus) +inline S8_String operator""_s(const char *str, size_t size) { return {(char *)str, (int64_t)size}; } +inline bool operator==(S8_String a, S8_String b) { return S8_AreEqual(a, b, 0); } +inline bool operator!=(S8_String a, S8_String b) { return !S8_AreEqual(a, b, 0); } + #endif +#endif + +#define S8_ALLOCATE(allocator, size) MA_PushSize(allocator, size) +#define S8_ASSERT(x) IO_Assert(x) +#define S8_MemoryCopy MA_MemoryCopy +#include + +#ifndef S8_VSNPRINTF + #include + #define S8_VSNPRINTF vsnprintf +#endif + +#ifndef S8_ALLOCATE + #include + #define S8_ALLOCATE(allocator, size) malloc(size) +#endif + +#ifndef S8_ASSERT + #include + #define S8_ASSERT(x) assert(x) +#endif + +#ifndef S8_MemoryCopy + #include + #define S8_MemoryCopy(dst, src, s) memcpy(dst, src, s) +#endif + +#ifndef S8_StaticFunc + #if defined(__GNUC__) || defined(__clang__) + #define S8_StaticFunc __attribute__((unused)) static + #else + #define S8_StaticFunc static + #endif +#endif + +S8_StaticFunc int64_t S8__ClampTop(int64_t val, int64_t max) { + if (val > max) val = max; + return val; +} + +S8_API char CHAR_ToLowerCase(char a) { + if (a >= 'A' && a <= 'Z') a += 32; + return a; +} + +S8_API char CHAR_ToUpperCase(char a) { + if (a >= 'a' && a <= 'z') a -= 32; + return a; +} + +S8_API bool CHAR_IsWhitespace(char w) { + bool result = w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r'; + return result; +} + +S8_API bool CHAR_IsAlphabetic(char a) { + bool result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z'); + return result; +} + +S8_API bool CHAR_IsIdent(char a) { + bool result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z') || a == '_'; + return result; +} + +S8_API bool CHAR_IsDigit(char a) { + bool result = a >= '0' && a <= '9'; + return result; +} + +S8_API bool CHAR_IsAlphanumeric(char a) { + bool result = CHAR_IsDigit(a) || CHAR_IsAlphabetic(a); + return result; +} + +S8_API bool S8_AreEqual(S8_String a, S8_String b, unsigned ignore_case) { + if (a.len != b.len) return false; + for (int64_t i = 0; i < a.len; i++) { + char A = a.str[i]; + char B = b.str[i]; + if (ignore_case) { + A = CHAR_ToLowerCase(A); + B = CHAR_ToLowerCase(B); + } + if (A != B) + return false; + } + return true; +} + +S8_API bool S8_EndsWith(S8_String a, S8_String end, unsigned ignore_case) { + S8_String a_end = S8_GetPostfix(a, end.len); + bool result = S8_AreEqual(end, a_end, ignore_case); + return result; +} + +S8_API bool S8_StartsWith(S8_String a, S8_String start, unsigned ignore_case) { + S8_String a_start = S8_GetPrefix(a, start.len); + bool result = S8_AreEqual(start, a_start, ignore_case); + return result; +} + +S8_API S8_String S8_Make(char *str, int64_t len) { + S8_String result; + result.str = (char *)str; + result.len = len; + return result; +} + +S8_API S8_String S8_Copy(S8_Allocator allocator, S8_String string) { + char *copy = (char *)S8_ALLOCATE(allocator, sizeof(char) * (string.len + 1)); + S8_MemoryCopy(copy, string.str, string.len); + copy[string.len] = 0; + S8_String result = S8_Make(copy, string.len); + return result; +} + +S8_API S8_String S8_CopyChar(S8_Allocator allocator, char *s) { + int64_t len = S8_Length(s); + char *copy = (char *)S8_ALLOCATE(allocator, sizeof(char) * (len + 1)); + S8_MemoryCopy(copy, s, len); + copy[len] = 0; + S8_String result = S8_Make(copy, len); + return result; +} + +S8_API S8_String S8_NormalizePath(S8_Allocator allocator, S8_String s) { + S8_String copy = S8_Copy(allocator, s); + for (int64_t i = 0; i < copy.len; i++) { + if (copy.str[i] == '\\') + copy.str[i] = '/'; + } + return copy; +} + +S8_API void S8_NormalizePathUnsafe(S8_String s) { + for (int64_t i = 0; i < s.len; i++) { + if (s.str[i] == '\\') + s.str[i] = '/'; + } +} + +S8_API S8_String S8_Chop(S8_String string, int64_t len) { + len = S8__ClampTop(len, string.len); + S8_String result = S8_Make(string.str, string.len - len); + return result; +} + +S8_API S8_String S8_Skip(S8_String string, int64_t len) { + len = S8__ClampTop(len, string.len); + int64_t remain = string.len - len; + S8_String result = S8_Make(string.str + len, remain); + return result; +} + +S8_API bool S8_IsPointerInside(S8_String string, char *p) { + uintptr_t pointer = (uintptr_t)p; + uintptr_t start = (uintptr_t)string.str; + uintptr_t stop = start + (uintptr_t)string.len; + bool result = pointer >= start && pointer < stop; + return result; +} + +S8_API S8_String S8_SkipToP(S8_String string, char *p) { + if (S8_IsPointerInside(string, p)) { + S8_String result = S8_Make(p, p - string.str); + return result; + } + return string; +} + +S8_API S8_String S8_SkipPast(S8_String string, S8_String a) { + if (S8_IsPointerInside(string, a.str)) { + S8_String on_p = S8_Make(a.str, a.str - string.str); + S8_String result = S8_Skip(on_p, a.len); + return result; + } + return string; +} + +S8_API S8_String S8_GetPostfix(S8_String string, int64_t len) { + len = S8__ClampTop(len, string.len); + int64_t remain_len = string.len - len; + S8_String result = S8_Make(string.str + remain_len, len); + return result; +} + +S8_API S8_String S8_GetPrefix(S8_String string, int64_t len) { + len = S8__ClampTop(len, string.len); + S8_String result = S8_Make(string.str, len); + return result; +} + +S8_API S8_String S8_GetNameNoExt(S8_String s) { + return S8_SkipToLastSlash(S8_ChopLastPeriod(s)); +} + +S8_API S8_String S8_Slice(S8_String string, int64_t first_index, int64_t one_past_last_index) { + if (one_past_last_index < 0) one_past_last_index = string.len + one_past_last_index + 1; + if (first_index < 0) first_index = string.len + first_index; + S8_ASSERT(first_index < one_past_last_index && "S8_Slice, first_index is bigger then one_past_last_index"); + S8_ASSERT(string.len > 0 && "Slicing string of length 0! Might be an error!"); + S8_String result = string; + if (string.len > 0) { + if (one_past_last_index > first_index) { + first_index = S8__ClampTop(first_index, string.len - 1); + one_past_last_index = S8__ClampTop(one_past_last_index, string.len); + result.str += first_index; + result.len = one_past_last_index - first_index; + } + else { + result.len = 0; + } + } + return result; +} + +S8_API S8_String S8_Trim(S8_String string) { + if (string.len == 0) + return string; + + int64_t whitespace_begin = 0; + for (; whitespace_begin < string.len; whitespace_begin++) { + if (!CHAR_IsWhitespace(string.str[whitespace_begin])) { + break; + } + } + + int64_t whitespace_end = string.len; + for (; whitespace_end != whitespace_begin; whitespace_end--) { + if (!CHAR_IsWhitespace(string.str[whitespace_end - 1])) { + break; + } + } + + if (whitespace_begin == whitespace_end) { + string.len = 0; + } + else { + string = S8_Slice(string, whitespace_begin, whitespace_end); + } + + return string; +} + +S8_API S8_String S8_TrimEnd(S8_String string) { + int64_t whitespace_end = string.len; + for (; whitespace_end != 0; whitespace_end--) { + if (!CHAR_IsWhitespace(string.str[whitespace_end - 1])) { + break; + } + } + + S8_String result = S8_GetPrefix(string, whitespace_end); + return result; +} + +S8_API S8_String S8_ToLowerCase(S8_Allocator allocator, S8_String s) { + S8_String copy = S8_Copy(allocator, s); + for (int64_t i = 0; i < copy.len; i++) { + copy.str[i] = CHAR_ToLowerCase(copy.str[i]); + } + return copy; +} + +S8_API S8_String S8_ToUpperCase(S8_Allocator allocator, S8_String s) { + S8_String copy = S8_Copy(allocator, s); + for (int64_t i = 0; i < copy.len; i++) { + copy.str[i] = CHAR_ToUpperCase(copy.str[i]); + } + return copy; +} + +S8_API bool S8_Seek(S8_String string, S8_String find, S8_FindFlag flags, int64_t *index_out) { + bool ignore_case = flags & S8_FindFlag_IgnoreCase ? true : false; + bool result = false; + if (flags & S8_FindFlag_MatchFindLast) { + for (int64_t i = string.len; i != 0; i--) { + int64_t index = i - 1; + S8_String substring = S8_Slice(string, index, index + find.len); + if (S8_AreEqual(substring, find, ignore_case)) { + if (index_out) + *index_out = index; + result = true; + break; + } + } + } + else { + for (int64_t i = 0; i < string.len; i++) { + S8_String substring = S8_Slice(string, i, i + find.len); + if (S8_AreEqual(substring, find, ignore_case)) { + if (index_out) + *index_out = i; + result = true; + break; + } + } + } + + return result; +} + +S8_API int64_t S8_Find(S8_String string, S8_String find, S8_FindFlag flag) { + int64_t result = -1; + S8_Seek(string, find, flag, &result); + return result; +} + +S8_API S8_List S8_Split(S8_Allocator allocator, S8_String string, S8_String find, S8_SplitFlag flags) { + S8_List result = S8_MakeEmptyList(); + int64_t index = 0; + + S8_FindFlag find_flag = flags & S8_SplitFlag_IgnoreCase ? S8_FindFlag_IgnoreCase : S8_FindFlag_None; + while (S8_Seek(string, find, find_flag, &index)) { + S8_String before_match = S8_Make(string.str, index); + S8_AddNode(allocator, &result, before_match); + if (flags & S8_SplitFlag_SplitInclusive) { + S8_String match = S8_Make(string.str + index, find.len); + S8_AddNode(allocator, &result, match); + } + string = S8_Skip(string, index + find.len); + } + if (string.len) S8_AddNode(allocator, &result, string); + return result; +} + +S8_API S8_String S8_MergeWithSeparator(S8_Allocator allocator, S8_List list, S8_String separator) { + if (list.node_count == 0) return S8_MakeEmpty(); + if (list.char_count == 0) return S8_MakeEmpty(); + + int64_t base_size = (list.char_count + 1); + int64_t sep_size = (list.node_count - 1) * separator.len; + int64_t size = base_size + sep_size; + char *buff = (char *)S8_ALLOCATE(allocator, sizeof(char) * (size + 1)); + S8_String string = S8_Make(buff, 0); + for (S8_Node *it = list.first; it; it = it->next) { + S8_ASSERT(string.len + it->string.len <= size); + S8_MemoryCopy(string.str + string.len, it->string.str, it->string.len); + string.len += it->string.len; + if (it != list.last) { + S8_MemoryCopy(string.str + string.len, separator.str, separator.len); + string.len += separator.len; + } + } + S8_ASSERT(string.len == size - 1); + string.str[size] = 0; + return string; +} + +S8_API S8_String S8_Merge(S8_Allocator allocator, S8_List list) { + return S8_MergeWithSeparator(allocator, list, S8_Lit("")); +} + +S8_API S8_String S8_ReplaceAll(S8_Allocator allocator, S8_String string, S8_String replace, S8_String with, bool ignore_case) { + S8_SplitFlag split_flag = ignore_case ? S8_SplitFlag_IgnoreCase : S8_SplitFlag_None; + S8_List list = S8_Split(allocator, string, replace, split_flag | S8_SplitFlag_SplitInclusive); + for (S8_Node *it = list.first; it; it = it->next) { + if (S8_AreEqual(it->string, replace, ignore_case)) { + S8_ReplaceNodeString(&list, it, with); + } + } + S8_String result = S8_Merge(allocator, list); + return result; +} + +S8_API S8_List S8_FindAll(S8_Allocator allocator, S8_String string, S8_String find, bool ignore_case) { // @untested + S8_List result = S8_MakeEmptyList(); + int64_t index = 0; + + S8_FindFlag find_flag = ignore_case ? S8_FindFlag_IgnoreCase : 0; + while (S8_Seek(string, find, find_flag, &index)) { + S8_String match = S8_Make(string.str + index, find.len); + S8_AddNode(allocator, &result, match); + string = S8_Skip(string, index + find.len); + } + return result; +} + +S8_API S8_String S8_ChopLastSlash(S8_String s) { + S8_String result = s; + S8_Seek(s, S8_Lit("/"), S8_FindFlag_MatchFindLast, &result.len); + return result; +} + +S8_API S8_String S8_ChopLastPeriod(S8_String s) { + S8_String result = s; + S8_Seek(s, S8_Lit("."), S8_FindFlag_MatchFindLast, &result.len); + return result; +} + +S8_API S8_String S8_SkipToLastSlash(S8_String s) { + int64_t pos; + S8_String result = s; + if (S8_Seek(s, S8_Lit("/"), S8_FindFlag_MatchFindLast, &pos)) { + result = S8_Skip(result, pos + 1); + } + return result; +} + +S8_API S8_String S8_SkipToLastPeriod(S8_String s) { + int64_t pos; + S8_String result = s; + if (S8_Seek(s, S8_Lit("."), S8_FindFlag_MatchFindLast, &pos)) { + result = S8_Skip(result, pos + 1); + } + return result; +} + +S8_API int64_t S8_Length(char *string) { + int64_t len = 0; + while (*string++ != 0) + len++; + return len; +} + +S8_API int64_t S8_WideLength(wchar_t *string) { + int64_t len = 0; + while (*string++ != 0) + len++; + return len; +} + +S8_API S8_String S8_MakeFromChar(char *string) { + S8_String result; + result.str = (char *)string; + result.len = S8_Length(string); + return result; +} + +S8_API S8_String S8_MakeEmpty(void) { + return S8_Make(0, 0); +} + +S8_API bool strings_equal(char *a, char *b) { + S8_String as = S8_MakeFromChar(a); + S8_String bs = S8_MakeFromChar(b); + bool result = S8_AreEqual(as, bs, false); + return result; +} + +S8_API S8_List S8_MakeEmptyList(void) { + S8_List result; + result.first = 0; + result.last = 0; + result.char_count = 0; + result.node_count = 0; + return result; +} + +S8_API S8_String S8_FormatV(S8_Allocator allocator, const char *str, va_list args1) { + va_list args2; + va_copy(args2, args1); + int64_t len = S8_VSNPRINTF(0, 0, str, args2); + va_end(args2); + + char *result = (char *)S8_ALLOCATE(allocator, sizeof(char) * (len + 1)); + S8_VSNPRINTF(result, (int)(len + 1), str, args1); + S8_String res = S8_Make(result, len); + return res; +} + +S8_API S8_String S8_Format(S8_Allocator allocator, const char *str, ...) { + S8_FORMAT(allocator, str, result); + return result; +} + +S8_API S8_Node *S8_CreateNode(S8_Allocator allocator, S8_String string) { + S8_Node *result = (S8_Node *)S8_ALLOCATE(allocator, sizeof(S8_Node)); + result->string = string; + result->next = 0; + return result; +} + +S8_API void S8_ReplaceNodeString(S8_List *list, S8_Node *node, S8_String new_string) { + list->char_count -= node->string.len; + list->char_count += new_string.len; + node->string = new_string; +} + +S8_API void S8_AddExistingNode(S8_List *list, S8_Node *node) { + if (list->first) { + list->last->next = node; + list->last = list->last->next; + } + else { + list->first = list->last = node; + } + list->node_count += 1; + list->char_count += node->string.len; +} + +S8_API void S8_AddArray(S8_Allocator allocator, S8_List *list, char **array, int count) { + for (int i = 0; i < count; i += 1) { + S8_String s = S8_MakeFromChar(array[i]); + S8_AddNode(allocator, list, s); + } +} + +S8_API void S8_AddArrayWithPrefix(S8_Allocator allocator, S8_List *list, char *prefix, char **array, int count) { + for (int i = 0; i < count; i += 1) { + S8_AddF(allocator, list, "%s%s", prefix, array[i]); + } +} + +S8_API S8_List S8_MakeList(S8_Allocator allocator, S8_String a) { + S8_List result = S8_MakeEmptyList(); + S8_AddNode(allocator, &result, a); + return result; +} + +S8_API S8_List S8_CopyList(S8_Allocator allocator, S8_List a) { + S8_List result = S8_MakeEmptyList(); + for (S8_Node *it = a.first; it; it = it->next) S8_AddNode(allocator, &result, it->string); + return result; +} + +S8_API S8_List S8_ConcatLists(S8_Allocator allocator, S8_List a, S8_List b) { + S8_List result = S8_MakeEmptyList(); + for (S8_Node *it = a.first; it; it = it->next) S8_AddNode(allocator, &result, it->string); + for (S8_Node *it = b.first; it; it = it->next) S8_AddNode(allocator, &result, it->string); + return result; +} + +S8_API S8_Node *S8_AddNode(S8_Allocator allocator, S8_List *list, S8_String string) { + S8_Node *node = S8_CreateNode(allocator, string); + S8_AddExistingNode(list, node); + return node; +} + +S8_API S8_Node *S8_Add(S8_Allocator allocator, S8_List *list, S8_String string) { + S8_String copy = S8_Copy(allocator, string); + S8_Node *node = S8_CreateNode(allocator, copy); + S8_AddExistingNode(list, node); + return node; +} + +S8_API S8_String S8_AddF(S8_Allocator allocator, S8_List *list, const char *str, ...) { + S8_FORMAT(allocator, str, result); + S8_AddNode(allocator, list, result); + return result; +} + +#ifdef FIRST_UTF_HEADER + +S8_API S16_String S8_ToWidecharEx(S8_Allocator allocator, S8_String string) { + S8_ASSERT(sizeof(wchar_t) == 2); + wchar_t *buffer = (wchar_t *)S8_ALLOCATE(allocator, sizeof(wchar_t) * (string.len + 1)); + int64_t size = UTF_CreateWidecharFromChar(buffer, string.len + 1, string.str, string.len); + S16_String result = {buffer, size}; + return result; +} + +S8_API wchar_t *S8_ToWidechar(S8_Allocator allocator, S8_String string) { + S16_String result = S8_ToWidecharEx(allocator, string); + return result.str; +} + +S8_API S8_String S8_FromWidecharEx(S8_Allocator allocator, wchar_t *wstring, int64_t wsize) { + S8_ASSERT(sizeof(wchar_t) == 2); + + int64_t buffer_size = (wsize + 1) * 2; + char *buffer = (char *)S8_ALLOCATE(allocator, buffer_size); + int64_t size = UTF_CreateCharFromWidechar(buffer, buffer_size, wstring, wsize); + S8_String result = S8_Make(buffer, size); + + S8_ASSERT(size < buffer_size); + return result; +} + +S8_API S8_String S8_FromWidechar(S8_Allocator allocator, wchar_t *wstring) { + int64_t size = S8_WideLength(wstring); + S8_String result = S8_FromWidecharEx(allocator, wstring, size); + return result; +} + +#endif + +#ifndef FIRST_HASH_HEADER + #define FIRST_HASH_HEADER + #include + + #ifndef HASH_API_FUNCTION + #ifdef __cplusplus + #define HASH_API_FUNCTION extern "C" + #else + #define HASH_API_FUNCTION + #endif + #endif + +// FNV HASH (1a?) +HASH_API_FUNCTION uint64_t HashBytes(void *data, uint64_t size) { + uint8_t *data8 = (uint8_t *)data; + uint64_t hash = (uint64_t)14695981039346656037ULL; + for (uint64_t i = 0; i < size; i++) { + hash = hash ^ (uint64_t)(data8[i]); + hash = hash * (uint64_t)1099511628211ULL; + } + return hash; +} + +HASH_API_FUNCTION uint64_t HashMix(uint64_t x, uint64_t y) { + x ^= y; + x *= 0xff51afd7ed558ccd; + x ^= x >> 32; + return x; +} + #define WRAP_AROUND_POWER_OF_2(x, pow2) (((x) & ((pow2)-1llu))) +#endif + +#ifndef FIRST_LL_HEADER + #define FIRST_LL_HEADER + #define SLL_QUEUE_ADD_MOD(f, l, n, next) \ + do { \ + (n)->next = 0; \ + if ((f) == 0) { \ + (f) = (l) = (n); \ + } else { \ + (l) = (l)->next = (n); \ + } \ + } while (0) + #define SLL_QUEUE_ADD(f, l, n) SLL_QUEUE_ADD_MOD(f, l, n, next) + + #define SLL_QUEUE_POP_FIRST_MOD(f, l, next) \ + do { \ + if ((f) == (l)) { \ + (f) = (l) = 0; \ + } else { \ + (f) = (f)->next; \ + } \ + } while (0) + #define SLL_QUEUE_POP_FIRST(f, l) SLL_QUEUE_POP_FIRST_MOD(f, l, next) + + #define SLL_STACK_ADD_MOD(stack_base, new_stack_base, next) \ + do { \ + (new_stack_base)->next = (stack_base); \ + (stack_base) = (new_stack_base); \ + } while (0) + #define SLL_STACK_ADD(stack_base, new_stack_base) \ + SLL_STACK_ADD_MOD(stack_base, new_stack_base, next) + + #define SLL_STACK_POP_AND_STORE(stack_base, out_node) \ + do { \ + if (stack_base) { \ + (out_node) = (stack_base); \ + (stack_base) = (stack_base)->next; \ + (out_node)->next = 0; \ + } \ + } while (0) + + #define DLL_QUEUE_ADD_MOD(f, l, node, next, prev) \ + do { \ + if ((f) == 0) { \ + (f) = (l) = (node); \ + (node)->prev = 0; \ + (node)->next = 0; \ + } else { \ + (l)->next = (node); \ + (node)->prev = (l); \ + (node)->next = 0; \ + (l) = (node); \ + } \ + } while (0) + #define DLL_QUEUE_ADD(f, l, node) DLL_QUEUE_ADD_MOD(f, l, node, next, prev) + #define DLL_QUEUE_ADD_FRONT(f, l, node) DLL_QUEUE_ADD_MOD(l, f, node, prev, next) + #define DLL_QUEUE_REMOVE_MOD(first, last, node, next, prev) \ + do { \ + if ((first) == (last)) { \ + (first) = (last) = 0; \ + } else if ((last) == (node)) { \ + (last) = (last)->prev; \ + (last)->next = 0; \ + } else if ((first) == (node)) { \ + (first) = (first)->next; \ + (first)->prev = 0; \ + } else { \ + (node)->prev->next = (node)->next; \ + (node)->next->prev = (node)->prev; \ + } \ + if (node) { \ + (node)->prev = 0; \ + (node)->next = 0; \ + } \ + } while (0) + #define DLL_QUEUE_REMOVE(first, last, node) DLL_QUEUE_REMOVE_MOD(first, last, node, next, prev) + + #define DLL_STACK_ADD_MOD(first, node, next, prev) \ + do { \ + (node)->next = (first); \ + if ((first)) \ + (first)->prev = (node); \ + (first) = (node); \ + (node)->prev = 0; \ + } while (0) + #define DLL_STACK_ADD(first, node) DLL_STACK_ADD_MOD(first, node, next, prev) + #define DLL_STACK_REMOVE_MOD(first, node, next, prev) \ + do { \ + if ((node) == (first)) { \ + (first) = (first)->next; \ + if ((first)) \ + (first)->prev = 0; \ + } else { \ + (node)->prev->next = (node)->next; \ + if ((node)->next) \ + (node)->next->prev = (node)->prev; \ + } \ + if (node) { \ + (node)->prev = 0; \ + (node)->next = 0; \ + } \ + } while (0) + #define DLL_STACK_REMOVE(first, node) DLL_STACK_REMOVE_MOD(first, node, next, prev) + + #define DLL_INSERT_NEXT_MOD(base, new, next, prev) \ + do { \ + if ((base) == 0) { \ + (base) = (new); \ + (new)->next = 0; \ + (new)->prev = 0; \ + } else { \ + (new)->next = (base)->next; \ + (base)->next = (new); \ + (new)->prev = (base); \ + if ((new)->next) (new)->next->prev = (new); \ + } \ + } while (0) + #define DLL_INSERT_NEXT(base, new) DLL_INSERT_NEXT_MOD(base, new, next, prev) + #define DLL_INSERT_PREV(base, new) DLL_INSERT_NEXT_MOD(base, new, next, prev) +#endif + +// Quick and dirty filesystem operations + +#ifndef OS_API + #define OS_API +#endif + +typedef enum OS_Result { + OS_SUCCESS, + OS_ALREADY_EXISTS, + OS_PATH_NOT_FOUND, + OS_FAILURE, +} OS_Result; + +enum { + OS_NO_FLAGS = 0, + OS_RECURSIVE = 1, + OS_RELATIVE_PATHS = 2, +}; + +typedef struct OS_Date OS_Date; +struct OS_Date { + uint32_t year; + uint32_t month; + uint32_t day; + uint32_t hour; + uint32_t minute; + uint32_t second; +}; + +typedef struct OS_FileIter OS_FileIter; +struct OS_FileIter { + bool is_valid; + bool is_directory; + S8_String absolute_path; + S8_String relative_path; + S8_String filename; + + S8_String path; + MA_Arena *arena; + union { + struct OS_Win32_FileIter *w32; + void *dir; + }; +}; + +const bool os_copy_overwrite = true; +const bool os_copy_dont_overwrite = false; + +#ifdef _WIN32 + #ifndef NOMINMAX + #define NOMINMAX + #endif + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #include + #include + #include + +OS_API bool OS_EnableTerminalColors(void) { + // Enable color terminal output + { + // Set output mode to handle virtual terminal sequences + HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); + if (hOut != INVALID_HANDLE_VALUE) { + DWORD dwMode = 0; + if (GetConsoleMode(hOut, &dwMode)) { + dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + if (SetConsoleMode(hOut, dwMode)) { + return true; + } else { + IO_Printf("Failed to enable colored terminal output C\n"); + } + } else { + IO_Printf("Failed to enable colored terminal output B\n"); + } + } else { + IO_Printf("Failed to enable colored terminal output A\n"); + } + } + return false; +} + +OS_API bool OS_IsAbsolute(S8_String path) { + bool result = path.len > 3 && CHAR_IsAlphabetic(path.str[0]) && path.str[1] == ':' && path.str[2] == '/'; + return result; +} + +OS_API S8_String OS_GetExePath(MA_Arena *arena) { + wchar_t wbuffer[1024]; + DWORD wsize = GetModuleFileNameW(0, wbuffer, MA_Lengthof(wbuffer)); + IO_Assert(wsize != 0); + + S8_String path = S8_FromWidecharEx(arena, wbuffer, wsize); + S8_NormalizePathUnsafe(path); + return path; +} + +OS_API S8_String OS_GetExeDir(MA_Arena *arena) { + MA_Temp scratch = MA_GetScratch(); + S8_String path = OS_GetExePath(scratch.arena); + path = S8_ChopLastSlash(path); + path = S8_Copy(arena, path); + MA_ReleaseScratch(scratch); + return path; +} + +OS_API S8_String os_cwd(MA_Arena *arena) { + wchar_t wbuffer[1024]; + DWORD wsize = GetCurrentDirectoryW(MA_Lengthof(wbuffer), wbuffer); + IO_Assert(wsize != 0); + IO_Assert(wsize < 1022); + wbuffer[wsize++] = '/'; + wbuffer[wsize] = 0; + + S8_String path = S8_FromWidecharEx(arena, wbuffer, wsize); + S8_NormalizePathUnsafe(path); + return path; +} + +OS_API void os_set_working_dir(char *path) { + IO_Printf("cd %s\n", path); + wchar_t wpath[1024]; + UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path, S8_Length(path)); + SetCurrentDirectoryW(wpath); +} + +OS_API S8_String OS_GetAbsolutePath(MA_Arena *arena, S8_String relative) { + wchar_t wpath[1024]; + UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), relative.str, relative.len); + wchar_t wpath_abs[1024]; + DWORD written = GetFullPathNameW((wchar_t *)wpath, MA_Lengthof(wpath_abs), wpath_abs, 0); + if (written == 0) + return S8_MakeEmpty(); + S8_String path = S8_FromWidecharEx(arena, wpath_abs, written); + S8_NormalizePathUnsafe(path); + return path; +} + +OS_API bool OS_FileExists(S8_String path) { + wchar_t wbuff[1024]; + UTF_CreateWidecharFromChar(wbuff, MA_Lengthof(wbuff), path.str, path.len); + DWORD attribs = GetFileAttributesW(wbuff); + bool result = attribs == INVALID_FILE_ATTRIBUTES ? false : true; + return result; +} + +OS_API bool OS_IsDir(S8_String path) { + wchar_t wbuff[1024]; + UTF_CreateWidecharFromChar(wbuff, MA_Lengthof(wbuff), path.str, path.len); + DWORD dwAttrib = GetFileAttributesW(wbuff); + return dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY); +} + +OS_API bool OS_IsFile(S8_String path) { + wchar_t wbuff[1024]; + UTF_CreateWidecharFromChar(wbuff, MA_Lengthof(wbuff), path.str, path.len); + DWORD dwAttrib = GetFileAttributesW(wbuff); + bool is_file = (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) == 0; + return dwAttrib != INVALID_FILE_ATTRIBUTES && is_file; +} + +OS_API double OS_GetTime(void) { + static int64_t counts_per_second; + if (counts_per_second == 0) { + LARGE_INTEGER freq; + QueryPerformanceFrequency(&freq); + counts_per_second = freq.QuadPart; + } + + LARGE_INTEGER time; + QueryPerformanceCounter(&time); + double result = (double)time.QuadPart / (double)counts_per_second; + return result; +} + +/* +User needs to copy particular filename to keep it. + +for (OS_FileIter it = OS_IterateFiles(it); OS_IsValid(iter); OS_Advance(it)) { +} + +*/ + +typedef struct OS_Win32_FileIter { + HANDLE handle; + WIN32_FIND_DATAW data; +} OS_Win32_FileIter; + +OS_API bool OS_IsValid(OS_FileIter it) { + return it.is_valid; +} + +OS_API void OS_Advance(OS_FileIter *it) { + while (FindNextFileW(it->w32->handle, &it->w32->data) != 0) { + WIN32_FIND_DATAW *data = &it->w32->data; + + // Skip '.' and '..' + if (data->cFileName[0] == '.' && data->cFileName[1] == '.' && data->cFileName[2] == 0) continue; + if (data->cFileName[0] == '.' && data->cFileName[1] == 0) continue; + + it->is_directory = data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; + it->filename = S8_FromWidecharEx(it->arena, data->cFileName, S8_WideLength(data->cFileName)); + const char *is_dir = it->is_directory ? "/" : ""; + const char *separator = it->path.str[it->path.len - 1] == '/' ? "" : "/"; + it->relative_path = S8_Format(it->arena, "%.*s%s%.*s%s", S8_Expand(it->path), separator, S8_Expand(it->filename), is_dir); + it->absolute_path = OS_GetAbsolutePath(it->arena, it->relative_path); + it->is_valid = true; + + if (it->is_directory) { + IO_Assert(it->relative_path.str[it->relative_path.len - 1] == '/'); + IO_Assert(it->absolute_path.str[it->absolute_path.len - 1] == '/'); + } + return; + } + + it->is_valid = false; + DWORD error = GetLastError(); + IO_Assert(error == ERROR_NO_MORE_FILES); + FindClose(it->w32->handle); +} + +OS_API OS_FileIter OS_IterateFiles(MA_Arena *scratch_arena, S8_String path) { + OS_FileIter it = {0}; + it.arena = scratch_arena; + it.path = path; + + S8_String modified_path = S8_Format(it.arena, "%.*s\\*", S8_Expand(path)); + wchar_t *wbuff = MA_PushArray(it.arena, wchar_t, modified_path.len + 1); + int64_t wsize = UTF_CreateWidecharFromChar(wbuff, modified_path.len + 1, modified_path.str, modified_path.len); + IO_Assert(wsize); + + it.w32 = MA_PushStruct(it.arena, OS_Win32_FileIter); + it.w32->handle = FindFirstFileW(wbuff, &it.w32->data); + if (it.w32->handle == INVALID_HANDLE_VALUE) { + it.is_valid = false; + return it; + } + + IO_Assert(it.w32->data.cFileName[0] == '.' && it.w32->data.cFileName[1] == 0); + OS_Advance(&it); + return it; +} + +OS_API OS_Result os_make_dir(char *path) { + IO_Printf("mkdir %s\n", path); + wchar_t wpath[1024]; + UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path, S8_Length(path)); + BOOL success = CreateDirectoryW(wpath, NULL); + OS_Result result = OS_SUCCESS; + if (success == 0) { + DWORD error = GetLastError(); + if (error == ERROR_ALREADY_EXISTS) { + result = OS_ALREADY_EXISTS; + } else if (error == ERROR_PATH_NOT_FOUND) { + result = OS_PATH_NOT_FOUND; + } else { + IO_Assert(0); + } + } + return result; +} + +OS_API OS_Result os_copy(char *from, char *to, bool overwrite) { + const char *ow = overwrite ? "-n" : ""; + IO_Printf("cp %s %s %s\n", ow, from, to); + + wchar_t wfrom[1024]; + UTF_CreateWidecharFromChar(wfrom, MA_Lengthof(wfrom), from, S8_Length(from)); + + wchar_t wto[1024]; + UTF_CreateWidecharFromChar(wto, MA_Lengthof(wto), to, S8_Length(to)); + + BOOL fail_if_exists = !overwrite; + BOOL success = CopyFileW(wfrom, wto, fail_if_exists); + + OS_Result result = OS_SUCCESS; + if (success == FALSE) + result = OS_FAILURE; + return result; +} + +OS_API OS_Result os_delete_file(S8_String path) { + IO_Printf("rm %.*s\n", S8_Expand(path)); + wchar_t wpath[1024]; + UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len); + BOOL success = DeleteFileW(wpath); + OS_Result result = OS_SUCCESS; + if (success == 0) + result = OS_PATH_NOT_FOUND; + return result; +} + +OS_API OS_Result OS_DeleteDir(S8_String path, unsigned flags) { + IO_Todo(); + return OS_FAILURE; + #if 0 + if (flags & OS_RECURSIVE) { + MA_Temp scratch = MA_GetScratch(); + S8_List list = OS_ListDir(scratch.arena, path, OS_RECURSIVE); + S8_Node *dirs_to_remove = 0; + for (S8_Node *it = list.first; it; it = it->next) { + if (!S8_EndsWith(it->string, S8_Lit("/"), S8_IgnoreCase)) { + os_delete_file(it->string); + } + else { + S8_Node *node = S8_CreateNode(scratch.arena, it->string); + SLL_STACK_ADD(dirs_to_remove, node); + } + } + for (S8_Node *it = dirs_to_remove; it; it = it->next) { + OS_DeleteDir(it->string, OS_NO_FLAGS); + } + OS_Result result = OS_DeleteDir(path, OS_NO_FLAGS); + MA_ReleaseScratch(scratch); + return result; + } + else { + wchar_t wpath[1024]; + UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len); + BOOL success = RemoveDirectoryW(wpath); + OS_Result result = OS_SUCCESS; + if (success == 0) + result = OS_PATH_NOT_FOUND; + return result; + } + #endif +} + +static OS_Result OS__WriteFile(S8_String path, S8_String data, bool append) { + wchar_t wpath[1024]; + UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len); + OS_Result result = OS_FAILURE; + + DWORD access = GENERIC_WRITE; + DWORD creation_disposition = CREATE_ALWAYS; + if (append) { + access = FILE_APPEND_DATA; + creation_disposition = OPEN_ALWAYS; + } + + HANDLE handle = CreateFileW(wpath, access, 0, NULL, creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL); + if (handle != INVALID_HANDLE_VALUE) { + DWORD bytes_written = 0; + IO_Assert(data.len == (DWORD)data.len); // @Todo: can only read 32 byte size files? + BOOL error = WriteFile(handle, data.str, (DWORD)data.len, &bytes_written, NULL); + if (error == TRUE) { + if (bytes_written == data.len) { + result = OS_SUCCESS; + } + } + CloseHandle(handle); + } else result = OS_PATH_NOT_FOUND; + + return result; +} + +OS_API OS_Result OS_AppendFile(S8_String path, S8_String string) { + return OS__WriteFile(path, string, true); +} + +OS_API OS_Result os_write_file(S8_String path, S8_String string) { + return OS__WriteFile(path, string, false); +} + +OS_API S8_String OS_ReadFile(MA_Arena *arena, S8_String path) { + bool success = false; + S8_String result = S8_MakeEmpty(); + MA_Temp checkpoint = MA_BeginTemp(arena); + + wchar_t wpath[1024]; + UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len); + HANDLE handle = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (handle != INVALID_HANDLE_VALUE) { + LARGE_INTEGER file_size; + if (GetFileSizeEx(handle, &file_size)) { + if (file_size.QuadPart != 0) { + result.len = (int64_t)file_size.QuadPart; + result.str = (char *)MA_PushSizeNonZeroed(arena, result.len + 1); + DWORD read; + if (ReadFile(handle, result.str, (DWORD)result.len, &read, NULL)) { // @todo: can only read 32 byte size files? + if (read == result.len) { + success = true; + result.str[result.len] = 0; + } + } + } + } + CloseHandle(handle); + } + + if (!success) { + result = S8_MakeEmpty(); + MA_EndTemp(checkpoint); + } + + return result; +} + +OS_API int64_t OS_GetFileModTime(S8_String file) { + FILETIME time = {0}; + WIN32_FIND_DATAW data; + + wchar_t wpath[1024]; + UTF_CreateWidecharFromChar(wpath, 1024, file.str, file.len); + HANDLE handle = FindFirstFileW(wpath, &data); + if (handle != INVALID_HANDLE_VALUE) { + FindClose(handle); + time = data.ftLastWriteTime; + } else { + return -1; + } + int64_t result = (int64_t)time.dwHighDateTime << 32 | time.dwLowDateTime; + return result; +} + +OS_API OS_Date OS_GetDate(void) { + SYSTEMTIME local; + GetLocalTime(&local); + + OS_Date result = {0}; + result.year = local.wYear; + result.month = local.wMonth; + result.day = local.wDay; + result.hour = local.wHour; + result.second = local.wSecond; + // result.milliseconds = local.wMilliseconds; + return result; +} + +#elif __linux__ || __APPLE__ || __unix__ + #include + #include + #include + #include + #include + + #if OS_MAC + #include + +OS_API S8_String OS_GetExePath(MA_Arena *arena) { + char buf[PATH_MAX]; + uint32_t bufsize = PATH_MAX; + if (_NSGetExecutablePath(buf, &bufsize)) { + return S8_MakeEmpty(); + } + + S8_String result = S8_Copy(arena, S8_MakeFromChar(buf)); + return result; +} + + #else + +OS_API S8_String OS_GetExePath(MA_Arena *arena) { + char buffer[PATH_MAX] = {}; + if (readlink("/proc/self/exe", buffer, PATH_MAX) == -1) { + return S8_MakeEmpty(); + } + S8_String result = S8_Copy(arena, S8_MakeFromChar(buffer)); + return result; +} + + #endif + +OS_API bool OS_EnableTerminalColors(void) { return true; } + +OS_API bool OS_IsAbsolute(S8_String path) { + bool result = path.len >= 1 && path.str[0] == '/'; + return result; +} + +OS_API S8_String OS_GetExeDir(MA_Arena *arena) { + MA_Temp scratch = MA_GetScratch(); + S8_String path = OS_GetExePath(scratch.arena); + S8_String dir = S8_ChopLastSlash(path); + S8_String copy = S8_Copy(arena, dir); + MA_ReleaseScratch(scratch); + return copy; +} + +OS_API S8_String os_cwd(MA_Arena *arena) { + char *buffer = (char *)MA_PushSizeNonZeroed(arena, PATH_MAX); + char *cwd = getcwd(buffer, PATH_MAX); + S8_String result = S8_MakeFromChar(cwd); + return result; +} + +OS_API void os_set_working_dir(char *path) { + IO_Printf("cd %s\n", path); + MA_Temp scratch = MA_GetScratch(); + S8_String copy = S8_Copy(scratch.arena, S8_MakeFromChar(path)); + chdir(copy.str); + MA_ReleaseScratch(scratch); +} + +OS_API S8_String OS_GetAbsolutePath(MA_Arena *arena, S8_String relative) { + MA_Temp scratch = MA_GetScratch1(arena); + S8_String copy = S8_Copy(scratch.arena, relative); + + char *buffer = (char *)MA_PushSizeNonZeroed(arena, PATH_MAX); + realpath((char *)copy.str, buffer); + S8_String result = S8_MakeFromChar(buffer); + + MA_ReleaseScratch(scratch); + return result; +} + +OS_API bool OS_FileExists(S8_String path) { + MA_Temp scratch = MA_GetScratch(); + S8_String copy = S8_Copy(scratch.arena, path); + + bool result = false; + if (access((char *)copy.str, F_OK) == 0) { + result = true; + } + + MA_ReleaseScratch(scratch); + return result; +} + +OS_API bool OS_IsDir(S8_String path) { + MA_Temp scratch = MA_GetScratch(); + S8_String copy = S8_Copy(scratch.arena, path); + + struct stat s; + if (stat(copy.str, &s) != 0) + return false; + + bool result = S_ISDIR(s.st_mode); + MA_ReleaseScratch(scratch); + return result; +} + +OS_API bool OS_IsFile(S8_String path) { + MA_Temp scratch = MA_GetScratch(); + S8_String copy = S8_Copy(scratch.arena, path); + + struct stat s; + if (stat(copy.str, &s) != 0) + return false; + bool result = S_ISREG(s.st_mode); + MA_ReleaseScratch(scratch); + return result; +} + +OS_API double OS_GetTime(void) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + uint64_t timeu64 = (((uint64_t)ts.tv_sec) * 1000000ull) + ((uint64_t)ts.tv_nsec) / 1000ull; + double timef = (double)timeu64; + double result = timef / 1000000.0; // Microseconds to seconds + return result; +} + +OS_API bool OS_IsValid(OS_FileIter it) { + return it.is_valid; +} + +OS_API void OS_Advance(OS_FileIter *it) { + struct dirent *file = 0; + while ((file = readdir((DIR *)it->dir)) != NULL) { + if (file->d_name[0] == '.' && file->d_name[1] == '.' && file->d_name[2] == 0) continue; + if (file->d_name[0] == '.' && file->d_name[1] == 0) continue; + + it->is_directory = file->d_type == DT_DIR; + it->filename = S8_CopyChar(it->arena, file->d_name); + + const char *dir_char_ending = it->is_directory ? "/" : ""; + const char *separator = it->path.str[it->path.len - 1] == '/' ? "" : "/"; + it->relative_path = S8_Format(it->arena, "%.*s%s%s%s", S8_Expand(it->path), separator, file->d_name, dir_char_ending); + it->absolute_path = OS_GetAbsolutePath(it->arena, it->relative_path); + if (it->is_directory) it->absolute_path = S8_Format(it->arena, "%.*s/", S8_Expand(it->absolute_path)); + it->is_valid = true; + return; + } + it->is_valid = false; + closedir((DIR *)it->dir); +} + +OS_API OS_FileIter OS_IterateFiles(MA_Arena *arena, S8_String path) { + OS_FileIter it = {0}; + it.arena = arena; + it.path = path = S8_Copy(arena, path); + it.dir = (void *)opendir((char *)path.str); + if (!it.dir) return it; + + OS_Advance(&it); + return it; +} + +OS_API OS_Result os_make_dir(char *path) { + IO_Printf("mkdir %s\n", path); + MA_Temp scratch = MA_GetScratch(); + path = S8_Copy(scratch.arena, S8_MakeFromChar(path)); + int error = mkdir(path.str, 0755); + MA_ReleaseScratch(scratch); + return error == 0 ? OS_SUCCESS : OS_FAILURE; +} + +OS_API OS_Result os_copy(char *from, char *to, bool overwrite) { + const char *ow = overwrite ? "-n" : ""; + int result = os_systemf("cp %s %s %s", ow, from, to); + return result == 0 ? OS_SUCCESS : OS_FAILURE; +} + +OS_API OS_Result os_delete_file(S8_String path) { + int result = os_systemf("rm %.*s", S8_Expand(path)); + return result == 0 ? OS_SUCCESS : OS_FAILURE; +} + +OS_API OS_Result OS_DeleteDir(S8_String path, unsigned flags) { + IO_Assert(flags & OS_RECURSIVE); + int result = os_systemf("rm -r %.*s", S8_Expand(path)); + return result == 0 ? OS_SUCCESS : OS_FAILURE; +} + +OS_API int64_t OS_GetFileModTime(S8_String file) { + MA_Temp scratch = MA_GetScratch(); + file = S8_Copy(scratch.arena, file); + + struct stat attrib = {}; + stat(file.str, &attrib); + struct timespec ts = attrib.IF_LINUX_ELSE(st_mtim, st_mtimespec); + int64_t result = (((int64_t)ts.tv_sec) * 1000000ll) + ((int64_t)ts.tv_nsec) / 1000ll; + + MA_ReleaseScratch(scratch); + return result; +} + +OS_API OS_Date OS_GetDate(void) { + time_t t = time(NULL); + struct tm date = *localtime(&t); + + OS_Date s = {0}; + s.second = date.tm_sec; + s.year = date.tm_year; + s.month = date.tm_mon; + s.day = date.tm_mday; + s.hour = date.tm_hour; + s.minute = date.tm_min; + + return s; +} + +OS_API OS_Result OS_AppendFile(S8_String path, S8_String string) { + MA_Temp scratch = MA_GetScratch(); + path = S8_Copy(scratch.arena, path); + + OS_Result result = OS_FAILURE; + FILE *f = fopen((const char *)path.str, "a"); + if (f) { + result = OS_SUCCESS; + + size_t written = fwrite(string.str, 1, string.len, f); + if (written < string.len) { + result = OS_FAILURE; + } + + int error = fclose(f); + if (error != 0) { + result = OS_FAILURE; + } + } + + MA_ReleaseScratch(scratch); + return result; +} + +OS_API S8_String OS_ReadFile(MA_Arena *arena, S8_String path) { + S8_String result = {}; + + // ftell returns insane size if file is + // a directory **on some machines** KEKW + if (OS_IsDir(path)) { + return result; + } + + MA_Temp scratch = MA_GetScratch1(arena); + path = S8_Copy(scratch.arena, path); + + FILE *f = fopen(path.str, "rb"); + if (f) { + fseek(f, 0, SEEK_END); + result.len = ftell(f); + fseek(f, 0, SEEK_SET); + + result.str = (char *)MA_PushSizeNonZeroed(arena, result.len + 1); + fread(result.str, result.len, 1, f); + result.str[result.len] = 0; + + fclose(f); + } + + MA_ReleaseScratch(scratch); + return result; +} + +OS_API OS_Result os_write_file(S8_String path, S8_String string) { + MA_Temp scratch = MA_GetScratch(); + path = S8_Copy(scratch.arena, path); + + OS_Result result = OS_FAILURE; + FILE *f = fopen((const char *)path.str, "w"); + if (f) { + result = OS_SUCCESS; + + size_t written = fwrite(string.str, 1, string.len, f); + if (written < string.len) { + result = OS_FAILURE; + } + + int error = fclose(f); + if (error != 0) { + result = OS_FAILURE; + } + } + + MA_ReleaseScratch(scratch); + return result; +} +#endif + +#if _WIN32 || __linux__ || __APPLE__ || __unix__ +OS_API int os_systemf(const char *string, ...) { + MA_Temp scratch = MA_GetScratch(); + S8_FORMAT(scratch.arena, string, result); + IO_Printf("%.*s\n", S8_Expand(result)); + int error_code = system(result.str); + MA_ReleaseScratch(scratch); + return error_code; +} + +OS_API bool OS_ExpandIncludesList(MA_Arena *arena, S8_List *out, S8_String filepath) { + S8_String c = OS_ReadFile(arena, filepath); + if (c.str == 0) return false; + S8_String path = S8_ChopLastSlash(filepath); + S8_String include = S8_Lit("#include \""); + for (;;) { + int64_t idx = -1; + if (S8_Seek(c, include, 0, &idx)) { + S8_String str_to_add = S8_GetPrefix(c, idx); + S8_AddNode(arena, out, str_to_add); + S8_String save = c; + c = S8_Skip(c, idx + include.len); + + S8_String filename = c; + filename.len = 0; + while (filename.str[filename.len] != '"' && filename.len < c.len) { + filename.len += 1; + } + + c = S8_Skip(c, filename.len + 1); + S8_String inc_path = S8_Format(arena, "%.*s/%.*s", S8_Expand(path), S8_Expand(filename)); + if (!OS_ExpandIncludesList(arena, out, inc_path)) { + S8_String s = S8_GetPrefix(save, save.len - c.len); + S8_AddNode(arena, out, s); + } + } else { + S8_AddNode(arena, out, c); + break; + } + } + return true; +} + +OS_API S8_String OS_ExpandIncludes(MA_Arena *arena, S8_String filepath) { + S8_List out = S8_MakeEmptyList(); + S8_String result = S8_MakeEmpty(); + MA_ScratchScope(s) { + OS_ExpandIncludesList(s.arena, &out, filepath); + result = S8_Merge(arena, out); + } + return result; +} +#endif + +typedef struct M_Allocator M_Allocator; + +typedef enum M_AllocatorOp { + M_AllocatorOp_Invalid, + M_AllocatorOp_Allocate, + M_AllocatorOp_Deallocate, + M_AllocatorOp_Reallocate, +} M_AllocatorOp; + +typedef void *M_AllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size, size_t old_size); +void *MA_AllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size, size_t old_size); +void *MA_ExclusiveAllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size, size_t old_size); + +struct M_Allocator { + // it's int for type safety because C++ somehow allows this: + // struct Array { M_Allocator allocator; } + // Array = {arena}; WTF???!??!? + // without actually passing M_Allocator but just a pointer + int *obj; + M_AllocatorProc *p; +}; + +#define M_AllocStruct(a, T) (T *)M_Alloc((a), sizeof(T)) +#define M_AllocArray(a, T, c) (T *)M_Alloc((a), sizeof(T) * (c)) +#define M_AllocStructNonZeroed(a, T) (T *)M_AllocNonZeroed((a), sizeof(T)) +#define M_AllocArrayNonZeroed(a, T, c) (T *)M_AllocNonZeroed((a), sizeof(T) * (c)) +#define M_AllocStructCopy(a, T, p) (T *)M_PushCopy(a, (p), sizeof(T)) + +#define M_Alloc(a, size) M__Alloc(a, size) +#define M_AllocNonZeroed(a, size) M__AllocNonZeroed(a, size) +#define M_AllocCopy(a, p, size) M__AllocCopy(a, p, size) +#define M_Realloc(a, p, size, old_size) M__Realloc(a, p, size, old_size) +#define M_Dealloc(a, p) M__Dealloc(a, p) + +void *M__AllocNonZeroed(M_Allocator allocator, size_t size); +void *M__Alloc(M_Allocator allocator, size_t size); +void *M__AllocCopy(M_Allocator allocator, void *p, size_t size); +void *M__Realloc(M_Allocator allocator, void *p, size_t size, size_t old_size); +void M__Dealloc(M_Allocator allocator, void *p); + +M_Allocator M_GetSystemAllocator(void); +M_Allocator MA_GetExclusiveAllocator(MA_Arena *arena); +M_Allocator MA_GetAllocator(MA_Arena *arena); +M_Allocator MA_BootstrapExclusive(void); + +#ifndef MA_CMalloc + #include + #define MA_CMalloc(x) malloc(x) + #define MA_CFree(x) free(x) + #define MA_CRealloc(p, size) realloc(p, size) +#endif + +M_Allocator MA_BootstrapExclusive(void) { + MA_Arena bootstrap_arena = {0}; + MA_Arena *arena = MA_PushStruct(&bootstrap_arena, MA_Arena); + *arena = bootstrap_arena; + arena->base_len = arena->len; + return MA_GetExclusiveAllocator(arena); +} + +void *M__AllocNonZeroed(M_Allocator allocator, size_t size) { + void *p = allocator.p(allocator.obj, M_AllocatorOp_Allocate, NULL, size, 0); + return p; +} + +void *M__Alloc(M_Allocator allocator, size_t size) { + void *p = allocator.p(allocator.obj, M_AllocatorOp_Allocate, NULL, size, 0); + MA_MemoryZero(p, size); + return p; +} + +void *M__AllocCopy(M_Allocator allocator, void *p, size_t size) { + void *copy_buffer = M__AllocNonZeroed(allocator, size); + MA_MemoryCopy(copy_buffer, p, size); + return copy_buffer; +} + +void M__Dealloc(M_Allocator allocator, void *p) { + allocator.p(allocator.obj, M_AllocatorOp_Deallocate, p, 0, 0); +} + +void *M__Realloc(M_Allocator allocator, void *p, size_t size, size_t old_size) { + void *result = allocator.p(allocator.obj, M_AllocatorOp_Reallocate, p, size, old_size); + // @todo: add old_size? because we can't zero + return result; +} + +MA_StaticFunc void *M_ClibAllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size, size_t old_size) { + if (kind == M_AllocatorOp_Allocate) { + return MA_CMalloc(size); + } + + if (kind == M_AllocatorOp_Deallocate) { + MA_CFree(p); + return NULL; + } + + if (kind == M_AllocatorOp_Reallocate) { + return MA_CRealloc(p, size); + } + + MA_Assertf(0, "MA_Arena invalid codepath"); + return NULL; +} + +void *MA_AllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size, size_t old_size) { + if (kind == M_AllocatorOp_Allocate) { + return MA__PushSizeNonZeroed((MA_Arena *)allocator, size); + } + + else if (kind == M_AllocatorOp_Reallocate) { + void *new_p = MA__PushSizeNonZeroed((MA_Arena *)allocator, size); + MA_MemoryCopy(new_p, p, old_size); + return new_p; + } + + else if (kind == M_AllocatorOp_Deallocate) { + return NULL; + } + + MA_Assertf(0, "MA_Arena invalid codepath"); + return NULL; +} + +void *MA_ExclusiveAllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size, size_t old_size) { + MA_Arena *arena = (MA_Arena *)allocator; + if (kind == M_AllocatorOp_Reallocate) { + if (size > old_size) { + size_t size_to_push = size - old_size; + void *result = MA__PushSizeNonZeroed(arena, size_to_push); + if (!p) p = result; + return p; + } + } + + if (kind == M_AllocatorOp_Deallocate) { + MA_DeallocateArena(arena); + return NULL; + } + + MA_Assertf(0, "MA_Arena invalid codepath"); + return NULL; +} + +M_Allocator MA_GetExclusiveAllocator(MA_Arena *arena) { + M_Allocator allocator = {(int *)arena, MA_ExclusiveAllocatorProc}; + return allocator; +} + +M_Allocator MA_GetAllocator(MA_Arena *arena) { + M_Allocator allocator = {(int *)arena, MA_AllocatorProc}; + return allocator; +} + +M_Allocator M_GetSystemAllocator(void) { + M_Allocator allocator; + allocator.obj = 0; + allocator.p = M_ClibAllocatorProc; + return allocator; +} + + +#define CL_Allocator MA_Arena * +#define CL_Allocate(a, s) MA_PushSizeNonZeroed(a, s) +#define CL_ASSERT IO_Assert +#define AND_CL_STRING_TERMINATE_ON_NEW_LINE + +#include +#include +#include + +#ifndef CL_API_FUNCTION + #ifdef __cplusplus + #define CL_API_FUNCTION extern "C" + #else + #define CL_API_FUNCTION + #endif +#endif + +#ifndef CL_INLINE + #ifndef _MSC_VER + #ifdef __cplusplus + #define CL_INLINE inline + #else + #define CL_INLINE + #endif + #else + #define CL_INLINE __forceinline + #endif +#endif + +#ifndef CL_Allocator +struct MA_Arena; + #define CL_Allocator MA_Arena * +#endif + +#ifndef AND_CL_STRING_TERMINATE_ON_NEW_LINE + #define AND_CL_STRING_TERMINATE_ON_NEW_LINE &&*T->stream != '\n' +#endif + +typedef enum CL_Kind { + CL_EOF, + CL_MUL, + CL_DIV, + CL_MOD, + CL_LEFTSHIFT, + CL_RIGHTSHIFT, + CL_ADD, + CL_SUB, + CL_EQUALS, + CL_LESSERTHEN, + CL_GREATERTHEN, + CL_LESSERTHEN_OR_EQUAL, + CL_GREATERTHEN_OR_EQUAL, + CL_NOTEQUALS, + CL_BITAND, + CL_BITOR, + CL_BITXOR, + CL_AND, + CL_OR, + CL_NEG, + CL_NOT, + CL_DECREMENT, + CL_INCREMENT, + CL_POSTDECREMENT, + CL_POSTINCREMENT, + CL_ASSIGN, + CL_DIVASSIGN, + CL_MULASSIGN, + CL_MODASSIGN, + CL_SUBASSIGN, + CL_ADDASSIGN, + CL_ANDASSIGN, + CL_ORASSIGN, + CL_XORASSIGN, + CL_LEFTSHIFTASSIGN, + CL_RIGHTSHIFTASSIGN, + CL_OPENPAREN, + CL_CLOSEPAREN, + CL_OPENBRACE, + CL_CLOSEBRACE, + CL_OPENBRACKET, + CL_CLOSEBRACKET, + CL_COMMA, + CL_MACRO_CONCAT, + CL_PREPROC_STRINGIFY, + CL_QUESTION, + CL_THREEDOTS, + CL_SEMICOLON, + CL_DOT, + CL_COLON, + CL_TAG, + CL_ARROW, + CL_EXPRSIZEOF, + CL_DOCCOMMENT, + CL_COMMENT, + CL_IDENTIFIER, + CL_STRINGLIT, + CL_CHARLIT, + CL_ERROR, + CL_FLOAT, + CL_INT, + CL_PREPROC_NULL, + CL_PREPROC_DEFINE, + CL_PREPROC_IFDEF, + CL_PREPROC_IFNDEF, + CL_PREPROC_INCLUDE, + CL_PREPROC_ENDIF, + CL_PREPROC_IF, + CL_PREPROC_PRAGMA, + CL_PREPROC_ERROR, + CL_PREPROC_ELSE, + CL_PREPROC_ELIF, + CL_PREPROC_UNDEF, + CL_KEYWORD_VOID, + CL_KEYWORD_INT, + CL_KEYWORD_CHAR, + CL_KEYWORD_UNSIGNED, + CL_KEYWORD_SIGNED, + CL_KEYWORD_LONG, + CL_KEYWORD_SHORT, + CL_KEYWORD_DOUBLE, + CL_KEYWORD_FLOAT, + CL_KEYWORD__BOOL, + CL_KEYWORD__COMPLEX, + CL_KEYWORD__IMAGINARY, + CL_KEYWORD_STATIC, + CL_KEYWORD_AUTO, + CL_KEYWORD_CONST, + CL_KEYWORD_EXTERN, + CL_KEYWORD_INLINE, + CL_KEYWORD_REGISTER, + CL_KEYWORD_RESTRICT, + CL_KEYWORD_VOLATILE, + CL_KEYWORD__THREAD_LOCAL, + CL_KEYWORD__ATOMIC, + CL_KEYWORD__NORETURN, + CL_KEYWORD_STRUCT, + CL_KEYWORD_UNION, + CL_KEYWORD_ENUM, + CL_KEYWORD_TYPEDEF, + CL_KEYWORD_DEFAULT, + CL_KEYWORD_BREAK, + CL_KEYWORD_RETURN, + CL_KEYWORD_SWITCH, + CL_KEYWORD_IF, + CL_KEYWORD_ELSE, + CL_KEYWORD_FOR, + CL_KEYWORD_WHILE, + CL_KEYWORD_CASE, + CL_KEYWORD_CONTINUE, + CL_KEYWORD_DO, + CL_KEYWORD_GOTO, + CL_KEYWORD_SIZEOF, + CL_KEYWORD__ALIGNAS, + CL_KEYWORD__ALIGNOF, + CL_KEYWORD__STATIC_ASSERT, + CL_KEYWORD__GENERIC, + CL_COUNT, +} CL_Kind; + +typedef enum CL_Fix { + CL_FIX_NONE, + CL_SUFFIX_U, + CL_SUFFIX_UL, + CL_SUFFIX_ULL, + CL_SUFFIX_L, + CL_SUFFIX_LL, + CL_SUFFIX_F, + CL_SUFFIX_FL, + CL_PREFIX_U8, + CL_PREFIX_U16, + CL_PREFIX_U32, + CL_PREFIX_L, +} CL_Fix; + +typedef struct CL_Token CL_Token; +struct CL_Token { + CL_Kind kind; + CL_Fix fix; + + bool is_hex : 1; + bool is_inside_macro : 1; + bool is_system_include : 1; + bool is_there_whitespace_before_token : 1; + + uint32_t id; + int len; + char *str; + + // Not storing line_begin like I would normally cause the user could + // override the line and file information using directives. + // On error need to do search if I want nice error context. + int line, column; + char *file; + + union { + double f64; + uint64_t u64; + char *intern; + char *string_literal; + struct CL_Message *error; + }; +}; + +typedef struct CL_Message CL_Message; +struct CL_Message { + CL_Message *next; + char *string; + CL_Token token; +}; + +typedef struct CL_Lexer CL_Lexer; +struct CL_Lexer { + CL_Message *first_message; + CL_Message *last_message; + int errors; + + char *stream; + char *stream_begin; + int line; + int column; + char *file; + bool inside_of_macro; + + // filters + bool skip_comments : 1; + bool skip_macros : 1; + bool select_includes : 1; + bool select_comments : 1; + bool select_macros : 1; + + CL_Allocator arena; +}; + +typedef struct CL_SearchPaths CL_SearchPaths; +struct CL_SearchPaths { + char **include_path; + int include_path_count; + + char **system_include_path; + int system_include_path_count; + + char *file_begin_to_ignore; +}; + +CL_API_FUNCTION CL_Token CL_Next(CL_Lexer *T); +CL_API_FUNCTION CL_Lexer CL_Begin(CL_Allocator arena, char *stream, char *filename); +CL_API_FUNCTION char *CL_ResolveFilepath(CL_Allocator arena, CL_SearchPaths *search_paths, char *filename, char *parent_file, bool is_system_include); +CL_API_FUNCTION void CL_StringifyMessage(char *buff, int buff_size, CL_Message *msg); +CL_API_FUNCTION void CL_Stringify(char *buff, int buff_size, CL_Token *token); + +extern const char *CL_FixString[]; +extern const char *CL_KindString[]; + +CL_INLINE int CL_StringLength(char *string) { + int len = 0; + while (*string++ != 0) len++; + return len; +} + +CL_INLINE bool CL_StringsAreEqual(char *a, int64_t alen, const char *b, int64_t blen) { + if (alen != blen) return false; + for (int i = 0; i < alen; i += 1) { + if (a[i] != b[i]) return false; + } + return true; +} + +CL_INLINE bool CL_IsIdentifier(CL_Token *token, char *str) { + int str_len = CL_StringLength(str); + bool result = token->kind == CL_IDENTIFIER && CL_StringsAreEqual(token->str, token->len, str, str_len); + return result; +} + +CL_INLINE bool CL_IsAssign(CL_Kind op) { + bool result = op >= CL_ASSIGN && op <= CL_RIGHTSHIFTASSIGN; + return result; +} + +CL_INLINE bool CL_IsKeywordType(CL_Kind op) { + bool result = op >= CL_KEYWORD_VOID && op <= CL_KEYWORD__IMAGINARY; + return result; +} + +CL_INLINE bool CL_IsKeywordTypeOrSpec(CL_Kind op) { + bool result = op >= CL_KEYWORD_VOID && op <= CL_KEYWORD_TYPEDEF; + return result; +} + +CL_INLINE bool CL_IsMacro(CL_Kind kind) { + bool result = kind >= CL_PREPROC_DEFINE && kind <= CL_PREPROC_UNDEF; + return result; +} + +CL_INLINE bool CL_IsKeyword(CL_Kind kind) { + bool result = kind >= CL_KEYWORD_VOID && kind <= CL_KEYWORD__GENERIC; + return result; +} + +CL_INLINE bool CL_IsKeywordOrIdent(CL_Kind kind) { + bool result = CL_IsKeyword(kind) || kind == CL_IDENTIFIER; + return result; +} + +/* +- I'm pretty sure I can remove allocations for most of the current functions. +- I also can fix ResolvePath stuff so that it uses string+len and doesn't need allocations +- Add lexing options like in stb_c_lexer.h + +Instead of AND_CL_STRING_TERMINATE_ON_NEW_LINE he is doing some weird cool stuff with redefining +https://github.com/nothings/stb/blob/master/stb_c_lexer.h + +CL_MULTILINE_SSTRINGS +CL_DOLLAR_IDENT + +- Add proper string parsing, as additional function, CL_ParseString() or something, this is the only one that would need allocations + +*/ + +#ifndef CL_PRIVATE_FUNCTION + #if defined(__GNUC__) || defined(__clang__) + #define CL_PRIVATE_FUNCTION __attribute__((unused)) static + #else + #define CL_PRIVATE_FUNCTION static + #endif +#endif + +#ifndef CL_Allocate + #include + #define CL_Allocate(allocator, size) malloc(size) +#endif + +#ifndef CL_STRING_TO_DOUBLE + #include + #define CL_STRING_TO_DOUBLE(str, len) strtod(str, 0) +#endif + +#ifndef CL_ASSERT + #include + #define CL_ASSERT(x) assert(x) +#endif + +#ifndef CL_VSNPRINTF + #include + #define CL_VSNPRINTF vsnprintf +#endif + +#ifndef CL_SNPRINTF + #include + #define CL_SNPRINTF snprintf +#endif + +#ifndef CL__MemoryCopy + #include + #define CL__MemoryCopy(dst, src, s) memcpy(dst, src, s) +#endif + +#ifndef CL_MemoryZero + #include + #define CL_MemoryZero(p, size) memset(p, 0, size) +#endif + +#ifndef CL_FileExists + #define CL_FileExists CL__FileExists + #include +CL_PRIVATE_FUNCTION bool CL_FileExists(char *name) { + bool result = false; + FILE *f = fopen(name, "rb"); + if (f) { + result = true; + fclose(f); + } + return result; +} +#endif + +CL_PRIVATE_FUNCTION void CL_ReportError(CL_Lexer *T, CL_Token *token, const char *string, ...); + +CL_PRIVATE_FUNCTION char *CL_PushStringCopy(CL_Allocator arena, char *p, int size) { + char *copy_buffer = (char *)CL_Allocate(arena, size + 1); + CL__MemoryCopy(copy_buffer, p, size); + copy_buffer[size] = 0; + return copy_buffer; +} + +CL_INLINE void CL_Advance(CL_Lexer *T) { + if (*T->stream == '\n') { + T->line += 1; + T->column = 0; + } + else if (*T->stream == ' ') { + T->column += 1; + } + else if (*T->stream == '\t') { + T->column += 1; + } + else if (*T->stream == 0) { + return; + } + T->stream += 1; +} + +CL_INLINE bool CL_IsAlphabetic(char c) { + bool result = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); + return result; +} + +CL_INLINE bool CL_IsNumeric(char c) { + bool result = (c >= '0' && c <= '9'); + return result; +} + +CL_INLINE bool CL_IsHexNumeric(char c) { + bool result = (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); + return result; +} + +CL_INLINE bool CL_IsWhitespace(char c) { + bool result = c == ' ' || c == '\n' || c == '\r' || c == '\t'; + return result; +} + +CL_INLINE bool CL_IsAlphanumeric(char c) { + bool result = CL_IsAlphabetic(c) || CL_IsNumeric(c); + return result; +} + +CL_API_FUNCTION void CL_SetTokenLength(CL_Lexer *T, CL_Token *token) { + intptr_t diff = T->stream - token->str; + CL_ASSERT(diff < 2147483647); + token->len = (int)diff; +} + +CL_PRIVATE_FUNCTION uint64_t CL_CharMapToNumber(char c) { + switch (c) { + case '0': return 0; break; + case '1': return 1; break; + case '2': return 2; break; + case '3': return 3; break; + case '4': return 4; break; + case '5': return 5; break; + case '6': return 6; break; + case '7': return 7; break; + case '8': return 8; break; + case '9': return 9; break; + case 'a': + case 'A': return 10; break; + case 'b': + case 'B': return 11; break; + case 'c': + case 'C': return 12; break; + case 'd': + case 'D': return 13; break; + case 'e': + case 'E': return 14; break; + case 'f': + case 'F': return 15; break; + default: return 255; + } +} + +CL_PRIVATE_FUNCTION uint64_t CL_ParseInteger(CL_Lexer *T, CL_Token *token, char *string, uint64_t len, uint64_t base) { + CL_ASSERT(base >= 2 && base <= 16); + uint64_t acc = 0; + for (uint64_t i = 0; i < len; i++) { + uint64_t num = CL_CharMapToNumber(string[i]); + if (num >= base) { + CL_ReportError(T, token, "Internal compiler error! Failed to parse a number"); + break; + } + acc *= base; + acc += num; + } + return acc; +} + +typedef struct CL_UTF32Result { + uint32_t out_str; + int advance; + int error; +} CL_UTF32Result; + +CL_PRIVATE_FUNCTION CL_UTF32Result CL_UTF8ToUTF32(char *c, int max_advance) { + CL_UTF32Result result = {0}; + + if ((c[0] & 0x80) == 0) { // Check if leftmost zero of first byte is unset + if (max_advance >= 1) { + result.out_str = c[0]; + result.advance = 1; + } + else result.error = 1; + } + + else if ((c[0] & 0xe0) == 0xc0) { + if ((c[1] & 0xc0) == 0x80) { // Continuation byte required + if (max_advance >= 2) { + result.out_str = (uint32_t)(c[0] & 0x1f) << 6u | (c[1] & 0x3f); + result.advance = 2; + } + else result.error = 2; + } + else result.error = 2; + } + + else if ((c[0] & 0xf0) == 0xe0) { + if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80) { // Two continuation bytes required + if (max_advance >= 3) { + result.out_str = (uint32_t)(c[0] & 0xf) << 12u | (uint32_t)(c[1] & 0x3f) << 6u | (c[2] & 0x3f); + result.advance = 3; + } + else result.error = 3; + } + else result.error = 3; + } + + else if ((c[0] & 0xf8) == 0xf0) { + if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80 && (c[3] & 0xc0) == 0x80) { // Three continuation bytes required + if (max_advance >= 4) { + result.out_str = (uint32_t)(c[0] & 0xf) << 18u | (uint32_t)(c[1] & 0x3f) << 12u | (uint32_t)(c[2] & 0x3f) << 6u | (uint32_t)(c[3] & 0x3f); + result.advance = 4; + } + else result.error = 4; + } + else result.error = 4; + } + else result.error = 4; + + return result; +} + +// @todo I think I should look at this again +CL_PRIVATE_FUNCTION void CL_ParseCharLiteral(CL_Lexer *T, CL_Token *token) { + token->kind = CL_CHARLIT; + token->str = T->stream; + while (*T->stream != '\'') { + if (*T->stream == '\\') { + CL_Advance(T); + } + if (*T->stream == 0) { + CL_ReportError(T, token, "Unclosed character literal!"); + return; + } + CL_Advance(T); + } + CL_SetTokenLength(T, token); + + if (token->str[0] == '\\') { + switch (token->str[1]) { + case '\\': token->u64 = '\\'; break; + case '\'': token->u64 = '\''; break; + case '"': token->u64 = '"'; break; + case 't': token->u64 = '\t'; break; + case 'v': token->u64 = '\v'; break; + case 'f': token->u64 = '\f'; break; + case 'n': token->u64 = '\n'; break; + case 'r': token->u64 = '\r'; break; + case 'a': token->u64 = '\a'; break; + case 'b': token->u64 = '\b'; break; + case '0': token->u64 = '\0'; break; + case 'x': + case 'X': CL_ASSERT(!"Not implemented"); break; // Hex constant + case 'u': CL_ASSERT(!"Not implemented"); break; // Unicode constant + default: { + CL_ReportError(T, token, "Unknown escape code"); + } + } + } + + else { + if (token->len > 4) { + CL_ReportError(T, token, "This character literal has invalid format, it's too big"); + goto skip_utf_encode; + } + + token->u64 = 0; + int i = 0; + + for (; i < token->len;) { + CL_UTF32Result result = CL_UTF8ToUTF32(token->str + i, (int)token->len); + i += result.advance; + token->u64 |= result.out_str << (8 * (token->len - i)); + if (result.error) { + CL_ReportError(T, token, "This character literal couldnt be parsed as utf8"); + break; + } + } + if (i != token->len) { + CL_ReportError(T, token, "Character literal decode error"); + } + } + +skip_utf_encode: + CL_Advance(T); +} + +// It combines strings, verifies the escape sequences but doesn't do any allocations +// so the final string actually needs additional transformation pass. A pass +// that will combine the string snippets, replace escape sequences with actual values etc. +// +// @warning: @not_sure: we are not setting token->string_literal +// +// "String 1" "String 2" - those strings snippets are combined +// @todo: look at this again +// @todo: make a manual correct version that user can execute if he needs to +CL_PRIVATE_FUNCTION void CL_CheckString(CL_Lexer *T, CL_Token *token) { + token->kind = CL_STRINGLIT; +combine_next_string_literal: + while (*T->stream != '"' && *T->stream != 0 AND_CL_STRING_TERMINATE_ON_NEW_LINE) { + if (*T->stream == '\\') { + CL_Advance(T); + switch (*T->stream) { + case 'a': + case 'b': + case 'e': + case 'f': + case 'n': + case 'r': + case 't': + case 'v': + case '\\': + case '\'': + case '?': + case '"': + case 'x': + case 'X': // Hex constant + case 'u': // Unicode constant + case 'U': + break; + case '0': // octal numbers or null + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + break; + default: { + CL_ReportError(T, token, "Invalid escape sequence"); + return; + } + } + } + CL_Advance(T); + } + CL_Advance(T); + + // Try to seek if there is a consecutive string. + // If there is such string we try to combine it. + { + char *seek_for_next_string = T->stream; + while (CL_IsWhitespace(*seek_for_next_string)) { + seek_for_next_string += 1; + } + + if (*seek_for_next_string == '"') { + seek_for_next_string += 1; + while (T->stream != seek_for_next_string) CL_Advance(T); + goto combine_next_string_literal; + } + } + CL_SetTokenLength(T, token); +} + +CL_PRIVATE_FUNCTION void CL_IsIdentifierKeyword(CL_Token *token) { + if (token->len == 1) return; + char *c = token->str; + switch (c[0]) { + case 'v': { + switch (c[1]) { + case 'o': { + if (CL_StringsAreEqual(token->str, token->len, "void", 4)) { + token->kind = CL_KEYWORD_VOID; + } + else if (CL_StringsAreEqual(token->str, token->len, "volatile", 8)) { + token->kind = CL_KEYWORD_VOLATILE; + } + } break; + } + } break; + case 'i': { + switch (c[1]) { + case 'n': { + if (CL_StringsAreEqual(token->str, token->len, "int", 3)) { + token->kind = CL_KEYWORD_INT; + } + else if (CL_StringsAreEqual(token->str, token->len, "inline", 6)) { + token->kind = CL_KEYWORD_INLINE; + } + } break; + case 'f': { + if (CL_StringsAreEqual(token->str, token->len, "if", 2)) { + token->kind = CL_KEYWORD_IF; + } + } break; + } + } break; + case 'c': { + switch (c[1]) { + case 'h': { + if (CL_StringsAreEqual(token->str, token->len, "char", 4)) { + token->kind = CL_KEYWORD_CHAR; + } + } break; + case 'o': { + if (CL_StringsAreEqual(token->str, token->len, "const", 5)) { + token->kind = CL_KEYWORD_CONST; + } + else if (CL_StringsAreEqual(token->str, token->len, "continue", 8)) { + token->kind = CL_KEYWORD_CONTINUE; + } + } break; + case 'a': { + if (CL_StringsAreEqual(token->str, token->len, "case", 4)) { + token->kind = CL_KEYWORD_CASE; + } + } break; + } + } break; + case 'u': { + switch (c[1]) { + case 'n': { + if (CL_StringsAreEqual(token->str, token->len, "unsigned", 8)) { + token->kind = CL_KEYWORD_UNSIGNED; + } + else if (CL_StringsAreEqual(token->str, token->len, "union", 5)) { + token->kind = CL_KEYWORD_UNION; + } + } break; + } + } break; + case 's': { + switch (c[1]) { + case 'i': { + if (CL_StringsAreEqual(token->str, token->len, "signed", 6)) { + token->kind = CL_KEYWORD_SIGNED; + } + else if (CL_StringsAreEqual(token->str, token->len, "sizeof", 6)) { + token->kind = CL_KEYWORD_SIZEOF; + } + } break; + case 'h': { + if (CL_StringsAreEqual(token->str, token->len, "short", 5)) { + token->kind = CL_KEYWORD_SHORT; + } + } break; + case 't': { + if (CL_StringsAreEqual(token->str, token->len, "static", 6)) { + token->kind = CL_KEYWORD_STATIC; + } + else if (CL_StringsAreEqual(token->str, token->len, "struct", 6)) { + token->kind = CL_KEYWORD_STRUCT; + } + } break; + case 'w': { + if (CL_StringsAreEqual(token->str, token->len, "switch", 6)) { + token->kind = CL_KEYWORD_SWITCH; + } + } break; + } + } break; + case 'l': { + switch (c[1]) { + case 'o': { + if (CL_StringsAreEqual(token->str, token->len, "long", 4)) { + token->kind = CL_KEYWORD_LONG; + } + } break; + } + } break; + case 'd': { + switch (c[1]) { + case 'o': { + if (CL_StringsAreEqual(token->str, token->len, "double", 6)) { + token->kind = CL_KEYWORD_DOUBLE; + } + else if (CL_StringsAreEqual(token->str, token->len, "do", 2)) { + token->kind = CL_KEYWORD_DO; + } + } break; + case 'e': { + if (CL_StringsAreEqual(token->str, token->len, "default", 7)) { + token->kind = CL_KEYWORD_DEFAULT; + } + } break; + } + } break; + case 'f': { + switch (c[1]) { + case 'l': { + if (CL_StringsAreEqual(token->str, token->len, "float", 5)) { + token->kind = CL_KEYWORD_FLOAT; + } + } break; + case 'o': { + if (CL_StringsAreEqual(token->str, token->len, "for", 3)) { + token->kind = CL_KEYWORD_FOR; + } + } break; + } + } break; + case '_': { + switch (c[1]) { + case 'B': { + if (CL_StringsAreEqual(token->str, token->len, "_Bool", 5)) { + token->kind = CL_KEYWORD__BOOL; + } + } break; + case 'C': { + if (CL_StringsAreEqual(token->str, token->len, "_Complex", 8)) { + token->kind = CL_KEYWORD__COMPLEX; + } + } break; + case 'I': { + if (CL_StringsAreEqual(token->str, token->len, "_Imaginary", 10)) { + token->kind = CL_KEYWORD__IMAGINARY; + } + } break; + case 'T': { + if (CL_StringsAreEqual(token->str, token->len, "_Thread_local", 13)) { + token->kind = CL_KEYWORD__THREAD_LOCAL; + } + } break; + case 'A': { + if (CL_StringsAreEqual(token->str, token->len, "_Atomic", 7)) { + token->kind = CL_KEYWORD__ATOMIC; + } + else if (CL_StringsAreEqual(token->str, token->len, "_Alignas", 8)) { + token->kind = CL_KEYWORD__ALIGNAS; + } + else if (CL_StringsAreEqual(token->str, token->len, "_Alignof", 8)) { + token->kind = CL_KEYWORD__ALIGNOF; + } + } break; + case 'N': { + if (CL_StringsAreEqual(token->str, token->len, "_Noreturn", 9)) { + token->kind = CL_KEYWORD__NORETURN; + } + } break; + case 'S': { + if (CL_StringsAreEqual(token->str, token->len, "_Static_assert", 14)) { + token->kind = CL_KEYWORD__STATIC_ASSERT; + } + } break; + case 'G': { + if (CL_StringsAreEqual(token->str, token->len, "_Generic", 8)) { + token->kind = CL_KEYWORD__GENERIC; + } + } break; + } + } break; + case 'a': { + switch (c[1]) { + case 'u': { + if (CL_StringsAreEqual(token->str, token->len, "auto", 4)) { + token->kind = CL_KEYWORD_AUTO; + } + } break; + } + } break; + case 'e': { + switch (c[1]) { + case 'x': { + if (CL_StringsAreEqual(token->str, token->len, "extern", 6)) { + token->kind = CL_KEYWORD_EXTERN; + } + } break; + case 'n': { + if (CL_StringsAreEqual(token->str, token->len, "enum", 4)) { + token->kind = CL_KEYWORD_ENUM; + } + } break; + case 'l': { + if (CL_StringsAreEqual(token->str, token->len, "else", 4)) { + token->kind = CL_KEYWORD_ELSE; + } + } break; + } + } break; + case 'r': { + switch (c[1]) { + case 'e': { + if (CL_StringsAreEqual(token->str, token->len, "register", 8)) { + token->kind = CL_KEYWORD_REGISTER; + } + else if (CL_StringsAreEqual(token->str, token->len, "restrict", 8)) { + token->kind = CL_KEYWORD_RESTRICT; + } + else if (CL_StringsAreEqual(token->str, token->len, "return", 6)) { + token->kind = CL_KEYWORD_RETURN; + } + } break; + } + } break; + case 't': { + switch (c[1]) { + case 'y': { + if (CL_StringsAreEqual(token->str, token->len, "typedef", 7)) { + token->kind = CL_KEYWORD_TYPEDEF; + } + } break; + } + } break; + case 'b': { + switch (c[1]) { + case 'r': { + if (CL_StringsAreEqual(token->str, token->len, "break", 5)) { + token->kind = CL_KEYWORD_BREAK; + } + } break; + } + } break; + case 'w': { + switch (c[1]) { + case 'h': { + if (CL_StringsAreEqual(token->str, token->len, "while", 5)) { + token->kind = CL_KEYWORD_WHILE; + } + } break; + } + } break; + case 'g': { + switch (c[1]) { + case 'o': { + if (CL_StringsAreEqual(token->str, token->len, "goto", 4)) { + token->kind = CL_KEYWORD_GOTO; + } + } break; + } + } break; + } +} + +CL_PRIVATE_FUNCTION void CL_EatMacroWhitespace(CL_Lexer *T) { + while (T->stream[0] == ' ' || T->stream[0] == '\t') CL_Advance(T); +} + +CL_PRIVATE_FUNCTION void CL_EatUntil(CL_Lexer *T, char c) { + while (T->stream[0] != c && T->stream[0] != 0) CL_Advance(T); +} + +CL_PRIVATE_FUNCTION void CL_LexMacroInclude(CL_Lexer *T, CL_Token *token) { + token->kind = CL_PREPROC_INCLUDE; + CL_EatMacroWhitespace(T); + char end = 0; + if (*T->stream == '"') { + end = '"'; + } + else if (*T->stream == '<') { + end = '>'; + token->is_system_include = true; + } + else { + CL_ReportError(T, token, "Invalid include directive, file not specified"); + return; + } + CL_Advance(T); + + token->str = T->stream; + while (*T->stream != end) { + if (*T->stream == 0) { + CL_ReportError(T, token, "Invalid include directive, reached end of file while reading filename"); + } + if (*T->stream == '\n') { + CL_ReportError(T, token, "Invalid include directive filename, got newline character while reading filename"); + } + CL_Advance(T); + } + CL_SetTokenLength(T, token); + CL_Advance(T); + + // @not_sure: this is because we want null terminated input into path resolution stuff + token->string_literal = CL_PushStringCopy(T->arena, token->str, token->len); +} + +CL_PRIVATE_FUNCTION bool CL_LexMacro(CL_Lexer *T, CL_Token *token) { + CL_EatMacroWhitespace(T); + token->str = T->stream; + while (CL_IsAlphabetic(*T->stream)) CL_Advance(T); + CL_SetTokenLength(T, token); + + switch (*token->str) { + case 'd': + if (CL_StringsAreEqual(token->str, token->len, "define", 6)) { + token->kind = CL_PREPROC_DEFINE; + } + break; + + case 'i': + if (CL_StringsAreEqual(token->str, token->len, "ifdef", 5)) { + token->kind = CL_PREPROC_IFDEF; + } + else if (CL_StringsAreEqual(token->str, token->len, "ifndef", 6)) { + token->kind = CL_PREPROC_IFNDEF; + } + else if (CL_StringsAreEqual(token->str, token->len, "include", 7)) { + token->kind = CL_PREPROC_INCLUDE; + CL_LexMacroInclude(T, token); + } + else if (CL_StringsAreEqual(token->str, token->len, "if", 2)) { + token->kind = CL_PREPROC_IF; + } + break; + + case 'e': + if (CL_StringsAreEqual(token->str, token->len, "endif", 5)) { + token->kind = CL_PREPROC_ENDIF; + } + else if (CL_StringsAreEqual(token->str, token->len, "error", 5)) { + token->kind = CL_PREPROC_ERROR; + CL_EatMacroWhitespace(T); + token->str = T->stream; + CL_EatUntil(T, '\n'); + CL_SetTokenLength(T, token); + } + else if (CL_StringsAreEqual(token->str, token->len, "else", 4)) { + token->kind = CL_PREPROC_ELSE; + } + else if (CL_StringsAreEqual(token->str, token->len, "elif", 4)) { + token->kind = CL_PREPROC_ELIF; + } + break; + + case 'p': + if (CL_StringsAreEqual(token->str, token->len, "pragma", 6)) { + token->kind = CL_PREPROC_PRAGMA; + } + break; + + case 'u': + if (CL_StringsAreEqual(token->str, token->len, "undef", 5)) { + token->kind = CL_PREPROC_UNDEF; + } + break; + default: return false; + } + return true; +} + +// Skipped space here is for case #define Memes (a), this is not a function like macro because of space +static uint32_t CL_TokenID; // @todo: make it read only +CL_PRIVATE_FUNCTION void CL_PrepareToken(CL_Lexer *T, CL_Token *token, bool skipped_space) { + CL_MemoryZero(token, sizeof(*token)); + token->str = T->stream; + token->line = T->line; + token->column = T->column; + token->file = T->file; + token->id = ++CL_TokenID; + if (skipped_space) token->is_there_whitespace_before_token = true; + CL_Advance(T); +} + +CL_PRIVATE_FUNCTION void CL_DefaultTokenize(CL_Lexer *T, CL_Token *token) { + char *c = token->str; + switch (*c) { + case 0: break; + case '(': token->kind = CL_OPENPAREN; break; + case ')': token->kind = CL_CLOSEPAREN; break; + case '{': token->kind = CL_OPENBRACE; break; + case '}': token->kind = CL_CLOSEBRACE; break; + case '[': token->kind = CL_OPENBRACKET; break; + case ']': token->kind = CL_CLOSEBRACKET; break; + case ',': token->kind = CL_COMMA; break; + case '~': token->kind = CL_NEG; break; + case '?': token->kind = CL_QUESTION; break; + case ';': token->kind = CL_SEMICOLON; break; + case ':': token->kind = CL_COLON; break; + case '.': { + token->kind = CL_DOT; + if (T->stream[0] == '.' && T->stream[1] == '.') { + CL_Advance(T); + CL_Advance(T); + token->kind = CL_THREEDOTS; + } + } break; + case '/': { + token->kind = CL_DIV; + if (*T->stream == '/') { + token->kind = CL_COMMENT; + CL_Advance(T); + CL_EatUntil(T, '\n'); + CL_SetTokenLength(T, token); + } + else if (*T->stream == '*') { + token->kind = CL_COMMENT; + CL_Advance(T); + for (;;) { + if (T->stream[0] == '*' && T->stream[1] == '/') { + break; + } + if (T->stream[0] == 0) { + CL_ReportError(T, token, "Unclosed block comment"); + goto error_end_path; + } + CL_Advance(T); + } + token->str += 2; + CL_SetTokenLength(T, token); + CL_Advance(T); + CL_Advance(T); + } + else if (*T->stream == '=') { + token->kind = CL_DIVASSIGN; + CL_Advance(T); + } + } break; + case '#': { + if (*T->stream == '#') { + token->kind = CL_MACRO_CONCAT; + CL_Advance(T); + } + else { + bool is_macro_directive = CL_LexMacro(T, token); + if (is_macro_directive) { + T->inside_of_macro = true; + } + else { + if (!T->inside_of_macro) { + CL_ReportError(T, token, "Invalid preprocessor directive"); + goto error_end_path; + } + + token->kind = CL_PREPROC_STRINGIFY; + token->str = T->stream; + while (*T->stream == '_' || CL_IsAlphanumeric(*T->stream)) + CL_Advance(T); + CL_SetTokenLength(T, token); + } + } + } break; + case '>': { + if (*T->stream == '=') { + token->kind = CL_GREATERTHEN_OR_EQUAL; + CL_Advance(T); + } + else if (*T->stream == '>') { + CL_Advance(T); + if (*T->stream == '=') { + CL_Advance(T); + token->kind = CL_RIGHTSHIFTASSIGN; + } + else { + token->kind = CL_RIGHTSHIFT; + } + } + else { + token->kind = CL_GREATERTHEN; + } + } break; + case '<': { + token->kind = CL_LESSERTHEN; + if (*T->stream == '=') { + token->kind = CL_LESSERTHEN_OR_EQUAL; + CL_Advance(T); + } + else if (*T->stream == '<') { + CL_Advance(T); + if (*T->stream == '=') { + CL_Advance(T); + token->kind = CL_LEFTSHIFTASSIGN; + } + else { + token->kind = CL_LEFTSHIFT; + } + } + } break; + case '&': { + if (*T->stream == '=') { + token->kind = CL_ANDASSIGN; + CL_Advance(T); + } + else if (*T->stream == '&') { + token->kind = CL_AND; + CL_Advance(T); + } + else { + token->kind = CL_BITAND; + } + } break; + case '-': { + if (*T->stream == '-') { + token->kind = CL_DECREMENT; + CL_Advance(T); + } + else if (*T->stream == '=') { + token->kind = CL_SUBASSIGN; + CL_Advance(T); + } + else { + token->kind = CL_SUB; + } + } break; + case '+': { + if (*T->stream == '+') { + token->kind = CL_INCREMENT; + CL_Advance(T); + } + else if (*T->stream == '=') { + token->kind = CL_ADDASSIGN; + CL_Advance(T); + } + else { + token->kind = CL_ADD; + } + } break; + case '|': { + if (*T->stream == '|') { + token->kind = CL_OR; + CL_Advance(T); + } + else if (*T->stream == '=') { + token->kind = CL_ORASSIGN; + CL_Advance(T); + } + else { + token->kind = CL_BITOR; + } + } break; + case '=': { + if (*T->stream != '=') { + token->kind = CL_ASSIGN; + } + else { + CL_Advance(T); + token->kind = CL_EQUALS; + } + } break; + case '!': { + if (*T->stream != '=') { + token->kind = CL_NOT; + } + else { + CL_Advance(T); + token->kind = CL_NOTEQUALS; + } + } break; + case '*': { + token->kind = CL_MUL; + if (*T->stream == '=') { + CL_Advance(T); + token->kind = CL_MULASSIGN; + } + } break; + case '%': { + token->kind = CL_MOD; + if (*T->stream == '=') { + token->kind = CL_MODASSIGN; + CL_Advance(T); + } + } break; + case '^': { + token->kind = CL_BITXOR; + if (*T->stream == '=') { + CL_Advance(T); + token->kind = CL_XORASSIGN; + } + } break; + case '"': { + CL_CheckString(T, token); + } break; + case '\'': { + CL_ParseCharLiteral(T, token); + } break; + case 'U': { // @todo Unicode32 + if (*T->stream == '"') { + token->fix = CL_PREFIX_U32; + CL_Advance(T); + CL_CheckString(T, token); + } + else if (*T->stream == '\'') { + token->fix = CL_PREFIX_U32; + CL_Advance(T); + CL_ParseCharLiteral(T, token); + } + else goto parse_regular_char; + } break; + case 'u': { // Unicode16 + if (*T->stream == '8') { // Unicode8 + if (T->stream[1] == '"') { // U8 STRING + token->fix = CL_PREFIX_U8; + CL_Advance(T); + CL_Advance(T); + CL_CheckString(T, token); + } + else if (T->stream[1] == '\'') { // U8 CHAR + token->fix = CL_PREFIX_U8; + CL_Advance(T); + CL_Advance(T); + CL_ParseCharLiteral(T, token); + } + else goto parse_regular_char; + } + else if (*T->stream == '"') { // U16 STRING + token->fix = CL_PREFIX_U16; + CL_Advance(T); + CL_CheckString(T, token); + } + else if (*T->stream == '\'') { // U16 CHAR + CL_Advance(T); + CL_ParseCharLiteral(T, token); + } + else goto parse_regular_char; + } + case 'L': { // Widechar + if (*T->stream == '"') { + token->fix = CL_PREFIX_L; + CL_Advance(T); + CL_CheckString(T, token); // @todo UTF16 + } + else if (*T->stream == '\'') { + token->fix = CL_PREFIX_L; + CL_Advance(T); + CL_ParseCharLiteral(T, token); + } + else goto parse_regular_char; + } break; + case 'A': + case 'a': + case 'B': + case 'b': + case 'C': + case 'c': + case 'D': + case 'd': + case 'E': + case 'e': + case 'F': + case 'f': + case 'G': + case 'g': + case 'H': + case 'h': + case 'I': + case 'i': + case 'J': + case 'j': + case 'K': + case 'k': + /*case 'L':*/ case 'l': + case 'M': + case 'm': + case 'N': + case 'n': + case 'O': + case 'o': + case 'P': + case 'p': + case 'Q': + case 'q': + case 'R': + case 'r': + case 'S': + case 's': + case 'T': + case 't': + // case 'U': case 'u': + case 'V': + case 'v': + case 'W': + case 'w': + case 'X': + case 'x': + case 'Y': + case 'y': + case 'Z': + case 'z': + case '_': + parse_regular_char : { + token->kind = CL_IDENTIFIER; + while (*T->stream == '_' || CL_IsAlphanumeric(*T->stream)) { + CL_Advance(T); + } + CL_SetTokenLength(T, token); + CL_IsIdentifierKeyword(token); + } break; + case '0': { + if (*T->stream == 'x' || *T->stream == 'X') { + token->kind = CL_INT; + token->is_hex = true; + CL_Advance(T); + while (CL_IsHexNumeric(*T->stream)) { + CL_Advance(T); + } + uint64_t len = T->stream - token->str; + CL_ASSERT(len > 2); + token->u64 = CL_ParseInteger(T, token, token->str + 2, len - 2, 16); + break; + } + } + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + token->kind = CL_INT; + for (;;) { + if (*T->stream == '.') { + if (token->kind == CL_FLOAT) { + CL_ReportError(T, token, "Failed to parse a floating point number, invalid format, found multiple '.'"); + } + + if (token->kind == CL_INT) { + token->kind = CL_FLOAT; + } + } + else if (CL_IsNumeric(*T->stream) == false) { + break; + } + CL_Advance(T); + } + + if (token->kind == CL_INT) { + uint64_t len = T->stream - token->str; + CL_ASSERT(len > 0); + token->u64 = CL_ParseInteger(T, token, token->str, len, 10); + } + + else if (token->kind == CL_FLOAT) { + token->f64 = CL_STRING_TO_DOUBLE(token->str, token->len); + } + + else { + CL_ASSERT(token->kind == CL_ERROR); + } + + if (*T->stream == 'f' || *T->stream == 'F') { + CL_Advance(T); + token->fix = CL_SUFFIX_F; + } + + else if (*T->stream == 'l' || *T->stream == 'L') { + CL_Advance(T); + token->fix = CL_SUFFIX_L; + if (*T->stream == 'l' || *T->stream == 'L') { + CL_Advance(T); + token->fix = CL_SUFFIX_LL; + if (*T->stream == 'u' || *T->stream == 'U') { + CL_Advance(T); + token->fix = CL_SUFFIX_ULL; + } + } + else if (*T->stream == 'u' || *T->stream == 'U') { + CL_Advance(T); + token->fix = CL_SUFFIX_UL; + } + } + + else if (*T->stream == 'u' || *T->stream == 'U') { + CL_Advance(T); + token->fix = CL_SUFFIX_U; + if (*T->stream == 'l' || *T->stream == 'L') { + CL_Advance(T); + token->fix = CL_SUFFIX_UL; + if (*T->stream == 'l' || *T->stream == 'L') { + CL_Advance(T); + token->fix = CL_SUFFIX_ULL; + } + } + } + + } break; + + default: { + CL_ReportError(T, token, "Unhandled character, skipping ..."); + } break; + } + +error_end_path:; +} + +CL_PRIVATE_FUNCTION bool CL_EatWhitespace(CL_Lexer *T) { + bool skipped = false; + for (;;) { + if (CL_IsWhitespace(*T->stream)) { + if (*T->stream == '\n') T->inside_of_macro = false; + CL_Advance(T); + skipped = true; + } + else if (T->stream[0] == '\\' && T->stream[1] == '\n') { + CL_Advance(T); + CL_Advance(T); + skipped = true; + } + else if (T->stream[0] == '\\' && T->stream[1] == '\r' && T->stream[2] == '\n') { + CL_Advance(T); + CL_Advance(T); + CL_Advance(T); + skipped = true; + } + else { + break; + } + } + return skipped; +} + +CL_PRIVATE_FUNCTION void CL_TryToFinalizeToken(CL_Lexer *T, CL_Token *token) { + if (!token->len) { + CL_SetTokenLength(T, token); + } + if (T->inside_of_macro) { + token->is_inside_macro = true; + } +} + +CL_PRIVATE_FUNCTION void CL_InitNextToken(CL_Lexer *T, CL_Token *token) { + // Skip comments, comments get allocated on perm and gathered on the Tokenizer. + // First non comment token gets those comments attached. + for (;;) { + bool skipped = CL_EatWhitespace(T); + CL_PrepareToken(T, token, skipped); + CL_DefaultTokenize(T, token); + + if (token->kind == CL_EOF) { + break; + } + + if (T->select_includes) { + if (token->kind != CL_PREPROC_INCLUDE) continue; + } + + if (T->select_macros) { + if (!token->is_inside_macro) continue; + } + + if (T->select_comments) { + if (token->kind != CL_COMMENT) continue; + } + + if (T->skip_comments) { + if (token->kind == CL_COMMENT) continue; + } + + if (T->skip_macros) { + if (token->is_inside_macro) continue; + } + + break; + } + CL_TryToFinalizeToken(T, token); +} + +CL_API_FUNCTION CL_Token CL_Next(CL_Lexer *T) { + CL_Token result; + CL_MemoryZero(&result, sizeof(CL_Token)); + CL_InitNextToken(T, &result); + return result; +} + +CL_API_FUNCTION CL_Lexer CL_Begin(CL_Allocator arena, char *stream, char *filename) { + CL_Lexer lexer = {0}; + lexer.stream = lexer.stream_begin = stream; + lexer.file = filename; + lexer.arena = arena; + lexer.skip_comments = true; + return lexer; +} + +// +// +// + +CL_PRIVATE_FUNCTION char *CL_ChopLastSlash(CL_Allocator arena, char *str) { + int i = 0; + int slash_pos = -1; + while (str[i]) { + if (str[i] == '/') { + slash_pos = i; + } + i += 1; + } + + char *result = str; + if (slash_pos != -1) { + result = CL_PushStringCopy(arena, str, slash_pos); + } + else { + result = (char *)"./"; + } + return result; +} + +CL_PRIVATE_FUNCTION char *CL_JoinPath(CL_Allocator arena, char *a, char *b) { + int alen = CL_StringLength(a); + int blen = CL_StringLength(b); + int additional_len = 0; + + if (alen && a[alen - 1] != '/') additional_len = 1; + char *result = (char *)CL_Allocate(arena, sizeof(char) * (alen + blen + 1 + additional_len)); + CL__MemoryCopy(result, a, alen); + if (additional_len) result[alen++] = '/'; + CL__MemoryCopy(result + alen, b, blen); + result[alen + blen] = 0; + return result; +} + +CL_PRIVATE_FUNCTION bool CL_IsAbsolutePath(char *path) { +#ifdef _WIN32 + bool result = CL_IsAlphabetic(path[0]) && path[1] == ':' && path[2] == '/'; +#else + bool result = path[0] == '/'; +#endif + return result; +} + +CL_PRIVATE_FUNCTION char *CL_SkipToLastSlash(char *p) { + int last_slash = 0; + for (int i = 0; p[i]; i += 1) { + if (p[i] == '/') last_slash = i; + } + return p + last_slash; +} + +CL_API_FUNCTION char *CL_ResolveFilepath(CL_Allocator arena, CL_SearchPaths *search_paths, char *filename, char *parent_file, bool is_system_include) { + CL_SearchPaths null_search_paths = {0}; + if (search_paths == 0) search_paths = &null_search_paths; + + if (search_paths->file_begin_to_ignore) { + char *name = CL_SkipToLastSlash(filename); + int namelen = CL_StringLength(name); + char *ignore = search_paths->file_begin_to_ignore; + int ignorelen = CL_StringLength(ignore); + if (namelen > ignorelen) { + namelen = ignorelen; + } + if (CL_StringsAreEqual(name, namelen, search_paths->file_begin_to_ignore, ignorelen)) { + return 0; + } + } + + if (CL_IsAbsolutePath(filename) && CL_FileExists(filename)) { + return filename; + } + + if (is_system_include) { + for (int path_i = 0; path_i < search_paths->system_include_path_count; path_i += 1) { + char *path_it = search_paths->system_include_path[path_i]; + char *file = CL_JoinPath(arena, path_it, filename); + if (CL_FileExists(file)) { + return file; + } + } + } + else { + if (parent_file) { + char *parent_dir = CL_ChopLastSlash(arena, parent_file); + char *file = CL_JoinPath(arena, parent_dir, filename); + if (CL_FileExists(file)) { + return file; + } + } + + for (int path_i = 0; path_i < search_paths->include_path_count; path_i += 1) { + char *path_it = search_paths->include_path[path_i]; + char *file = CL_JoinPath(arena, path_it, filename); + if (CL_FileExists(file)) { + return file; + } + } + } + return 0; +} + +// +// +// + +const char *CL_FixString[] = { + "SUFFIX_INVALID", + "SUFFIX_U", + "SUFFIX_UL", + "SUFFIX_ULL", + "SUFFIX_L", + "SUFFIX_LL", + "SUFFIX_F", + "SUFFIX_FL", + "PREFIX_U8", + "PREFIX_U16", + "PREFIX_U32", + "PREFIX_L", +}; + +const char *CL_KindString[] = { + "EOF", + "*", + "/", + "%", + "<<", + ">>", + "+", + "-", + "==", + "<", + ">", + "<=", + ">=", + "!=", + "&", + "|", + "^", + "&&", + "||", + "~", + "!", + "--", + "++", + "--", + "++", + "=", + "/=", + "*=", + "%=", + "-=", + "+=", + "&=", + "|=", + "^=", + "<<=", + ">>=", + "(", + ")", + "{", + "}", + "[", + "]", + ",", + "##", + "#Stringify", + "?", + "...", + ";", + ".", + ":", + "TAG", + "->", + "SIZEOF", + "DOCCOMMENT", + "COMMENT", + "IDENTIFIER", + "STRING_LITERAL", + "CHARACTER_LITERAL", + "ERROR TOKEN", + "FLOAT", + "INT", + "PREPROC_NULL", + "PREPROC_DEFINE", + "PREPROC_IFDEF", + "PREPROC_IFNDEF", + "PREPROC_INCLUDE", + "PREPROC_ENDIF", + "PREPROC_IF", + "PREPROC_PRAGMA", + "PREPROC_ERROR", + "PREPROC_ELSE", + "PREPROC_ELIF", + "PREPROC_UNDEF", + "KEYWORD_VOID", + "KEYWORD_INT", + "KEYWORD_CHAR", + "KEYWORD_UNSIGNED", + "KEYWORD_SIGNED", + "KEYWORD_LONG", + "KEYWORD_SHORT", + "KEYWORD_DOUBLE", + "KEYWORD_FLOAT", + "KEYWORD__BOOL", + "KEYWORD__COMPLEX", + "KEYWORD__IMAGINARY", + "KEYWORD_STATIC", + "KEYWORD_AUTO", + "KEYWORD_CONST", + "KEYWORD_EXTERN", + "KEYWORD_INLINE", + "KEYWORD_REGISTER", + "KEYWORD_RESTRICT", + "KEYWORD_VOLATILE", + "KEYWORD__THREAD_LOCAL", + "KEYWORD__ATOMIC", + "KEYWORD__NORETURN", + "KEYWORD_STRUCT", + "KEYWORD_UNION", + "KEYWORD_ENUM", + "KEYWORD_TYPEDEF", + "KEYWORD_DEFAULT", + "KEYWORD_BREAK", + "KEYWORD_RETURN", + "KEYWORD_SWITCH", + "KEYWORD_IF", + "KEYWORD_ELSE", + "KEYWORD_FOR", + "KEYWORD_WHILE", + "KEYWORD_CASE", + "KEYWORD_CONTINUE", + "KEYWORD_DO", + "KEYWORD_GOTO", + "KEYWORD_SIZEOF", + "KEYWORD__ALIGNAS", + "KEYWORD__ALIGNOF", + "KEYWORD__STATIC_ASSERT", + "KEYWORD__GENERIC", +}; + +CL_API_FUNCTION void CL_StringifyMessage(char *buff, int buff_size, CL_Message *msg) { + CL_SNPRINTF(buff, buff_size, "%s(%d,%d): %15s", msg->token.file, msg->token.line + 1, msg->token.column + 1, msg->string); +} + +CL_API_FUNCTION void CL_Stringify(char *buff, int buff_size, CL_Token *token) { + const char *token_kind = "UNKNOWN"; + if (token->kind < CL_COUNT) token_kind = CL_KindString[token->kind]; + CL_SNPRINTF(buff, buff_size, "%s(%d,%d): %15s %15.*s", token->file, token->line + 1, token->column + 1, token_kind, token->len, token->str); +} + +#define CL_SLL_QUEUE_ADD_MOD(f, l, n, next) \ + do { \ + (n)->next = 0; \ + if ((f) == 0) { \ + (f) = (l) = (n); \ + } \ + else { \ + (l) = (l)->next = (n); \ + } \ + } while (0) +#define CL_SLL_QUEUE_ADD(f, l, n) CL_SLL_QUEUE_ADD_MOD(f, l, n, next) + +#define CL__FORMAT(arena, string, result) \ + va_list args1, args2; \ + va_start(args1, string); \ + va_copy(args2, args1); \ + int len = CL_VSNPRINTF(0, 0, string, args2); \ + va_end(args2); \ + char *result = (char *)CL_Allocate((arena), len + 1); \ + CL_VSNPRINTF(result, len + 1, string, args1); \ + va_end(args1) + +CL_PRIVATE_FUNCTION void CL_ReportError(CL_Lexer *T, CL_Token *token, const char *string, ...) { + CL__FORMAT(T->arena, string, message_string); + CL_Message *result = (CL_Message *)CL_Allocate(T->arena, sizeof(CL_Message)); + CL_MemoryZero(result, sizeof(CL_Message)); + CL_SLL_QUEUE_ADD(T->first_message, T->last_message, result); + + result->string = (char *)string; + result->token = *token; + token->kind = CL_ERROR; + token->error = result; + T->errors += 1; +} + +THREAD_LOCAL MA_Arena Perm; +#define SRC_CACHE_ENTRY_COUNT 1024 + +typedef struct SRC_CacheEntry SRC_CacheEntry; +struct SRC_CacheEntry { + uint64_t filepath_hash; + uint64_t file_hash; + uint64_t includes_hash; + uint64_t total_hash; +}; + +typedef struct SRC_Cache SRC_Cache; +struct SRC_Cache { + int entry_cap; + int entry_len; + SRC_CacheEntry entries[SRC_CACHE_ENTRY_COUNT]; +}; + +double SRC_Time; +SRC_Cache *SRC_InMemoryCache; +SRC_Cache *SRC_FromFileCache; +S8_String SRC_CacheFilename; +CL_SearchPaths SRC_SearchPaths = {0}; // @todo; + +void SRC_InitCache(MA_Arena *arena, S8_String cachefilename) { + SRC_CacheFilename = cachefilename; + + SRC_InMemoryCache = MA_PushStruct(arena, SRC_Cache); + SRC_InMemoryCache->entry_cap = SRC_CACHE_ENTRY_COUNT; + + SRC_FromFileCache = MA_PushStruct(arena, SRC_Cache); + SRC_FromFileCache->entry_cap = SRC_CACHE_ENTRY_COUNT; + + S8_String cache = OS_ReadFile(arena, SRC_CacheFilename); + if (cache.len) MA_MemoryCopy(SRC_FromFileCache, cache.str, cache.len); +} + +void SRC_SaveCache() { + S8_String dump = S8_Make((char *)SRC_InMemoryCache, sizeof(SRC_Cache)); + os_write_file(SRC_CacheFilename, dump); +} + +SRC_CacheEntry *SRC_AddHash(uint64_t filepath, uint64_t file, uint64_t includes) { + IO_Assert(SRC_InMemoryCache->entry_len + 1 < SRC_InMemoryCache->entry_cap); + SRC_CacheEntry *result = SRC_InMemoryCache->entries + SRC_InMemoryCache->entry_len++; + result->filepath_hash = filepath; + result->file_hash = file; + result->includes_hash = includes; + result->total_hash = HashBytes(result, sizeof(uint64_t) * 3); + return result; +} + +SRC_CacheEntry *SRC_FindCache(SRC_Cache *cache, uint64_t filepath_hash) { + for (int cache_i = 0; cache_i < cache->entry_len; cache_i += 1) { + SRC_CacheEntry *it = cache->entries + cache_i; + if (it->filepath_hash == filepath_hash) { + return it; + } + } + return 0; +} + +SRC_CacheEntry *SRC_HashFile(S8_String file, char *parent_file) { + char *resolved_file = CL_ResolveFilepath(&Perm, &SRC_SearchPaths, file.str, parent_file, false); + if (!resolved_file) { + IO_Printf("Failed to resolve file: %.*s\n", S8_Expand(file)); + return 0; + } + + uint64_t filepath_hash = HashBytes(resolved_file, S8_Length(resolved_file)); + SRC_CacheEntry *entry = SRC_FindCache(SRC_InMemoryCache, filepath_hash); + if (entry) return entry; + + S8_String filecontent = OS_ReadFile(&Perm, S8_MakeFromChar(resolved_file)); + IO_Assert(filecontent.str); + + uint64_t file_hash = HashBytes(filecontent.str, filecontent.len); + uint64_t includes_hash = 13; + + CL_Lexer lexer = CL_Begin(&Perm, filecontent.str, resolved_file); + lexer.select_includes = true; + + for (CL_Token token = CL_Next(&lexer); token.kind != CL_EOF; token = CL_Next(&lexer)) { + if (token.is_system_include) continue; + + S8_String file_it = S8_MakeFromChar(token.string_literal); + SRC_CacheEntry *cache = SRC_HashFile(file_it, resolved_file); + if (!cache) { + // error was reported already IO_Printf("Missing cache for: %.*s\n", S8_Expand(file_it)); + continue; + } + + includes_hash = HashMix(includes_hash, cache->total_hash); + } + + SRC_CacheEntry *result = SRC_AddHash(filepath_hash, file_hash, includes_hash); + return result; +} + +bool SRC_WasModified(S8_String file, S8_String artifact_path) { + double time_start = OS_GetTime(); + + if (OS_FileExists(file) == false) { + IO_Printf("FAILED File doesnt exist: %.*s\n", S8_Expand(file)); + exit(0); + } + if (OS_IsAbsolute(file) == false) { + file = OS_GetAbsolutePath(&Perm, file); + } + + S8_String without_ext = S8_ChopLastPeriod(file); + S8_String name_only = S8_SkipToLastSlash(without_ext); + + if (artifact_path.len == 0) artifact_path = S8_Format(&Perm, "%.*s.%s", S8_Expand(name_only), IF_WINDOWS_ELSE("obj", "o")); + bool modified = OS_FileExists(artifact_path) == false; + + SRC_CacheEntry *in_memory = SRC_HashFile(file, 0); + IO_Assert(in_memory); + + if (modified == false) { + SRC_CacheEntry *from_file = SRC_FindCache(SRC_FromFileCache, in_memory->filepath_hash); + if (from_file == 0 || (in_memory->total_hash != from_file->total_hash)) { + modified = true; + } + } + + SRC_Time = SRC_Time + (OS_GetTime() - time_start); + + return modified; +} + +S8_String CL_Flags = S8_ConstLit("/MP /Zi /FC /WX /W3 /wd4200 /diagnostics:column /nologo -D_CRT_SECURE_NO_WARNINGS /GF /Gm- /Oi"); +S8_String CL_Link = S8_ConstLit("/link /incremental:no"); +S8_String CL_StdOff = S8_ConstLit("/GR- /EHa-"); +S8_String CL_StdOn = S8_ConstLit("/EHsc"); +S8_String CL_Debug = S8_ConstLit("-Od -D_DEBUG -fsanitize=address -RTC1"); +S8_String CL_Release = S8_ConstLit("-O2 -MT -DNDEBUG -GL"); +S8_String CL_ReleaseLink = S8_ConstLit("-opt:ref -opt:icf"); +/* +/FC = Print full paths in diagnostics +/Gm- = Old feature, 'minimal compilation', in case it's not off by default +/GF = Pools strings as read-only. If you try to modify strings under /GF, an application error occurs. +/Oi = Replaces some function calls with intrinsic +/MP = Multithreaded compilation +/GR- = Disable runtime type information +/EHa- = Disable exceptions +/EHsc = Enable exceptions +/MT = Link static libc. The 'd' means debug version +/MD = Link dynamic libc. The 'd' means debug version +/GL = Whole program optimization +/RTC1 = runtime error checks +/opt:ref = eliminates functions and data that are never referenced +/opt:icf = eliminates redundant 'COMDAT's +*/ + +S8_String Clang_Flags = S8_ConstLit("-fdiagnostics-absolute-paths -Wno-writable-strings"); +S8_String Clang_NoStd = S8_ConstLit("-fno-exceptions"); +S8_String Clang_Debug = S8_ConstLit("-fsanitize=address -g"); +/* +-std=c++11 + */ + +S8_String GCC_Flags = S8_ConstLit("-Wno-write-strings"); +S8_String GCC_NoStd = S8_ConstLit("-fno-exceptions"); +S8_String GCC_Debug = S8_ConstLit("-fsanitize=address -g"); + +#if !defined(BUILD_TOOL_LIB) +int main(int argument_count, char **arguments) { + os_make_dir("build"); + os_set_working_dir("build"); + S8_String working_dir = os_cwd(&Perm); + + S8_String cc = S8_Lit(IF_WINDOWS("cl") IF_MAC("clang") IF_LINUX("gcc")); + S8_List list = {0}; + for (int i = 1; i < argument_count; i += 1) { + S8_String it = S8_MakeFromChar(arguments[i]); + if (S8_StartsWith(it, S8_Lit("cc="), false)) cc = S8_Skip(it, 3); + S8_AddNode(&Perm, &list, it); + } + + // Search for build file in the project directory + S8_String build_file = {0}; + { + for (OS_FileIter it = OS_IterateFiles(&Perm, S8_Lit("..")); OS_IsValid(it); OS_Advance(&it)) { + if (S8_Seek(it.filename, S8_Lit("build_file.c"), S8_IgnoreCase, NULL)) { + build_file = it.absolute_path; + } + } + + if (build_file.str == 0) { + IO_Printf("ERROR: couldnt find build file in current dir: %.*s, exiting ... \n", S8_Expand(working_dir)); + IO_Printf("- proper build file contains 'build_file.c' in it's name\n"); + IO_Printf("- alternative compiler can be chosen like this: bld cc=clang\n"); + return 0; + } + } + + SRC_InitCache(&Perm, S8_Lit("build_tool.cache")); + S8_String name_no_ext = S8_GetNameNoExt(build_file); + S8_String exe_name = S8_Format(&Perm, "%.*s.exe", S8_Expand(name_no_ext)); + + // Compile the build file only if code changed + double time_build_file_compiled = 0; + if (SRC_WasModified(build_file, exe_name)) { + time_build_file_compiled = OS_GetTime(); + int result = 0; + if (S8_AreEqual(cc, S8_Lit("cl"), false)) { + result = os_systemf("cl \"%.*s\" -nologo -Zi -WX -W3 -wd4200 -diagnostics:column /Fe:%.*s /Fd:%.*s.pdb", S8_Expand(build_file), S8_Expand(exe_name), S8_Expand(name_no_ext)); + } else if (S8_AreEqual(cc, S8_Lit("clang"), false)) { + result = os_systemf("clang \"%.*s\" -o %.*s -g -fdiagnostics-absolute-paths -Wno-writable-strings %s", S8_Expand(build_file), S8_Expand(exe_name), IF_LINUX_ELSE("-lm", "")); + } else { + result = os_systemf("g \"%.*s\" -o %.*s -g -Wno-write-strings %s", S8_Expand(build_file), S8_Expand(exe_name), IF_LINUX_ELSE("-lm", "")); + } + + if (result != 0) { + IO_Printf("ERROR: failed to compile build file: %.*s, exitcode(%d), %f, deleting cache\n", S8_Expand(build_file), result, OS_GetTime() - time_build_file_compiled); + os_delete_file(S8_Lit("build_tool.cache")); + return 1; + } + time_build_file_compiled = OS_GetTime() - time_build_file_compiled; + } + + // Run the build file + S8_String cmd_args_merged = S8_MergeWithSeparator(&Perm, list, S8_Lit(" ")); + double time = OS_GetTime(); + if (build_file.str) { + exe_name = OS_GetAbsolutePath(&Perm, exe_name); + int result = os_systemf("%.*s %.*s", S8_Expand(exe_name), S8_Expand(cmd_args_merged)); + if (result != 0) { + IO_Printf("ERROR: failed to run build_file: %.*s, exitcode(%d), %f, deleting cache\n", S8_Expand(build_file), result, OS_GetTime() - time); + os_delete_file(S8_Lit("build_tool.cache")); + return 1; + } + } + + SRC_SaveCache(); + if (time_build_file_compiled) IO_Printf("build file compiled in: %f\n", time_build_file_compiled); + IO_Printf("executed in : %f\n", OS_GetTime() - time); +} +#endif diff --git a/src/meta/parser.c b/src/meta/parser.c new file mode 100644 index 0000000..8d51ee0 --- /dev/null +++ b/src/meta/parser.c @@ -0,0 +1,307 @@ +typedef enum { + +#define AST_FLAG_XLIST\ + X(null)\ + X(string)\ + X(integer)\ + X(real)\ + X(binary)\ + X(enum)\ + X(enum_member)\ + X(struct)\ + X(struct_member)\ + X(var)\ + X(type_name)\ + X(type_pointer)\ + X(type_array)\ + +#define X(NAME) ast_flag_##NAME, + AST_FLAG_XLIST +#undef X +} ast_flag_t; + +typedef struct ast_t ast_t; +struct ast_t { + ast_flag_t flags; + + lex_t *pos; + ast_t *next; + ast_t *first; + ast_t *last; + i32 len; + + s8_t string; + f64 real; + i64 integer; +}; + +s8_t s8_serial_ast_flag_t(ma_arena_t *arena, ast_flag_t flag) { + sb8_t *sb = sb8_serial_begin(arena); +#define X(NAME) if (flag & set_bit(ast_flag_##NAME)) sb8_printf(sb, #NAME); + AST_FLAG_XLIST +#undef X + s8_t result = sb8_serial_end(sb); + return result; +} + +ast_t *create_ast(parser_t *par, lex_t *pos, ast_flag_t flags) { + ast_t *result = ma_push_type(par->arena, ast_t); + memset(result, 0, sizeof(ast_t)); + result->flags = flags; + result->pos = pos; + return result; +} + +void ast_append(ast_t *parent, ast_t *node) { + SLLQ_APPEND(parent->first, parent->last, node); + parent->len += 1; +} + +ast_t *create_ast_binary(parser_t *par, lex_t *pos, ast_t *left, lex_kind_t op, ast_t *right) { + ast_t *result = create_ast(par, pos, set_bit(ast_flag_string) | set_bit(ast_flag_binary) | set_bit(ast_flag_integer)); + ast_append(result, left); + ast_append(result, right); + result->integer = op; + result->string = s8_serial_simple_lex_kind_t(op); + return result; +} + +ast_t *parse_expr(parser_t *par); +ast_t *parse_lit_expr(parser_t *par) { + lex_t *token = parser_next(par); + if (token->kind == lex_kind_int) { + ast_t *result = create_ast(par, token, set_bit(ast_flag_integer) | set_bit(ast_flag_string)); + result->integer = token->integer; + result->string = token->s8; + return result; + } else if (token->kind == lex_kind_real) { + ast_t *result = create_ast(par, token, set_bit(ast_flag_real) | set_bit(ast_flag_string)); + result->real = (double)token->real; + result->string = token->s8; + return result; + } else if (token->kind == lex_kind_open_paren) { + ast_t *result = parse_expr(par); + parser_expect(par, lex_kind_close_paren); + return result; + } else { + lex_panicf(token, "got invalid token of kind: %S while parsing expression", s8_serial_lex_kind_t(token->kind)); + return 0; + } +} + +ast_t *parse_mul_expr(parser_t *par) { + ast_t *left = parse_lit_expr(par); + while (par->at->kind == lex_kind_multiply || par->at->kind == lex_kind_divide || par->at->kind == lex_kind_modulo) { + lex_t *op = parser_next(par); + left = create_ast_binary(par, op, left, op->kind, parse_lit_expr(par)); + } + return left; +} + +ast_t *parse_add_expr(parser_t *par) { + ast_t *left = parse_mul_expr(par); + while (par->at->kind == lex_kind_plus || par->at->kind == lex_kind_minus) { + lex_t *op = parser_next(par); + left = create_ast_binary(par, op, left, op->kind, parse_lit_expr(par)); + } + return left; +} + +ast_t *parse_logical_and_expr(parser_t *par) { + ast_t *left = parse_add_expr(par); + while (par->at->kind == lex_kind_or) { + lex_t *op = parser_next(par); + left = create_ast_binary(par, op, left, op->kind, parse_lit_expr(par)); + } + return left; +} + +ast_t *parse_logical_or_expr(parser_t *par) { + ast_t *left = parse_logical_and_expr(par); + while (par->at->kind == lex_kind_or) { + lex_t *op = parser_next(par); + left = create_ast_binary(par, op, left, op->kind, parse_lit_expr(par)); + } + return left; +} + +ast_t *parse_expr(parser_t *par) { + ast_t *expr = parse_logical_or_expr(par); + return expr; +} + +ast_t *parse_expr_str(ma_arena_t *arena, char *file_name, char *stream) { + lex_array_t tokens = lex_tokens(arena, file_name, stream); + parser_t *par = parser_make(arena, tokens.data); + ast_t *result = parse_expr(par); + return result; +} + +i64 eval_const_expr(ast_t *expr) { + if (expr->flags & set_bit(ast_flag_integer)) { + return expr->integer; + } else if (expr->flags & set_bit(ast_flag_binary)) { + assert(expr->first != expr->last); + i64 left = eval_const_expr(expr->first); + i64 right = eval_const_expr(expr->last); + + switch(expr->integer) { + case lex_kind_plus: return left + right; + case lex_kind_minus: return left - right; + case lex_kind_multiply: return left * right; + case lex_kind_divide: return left / right; + case lex_kind_modulo: return left % right; + case lex_kind_and: return left && right; + case lex_kind_or: return left || right; + default: lex_panicf(expr->pos, "unhandled binary operator: %S", s8_serial_lex_kind_t(expr->integer)); + } + } else { + ma_temp_t scratch = ma_begin_scratch(); + lex_panicf(expr->pos, "unhandled ast in const expression evaluation: %S", s8_serial_ast_flag_t(scratch.arena, expr->flags)); + ma_end_scratch(scratch); + } + return 0; +} + +#define test_expr(x) do {\ + lex_array_t tokens = lex_tokens(scratch.arena, "parser_test", #x);\ + parser_t *par = parser_make(scratch.arena, tokens.data);\ + ast_t *expr = parse_expr(par);\ + assert(expr != NULL);\ + i64 value = eval_const_expr(expr);\ + assert(value == x);\ +} while (0) + +void run_parser_test() { + ma_temp_t scratch = ma_begin_scratch(); + test_expr(32 + 2 + 5 + 5); + test_expr(32 - 2 + 5 - 5); + test_expr(2 * 2 / 4 * 5 + 2 + 3); + test_expr(2 * 5 * 5 / 2 + 2 - 1 - 1); + test_expr(2 * (5 * 5) / 2 + (2 - 1 - 1)); + test_expr((2 * (5 * 5) / (2)) + (2 - 1 - 1)); + test_expr(10 % 3); + test_expr(10 % 3 + 4 || 2); + test_expr(10 % 3 + 4 || 2 && (4 && 2) || 3 && 1 || 0); + ma_end_scratch(scratch); +} + +ast_t *parse_struct_mem(parser_t *par, s8_t *name) { + lex_t *type_name = parser_expect(par, lex_kind_ident); + ast_t *type = create_ast(par, type_name, set_bit(ast_flag_type_name) | set_bit(ast_flag_string)); + type->string = type_name->s8; + + while (parser_match(par, lex_kind_multiply)) { + ast_t *pointer = create_ast(par, par->at, set_bit(ast_flag_type_pointer) | set_bit(ast_flag_string)); + ast_append(pointer, type); + pointer->string = s8_fmt(par->arena, "%S*", type->string); + type = pointer; + } + + *name = parser_expect(par, lex_kind_ident)->s8; + + while (parser_match(par, lex_kind_open_bracket)) { + ast_t *array = create_ast(par, par->at, set_bit(ast_flag_type_array) | set_bit(ast_flag_string)); + ast_append(array, type); + lex_t *num = parser_match(par, lex_kind_int); + if (num) { + array->flags |= set_bit(ast_flag_integer); + array->integer = (int)num->integer; + array->string = s8_fmt(par->arena, "%S[%d]", type->string, (int)array->integer); + } else { + array->string = s8_fmt(par->arena, "%S[]", type->string); + } + parser_expect(par, lex_kind_close_bracket); + type = array; + } + + return type; +} + +ast_t *parse_decls(ma_arena_t *arena, char *file, char *code) { + lex_array_t tokens = lex_tokens(arena, file, code); + parser_t *par = parser_make(arena, tokens.data); + ast_t *result = create_ast(par, par->at, set_bit(ast_flag_string)); + result->string = s8_copy_char(arena, file); + for (;par->at->kind != lex_kind_eof;) { + lex_t *pos = par->at; + if (parser_matchi(par, s8_lit("enum"))) { + ast_t *n = create_ast(par, pos, set_bit(ast_flag_string) | set_bit(ast_flag_enum)); + ast_append(result, n); + + parser_expect(par, lex_kind_open_brace); + while (par->at->kind == lex_kind_ident) { + lex_t *val = parser_expect(par, lex_kind_ident); + ast_t *mem = create_ast(par, val, set_bit(ast_flag_enum_member) | set_bit(ast_flag_string)); + mem->string = val->s8; + ast_append(n, mem); + + // if (parser_match(par, lex_kind_assign)) { + // parse_expr(); + // } + + if (!parser_match(par, lex_kind_comma)) break; + } + parser_expect(par, lex_kind_close_brace); + n->string = parser_expect(par, lex_kind_ident)->s8; + parser_expect(par, lex_kind_semicolon); + } else if (parser_matchi(par, s8_lit("struct"))) { + ast_t *n = create_ast(par, pos, set_bit(ast_flag_string) | set_bit(ast_flag_struct)); + ast_append(result, n); + n->string = parser_expect(par, lex_kind_ident)->s8; + + parser_expect(par, lex_kind_open_brace); + while (par->at->kind != lex_kind_close_brace) { + ast_t *mem = create_ast(par, par->at, set_bit(ast_flag_struct_member) | set_bit(ast_flag_var) | set_bit(ast_flag_string)); + ast_append(n, mem); + + ast_t *type = parse_struct_mem(par, &mem->string); + ast_append(mem, type); + + parser_expect(par, lex_kind_semicolon); + } + parser_expect(par, lex_kind_close_brace); + parser_expect(par, lex_kind_semicolon); + + } else { + parser_next(par); + } + } + + return result; +} + +ast_t *parse_table(ma_arena_t *arena, char *file, char *code) { + lex_array_t tokens = lex_tokens(arena, file, code); + parser_t *par = parser_make(arena, tokens.data); + ast_t *table = create_ast(par, par->at, 0); + while (par->at->kind != lex_kind_eof) { + ast_t *row = create_ast(par, par->at, 0); + ast_append(table, row); + while (par->at->kind != lex_kind_eof) { + parser_match(par, lex_kind_bit_or); + + lex_t *token = par->at; + if (parser_match(par, lex_kind_ident) || parser_match(par, lex_kind_string)) { + ast_t *col = create_ast(par, par->at, set_bit(ast_flag_string)); + ast_append(row, col); + col->string = token->s8; + } else if (parser_match(par, lex_kind_int)) { + ast_t *col = create_ast(par, par->at, set_bit(ast_flag_string) | set_bit(ast_flag_integer)); + ast_append(row, col); + col->string = token->s8; + col->integer = token->integer; + } else if (parser_match(par, lex_kind_real)) { + ast_t *col = create_ast(par, par->at, set_bit(ast_flag_string) | set_bit(ast_flag_real)); + ast_append(row, col); + col->string = token->s8; + col->real = token->real; + } else if (parser_match(par, lex_kind_bit_or) || parser_match(par, lex_kind_eof)) { + break; + } else { + lex_panicf(par->at, "invalid token: %S", s8_serial_lex_kind_t(par->at->kind)); + } + } + } + return table; +} \ No newline at end of file diff --git a/src/meta/serialize.c b/src/meta/serialize.c new file mode 100644 index 0000000..aa93461 --- /dev/null +++ b/src/meta/serialize.c @@ -0,0 +1,224 @@ +s8_t s8_ast_to_cvar(ma_arena_t *arena, ast_t *ast, s8_t *name) { + if (ast->flags & set_bit(ast_flag_type_name)) { + return ast->string; + } else if (ast->flags & set_bit(ast_flag_type_pointer)) { + s8_t base = s8_ast_to_cvar(arena, ast->first, name); + return s8_fmt(arena, "%S*", base); + } else if (ast->flags & set_bit(ast_flag_type_array)) { + if (ast->flags & set_bit(ast_flag_integer)) { + *name = s8_fmt(arena, "%S[%d]", *name, ast->integer); + } else { + *name = s8_fmt(arena, "%S[%d]", *name, ast->integer); + } + + s8_t base = s8_ast_to_cvar(arena, ast->first, name); + return base; + } else { + assert(!"invalid ast_str case"); + return (s8_t){0}; + } +} + +void sb8_serial_ast(sb8_t *sb, ast_t *n) { + if (n->flags & set_bit(ast_flag_string)) { + sb8_stmtf(sb, "%S", n->string); + } + + if (n->first) { + sb8_printf(sb, "{"); + sb->indent += 1; + for (ast_t *it = n->first; it; it = it->next) { + sb8_serial_ast(sb, it); + } + sb->indent -= 1; + sb8_stmtf(sb, "}"); + } +} + +s8_t s8_serial_ast(ma_arena_t *arena, ast_t *n) { + sb8_t *sb = sb8_serial_begin(arena); + sb8_serial_ast(sb, n); + return sb8_serial_end(sb); +} + +s8_t s8_serial_ast_to_code(ma_arena_t *arena, ast_t *n); +void sb8_serial_ast_to_code(sb8_t *sb, ast_t *n) { + if (n->flags & set_bit(ast_flag_enum)) { + sb8_printf(sb, "typedef enum {"); + sb->indent += 1; + for (ast_t *it = n->first; it; it = it->next) { + sb8_stmtf(sb, "%S,", it->string); + } + sb->indent -= 1; + sb8_stmtf(sb, "} %S;", n->string); + } else if (n->flags & set_bit(ast_flag_struct)) { + sb8_stmtf(sb, "typedef struct %S %S;", n->string, n->string); + sb8_stmtf(sb, "struct %S {", n->string); + sb->indent += 1; + for (ast_t *it = n->first; it; it = it->next) { + s8_t name = it->string; + s8_t type = s8_ast_to_cvar(sb->arena, it->first, &name); + sb8_stmtf(sb, "%S %S;", type, name); + } + sb->indent -= 1; + sb8_stmtf(sb, "};"); + + } else { + if (n->flags & set_bit(ast_flag_string)) { + sb8_printf(sb, "/*%S*/", n->string); + } else { + sb8_printf(sb, "/*null*/"); + } + + for (ast_t *it = n->first; it; it = it->next) { + sb8_indent(sb); + sb8_serial_ast_to_code(sb, it); + } + } +} + +s8_t s8_serial_ast_to_code(ma_arena_t *arena, ast_t *n) { + sb8_t *sb = sb8_serial_begin(arena); + sb8_serial_ast_to_code(sb, n); + s8_t result = sb8_serial_end(sb); + return result; +} + +s8_t s8_serial_ast_type_to_type_info(ma_arena_t *arena, ast_t *n) { + if (n->flags & set_bit(ast_flag_type_name)) { + return s8_fmt(arena, "type__%S", n->string); + } else if (n->flags & set_bit(ast_flag_type_pointer)) { + s8_t base = s8_serial_ast_type_to_type_info(arena, n->first); + return s8_fmt(arena, "(type_t){type_kind_pointer, s8_const_lit(\"%S\"), sizeof(void *), .base = &%S}", n->string, base); + } else if (n->flags & set_bit(ast_flag_type_array)) { + s8_t base = s8_serial_ast_type_to_type_info(arena, n->first); + return s8_fmt(arena, "(type_t){type_kind_array, s8_const_lit(\"%S\"), sizeof(%S), %d, .base = &%S}", n->string, n->string, (int)n->integer, base); + } else { + lex_panicf(n->pos, "expected type"); + } + return (s8_t){0}; +} + +void sb8_serial_ast_to_type_info(sb8_t *sb, ast_t *n) { + if (n->flags & set_bit(ast_flag_enum)) { + sb8_printf(sb, "type_t type__%S = { type_kind_enum, s8_const_lit(\"%S\"), sizeof(%S),", n->string, n->string, n->string); + sb->indent += 1; + + sb8_stmtf(sb, ".members = (type_member_t[]){"); + sb->indent += 1; + for (ast_t *it = n->first; it; it = it->next) { + sb8_stmtf(sb, "{.name = s8_const_lit(\"%S\"), .value = %S},", it->string, it->string); + } + sb->indent -= 1; + sb8_stmtf(sb, "},"); + + sb8_stmtf(sb, ".count = %d,", n->len); + sb->indent -= 1; + sb8_stmtf(sb, "};"); + } else if (n->flags & set_bit(ast_flag_struct)) { + sb8_printf(sb, "type_t type__%S = { type_kind_struct, s8_const_lit(\"%S\"), sizeof(%S),", n->string, n->string, n->string); + sb->indent += 1; + + sb8_stmtf(sb, ".members = (type_member_t[]){"); + sb->indent += 1; + for (ast_t *it = n->first; it; it = it->next) { + s8_t name = it->string; + s8_t type_info = s8_serial_ast_type_to_type_info(sb->arena, it->first); + sb8_stmtf(sb, "{.name = s8_const_lit(\"%S\"), .type = &%S, .offset = offsetof(%S, %S)},", name, type_info, n->string, name); + } + sb->indent -= 1; + sb8_stmtf(sb, "},"); + + sb8_stmtf(sb, ".count = %d,", n->len); + sb->indent -= 1; + sb8_stmtf(sb, "};"); + } else { + if (n->flags & set_bit(ast_flag_string)) { + sb8_printf(sb, "/*%S*/", n->string); + } else { + sb8_printf(sb, "/*null*/"); + } + + for (ast_t *it = n->first; it; it = it->next) { + sb8_indent(sb); + sb8_serial_ast_to_type_info(sb, it); + } + } +} + +s8_t s8_serial_ast_to_type_info(ma_arena_t *arena, ast_t *n) { + sb8_t *sb = sb8_serial_begin(arena); + sb8_serial_ast_to_type_info(sb, n); + s8_t result = sb8_serial_end(sb); + return result; +} + +// +// +// +int row_findi(ast_t *row, char *name) { + s8_t s = s8_from_char(name); + int i = 0; + for (ast_t *col = row->first; col; col = col->next) { + if (s8_equal(col->string, s)) { + return i; + } + i += 1; + } + return -1; +} + +ast_t *row_geti(ast_t *row, int idx) { + if (idx == -1) return NULL; + + int i = 0; + for (ast_t *col = row->first; col; col = col->next, i+=1) { + if (i == idx) return col; + } + return false; +} + +void sb8_serial_table_enum(sb8_t *c, sb8_t *h, ast_t *table, s8_t decl) { + int name_idx = row_findi(table->first, "name"); + int value_idx = row_findi(table->first, "value"); + + s8_t name_t = s8_fmt(c->arena, "%S_t", decl); + + sb8_printf(h, "typedef enum {\n"); + for (ast_t *row = table->first->next; row; row = row->next) { + s8_t name = row_geti(row, name_idx)->string; + ast_t *value = row_geti(row, value_idx); + sb8_printf(h, "%S_%S", decl, name); + if (value) sb8_printf(h, " = %S", value->string); + sb8_printf(h, ",\n"); + } + sb8_printf(h, "%S_count,\n", decl); + sb8_printf(h, "} %S;\n", name_t); + + sb8_stmtf(c, "type_t type__%S = { type_kind_enum, s8_const_lit(\"%S\"), sizeof(%S),", name_t, name_t, name_t); + c->indent += 1; + + sb8_stmtf(c, ".members = (type_member_t[]){"); + c->indent += 1; + int item_count = 0; + for (ast_t *row = table->first->next; row; row = row->next) { + s8_t name = row_geti(row, name_idx)->string; + ast_t *value = row_geti(row, value_idx); + sb8_stmtf(c, "{.name = s8_const_lit(\"%S_%S\"), .value = %S_%S},", decl, name, decl, name); + item_count += 1; + } + c->indent -= 1; + sb8_stmtf(c, "},"); + + sb8_stmtf(c, ".count = %d,", item_count); + c->indent -= 1; + sb8_stmtf(c, "};"); +} + +#define gen_c(arena) _gen_filename(arena, s8_lit(__FILE__), s8_lit("c")) +#define gen_h(arena) _gen_filename(arena, s8_lit(__FILE__), s8_lit("h")) +s8_t _gen_filename(ma_arena_t *arena, s8_t lit_file, s8_t ext) { + s8_t file_noext = s8_chop_last_period(s8_chop_last_period(lit_file)); + s8_t file = s8_fmt(arena, "%S.gen.%S", file_noext, ext); + return file; +} \ No newline at end of file diff --git a/src/wasm_app/debug.c b/src/wasm_app/debug.c new file mode 100644 index 0000000..f3a8a03 --- /dev/null +++ b/src/wasm_app/debug.c @@ -0,0 +1,3 @@ +void dr2f64(r2f64_t r) { + debugf("[%f %f %f %f]", r.x0, r.y0, r.x1, r.y1); +} \ No newline at end of file diff --git a/src/wasm_app/main.c b/src/wasm_app/main.c new file mode 100644 index 0000000..d6d9909 --- /dev/null +++ b/src/wasm_app/main.c @@ -0,0 +1,19 @@ +#include "core/core.h" +#include "core/core.c" +#include "app/app.h" +#include "app/app.c" +// #include "debug.c" +// #include "ui2.c" + +void on_update() { + // r2f64_t rect = {0, 0, 200, 200}; + // draw_rect(rect, (v4f32_t){0, 0, 0, 1}); + // draw_textf((v2f64_t){10 + i, 100}, "time = %f, dt = %f", time_g / 1000, dt_g); + + // f64_mod(4, 2); + + // i += 1; + // if (i > 200) i = 0; + + // ui_demo(); +} diff --git a/src/wasm_app/ui.c b/src/wasm_app/ui.c new file mode 100644 index 0000000..0ae7cad --- /dev/null +++ b/src/wasm_app/ui.c @@ -0,0 +1,280 @@ +#if 0 +typedef struct ui_id_t ui_id_t; +struct ui_id_t { + u64 value; +}; +ui_id_t ui_string_to_id(const char *string) { // FNV HASH (1a?) + u8 *data8 = (u8 *)string; + u64 hash = (u64)14695981039346656037ULL; + for (u64 i = 0; data8[i]; i++) { + hash = hash ^ (u64)(data8[i]); + hash = hash * (u64)1099511628211ULL; + } + return (ui_id_t){ .value = hash }; +} +#define ui_location_id() ui_string_to_id(FILE_AND_LINE) +ui_id_t ui_element_pressed; + +typedef enum { + cut_left, + cut_right, + cut_bottom, + cut_top, +} cut_t; + +typedef struct rcut_t rcut_t; +struct rcut_t { + r2f64_t *rect; + cut_t cut; +}; + +rcut_t rcut(r2f64_t *rect, cut_t cut) { + return (rcut_t){rect, cut}; +} + +r2f64_t ui_cut(r2f64_t *rect, f64 value, cut_t cut) { + if (cut == cut_left) return r2f64_cut_left(rect, value); + else if (cut == cut_right) return r2f64_cut_right(rect, value); + else if (cut == cut_bottom) return r2f64_cut_bottom(rect, value); + else if (cut == cut_top) return r2f64_cut_top(rect, value); + else assert(!"invalid codepath"); + return (r2f64_t){0}; +} + +r2f64_t ui_cut2(rcut_t rc, f64 x, f64 y) { + f64 cut_value = 0; + if (rc.cut == cut_left || rc.cut == cut_right) cut_value = x; + else cut_value = y; + r2f64_t result = ui_cut(rc.rect, cut_value, rc.cut); + return result; +} + +typedef struct ui_flags_t ui_flags_t; +struct ui_flags_t { + b32 draw_text : 1; + b32 text_centered : 1; + b32 text_y_centered; + + b32 draw_rect : 1; + + b32 checkable : 1; + b32 draw_checkbox : 1; + b32 draw_checked; + + b32 clickable : 1; + b32 slider : 1; +}; + +typedef struct ui_signal_t ui_signal_t; +struct ui_signal_t { + b32 pressed : 1; + b32 overlapping : 1; + b32 interacting : 1; +}; + +// TODO(Karol): Don't use inputs as flags! Use only flags. +typedef struct ui_input_t ui_input_t; +struct ui_input_t { + char *title; + b32 *value_b32; + f64 *value_f64; + ui_id_t id; +}; + +v2f64_t style_padding = {50, 10}; +ui_signal_t ui_widget(rcut_t rc, ui_input_t input, ui_flags_t flags) { + // Calculate rectangles + r2f64_t total_rect = {0}; + r2f64_t rect = {0}; + r2f64_t checkbox_rect = {0}; + { + v2f64_t size = style_padding; + f64 font_height = get_font_height(); + + size.y += font_height; + if (flags.draw_text) { + size.x += measure_text(input.title); + } + + total_rect = ui_cut2(rc, size.x, size.y); + rect = total_rect; + if (flags.draw_checkbox) { + checkbox_rect = r2f64_cut_left(&rect, size.y); + } + } + + // Solve interactions + ui_signal_t result = {0}; + { + if (r2f64_contains(total_rect, mouse_pos_g)) { + result.overlapping = true; + if (mouse_button_press_g[app_mouse_button_left]) { + ui_element_pressed = input.id; + result.pressed = true; + } + } + + if (ui_element_pressed.value == input.id.value && mouse_button_down_g[app_mouse_button_left]) { + result.interacting = true; + } + + if (flags.checkable && result.pressed) { + *input.value_b32 = !*input.value_b32; + } + + if (flags.slider && result.interacting) { + f64 mouse_pos = mouse_pos_g.x; + v2f64_t rect_size = r2f64_get_size(rect); + *input.value_f64 = (mouse_pos - rect.min.x) / rect_size.x; + *input.value_f64 = CLAMP(*input.value_f64, 0, 1.0); + } + } + + clip(total_rect); + + // Draw + if (flags.draw_rect) { + v4f32_t color = primary_color_global; + if (flags.clickable) { + if (result.overlapping) color = secondary_color_global; + if (flags.draw_checked && input.value_b32[0]) color = accent1_color_global; + } + draw_rect(rect, color); + } + + if (flags.slider) { + v2f64_t rect_size = r2f64_get_size(rect); + r2f64_t rect_split = r2f64_get_left(&rect, rect_size.x * input.value_f64[0]); + draw_rect(rect_split, accent1_color_global); + } + + if (flags.draw_text) { + v2f64_t text_pos = rect.min; + f64 text_width = measure_text(input.title); + f64 font_height = get_font_height(); + + v2f64_t rect_size = r2f64_get_size(rect); + v2f64_t offset_to_center = {(rect_size.x - text_width) / 2, (rect_size.y - font_height) / 2}; + if (flags.text_y_centered) { + text_pos.y += offset_to_center.y; + } + if (flags.text_centered) { + text_pos = v2f64_add(text_pos, offset_to_center); + } + draw_text(text_pos, input.title); + } + + if (flags.draw_checkbox) { + v4f32_t color = accent1_color_global; + if (flags.checkable) { + if (input.value_b32[0]) { + color = accent2_color_global; + } + } + if ((flags.checkable || flags.clickable) && result.overlapping) { + color = v4f32_lerp(color, v4f32(1,1,1,1), 0.25); + } + draw_rect(checkbox_rect, color); + } + + clip(r2f64(-1000, -1000, 1000000, 1000000)); + + return result; +} + +b32 ui_button(rcut_t rc, char *title) { + ui_signal_t sig = ui_widget(rc, (ui_input_t){.title = title}, (ui_flags_t){.draw_text = true, .text_centered = true, .draw_rect = true, .clickable = true}); + return sig.pressed; +} + +b32 ui_checked_button(b32 *value, rcut_t rc, char *title) { + ui_widget(rc, (ui_input_t){.title = title, .value_b32 = value}, (ui_flags_t){.draw_rect = true, .draw_text = true, .text_centered = true, .clickable = true, .checkable = true, .draw_checked = true}); + return *value; +} + +b32 ui_checkbox(rcut_t rc, b32 *value, char *title) { + ui_widget(rc, (ui_input_t){.value_b32 = value, .title = title}, (ui_flags_t){.draw_checkbox = true, .draw_text = true, .text_y_centered = true, .draw_rect = true, .checkable = true}); + return *value; +} + +void ui_label(rcut_t rc, char *title) { + ui_widget(rc, (ui_input_t){.title = title}, (ui_flags_t){.draw_text = true, .text_centered = true, .draw_rect = true}); +} + +void ui_slider(rcut_t rc, ui_id_t id, f64 *value, char *title) { + ui_widget(rc, (ui_input_t){.title = title, .value_f64 = value, .id = id}, (ui_flags_t){.draw_text = true, .text_centered = true, .slider = true, .draw_rect = true, .clickable = true}); +} + +void ui_begin_frame(void) { + if (mouse_button_release_g[app_mouse_button_left]) { + ui_element_pressed = (ui_id_t){0}; + } +} + +void ui_demo(void) { + ui_begin_frame(); + f64 font_height = get_font_height(); + r2f64_t window_rect = r2f64(0, 0, window_size_g.x, window_size_g.y); + { + draw_rect(window_rect, while_color_global); + } + + + static b32 panel_open = true; + static b32 cool_rect = false; + { + r2f64_t top_rect = r2f64_cut_top(&window_rect, font_height*2); + draw_rect(top_rect, primary_color_global); + + if (ui_checked_button(&cool_rect, rcut(&top_rect, cut_left), "file")) { + draw_rect(window_rect, v4f32(0.5, 0.1, 0.1, 1.0)); + } + if (ui_button(rcut(&top_rect, cut_left), "edit")) {} + if (ui_button(rcut(&top_rect, cut_left), "view")) {} + if (ui_checked_button(&panel_open, rcut(&top_rect, cut_left), "open panel")) { + + } + } + + if (panel_open) { + r2f64_t panel_rect = r2f64_cut_left(&window_rect, measure_text("1234567890")*2); + { + v4f32_t color = primary_color_global; color.a = 0.3f; + draw_rect(panel_rect, color); + } + + + + static f64 value = 0.2f; + ui_slider(rcut(&panel_rect, cut_top), ui_location_id(), &value, "value"); + + ui_widget(rcut(&panel_rect, cut_top), (ui_input_t){.title = "non centered label"}, (ui_flags_t){.draw_rect = true, .draw_text = true}); + + static b32 checkbox_memes; + ui_checkbox(rcut(&panel_rect, cut_top), &checkbox_memes, "checkbox"); + + + // TODO(Krzosa): Draw arrows / triangles + { + f64 font_height = get_font_height(); + f64 cut_size = font_height + style_padding.y; + r2f64_t rect = r2f64_cut_top(&panel_rect, cut_size); + + static int counter; + + if (ui_widget(rcut(&rect, cut_left), (ui_input_t){0}, (ui_flags_t){.draw_checkbox = true, .clickable = true}).pressed) { + counter -= 1; + } + char buff[256]; + stbsp_snprintf(buff, sizeof(buff), "counter: %d", counter); + ui_label(rcut(&rect, cut_left), buff); + if (ui_widget(rcut(&rect, cut_left), (ui_input_t){0}, (ui_flags_t){.draw_checkbox = true, .clickable = true}).pressed) { + counter += 1; + } + + } + + } + +} +#endif \ No newline at end of file diff --git a/src/wasm_app/ui2.c b/src/wasm_app/ui2.c new file mode 100644 index 0000000..3638b74 --- /dev/null +++ b/src/wasm_app/ui2.c @@ -0,0 +1,241 @@ +v2f64_t ui_calc_text_pos(r2f64_t rect, char *title) { + f64 font_height = get_font_height(); + f64 text_width = measure_text(title); + + v2f64_t text_pos = rect.min; + { + v2f64_t rect_size = r2f64_get_size(rect); + v2f64_t offset_to_center = {(rect_size.x - text_width) / 2, (rect_size.y - font_height) / 2}; + text_pos = v2f64_add(text_pos, offset_to_center); + } + + return text_pos; +} + +typedef struct ui_signal_t ui_signal_t; +struct ui_signal_t { + b8 pressed; + b8 overlapping; +}; + +ui_signal_t ui_interact(r2f64_t rect) { + ui_signal_t sig = {0}; + if (r2f64_contains(rect, mouse_pos_g)) { + sig.overlapping = true; + if (mouse_button_press_g[app_mouse_button_left]) { + sig.pressed = true; + } + } + return sig; +} + +b32 ui_button(r2f64_t rect, char *title) { + v2f64_t text_pos = ui_calc_text_pos(rect, title); + ui_signal_t sig = ui_interact(rect); + + v4f32_t rect_color = secondary_color_global; + v4f32_t text_color = black_color_global; + if (sig.overlapping) { + rect_color = primary_color_global; + } + + clip(rect); + draw_rect(rect, rect_color); + draw_text(text_pos, text_color, title, str_len(title)); + clip(r2f64(-1000, -1000, 1000000, 1000000)); + + return sig.pressed; +} + +b32 ui_checkbox(r2f64_t rect, b32 *value, char *title) { + v2f64_t text_pos = ui_calc_text_pos(rect, title); + ui_signal_t sig = ui_interact(rect); + if (sig.pressed) *value = !*value; + + v4f32_t rect_color = secondary_color_global; + v4f32_t text_color = black_color_global; + if (sig.overlapping) { + rect_color = primary_color_global; + } + if (*value) { + rect_color = v4f32_lerp(rect_color, accent2_color_global, 0.5); + } + + clip(rect); + draw_rect(rect, rect_color); + draw_text(text_pos, text_color, title, str_len(title)); + clip(r2f64(-1000, -1000, 1000000, 1000000)); + + return *value; +} + +typedef struct ui_id_t ui_id_t; +struct ui_id_t { u64 value; }; + +ui_id_t ui_string_to_id(const char *string, i32 len) { // FNV HASH (1a?) + u8 *data8 = (u8 *)string; + u64 hash = (u64)14695981039346656037ULL; + for (u64 i = 0; i < len; i++) { + hash = hash ^ (u64)(data8[i]); + hash = hash * (u64)1099511628211ULL; + } + return (ui_id_t){ .value = hash }; +} +#define ui_location_id() ui_string_to_id(FILE_AND_LINE, sizeof(FILE_AND_LINE) - 1) +ui_id_t ui_active_element = {0}; + +void ui_slider(r2f64_t rect, ui_id_t id, f64 *value, char *title) { + v2f64_t text_pos = ui_calc_text_pos(rect, title); + ui_signal_t sig = ui_interact(rect); + b32 interacting = false; + { + if (sig.pressed) { + ui_active_element = id; + } + if (id.value == ui_active_element.value && mouse_button_down_g[app_mouse_button_left]) { + interacting = true; + } + } + + if (interacting) { + f64 mouse_pos = mouse_pos_g.x; + v2f64_t rect_size = r2f64_get_size(rect); + *value = (mouse_pos - rect.min.x) / rect_size.x; + *value = CLAMP(*value, 0, 1.0); + } + + v2f64_t rect_size = r2f64_get_size(rect); + r2f64_t slider_rect = r2f64_get_left(&rect, rect_size.x * value[0]); + + v4f32_t rect_color = secondary_color_global; + if (sig.overlapping) rect_color = v4f32_lerp(rect_color, accent2_color_global, 0.2); + v4f32_t slider_color = accent2_color_global; + if (sig.overlapping) slider_color = v4f32_lerp(slider_color, accent2_color_global, 0.2); + v4f32_t text_color = black_color_global; + + clip(rect); + draw_rect(rect, rect_color); + draw_rect(slider_rect, slider_color); + draw_text(text_pos, text_color, title, str_len(title)); + clip(r2f64(-1000, -1000, 1000000, 1000000)); +} + +typedef struct ui_input_state ui_input_state; +struct ui_input_state { + char *buff; + int len; + int cap; + + int cursor; +}; + + +void ui_input_text(r2f64_t rect, ui_input_state *in) { + v2f64_t tp = ui_calc_text_pos(rect, "a"); + v2f64_t text_pos = {rect.min.x, tp.y}; + ui_signal_t sig = ui_interact(rect); + + v4f32_t rect_color = secondary_color_global; + if (r2f64_contains(rect, mouse_pos_g)) { + if (key_press_g == app_key_right) in->cursor += 1; + if (key_press_g == app_key_left) in->cursor -= 1; + if (key_press_g == app_key_backspace) { + + debugf("a"); + } + in->cursor = CLAMP(in->cursor, 0, in->len); + + + + + + if (sig.overlapping) rect_color = v4f32_lerp(rect_color, accent2_color_global, 0.2); + } + + + clip(rect); + draw_rect(rect, rect_color); + draw_text(text_pos, black_color_global, in->buff, in->len); + clip(r2f64(-1000, -1000, 1000000, 1000000)); + + f64 cx = measure_text_ex(in->buff, in->cursor); + v2f64_t cursor_pos = {text_pos.x + cx, text_pos.y}; + draw_rect(r2f64_center_halfdim(cursor_pos, v2f64(1, 6)), black_color_global); +} + +void ui_demo(void) { + if (mouse_button_release_g[app_mouse_button_left]) { + ui_active_element = (ui_id_t){0}; + } + + r2f64_t window_rect = (r2f64_t){(v2f64_t){0}, window_size_g}; + r2f64_t top_bar_rect = r2f64_cut_top(&window_rect, get_font_height() + 20); + draw_rect(window_rect, primary_color_global); + + f64 padding = 50; + draw_rect(top_bar_rect, secondary_color_global); + + static b32 open_file_panel; + f64 open_file_panel_xsize = 0; + { + // ▼▲▶◀ + char *title = "▶ file"; + if (open_file_panel) title = "▼ file"; + open_file_panel_xsize = measure_text(title) + padding*2; + r2f64_t rect = r2f64_cut_left(&top_bar_rect, open_file_panel_xsize); + if (ui_checkbox(rect, &open_file_panel, title)) { + } + r2f64_t gap_rect = r2f64_cut_left(&top_bar_rect, 1); + draw_rect(gap_rect, black_color_global); + } + + if (open_file_panel) { + f64 item_count = 20; + f64 item_size = get_font_height() + 20; + r2f64_t panel_rect = r2f64_get_top(&window_rect, item_count * item_size); + panel_rect = r2f64_get_left(&panel_rect, open_file_panel_xsize); + + { + static f64 slider_value; + char buff[64]; + stbsp_snprintf(buff, sizeof(buff), "%f", slider_value); + r2f64_t rect = r2f64_cut_top(&panel_rect, item_size); + ui_slider(rect, ui_location_id(), &slider_value, buff); + } + + { + static char buff[64]; + static ui_input_state input; + if (input.buff == NULL) { + input.buff = buff; + input.cap = sizeof(buff); + } + + r2f64_t rect = r2f64_cut_top(&panel_rect, item_size); + ui_input_text(rect, &input); + } + } + + + { + char *title = "edit"; + r2f64_t rect = r2f64_cut_left(&top_bar_rect, measure_text(title) + padding*2); + if (ui_button(rect, title)) { + } + r2f64_t gap_rect = r2f64_cut_left(&top_bar_rect, 1); + draw_rect(gap_rect, black_color_global); + } + + { + char *title = "view"; + r2f64_t rect = r2f64_cut_left(&top_bar_rect, measure_text(title) + padding*2); + if (ui_button(rect, title)) { + } + r2f64_t gap_rect = r2f64_cut_left(&top_bar_rect, 1); + draw_rect(gap_rect, black_color_global); + } + + if (input_text_len_g) { + debugf("%.*s", input_text_len_g, input_text_g); + } +} \ No newline at end of file diff --git a/todo.txt b/todo.txt new file mode 100644 index 0000000..d8ef0e3 --- /dev/null +++ b/todo.txt @@ -0,0 +1,23 @@ +platform + more then 2 platform abstraction + app platform game + in the future event based? + or maybe hybrid? + os functionality + gfx + w32 + canvas + dll hot reload + wasm: + input and key down (use KeyA KeyB etc.) + +type_info: + serialize + deserialize + + +app + loop styles: + infinite loop update -> gather all events -> loop event and update -> render (vsync wait) + block when no events + update (render) + other events (don't render) + block when no events + \ No newline at end of file