From e865dc349e47966fbf9d8c30f8635276ad5a1d7b Mon Sep 17 00:00:00 2001 From: Allen Webster Date: Mon, 29 Jun 2026 17:18:58 -0700 Subject: [PATCH] major version 1.0 --- README.md | 642 +- build.bat | 7 - build.sh | 12 + build_ld_meta.sh | 10 + build_with_cl.bat | 8 + build_with_clang.bat | 8 + {src => examples}/example1.c | 31 +- examples/example1.h | 54 + project.4coder | 47 +- src/base.c | 62 - src/base.h | 252 - src/binary/elf/mr4th_elf.c | 518 -- src/binary/elf/mr4th_elf.dump.c | 567 -- src/binary/elf/mr4th_elf.dump.h | 40 - src/binary/pe/mr4th_pe.h | 831 --- src/binary/pe/mr4th_pe.img_get_section.c | 68 - src/binary/pe/mr4th_pe.img_get_section.h | 9 - src/example1.h | 33 - src/symbol_set.define.h | 13 - src/symbol_set.h | 78 - .../mr4th/exefmt/elf/mr4th_elf.c | 480 ++ .../mr4th/exefmt}/elf/mr4th_elf.h | 65 +- .../mr4th/exefmt/elf/mr4th_elf.parse.c | 412 ++ .../mr4th/exefmt/elf/mr4th_elf.parse.h | 49 + symbol_set.ld_meta/mr4th/mr4th_base.c | 5838 +++++++++++++++++ symbol_set.ld_meta/mr4th/mr4th_base.h | 2035 ++++++ symbol_set.ld_meta/mr4th/mr4th_cmdln.c | 294 + symbol_set.ld_meta/mr4th/mr4th_cmdln.h | 64 + symbol_set.ld_meta/mr4th/mr4th_stdio.c | 56 + symbol_set.ld_meta/mr4th/mr4th_stdio.h | 23 + symbol_set.ld_meta/symbol_set.ld_meta.c | 146 + symbol_set/symbol_set.base.h | 269 + symbol_set/symbol_set.define.h | 36 + symbol_set/symbol_set.h | 216 + symbol_set/symbol_set.init.h | 40 + todo.txt | 44 +- 36 files changed, 10629 insertions(+), 2728 deletions(-) delete mode 100644 build.bat create mode 100644 build.sh create mode 100644 build_ld_meta.sh create mode 100644 build_with_cl.bat create mode 100644 build_with_clang.bat rename {src => examples}/example1.c (63%) create mode 100644 examples/example1.h delete mode 100644 src/base.c delete mode 100644 src/base.h delete mode 100644 src/binary/elf/mr4th_elf.c delete mode 100644 src/binary/elf/mr4th_elf.dump.c delete mode 100644 src/binary/elf/mr4th_elf.dump.h delete mode 100644 src/binary/pe/mr4th_pe.h delete mode 100644 src/binary/pe/mr4th_pe.img_get_section.c delete mode 100644 src/binary/pe/mr4th_pe.img_get_section.h delete mode 100644 src/example1.h delete mode 100644 src/symbol_set.define.h delete mode 100644 src/symbol_set.h create mode 100644 symbol_set.ld_meta/mr4th/exefmt/elf/mr4th_elf.c rename {src/binary => symbol_set.ld_meta/mr4th/exefmt}/elf/mr4th_elf.h (78%) create mode 100644 symbol_set.ld_meta/mr4th/exefmt/elf/mr4th_elf.parse.c create mode 100644 symbol_set.ld_meta/mr4th/exefmt/elf/mr4th_elf.parse.h create mode 100644 symbol_set.ld_meta/mr4th/mr4th_base.c create mode 100644 symbol_set.ld_meta/mr4th/mr4th_base.h create mode 100644 symbol_set.ld_meta/mr4th/mr4th_cmdln.c create mode 100644 symbol_set.ld_meta/mr4th/mr4th_cmdln.h create mode 100644 symbol_set.ld_meta/mr4th/mr4th_stdio.c create mode 100644 symbol_set.ld_meta/mr4th/mr4th_stdio.h create mode 100644 symbol_set.ld_meta/symbol_set.ld_meta.c create mode 100644 symbol_set/symbol_set.base.h create mode 100644 symbol_set/symbol_set.define.h create mode 100644 symbol_set/symbol_set.h create mode 100644 symbol_set/symbol_set.init.h diff --git a/README.md b/README.md index f02e2c7..0d8d97a 100644 --- a/README.md +++ b/README.md @@ -1,189 +1,553 @@ -# C Scripting Introduction +# C-Scripting -"C Scripting" is a technique I developed to solve a *problem* in C programming. The *problem* doesn't have a well known name, and I think many programmers only experience this problem as an intuitively felt limitation, and not as a *problem*. To introduce this idea properly I will have to spell out the *problem*, which I will get to shortly. I named my answer to this *problem* "C Scripting" because it feels like a step towards the kind of expressive freedom that one gets by choosing, or hybridizing with, a scripting language. In this introduction I name and describe the *problem* then I describe the basic idea of a C Scripting system. Past the introduction, the repository is filled with example code, further discussions of design tradeoffs, and possibilities for future improvements of the technique. +*Allen Webster. June 28 2026* -### Introduction: The Code-Data Binding Problem +The only way forward is inside out. -The *problem* I am talking about is the problem of creating and maintaining a structure of code-data relationships. It'll be easier to see what I mean after some examples. +### Index -Think of what you do to setup a new program with just a command line interface. You have a set of flags to implement, each needs a name and/or an abbreviation, maybe some expected argument type, some help menu text, and of course some way of binding information from the interface into the "main computation". +1. Start C-Scripting in your C program. +2. What is C-Scripting? +3. How does it work? +4. Known pitfalls and unexplored treachery. +5. Introduction to the Symbol Set programming primitives. +6. Awesome things you can do with C-Scripting. +7. The end. -`CLIFlag = (name:String & abbr:String & arg_type:ArgType & desc:String & ???:???)` +## 1. Start C-Scripting in your C program. -The first few pieces are easy enough to understand, but is that last piece that does the "binding into the main computation"? Let's answer that by looking at how systems like these tend to be done: +0. Pick a project of yours to integrate with - or start an empty "Hello World!" project to play with it. -**Immediate Mode CLI** -``` -{ - if (cli_flag(clictx, "fast", "F", ArgType_None, "go as fast as possible")){ - // do some init path stuff - } - if (cli_flag(clictx, "start_time", "S", ArgType_TimeStamp, - "set the time where the output stream begins (>= 0:00) (default 0:00)")){ - TimeStamp time_stamp = cli_read_arg_TimeStamp(clictx); - // do some init path stuff +1. Copy the files in the `symbol_set` folder into your project, add that folder to your includes path. + +2. If you are on Linux compile `symbol_set.ld_meta`. The shell script `build_ld_meta.sh` builds this program with `clang`. You will want to get the compiled version of this file ready at + +3. In any compilation unit where you are going to be C-Scripting, `#include "symbol_set.h"`. + +4. Before the header, `#define SY__MAIN` -- If you have multiple compilation units, `#define SY__MAIN` into just one of them (perhaps `main.c`). + +At this point your program should build and behave as it did before setup. If something went wrong for you when you tried, you can contact `support@mr4th.com` and I can try to help you. If it does build, then you're ready to start C-Scripting! + +## 2. What is C-Scripting? + +C-Scripting is when you're programming in C, and you've got an underlying architecture that lets you tap into features or expressivity that feel more scripting-like than C-like. I named this particular project "C-Scripting" but the spirit of the name goes back much futher. It's the idea that C doesn't have to be unproductive if you make smart decisions about how you equip yourself with tools. + +By looking for new ways to engage in C-Scripting, I have had two radical transformations to my style of C. The first came from embracing Arenas and non-conflicting local scratchs. This repository contains the kernel of my second radical transformation, a data structure I call a "Symbol Set". + +There are many ways to C-Script, but outside of this little discussion of the spirit of C-Scripting, *this* document is about C-Scripting with Symbol Sets. + +### What is a Symbol Set? + +When you define a symbol set, you give it a name, and a type. Symbol sets live at global scope, and act like an enum and an array working together. The type given in the definition sets the type of the array elements. + +When you define a symbol, you specify which symbol set it belongs to. The symbol is assigned a unique id in the enum and a corresponding slot in the array. When you define a symbol, you define the static initialization for the corresponding slot in the array. Symbols often have names, but there are also great uses for unnamed symbols. Unnamed symbols still get an id and a corresponding slot in the array, they just can't be referenced statically by other code. Usually you do this when you have no reason to use the names and it would be tedious to have to come up with pointless unique names. + +You can imagine it looks sort of like this: +```C +// define my Command type +struct MyCommand{ + String8 name; + String8 description; + Hook *hook; +}; + +// define the MY_COMMAND symbol set +SYMBOL_SET_DEFINE(MY_COMMAND, MyCommand); + +// define some commands +MY_COMMAND_DEFINE(Foo, "Do Foo."){ + do_foo(ctx); +} + +MY_COMMAND_DEFINE(EasyBar, "If it's not too hard, do Bar."){ + if (difficulty(ctx) < 3){ + do_bar(ctx); } } ``` -Here we put the data literals in as parameters to a function, and use the block of an if statement to attach some binding into the rest of the software. The hard part here will be using these expressions to run the help menu. There's a couple ways to do it, but it has to run a little "sideways" through the system. - -**Table Driven CLI** +You can then use these symbols essentially as if you had written the non C-Scripted version shown below: ``` -void cli_p_fast(CLICtx *ctx, void *arg){ /*...*/ } -void cli_p_state_time(CLICtx *ctx, void *arg){ /*...*/ } - -CLIFlag flags[] = { - { "fast", "F", ArgType_None, "go as fast as possible", cli_p_fast }, - { "start_time", "S", ArgType_TimeStamp, - "set the time where the output stream begins (>= 0:00) (default 0:00)", cli_p_state_time }, +void mycommand__Foo(void*){ + do_foo(ctx); } -``` - -Here we put the data literals together with some "hooks" (function pointers) and can specify the whole thing as a global data table. Except the "binding into the main computation" is through this function pointer now, and the function body is unhappily far away from the declaration, and since the `ArgType` couples to the function body, you've got a new long-range matching problem to add to your maintenance burden. The nice thing here of course is your help menu is going to be trivial to write, and other comprehension tasks like checking for duplicate abbreviations will be easy too. - -**Metaprogramming** -``` -CLI_FLAG(fast, "F", ArgType_None, "go as fast as possible"){ - // ... +void mycommand__EasyBar(void*){ + if (difficulty(ctx) < 3){ + do_bar(ctx); + } } -CLI_FLAG(start_time, "S", ArgType_TimeStamp, - "set the time where the output stream begins (>= 0:00) (default 0:00)"){ - // ... -} -``` - -This is the sort of thing I did in the 4coder command system. You get the best of both worlds from the previous two systems, but you have to adopt an entire metaprogram into your build. The metaprogram scans the code for `CLI_FLAG` and parses out the details to generate a data table like in the second example. Then you build that data table into your final program. In the final program, the macro expands into a hook function with the name given by the first part of the macro, and the rest discarded. The downside now is that a whole new metaprogram is a heavy thing, and it looks like you'll need to expand that metaprogram for each one of these you use. With some refinement it might be pretty reusable -- but even one metaprogram is a downside. - -**There are many other solutions.** You can bind with an enum and switch instead of hooks. You can bind with pointers to data output slots in the main context, and leave all the computation downstream. You can declare the flags in a table without code binding, then after the parse use extractors that read out the parsed data to effect the main context. The funny thing is, in all my years with C, none of them feel totally satisfying to work with, you're always trading off. - -**This comes up in a lot of cases too.** In a plugin system, you usually want a plugin to register some new operations in one or multiple categories. How do you bind the new operations with their interface information in the plugin and process that in the core? In a game or simulation you may have systems like event handlers, entity behavior hooks, procedural generation nodes, or context specific rules. - -### Introduction: A C Scripting System - -A C Scripting system allows for code written like this: -``` -MY_THING_SCRIPT(foo, FooBarGroup, "Generates a foo and increments the foo counter"){ - out->name = str8_lit("foo"); - ctx->foo += 1; -} -``` - -And in a true C Scripting system, there are two more requirements. One it should work *without* any code generation. And two, there should be some kind of automated "registration" process that builds a data structure that lets me "comprehend" the entire set of these things. In other words there should be no manually written (or generated) list that looks like this: -``` -{ - register_my_thing(mythings, &foo); - register_my_thing(mythings, &bar); -} -``` - -There's no way to achieve this in standard C. However with one non-standard trick we can get there in a fairly satisfying way. And I've tested out `clang` and `cl` for compilers, `clang` and `link` for linkers, and various combinations of optimizations and link time optimizations, and found that this non-standard trick continues to behave as expected across the board. I have yet to test this out on a Linux or Mac machine, or on an a machine with an architecture other than an AMD64, so the research on this technique isn't *finished* but it's looking *very good*. - -**Data Sections** - -The plan is to set aside a new *data section* in the final executable that is populated only by instances of a particular struct. This is the key non-standardized compiler feature that I need. This data section then acts as an array of this struct, with one element in the array for each global you defined this way: - -``` -SECTION(".mything") MyThing foo = { - "foo", MY_THING_GROUP(FooBarGroup), "Generates a foo and increments the foo counter", foo_hook +enum MY_COMMAND_ID{ + MY_COMMAND_ID__NULL = 0, + MY_COMMAND_ID__Foo, + MY_COMMAND_ID__EasyBar, }; -SECTION(".mything") MyThing bar = { - "bar", MY_THING_GROUP(FooBarGroup), "Generates a bar and decrements the foo counter", bar_hook +MyCommand MY_COMMAND_array[] = { + { 0 }, + { str8("Foo"), str8("Do Foo."), mycommand__Foo }, + { str8("EasyBar"), str8("If it's not too hard, do Bar."), mycommand__EasyBar } }; ``` -That's the plan - but that alone isn't enough. This way I would still have to put the hook in by name, and leave it defined somewhere else. But the part that I want to emphasize here is that the data section `.mything` now contains an array of these `MyThing` structs. If we ever want to do work over all the `MyThing`s we just have to locate that data section in our program -- and yes that is something we *can* do! +So you can loop over the array, and statically reference the symbol names to get indices into the array. -The other cool thing here is that these are still *also* global variables! They can be referenced by their name from anywhere, and what you get is like a hardcoded reference to a slot in that special array. +## 3. How does it work? -We can achieve this much with a macro like this: +### Implementing the Gather: Data Sections + +Under the hood, symbol sets are implemented by assigning each one a special data sections. + +Data sections exist in object files and executable files after you compile your code all the time, but usually we don't think about it. Typically there is a `.text` section that contains the executable code, a `.data` section that contains read-write data with non-zero initialized values, a `.rdata` section or `.rodata` for read only data, and a `.bss` section for read-write data that should be zero initialized, plus a handful of others that can contain information for other things like stack unwinding or debug information. + +Why use data sections for this? Because that's how you get the compiler to gather up all of the global variables in the same set into a single array. C doesn't have a feature that works this way on purpose. But any compiler that supports an extension that lets you assign a global variable to a specific data section, already contains the necessary internal logic to achieve this feature, it just couples that logic to the output data sections. And that's why I use data sections for this. + +### Locating Data Sections + +In order for this to work, we have to be able to find the base pointer of the array, and either the count of symbols, or the pointer to the one-past-last slot in the array. + +#### Linux Version: Linker Scripting + +On Linux the solution I have arranged for this is the program `symbol_set.ld_meta`. The way this program works is it scans your object files before you link them, and it generates a linker script. Then when you link your program with all those object files you also pass the linker script, and it tells the linker how to generate symbols that mark the beginning and end of each section, and as a bonus it packs all the mini data sections down into the normal .data section. + +In order for the `symbol_set.ld_meta` program to know which data sections and symbols need to be arranged, I adopt the restriction that sections that implement symbol sets have to start with `.sy.`. This seems to me to be in keeping with the traditional way of using the name of the section to organize how it interacts with the "tool chain" programs. + +Even deeper under the hood, the `symbol_set.ld_meta` program builds a list of all the sections it sees that start with `.sy.` and also all the unresolved symbols it sees that look like markers for symbol set ranges (`syfirst__` and `syopl__` prefixes). The rest is a matter of generating a linker script like this: +`sy.ld` ``` -#if COMPILER_CLANG || COMPILER_GCC -# define SECTION(N) __attribute__((__section__(N))) -#elif COMPILER_CL -# define SECTION(N) __declspec(allocate(N)) +SECTIONS { + .sy : { + syfirst__cmd = .; *(.sy.cmd); syopl__cmd = .; + } +} +INSERT AFTER .data; +``` + +#### Windows Version: Self Imaging + +On Windows the tool chain doesn't have anything as nice as a linker script. Instead we pay a little extra compute price on startup using a technique I call "Self Imaging". + +An "Image" in the context of executable formats and program loading is the "hot" version of the executable file. It's the version of the data that exists in the program's memory space, and on Windows our program's "Image" contains the section headers, and so we can find them at runtime. + +It is the entire contents of `symbol_set.c`, and you don't even have to call it! You just include it, and a "Before Main" function calls it for you. The rest of the details are just parsing the PE/COFF headers. + +### Resolving IDs: Pointer Arithmetic + +What we *want* is if we were to reference a symbol like `MY_COMMAND.Foo` that it would resolve to it's final id in the array. Unfortunately, we would need the linker to know about this and it doesn't. The linker can only patch up references to *addresses* because linkers don't have relocations for patching up indexes unfortunately. + +But that ability to patch up addresses gets us close to what we need. Recall, from C's perspective these "symbols" are just globals or static variables. So for example `MY_COMMAND_Foo` might be how you name the variable that stands for the `Foo` symbol of the `MY_COMMAND` symbol set. And therefore `&MY_COMMAND_Foo` is the address-of that variable. But that means, with the base pointer to the symbol set, we can do pointer arithmetic to get our IDs. Think `(&MY_COMMAND_Foo - MY_COMMAND__base)`. + +Unfortunately this means that static references to these IDs are not the low level constants we would want, they resolve to a subtract between a constant and a global variable. + +## 4. Known pitfalls and unexplored treachery. + +There are a few common ways things can go wrong. The most distressing of which is that you can do everything right and still get a section that isn't the right size to reflect the number of symbols you actually created. There are a couple of ways this can happen that I have smoothed over so far. + +First, sometimes small types or types with small alignments don't get packed the way you'd expect. After all, it's not really a C language array, the standard doesn't say they have to be laid out in any particular way, but I have found I can ensure proper packing by using `SY__ALIGN_AS_LIT` on the declaration, another wrapper for non-standard C. Then I can just round the type size up to a multiple 8 to set the underlying stride for the array. The final interface hides this from you, but it's worth noting the loss of packing efficiency for small type sizes. + +Second, sometimes a bunch of null data will get appended to the end. This is "Incremental Linking". When you are building on Windows you have to pass `-INCREMENTAL:NO` to your linker to prevent this, or if you are using `clang` as a linker you pass `-Xlinker -INCREMENTAL:NO`. + +Symbols are not necessarily packed in the order you think, or at least I wouldn't recommend depending on it if it does. Again we're dealing with non-standard behavior of C, the compiler is free to arrange global variables however it wants, and symbol sets remain perfectly useful without having to depend on order. + +Symbol IDs are not serialization-ready on their own. There is no way in this scheme to ensure a symbol gets the same ID between builds. I have a number of ideas on how this can be addressed, but I haven't begun development on any of them yet, so if you start using symbol sets you're currently on your own for the serialization problem. + +When you reference the ID of a symbol, it is not considered a constant expression, because it's not until after linking that the pointer arithmetic could be resolved. But since the C compiler can't see this, it rejects uses of symbol ID references that occur in statically initialized data. This is too bad, because when symbols can reference other symbols, there are so many more powerful ways to use the system. I have a work around for this limitation in the `Raw` handle space for referencing symbols. See `SY_INIT` in section 5 for more on this. + +On Windows, your section names cannot be more than 8 characters long and I've +already said you have to use 4 ".sy.". So you have to carefully allocate your 4 +character section names when you make a new symbol set. + +On Windows, your sections are not compacted together so each takes up a minimum of 4K and is allocated in 4k pages separately. I have a number of candidates for ways to eliminate this either from all builds or at least from release builds, but I haven't developed any of those ideas yet. + +The usefulness of having symbols in small consecutive IDs is great, but it is constrained to singular compiled binaries (executables,.exe,.so,.dll). For instance, in a plugin system, each plugin would have it's own ID space, and therefore to reference a specific symbol you'd need to start using two-part IDs everywhere. So far I haven't developed anything to explore how this would work out. + +## 5. Introduction to the Symbol Set programming primitives. + +In the file `symbol_set.h` there a couple important sections. The first contains some copy pastable boilerplate. The first contains the boilerplate for setting up a symbol set. + +These first 5 line block define the symbol set itself. For instance if `MY_COMMAND` is the name of my symbol set, and `MyCommand` is the type, the code that instantiates the symbol set could be: +```C +#define SYMBOL_SET_DEFINE MY_COMMAND +#define MY_COMMAND_Type MyCommand +#define MY_COMMAND_section ".sy.mcmd" +#define MY_COMMAND_marker mcmd +#include "symbol_set.define.h" +``` + +The next few blocks define various patterns of helper macros for defining symbols. + +The first block is how I setup macros for defining data-only symbol types. +```C +#define ZZZ_DEF(N,...) SyDefine(ZZZ, N) = { ... } +``` +For instance you could use symbol sets to sketch out basic sprite metadata by hand. You could setup the definition helper macro like: +```C +#define SPRITE_DEFINE(N,file,...) \ + SyDefine(SPRITES, N) = { str8(file), __VA_ARGS__ } +``` +And then you'd use it like: +```C +// the +SPRITE_DEFINE(Hero, "Hero.png", .x = 8, .y = 16); +``` + +The next block is how I setup macros for defining symbols with an attached function pointer: +```C +#define ZZZ_DEF(N,...) \ +static void funczz##N(void*); \ +SyDefine(ZZZ, N) = { funczz##N, ... }; \ +static void funczz##N(void *ptr) +``` +This is the version that all the "commands" example are using: +```C +#define MY_COMMAND_DEFINE(N,desc) \ + static void my_command__##N(Ctx*); \ + SyDefine(ZZZ, N) = { str8(#N), str8(desc), my_command__##N }; \ + static void my_command__##N(Ctx*) +``` +The macro first declares the underlying C function, fills out the global variable initialization and inclues the C function, and then it sets up the signature of the function again, so that you can follow the macro with `{ ... }` the function block. +```C +MY_COMMAND_DEFINE(Foo, "Do Foo.){ + do_foo(ctx); +} +``` + +The next block shows how you can also setup symbols that don't have names. +```C +#define ZZZ_DEF(...) SyDefineUnnamed(ZZZ) = { ... } +``` +This is useful for cases where you're not really interested in the id space of the symbol set, only the collection of values you want to put together declaratively. This can be useful for creating optional attachments to named symbols, or structures that create relationships between named symbols. For instance, you could say one symbol set defines the set of "tags" you can put on another type of symbol, think `MY_COMMAND_TAG` for `MY_COMMAND`. Then you could use unnamed symbols to set these tags: +```C +#define MY_COMMAND_ADD_TAG(CMD,TAG) \ + SyDefineUnnamed(MY_COMMAND_TAG_LINKS) = { \ + SyRaw(MY_COMMAND, CMD), SyRaw(MY_COMMAND_TAG, TAG) } +``` +And then you would use it like: +```C +MY_COMMAND_ADD_TAG(Foo,FooBar); +MY_COMMAND_ADD_TAG(EasyBar,FooBar); +MY_COMMAND_ADD_TAG(EasyBar,Easy); +``` +you can then rearrange this data into a matrix or tags lists, or whatever you need during a simple startup processing phase. + +I don't really know any examples of unnamed symbols that don't rely on referencing a symbol id from another symbol, so I now need to explain how we can make this work. + +### Inter-Symbol References + +First, we can't just store IDs into a symbol the way we'd want, because the data we store into symbols has to be statically resolved, but the IDs aren't resolvable until after linking. What we can do is use a variable address as a constant expression. Even though that the final address of a global variable isn't resolved until link time, the language can count on the linker to fill in the gap with relocations. But relocations werent' built for fixing up IDs. + +Instead what we do is we store the `Raw` for the symbol. The `Raw` is just the address of the global variable typed as a `SY__UAddr` an address widthed unsigned integer. Then we use `SyIDFromRaw` at init time to translate all of the `Raw`s into `ID`s in place (for this reason I put symbols in read-write memory even when I am treating them as strictly read-only after the ID fixup). + +I have developed a helper in `symbol_set.init.h` that I use for managing this. You can include this header after `symbol_set.h`. It sets up a symbol set called `SY_INIT` and each element of this set is a function pointer that describes how to fixup the symbol set. + +Using this helper defining a symbol that references another symbol looks like this: + +```C +typedef struct MyThing{ + SY__UAddr other_thing_id; +} MyThing; + +// + +#define MY_THING_DEF(N,O) \ + SyDefine(MY_THING, N) = { SyRaw(OTHER_THING, O) } + +#if SY__MAIN +SY_INIT_DEF(MY_THING){ + for (SyEach(MY_THING, thing)){ + thing->other_thing_id = SyIDFromRaw(OTHER_THING, thing->other_thing_id); + } +} #endif ``` -In the CL compiler you also need to insist that the data section exists by dropping this somewhere: -``` -#pragma section(
,read,write) +In summary, I wrap the reference to a symbol in the helper macro to make all the symbol definitions easy on the eyes, and to ensure there that `SyRaw` is used and not `SyID`, then if this is the `SY__MAIN` unit, I define a special `SY_INIT` symbol that describes how to loop over the symbol and fixup `SyRaw`s into `SyID`s. I have to manually make sure the symbol set that I use in `SyRaw` matches the one I use in `SyIDFromRaw` or I will just get 0 ids. + +Then I just have to call `sy__run_init();` early in `main`. I don't use a run-before-main for this, because I already use run-before-mains to automate the location of the array on Windows, and you can't do fixup before you locate symbols. `SY_INIT` is like run-before-main except you decide when to run all the `SY_INIT` functions explicitly, and therefore it can be timed strictly after the initial wiring up. + +This way of organizing the fixups feels makes the maintenance of lots of interconnected symbol sets a lot more manageable than it would be otherwise. I just consider the the ID fixup boilerplate a necessary component of defining the symbol set itself, and whenever I modify the symbol set types I try to be sure to recheck the ID fixup at the same time. + +### Symbol Set Referencing Primitives + +After defining a `SYMBOL_SET` here are the primitives you can use that reference it. + +`SyType(SYMBOL_SET)` - Expands to the underlying type of the symbol set. + +`SyDeclare(SYMBOL_SET,N)` - Forward declares the symbol N. Like any other C forward declare you may do this as many times as you want for the same name, so long as you only define it once. This is useful when you want to reference a symbol before you've defined it. + +`SyDefine(SYMBOL_SET,N)` - Sets up the definition of the symbol N. The macro sets up the variable for the symbol, with the type from the `SYMBOL_SET`, and leaves the value unset, you finish the definition by writing ` = { ... };` after the macro. + +`SyDefineUnnamed(SYMBOL_SET)` - Works like `SyDefine` except you don't specify a name. + +`SyAddress(SYMBOL_SET,N)` - Gives the address of the symbol's slot in the set's array, typed as a pointer to the set's underlying type. Static resolution. + +`SyID(SYMBOL_SET,N)` - Gives the 1-based ID of symbol. Literally runtime resolution, theoretically more like link-time resolution. + +`SyRaw(SYMBOL_SET,N)` - Gives the address of the symbol typed as `SY__UAddr`. + +`SyFirst(SYMBOL_SET)` `SyOpl(SYMBOL_SET)` - Gives the address of the first and one-past-last slot of the symbol set's array. + +`SyStride(SYMBOL_SET)` - The stride between elements in the symbol set's array. + +`SyCount(SYMBOL_SET)` - The number of symbols in the symbol set. + +`SyNext(SYMBOL_SET,addr)` - Increments a pointer in the symbol set's array to the next element in the array (basically pointer arithmetic by the `SyStride`). + +`SyEach(SYMBOL_SET,var)` - Expands to the control phrases of a for loop: `for (SyEach(SYMBOL_SET,var)){ ... }`. `var` is a pointer to the set's underlying type, that iterates the whole array. + +`SyEachID(SYMBOL_SET,id)` - Expands to the control phrases of a for loop: `for (SyEachID(SYMBOL_SET,id)){ ... }`. `id` is a `U32` 1-based index that iterates the ids of the symbol set. + +`SyIDFromAddress(SYMBOL_SET,addr)` - Convert a pointer to a symbol to it's ID. + +`SyAddressFromID(SYMBOL_SET,addr)` - Convert an ID to a symbol pointer. + +`SyAddressCheck(SYMBOL_SET,addr)` - Evaluates true if the address is inside the range of the symbol set's array. + +`SyIDCheck(SYMBOL_SET,id)` - Evaluates true if the ID is one of the symbol IDs in the symbol set. + +`SyIDFromAddress_Unchecked` and `SyAddressFromID_Unchecked` - Like `SyIDFromAddress` and `SyAddressFromID`, but skips run-time checking the parameter is in a valid range, only use if you know you've got a valid address/ID. + +`SyIDFromRaw` and `SyIDFromRaw_Unchecked` same as `SyIDFromAddress`/`_Unchecked` except excepts `SY__UAddr` type instead of expecting pointer types. + +`SyFlag(SYMBOL_SET,N)` - Just think `(1 << SyID(SYMBOL_SET,N))` + +## 6. Awesome things you can do with C-Scripting. + +### Organized Command Systems + +Suppose you're building some kind of editor application where you have to organize a lot of editing commands into key bindings, and menus. Further imagine it could look like this: +```C +EDITOR_COMMAND(InsertTodo, "Insert a TODO comment."){ + U32 view_id = ed_current_view(ctx); + U32 buffer_id = ed_buffer_from_view(ctx, view_id); + U32 cursor_pos = ed_cursor_pos_from_view(ctx, view_id); + ed_insert(ctx, buffer_id, cursor_pos, str8("// TODO: ")); +} +EDITOR_COMMAND_FLAGS(InsertTodo, ED_CmdFlag_Write); +BIND_KEY(DefaultMap, ED_Key_T, ED_Mdfr_Alt, InsertTodo); ``` -**Accessing the Data** +With a setup like this you could automatically assemble a statically defined keymap. And you could loop over all the commands you've written in your program, check their flags, string search and display their descriptions, and of course execute the command function if you have the parameters it needs. -That's how we'll declare the data, bind it to the code, and organize it into a comprehendible data structure. To access the data structure we'll need to be able to parse the data structure that describes the module's binary image and find the data section we're looking for. There are two steps to this: +### Organized Command Line Parameters -1. Locate the module's binary image - this involves a conversation with the operating system. -2. Parse the image to find the data section - this involves knowledge of the binary file format. - -Despite the fact that this sounds like asking you to dabble in some dark arts -- each of these is relatively easily achieved and abstracted. The final API can be as simple as: `RangePtr selfimg_get_section(String8 name);` - -Personally I've pushed my own system to the point of automating even this part by sticking it in a `BEFORE_MAIN` when I setup the data section, so that my interface just looks like a typed global pointer and count that is initialized to point at the array I need. This is an additional trick - and isn't *necessary*. But I will discuss how and why I did it this way in discussions of the design tradeoffs and future plans. - -Often, but not always, there's another step of processing that I want to run on the data in the array. Sometimes they are incomplete and the hook is actually a constructor that finishes the job. Sometimes they act as extensions to another array or at least relate to one another in ways that I want to index or construct in a higher data structure. - -``` -{ - RangePtr range = selfimg_get_section(str8_lit(".mything")); - MyThing *my_thing = (MyThing*)range.first; - U64 my_thing_count = ((MyThing*)range.first - (MyThing*)range.opl); +Similarly you could use C-scripting to arrange your command line parsing. Imagine: +```C +CMDLN_FLAG("start", "Specify the start time index for the scan. (default 0)", + .duplicate = CMDLN_Behavior_Warning){ + out->start = cmdln_read_u32(ctx); +} +CMDLN_FLAG("end", "Specify the end time index for the scan. (default end-of-timeline)", + .duplicate = CMDLN_Behavior_Warning){ + out->end = cmdln_read_u32(ctx); +} +CMDLN_FLAG("save", "Specify one or more intermediates to save.", + .duplicate = CMDLN_Behavior_Concatenate){ + U32 count = rpg_read_count(ctx); + for (U32 i = 0; i < count; i += 1){ + String8 intermediate_name = cmdln_read_idx_str8(ctx, i); + U32 intermediate = timeline_intermediate_from_name(intermediate_name); + if (intermediate != 0){ + timeline_add_save(out, intermediate); + } + } } ``` -**Wrapper Macro With Fused Hook Syntax** +### State Machine -Next what we do is wrap the data section name and type together into a single macro that does the declaration. With preprocessor stringification and pasting, we can make your syntax more terse now: +You can set up a state machine programming system in a symbol set that looks like this: -``` -#define MY_THING_DEF(name,group,desc,hook) \ - SECTION(".mything") MyThing name = { #name, MY_THING_GROUP(group), #desc, hook } +```C +typedef struct Ctx{ + U32 state_id; +} Ctx; -MY_THING_DEF(foo, FooBarGroup, "Generates a foo and increments the foo counter", foo_hook); -MY_THING_DEF(bar, FooBarGroup, "Generates a bar and decrements the foo counter", bar_hook); -``` +/////////////////////////// -Then I want to *fuse* the hook to the macro. To do this the last thing the macro expands has to be the function signature *without* a semi-colon or brace, and I need to be sure the hook is declared *before* the global variable in the special data section so that it can reference the function. +STATE(GameSetup){ + game_setup(ctx); + state_next(ctx, SyID(STATE, GameScene1)); +} -``` -#define MY_THING_DEF(name,group,desc,hook) \ - void hook(MyThingCtx *ctx); \ - SECTION(".mything") MyThing name = { #name, MY_THING_GROUP(group), #desc, hook }; \ - void hook(MyThingCtx *ctx) - -MY_THING_DEF(foo, FooBarGroup, "Generates a foo and increments the foo counter", foo_hook){ - // ... -} - -MY_THING_DEF(bar, FooBarGroup, "Generates a bar and decrements the foo counter", bar_hook){ +STATE(GameScene1){ + scene_character(ctx, 1, Left, SyID(CHARACTER, Hero)); + scene_character(ctx, 2, Right, SyID(CHARACTER, DreamShadow)); + scene_sound_effect(ctx, SyID(SOUND_EFFECT, Eerie)); + scene_line(ctx, 1, "Huh? Is there someone there?"); // ... + scene_line(ctx, 2, "Will you follow me into the deep?"); + scene_offer_decision(ctx, 1, "Yes", 2, "No"); + U32 decision = scene_player_decision(ctx); + switch (decision){ + case 1: { + state_next(ctx, SyID(STATE, GameScene2Deep)); + }break; + case 2: { + state_next(ctx, SyID(STATE, GameScene2Awake)); + }break; + } } ``` -And I like to take advantage of preprocessor pasting to free myself from the chore of naming the hook: +You can grab the symbol name in a macro with `#N` so we can save the symbol names as string data on symbols any time we'd like to, which means we can easily create an interface for running the state machine one step at a time and displaying the state, or logging each state as the machine runs, but a flat state machine is pretty limiting, so I recommend some upgrades. -``` -#define MY_THING_DEF(name,group,desc) \ - void my_thing_hook__##name(MyThingCtx *ctx); \ - SECTION(".mything") MyThing name = { #name, MY_THING_GROUP(group), #desc, my_thing_hook__##name }; \ - void my_thing_hook__##name(MyThingCtx *ctx) - -MY_THING_DEF(foo, FooBarGroup, "Generates a foo and increments the foo counter"){ - // ... +### Stack Machine + +You can extend the state machine with a stack. When you do a state transition, you can also push a new state onto the stack. Alternatively you can pop the stack. + +```C +typedef struct Ctx{ + U32 stack[100]; + U32 stack_count; +} Ctx; + +/////////////////////////// + +STATE(GameFight){ + if (fight_exit_condition(ctx)){ + state_pop(ctx); + } + else{ + fight_step(ctx); + state_stationary(ctx); + } } -MY_THING_DEF(bar, FooBarGroup, "Generates a bar and decrements the foo counter"){ - // ... +STATE(GameSceneTutorialBoss){ + setup_tutorial_boss(ctx); + state_next_push(ctx, SyID(STATE, GameSceneTutorialFinished), + SyID(STATE, GameFight); } ``` -**Reusable C Scripting System** +When you push a state, you also set a next state so that when you pop you don't necessarily have to go back to the state that did the push. This way state machines can be called like a subroutine. -The last challenge is to take this idea and make it possible to create and maintain *many* of these special arrays as needed. It turns out most of the setup and wrangling work can be offloaded to a generic set of macros that work the same way as this special case above works. I call one of these "special arrays" a "Symbol Set" and thus I call the headers that introduce this system my "Symbol Set" system. If you can see this, you are currently a member of mr4th.com, and so you should be able to see my the implementation as I have it in my codebase at: +Now when you're logging what happens you could just show the top of the stack, which gives the same effect as stepping line by line in a debugger, but at the granularity of states. You can also "backtrace" this stack and see everything that has led up to the current state, because again it's so easy to connect readable names to these states and automatically print them out. -https://git.mr4th.com/mr4th-members/mr4th/src/branch/main/src/mr4th_symbol_set.h -https://git.mr4th.com/mr4th-members/mr4th/src/branch/main/src/mr4th_symbol_set.define.h +So this above code says taht `GamesTutorialBoss` is a state that does some setup, and then it will appear to transition to `GameFight` but it does this by pushing `GameFight`, and at the same time, it sets `GameSceneTutorialFinished` as the next. This way `GameFight` can just pop and is generic and reusable for all sorts of contexts in the state machine, and doesn't need to hardwire itself for this particular relationship to `GameSceneTutorialFinished`. -There are so many subtle design choices to make in this system, and it so deeply integrates with base layer concerns, that I suggest learning how to build your own. That's what the rest of this repository is for! +### Stack Frames -*-- Allen Webster. April 17th, 2025* +You can extend the state machine stack with typed frames that group states together and grant them access to shared variables. When you set the next state, the new state be a member of the same frame as the current state. But when you push a state, you may push any state, and you initialize the new frame that you push. + +```C +typedef struct StackNode{ + struct StackNode *next; + void *data; + U32 state_id; + U32 pop_to; +} StackNode; + +typedef struct Ctx{ + Arena *stack_arena; + StackNode *stack; + U32 stack_count; +} Ctx; + +/////////////////////////// + +STACK_FRAME_STRUCT(Fight){ + CharVars entities[100]; + U32 entity_count; + U32 player_id; + F32 quick_time_cooldown; + U32 quick_time_entity; +}; + +STATE(FightStep, Fight){ + B32 done = 0; + U32 quick_times[100]; + U32 quick_time_entities[100]; + U32 quick_time_count = 0; + // ... gather above variables from frame->entities ... + + if (done){ + state_pop(ctx); + } + else if (quick_time_count > 0 && frame->quick_time_cooldown == 0){ + U32 idx = rng_n(&ctx->rng, quick_time_count); + frame->quick_time_cooldown = 5.f; + frame->quick_time_entity = quick_time_entities[idx]; + state_next(ctx, quick_times[idx]); + } + else{ + frame->quick_time_cooldown = ClampBot(0.f, frame->quick_time_cooldown - ctx->dt); + main_update(ctx, frame); + state_stationary(ctx); + } +} + +STATE(QuickTimeZombieAttack, Fight){ + qtime_before(ctx, 3.f); + qtime_press(ctx, Button_Left); + qtime_press(ctx, Button_Right); + qtime_press(ctx, Button_Left); + if (qtime_success(ctx)){ + stun_chr(ctx, frame, frame->quick_time_entity); + state_next(ctx, SyID(STATE, FightStep)); + } + else if (qtime_fail(ctx)){ + hit_chr(ctx, frame, frame->player_id, 10); + state_next(ctx, SyID(STATE, FightStep)); + } +} +``` + +Because you end up writing your own very simple main loop to run this state machine, and you implement the `state_*` transition functions, you can do things like track how long a state has run for, and make that information available to any of the state scripts `state_timer(ctx)` + +If you want you could then go further and attach other interesting things to the frames in this system, like self logging: +```C +STACK_FRAME_LOG(Fight){ + printf("entity_count = %u\n", frame->entity_count); + printf("player = [%u] %E\n", frame->player_id, frame->entities + frame->player_id); + printf("quick_time_cooldown = %f\n", frame->quick_time_cooldown); +} +``` + +### Organized Help Information + +Suppose you've got a lot of different kinds of symbols, perhaps you've got edit commands, signal filtering functions, file formats, etc. all modeled as symbol sets. Then say you decide that you'd like to start attaching more detailed help information to the symbols whose functioning is especially confusing. You can consider the following trick: + +```C +#define RuleClassXList(X) \ + X(COMMAND) \ + X(SIGNAL_FILER) \ + X(FILE_FORMAT) + +typedef enum RuleClass{ + RuleClass_NULL = 0, +#define X(RC) RuleClass_##RC, + RuleClassXList(X) +#undef X + RuleClass_COUNT +} RuleClass; + +typedef struct Rule{ + RuleClass rc; + UAddr id; + String8 text; +} Rule; + +// + +#define RULE(RC,N,text) SyDefine(RC,N) = { .rc = RuleClass_##RC, .id = SyRaw(RC,N), .text = str8(text) } + +#if SY__MAIN +SY_INIT(RULE){ + for (SyEach(RULE,rule)){ + switch (rule->rc){ +#define X(RC) case RuleClass_##RC: rule->id = SyIDFromRaw(RC, rule->id); break; + RuleClassXList(X) +#undef X + } + } +} +#endif +``` + +With this setup, you can start attaching "rules text" to any named symbol so long as you add that symbol's symbol set to the list of rule classes. + +## 7. The end. + +This is only the beginning. + +There's a final interpretation of the name C-Scripting that I chose for this project. It's also a reference to "C With Classes" the predecessor to C++. In a similar way C-Scripting is an exploratory project shaping something new out of the raw materials of C. But where I want to go next with this is not a programming language. diff --git a/build.bat b/build.bat deleted file mode 100644 index 3fe5f61..0000000 --- a/build.bat +++ /dev/null @@ -1,7 +0,0 @@ -@echo off - -set opts=-FC -GR- -EHa- -nologo -Zi -std:c11 -wd5105 -set code=%cd% -pushd build -cl %opts% %code%\src\example1.c -Feexample1 -link -INCREMENTAL:NO -popd diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..6b67cd2 --- /dev/null +++ b/build.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +code=$PWD +opts=-g +inc="-I$code/symbol_set" + +mkdir -p build +cd build + +clang -c $code/examples/example1.c $opts $inc +./symbol_set.ld_meta example1.o +clang -o example1 $code/examples/example1.c $opts $inc -T sy.ld diff --git a/build_ld_meta.sh b/build_ld_meta.sh new file mode 100644 index 0000000..a9b1ae0 --- /dev/null +++ b/build_ld_meta.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +code=$PWD +opts=-Wno-switch +inc="-I$code/ld_meta/mr4th" + +mkdir -p build +cd build + +clang -o symbol_set.ld_meta "$code/ld_meta/symbol_set.ld_meta.c" $opts $inc diff --git a/build_with_cl.bat b/build_with_cl.bat new file mode 100644 index 0000000..757d655 --- /dev/null +++ b/build_with_cl.bat @@ -0,0 +1,8 @@ +@echo off + +set code=%cd% +set opts=-FC -GR- -EHa- -nologo -Zi -std:c11 -wd5105 +set inc=-I%code%\symbol_set +pushd build +cl -Feexample1 %code%\examples\example1.c %opts% %inc% -link -INCREMENTAL:NO +popd diff --git a/build_with_clang.bat b/build_with_clang.bat new file mode 100644 index 0000000..a841fea --- /dev/null +++ b/build_with_clang.bat @@ -0,0 +1,8 @@ +@echo off + +set code=%cd% +set opts=-g +set inc=-I%code%\symbol_set +pushd build +clang -o example1 %code%\examples\example1.c %opts% %inc% -Xlinker -INCREMENTAL:NO +popd diff --git a/src/example1.c b/examples/example1.c similarity index 63% rename from src/example1.c rename to examples/example1.c index 108ee29..bc363aa 100644 --- a/src/example1.c +++ b/examples/example1.c @@ -2,12 +2,11 @@ ** C Scripting Example #1 */ -#include "base.h" +#define SY__MAIN 1 #include "symbol_set.h" #include "example1.h" - -#include "base.c" +#include COMMAND_SCRIPT(foo, "Generates foo and increments the foo counter"){ printf("foo, "); @@ -37,22 +36,17 @@ COMMAND_SCRIPT(baz, int main(void){ // init sequence U32 command_ids[] = { - COMMAND_ID(foo), COMMAND_ID(foo), COMMAND_ID(baz), - COMMAND_ID(bar), COMMAND_ID(baz), COMMAND_ID(bar), - COMMAND_ID(foo), COMMAND_ID(bar), COMMAND_ID(baz), + SyID(EX1_COMMAND, foo), SyID(EX1_COMMAND, foo), SyID(EX1_COMMAND, baz), + SyID(EX1_COMMAND, bar), SyID(EX1_COMMAND, baz), SyID(EX1_COMMAND, bar), + SyID(EX1_COMMAND, foo), SyID(EX1_COMMAND, bar), SyID(EX1_COMMAND, baz), }; // display the legend of rules printf("RULES:\n"); - { - EX1_Command *commands = SymbolBasePtr(EX1_CommandSymbols); - U64 count = SymbolCount(EX1_CommandSymbols); - for (U64 i = 0; i < count; i += 1){ - EX1_Command *command = commands + i; - printf("%.*s -> %.*s\n", - str8_expand(command->name), - str8_expand(command->description)); - } + for (SyEach(EX1_COMMAND, command)){ + printf("%.*s -> %.*s\n", + (int)(command->name.size), command->name.str, + (int)(command->description.size), command->description.str); } printf("\n"); @@ -60,8 +54,9 @@ int main(void){ printf("SEQUENCE:\n"); for (U32 i = 0; i < sizeof(command_ids)/sizeof(command_ids[0]); i += 1){ U32 id = command_ids[i]; - EX1_Command *command = COMMAND_METADATA_FROM_ID(id); - printf("%.*s, ", str8_expand(command->name)); + EX1_Command *command = SyAddressFromID(EX1_COMMAND, id); + printf("%.*s, ", + (int)(command->name.size), command->name.str); } printf("\n\n"); @@ -71,7 +66,7 @@ int main(void){ EX1_Ctx ctx = {0}; for (U32 i = 0; i < sizeof(command_ids)/sizeof(command_ids[0]); i += 1){ U32 id = command_ids[i]; - EX1_Command *command = COMMAND_METADATA_FROM_ID(id); + EX1_Command *command = SyAddressFromID(EX1_COMMAND, id); command->hook(&ctx); } } diff --git a/examples/example1.h b/examples/example1.h new file mode 100644 index 0000000..1e29746 --- /dev/null +++ b/examples/example1.h @@ -0,0 +1,54 @@ +#ifndef EXAMPLE1_H +#define EXAMPLE1_H + +//////////////////////////////// +// Example 1 Header + +#include +typedef int8_t S8; +typedef int16_t S16; +typedef int32_t S32; +typedef int64_t S64; +typedef uint8_t U8; +typedef uint16_t U16; +typedef uint32_t U32; +typedef uint64_t U64; +typedef S8 B8; +typedef S16 B16; +typedef S32 B32; +typedef S64 B64; +typedef float F32; +typedef double F64; + +typedef struct String8{ + U8 *str; + U64 size; +} String8; + +typedef struct EX1_Ctx{ + S64 foo; +} EX1_Ctx; + +typedef void EX1_Hook(EX1_Ctx *ctx); + +typedef struct EX1_Command{ + String8 name; + String8 description; + EX1_Hook *hook; +} EX1_Command; + +#define SYMBOL_SET_DEFINE EX1_COMMAND +#define EX1_COMMAND_Type EX1_Command +#define EX1_COMMAND_section ".sy.cmd" +#define EX1_COMMAND_marker cmd +#include "symbol_set.define.h" + +#define COMMAND_SCRIPT(N,desc) \ +void ex1_command__##N(EX1_Ctx *ctx); \ +SyDefine(EX1_COMMAND, N) = { \ +.name = {(U8*)#N, sizeof(#N) - 1}, \ +.description = {(U8*)desc, sizeof(desc) - 1}, \ +.hook = ex1_command__##N }; \ +void ex1_command__##N(EX1_Ctx *ctx) + +#endif //EXAMPLE1_H diff --git a/project.4coder b/project.4coder index 2859796..d35c584 100644 --- a/project.4coder +++ b/project.4coder @@ -1,38 +1,33 @@ version(2); project_name = "example1"; patterns = { -"*.c", -"*.cpp", -"*.h", -"*.m", -"*.bat", -"*.sh", -"*.4coder", -"*.txt", -}; -blacklist_patterns = { -".*", -}; -load_paths_base = { - { ".", .relative = true, .recursive = true, }, + "*.c", "*.cpp", "*.h", "*.m", "*.bat", "*.sh", "*.4coder", "*.txt" }; +blacklist_patterns = { ".*" }; +load_paths_base = { { ".", .relative = true, .recursive = true } }; load_paths = { - .win = load_paths_base, - .linux = load_paths_base, - .mac = load_paths_base, + .win = load_paths_base, .linux = load_paths_base, .mac = load_paths_base }; commands = { - .build = { .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, - .win = "build.bat", - .linux = "./build.sh", - .mac = "./build.sh", }, + .build_with_clang = { .out = "*compilation*", + .footer_panel = true, .save_dirty_files = true, + .win = "build_with_clang.bat", .linux = "./build.sh", .mac = "./build.sh" + }, + .build_with_cl = { .out = "*compilation*", + .footer_panel = true, .save_dirty_files = true, + .win = "build_with_cl.bat" + }, + .build_ld_meta = { .out = "*compilation*", + .footer_panel = true, .save_dirty_files = true, + .linux = "./build_ld_meta.sh", .mac = "./build_ld_meta.sh" + }, .run = { .out = "*run*", .footer_panel = false, .save_dirty_files = false, - .win = "build\\example1", - .linux = "build/example1", - .mac = "build/example1", }, + .win = "build\\example1", .linux = "build/example1", .mac = "build/example1" + }, }; + fkey_command = { -.F1 = "build", -.F2 = "run", + .F1 = "build_with_cl", + .F2 = "run", }; diff --git a/src/base.c b/src/base.c deleted file mode 100644 index d94a0ca..0000000 --- a/src/base.c +++ /dev/null @@ -1,62 +0,0 @@ -//////////////////////////////// -// Functions - - -#if OS_WINDOWS -# include -# include -# include -#endif - -static String8 -str8(U8 *str, U64 size){ - String8 result = {str, size}; - return(result); -} - -static B32 -str8_match(String8 a, String8 b, U32 flags){ - B32 result = 0; - if (a.size == b.size){ - U64 size = a.size; - result = 1; - for (U64 i = 0; i < size; i += 1){ - U8 ac = a.str[i]; - U8 bc = b.str[i]; - if (ac != bc){ - result = 0; - break; - } - } - } - return(result); -} - -#if OS_WINDOWS - -// see: https://devblogs.microsoft.com/oldnewthing/20041025-00/?p=37483 -static void* -os_this_image(void){ - extern U8 __ImageBase[]; - void *result = __ImageBase; - return(result); -} - -#else -# error os_this_image not implemented for this OS -#endif - -#if OS_WINDOWS - -# include "binary/pe/mr4th_pe.h" -# include "binary/pe/mr4th_pe.img_get_section.c" - -static RangePtr -self_img_get_section(void *image, String8 name){ - RangePtr result = pe_img_get_section(image, name); - return(result); -} - -#else -# error selfimg_get_section not implemented for this OS -#endif diff --git a/src/base.h b/src/base.h deleted file mode 100644 index f39a691..0000000 --- a/src/base.h +++ /dev/null @@ -1,252 +0,0 @@ -//////////////////////////////// -// Context Cracking - -// untangle compiler, os, & architecture -#if defined(__clang__) -# define COMPILER_CLANG 1 - -# if defined(_WIN32) -# define OS_WINDOWS 1 -# elif defined(__gnu_linux__) -# define OS_LINUX 1 -# elif defined(__APPLE__) && defined(__MACH__) -# define OS_MAC 1 -# else -# error missing OS detection -# endif - -# if defined(__amd64__) -# define ARCH_X64 1 -// TODO(allen): verify this works on clang -# elif defined(__i386__) -# define ARCH_X86 1 -// TODO(allen): verify this works on clang -# elif defined(__arm__) -# define ARCH_ARM 1 -// TODO(allen): verify this works on clang -# elif defined(__aarch64__) -# define ARCH_ARM64 1 -# else -# error missing ARCH detection -# endif - -#elif defined(_MSC_VER) -# define COMPILER_CL 1 - -# if defined(_WIN32) -# define OS_WINDOWS 1 -# else -# error missing OS detection -# endif - -# if defined(_M_AMD64) -# define ARCH_X64 1 -# elif defined(_M_I86) -# define ARCH_X86 1 -# elif defined(_M_ARM) -# define ARCH_ARM 1 -// TODO(allen): ARM64? -# else -# error missing ARCH detection -# endif - -#elif defined(__GNUC__) -# define COMPILER_GCC 1 - -# if defined(_WIN32) -# define OS_WINDOWS 1 -# elif defined(__gnu_linux__) -# define OS_LINUX 1 -# elif defined(__APPLE__) && defined(__MACH__) -# define OS_MAC 1 -# else -# error missing OS detection -# endif - -# if defined(__amd64__) -# define ARCH_X64 1 -# elif defined(__i386__) -# define ARCH_X86 1 -# elif defined(__arm__) -# define ARCH_ARM 1 -# elif defined(__aarch64__) -# define ARCH_ARM64 1 -# else -# error missing ARCH detection -# endif - -#else -# error no context cracking for this compiler -#endif - -#if !defined(COMPILER_CL) -# define COMPILER_CL 0 -#endif -#if !defined(COMPILER_CLANG) -# define COMPILER_CLANG 0 -#endif -#if !defined(COMPILER_GCC) -# define COMPILER_GCC 0 -#endif -#if !defined(OS_WINDOWS) -# define OS_WINDOWS 0 -#endif -#if !defined(OS_LINUX) -# define OS_LINUX 0 -#endif -#if !defined(OS_MAC) -# define OS_MAC 0 -#endif -#if !defined(ARCH_X64) -# define ARCH_X64 0 -#endif -#if !defined(ARCH_X86) -# define ARCH_X86 0 -#endif -#if !defined(ARCH_ARM) -# define ARCH_ARM 0 -#endif -#if !defined(ARCH_ARM64) -# define ARCH_ARM64 0 -#endif - - -// language -#if defined(__cplusplus) -# define LANG_CXX 1 -#else -# define LANG_C 1 -#endif - -#if !defined(LANG_CXX) -# define LANG_CXX 0 -#endif -#if !defined(LANG_C) -# define LANG_C 0 -#endif - - -//////////////////////////////// -// GLUE(a,b) STRIFY(s) - -#define GLUE__(a,b) a##b -#define GLUE_(a,b) GLUE__(a,b) -#define GLUE(a,b) GLUE_(a,b) - -#define STRIFY__(s) #s -#define STRIFY_(s) STRIFY__(s) -#define STRIFY(s) STRIFY_(s) - - -//////////////////////////////// -// SECTION() - -#if COMPILER_CLANG || COMPILER_GCC -# define SECTION(N) __attribute__((__section__(N))) -#elif COMPILER_CL -# define SECTION(N) __declspec(allocate(N)) -#else -# error SECTION not defined for this compiler -#endif - -// for CL users: #pragma section(
,read,write) - - -//////////////////////////////// -// BEFORE_MAIN(){ <...> } - -#if OS_WINDOWS - -# pragma section(".CRT$XCU", read) - -# if LANG_CXX - -# define BEFORE_MAIN_NAMED(n) \ -static void n(void); \ -__declspec(allocate(".CRT$XCU")) \ -__pragma(comment(linker,"/include:" #n "__")) \ -extern "C" void (*n##__)(void); \ -void (*n##__)(void) = n; \ -static void n(void) - -# else - -# define BEFORE_MAIN_NAMED(n) \ -static void n(void); \ -__declspec(allocate(".CRT$XCU")) \ -__pragma(comment(linker,"/include:" #n "__")) \ -void (*n##__)(void) = n; \ -static void n(void) - -# endif - -#elif OS_LINUX - -# define BEFORE_MAIN_NAMED(n) \ -__attribute__((constructor)) static void n(void) - -#else -# error BEFORE_MAIN missing for this OS -#endif - -#define BEFORE_MAIN_(n) BEFORE_MAIN_NAMED(n) -#define BEFORE_MAIN() BEFORE_MAIN_(GLUE(beforemain,__COUNTER__)) - - -//////////////////////////////// -// DO_NOT_ELIMINATE - -#if OS_WINDOWS -# define DO_NOT_ELIMINATE__S0(N) __pragma(comment(linker,"/include:" #N)) -# define DO_NOT_ELIMINATE__S1(N) DO_NOT_ELIMINATE__S0(N) -# define DO_NOT_ELIMINATE(N) DO_NOT_ELIMINATE__S1(N) -#else - -# define DO_NOT_ELIMINATE(N) - -#endif - - -//////////////////////////////// -// Types - -#include - -typedef int8_t S8; -typedef int16_t S16; -typedef int32_t S32; -typedef int64_t S64; -typedef uint8_t U8; -typedef uint16_t U16; -typedef uint32_t U32; -typedef uint64_t U64; -typedef S8 B8; -typedef S16 B16; -typedef S32 B32; -typedef S64 B64; -typedef float F32; -typedef double F64; - -typedef struct RangePtr{ - U8 *first; - U8 *opl; -} RangePtr; - -typedef struct String8{ - U8 *str; - U64 size; -} String8; - -#define str8_lit(str) str8((U8*)(str), sizeof(str) - 1) -#define str8_lit_const(str) { (U8*)(str), sizeof(str) - 1 } -#define str8_expand(s) ((int)((s).size)), ((s).str) - -//////////////////////////////// -// Functions - -static String8 str8(U8 *str, U64 size); -static B32 str8_match(String8 a, String8 b, U32 flags); - -static void* os_this_image(void); - -static RangePtr self_img_get_section(void *image, String8 name); diff --git a/src/binary/elf/mr4th_elf.c b/src/binary/elf/mr4th_elf.c deleted file mode 100644 index 8a7e24b..0000000 --- a/src/binary/elf/mr4th_elf.c +++ /dev/null @@ -1,518 +0,0 @@ -//////////////////////////////// -// Functions: ELF - -// normalizing - -MR4TH_SYM_COMPTIME U16 -elf_u16_decode(void *ptr, B32 flip){ - U16 result = 0; - if (flip){ - U8 *buf = (U8*)ptr; - result = buf[1] | (buf[0] << 8); - } - else{ - result = *(U16*)ptr; - } - return(result); -} - -MR4TH_SYM_COMPTIME U32 -elf_u32_decode(void *ptr, B32 flip){ - U32 result = 0; - if (flip){ - U8 *buf = (U8*)ptr; - result = buf[3] | (buf[2] << 8) | (buf[1] << 16) | (buf[0] << 24); - } - else{ - result = *(U32*)ptr; - } - return(result); -} - -MR4TH_SYM_COMPTIME U64 -elf_u64_decode(void *ptr, B32 flip){ - U64 result = 0; - if (flip){ - U8 *buf = (U8*)ptr; - result = ((((U64)buf[7]) << 0) | (((U64)buf[6]) << 8) | - (((U64)buf[5]) << 16) | (((U64)buf[4]) << 24) | - (((U64)buf[3]) << 32) | (((U64)buf[2]) << 40) | - (((U64)buf[1]) << 48) | (((U64)buf[0]) << 56)); - } - else{ - result = *(U64*)ptr; - } - return(result); -} - -MR4TH_SYM_COMPTIME void -elf_header64_from_header32(ELF_Header64 *dst, ELF_Header32 *src, ELF_Encoding src_encoding){ - B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB); - B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN); - - MemoryCopyArray(dst->ident, src->ident); - dst->type = elf_u16_decode(&src->type, flip_order); - dst->machine = elf_u16_decode(&src->machine, flip_order); - dst->version = elf_u32_decode(&src->version, flip_order); - dst->entry = (U64)elf_u32_decode(&src->entry, flip_order); - dst->segment_table_foff = (U64)elf_u32_decode(&src->segment_table_foff, flip_order); - dst->section_table_foff = (U64)elf_u32_decode(&src->section_table_foff, flip_order); - dst->flags = elf_u32_decode(&src->flags, flip_order); - dst->header_size = elf_u16_decode(&src->header_size, flip_order); - dst->segment_size = elf_u16_decode(&src->segment_size, flip_order); - dst->segment_count = elf_u16_decode(&src->segment_count, flip_order); - dst->section_size = elf_u16_decode(&src->section_size, flip_order); - dst->section_count = elf_u16_decode(&src->section_count, flip_order); - dst->string_section_index = elf_u16_decode(&src->string_section_index, flip_order); -} - -MR4TH_SYM_COMPTIME void -elf_header64_from_header64(ELF_Header64 *dst, ELF_Header64 *src, ELF_Encoding src_encoding){ - B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB); - B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN); - - if (!flip_order){ - MemoryCopyStruct(dst, src); - } - else{ - MemoryCopyArray(dst->ident, src->ident); - dst->type = elf_u16_decode(&src->type, 1); - dst->machine = elf_u16_decode(&src->machine, 1); - dst->version = elf_u32_decode(&src->version, 1); - dst->entry = elf_u32_decode(&src->entry, 1); - dst->segment_table_foff = elf_u32_decode(&src->segment_table_foff, 1); - dst->section_table_foff = elf_u32_decode(&src->section_table_foff, 1); - dst->flags = elf_u32_decode(&src->flags, 1); - dst->header_size = elf_u16_decode(&src->header_size, 1); - dst->segment_size = elf_u16_decode(&src->segment_size, 1); - dst->segment_count = elf_u16_decode(&src->segment_count, 1); - dst->section_size = elf_u16_decode(&src->section_size, 1); - dst->section_count = elf_u16_decode(&src->section_count, 1); - dst->string_section_index = elf_u16_decode(&src->string_section_index, 1); - } -} - -MR4TH_SYM_COMPTIME void -elf_section64_from_section32(ELF_Section64 *dst, ELF_Section32 *src, ELF_Encoding src_encoding){ - B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB); - B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN); - - dst->name = elf_u32_decode(&src->name, flip_order); - dst->type = elf_u32_decode(&src->type, flip_order); - dst->flags = elf_u32_decode(&src->flags, flip_order); - dst->addr = elf_u32_decode(&src->addr, flip_order); - dst->offset = elf_u32_decode(&src->offset, flip_order); - dst->size = elf_u32_decode(&src->size, flip_order); - dst->link = elf_u32_decode(&src->link, flip_order); - dst->info = elf_u32_decode(&src->info, flip_order); - dst->addralign = elf_u32_decode(&src->addralign, flip_order); - dst->entsize = elf_u32_decode(&src->entsize, flip_order); -} - -MR4TH_SYM_COMPTIME void -elf_section64_from_section64(ELF_Section64 *dst, ELF_Section64 *src, ELF_Encoding src_encoding){ - B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB); - B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN); - - if (!flip_order){ - MemoryCopyStruct(dst, src); - } - else{ - dst->name = elf_u32_decode(&src->name, 1); - dst->type = elf_u32_decode(&src->type, 1); - dst->flags = elf_u64_decode(&src->flags, 1); - dst->addr = elf_u64_decode(&src->addr, 1); - dst->offset = elf_u64_decode(&src->offset, 1); - dst->size = elf_u64_decode(&src->size, 1); - dst->link = elf_u32_decode(&src->link, 1); - dst->info = elf_u32_decode(&src->info, 1); - dst->addralign = elf_u64_decode(&src->addralign, 1); - dst->entsize = elf_u64_decode(&src->entsize, 1); - } -} - -MR4TH_SYM_COMPTIME void -elf_segment64_from_segment32(ELF_Segment64 *dst, ELF_Segment32 *src, ELF_Encoding src_encoding){ - B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB); - B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN); - - dst->type = elf_u32_decode(&src->type, flip_order); - dst->offset = elf_u32_decode(&src->offset, flip_order); - dst->vaddr = elf_u32_decode(&src->vaddr, flip_order); - dst->paddr = elf_u32_decode(&src->paddr, flip_order); - dst->file_size = elf_u32_decode(&src->file_size, flip_order); - dst->memory_size = elf_u32_decode(&src->memory_size, flip_order); - dst->flags = elf_u32_decode(&src->flags, flip_order); - dst->align = elf_u32_decode(&src->align, flip_order); -} - -MR4TH_SYM_COMPTIME void -elf_segment64_from_segment64(ELF_Segment64 *dst, ELF_Segment64 *src, ELF_Encoding src_encoding){ - B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB); - B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN); - - if (!flip_order){ - MemoryCopyStruct(dst, src); - } - else{ - dst->type = elf_u32_decode(&src->type, 1); - dst->flags = elf_u32_decode(&src->flags, 1); - dst->offset = elf_u64_decode(&src->offset, 1); - dst->vaddr = elf_u64_decode(&src->vaddr, 1); - dst->paddr = elf_u64_decode(&src->paddr, 1); - dst->file_size = elf_u64_decode(&src->file_size, 1); - dst->memory_size = elf_u64_decode(&src->memory_size, 1); - dst->align = elf_u64_decode(&src->align, 1); - } -} - -MR4TH_SYM_COMPTIME void -elf_symbol64_from_symbol32(ELF_Symbol64 *dst, ELF_Symbol32 *src, ELF_Encoding src_encoding){ - B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB); - B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN); - - dst->name = elf_u32_decode(&src->name, flip_order); - dst->value = elf_u32_decode(&src->value, flip_order); - dst->size = elf_u32_decode(&src->size, flip_order); - dst->info = src->info; - dst->other = src->other; - dst->section_index = elf_u16_decode(&src->section_index, flip_order); -} - -MR4TH_SYM_COMPTIME void -elf_symbol64_from_symbol64(ELF_Symbol64 *dst, ELF_Symbol64 *src, ELF_Encoding src_encoding){ - B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB); - B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN); - - if (!flip_order){ - MemoryCopyStruct(dst, src); - } - else{ - dst->name = elf_u32_decode(&src->name, 1); - dst->info = src->info; - dst->other = src->other; - dst->section_index = elf_u16_decode(&src->section_index, 1); - dst->value = elf_u64_decode(&src->value, 1); - dst->size = elf_u64_decode(&src->size, 1); - } -} - -MR4TH_SYM_COMPTIME ELF_RelocationAdd64* -elf_reloca64_from_reloc32(Arena *arena, ELF_Relocation32 *src, U64 count, ELF_Encoding src_encoding){ - B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB); - B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN); - - ELF_RelocationAdd64 *dst = push_array(arena, ELF_RelocationAdd64, count); - { - ELF_Relocation32 *srcptr = src; - ELF_RelocationAdd64 *dstptr = dst; - for (U64 i = 0; i < count; i += 1, srcptr += 1, dstptr += 1){ - dstptr->offset = elf_u32_decode(&srcptr->offset, flip_order); - dstptr->info = elf_u32_decode(&srcptr->info, flip_order); - } - } - - return(dst); -} - -MR4TH_SYM_COMPTIME ELF_RelocationAdd64* -elf_reloca64_from_reloc64(Arena *arena, ELF_Relocation64 *src, U64 count, ELF_Encoding src_encoding){ - B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB); - B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN); - - ELF_RelocationAdd64 *dst = push_array(arena, ELF_RelocationAdd64, count); - { - ELF_Relocation64 *srcptr = src; - ELF_RelocationAdd64 *dstptr = dst; - for (U64 i = 0; i < count; i += 1, srcptr += 1, dstptr += 1){ - dstptr->offset = elf_u64_decode(&srcptr->offset, flip_order); - dstptr->info = elf_u64_decode(&srcptr->info, flip_order); - } - } - - return(dst); -} - -MR4TH_SYM_COMPTIME ELF_RelocationAdd64* -elf_reloca64_from_reloca32(Arena *arena, ELF_RelocationAdd32 *src, U64 count, ELF_Encoding src_encoding){ - B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB); - B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN); - - ELF_RelocationAdd64 *dst = push_array(arena, ELF_RelocationAdd64, count); - { - ELF_RelocationAdd32 *srcptr = src; - ELF_RelocationAdd64 *dstptr = dst; - for (U64 i = 0; i < count; i += 1, srcptr += 1, dstptr += 1){ - dstptr->offset = elf_u32_decode(&srcptr->offset, flip_order); - dstptr->info = elf_u32_decode(&srcptr->info, flip_order); - dstptr->addend = (S32)elf_u32_decode(&srcptr->addend, flip_order); - } - } - - return(dst); -} - -MR4TH_SYM_COMPTIME ELF_RelocationAdd64* -elf_reloca64_from_reloca64(Arena *arena, ELF_RelocationAdd64 *src, U64 count, ELF_Encoding src_encoding){ - B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB); - B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN); - - ELF_RelocationAdd64 *dst = push_array(arena, ELF_RelocationAdd64, count); - { - ELF_RelocationAdd64 *srcptr = src; - ELF_RelocationAdd64 *dstptr = dst; - for (U64 i = 0; i < count; i += 1, srcptr += 1, dstptr += 1){ - dstptr->offset = elf_u64_decode(&srcptr->offset, flip_order); - dstptr->info = elf_u64_decode(&srcptr->info, flip_order); - dstptr->addend = (S64)elf_u64_decode(&srcptr->addend, flip_order); - } - } - - return(dst); -} - -// strings - -MR4TH_SYM_COMPTIME String8 -elf_str8_from_identification_idx(ELF_IdentificationIdx idx){ - String8 result = str8_lit("ERROR"); - switch (idx){ -#define X(N,C) case C: result = str8_lit(#N); break; - ELF_IdentificationIdx_XList(X) -#undef X - } - return(result); -} - -MR4TH_SYM_COMPTIME String8 -elf_str8_from_class(ELF_Class elf_class){ - String8 result = str8_lit("ERROR"); - switch (elf_class){ -#define X(N,C) case C: result = str8_lit(#N); break; - ELF_Class_XList(X) -#undef X - } - return(result); -} - -MR4TH_SYM_COMPTIME String8 -elf_str8_from_encoding(ELF_Encoding elf_encoding){ - String8 result = str8_lit("ERROR"); - switch (elf_encoding){ -#define X(N,C) case C: result = str8_lit(#N); break; - ELF_Encoding_XList(X) -#undef X - } - return(result); -} - -MR4TH_SYM_COMPTIME String8 -elf_str8_from_osabi(ELF_OsAbiExtension elf_osabi){ - String8 result = str8_lit("ERROR"); - switch (elf_osabi){ -#define X(N,C) case C: result = str8_lit(#N); break; - ELF_OsAbiExtension_XList(X) -#undef X - } - return(result); -} - -MR4TH_SYM_COMPTIME String8 -elf_str8_from_file_type(ELF_FileType file_type){ - String8 result = str8_lit("ERROR"); - switch (file_type){ -#define X(N,C) case C: result = str8_lit(#N); break; - ELF_FileType_XList(X) -#undef X - } - return(result); -} - -MR4TH_SYM_COMPTIME String8 -elf_str8_from_machine_type(ELF_FileType machine_type){ - String8 result = str8_lit("ERROR"); - switch (machine_type){ -#define X(N,C) case C: result = str8_lit(#N); break; - ELF_MachineType_XList(X) -#undef X - } - return(result); -} - -MR4TH_SYM_COMPTIME String8 -elf_str8_from_section_type(ELF_SectionType section_type){ - String8 result = str8_lit("ERROR"); - - if (ELF_SectionType_LOOS <= section_type && - section_type <= ELF_SectionType_HIOS){ - result = str8_lit("OS-CUSTOM-TYPE"); - } - - else if (ELF_SectionType_LOPROC <= section_type && - section_type <= ELF_SectionType_HIPROC){ - result = str8_lit("PROC-CUSTOM-TYPE"); - } - - else if (ELF_SectionType_LOUSER <= section_type && - section_type <= ELF_SectionType_HIUSER){ - result = str8_lit("USER-CUSTOM-TYPE"); - } - - else{ - switch (section_type){ -#define X(N,C) case C: result = str8_lit(#N); break; - ELF_SectionType_XList(X) -#undef X - } - } - - return(result); -} - -MR4TH_SYM_COMPTIME void -elf_str8list_from_section_flags(Arena *arena, String8List *out, U32 flags){ - U32 f = 1; - for (U32 i = 0; i < 20; i += 1){ - if ((flags & f) != 0){ - switch (f){ -#define X(N,C) case C: str8_list_push(arena, out, str8_lit(#N)); break; - ELF_SectionFlags_XList(X) -#undef X - } - } - f <<= 1; - } - if ((flags & ELF_SectionFlag_MASKOS) != 0){ - str8_list_pushf(arena, out, "OS-CUSTOM(%02x)", (flags >> 20)&0xFF); - } - if ((flags & ELF_SectionFlag_MASKPROC) != 0){ - str8_list_pushf(arena, out, "PROC-CUSTOM(%01x)", (flags >> 28)&0xF); - } -} - -MR4TH_SYM_COMPTIME String8 -elf_str8_from_section_flags(Arena *arena, U32 flags){ - ArenaTemp scratch = arena_get_scratch(&arena, 1); - String8List list = {0}; - elf_str8list_from_section_flags(scratch.arena, &list, flags); - String8 result = str8_join_flags(arena, &list); - arena_release_scratch(&scratch); - return(result); -} - -MR4TH_SYM_COMPTIME String8 -elf_str8_from_segment_type(ELF_SegmentType segment_type){ - String8 result = str8_lit("ERROR"); - - if (ELF_SegmentType_LOOS <= segment_type && - segment_type <= ELF_SegmentType_HIOS){ - result = str8_lit("OS-CUSTOM-TYPE"); - } - - else if (ELF_SegmentType_LOPROC <= segment_type && - segment_type <= ELF_SegmentType_HIPROC){ - result = str8_lit("PROC-CUSTOM-TYPE"); - } - - else{ - switch (segment_type){ -#define X(N,C) case C: result = str8_lit(#N); break; - ELF_SegmentType_XList(X) -#undef X - } - } - - return(result); -} - -MR4TH_SYM_COMPTIME void -elf_str8list_from_segment_flags(Arena *arena, String8List *out, ELF_SegmentFlags flags){ - U32 f = 1; - for (U32 i = 0; i < 20; i += 1){ - if ((flags & f) != 0){ - switch (f){ -#define X(N,C) case C: str8_list_push(arena, out, str8_lit(#N)); break; - ELF_SegmentFlags_XList(X) -#undef X - } - } - f <<= 1; - } - if ((flags & ELF_SegmentFlag_MASKOS) != 0){ - str8_list_pushf(arena, out, "OS-CUSTOM(%02x)", (flags >> 20)&0xFF); - } - if ((flags & ELF_SegmentFlag_MASKPROC) != 0){ - str8_list_pushf(arena, out, "PROC-CUSTOM(%01x)", (flags >> 28)&0xF); - } -} - -MR4TH_SYM_COMPTIME String8 -elf_str8_from_segment_flags(Arena *arena, ELF_SegmentFlags flags){ - ArenaTemp scratch = arena_get_scratch(&arena, 1); - String8List list = {0}; - elf_str8list_from_segment_flags(scratch.arena, &list, flags); - String8 result = str8_join_flags(arena, &list); - arena_release_scratch(&scratch); - return(result); -} - -MR4TH_SYM_COMPTIME String8 -elf_str8_from_symbol_binding(ELF_SymbolBinding bind){ - String8 result = str8_lit("ERROR"); - - if (ELF_SymbolBinding_LOOS <= bind && - bind <= ELF_SymbolBinding_HIOS){ - result = str8_lit("OS-CUSTOM-TYPE"); - } - - else if (ELF_SymbolBinding_LOPROC <= bind && - bind <= ELF_SymbolBinding_HIPROC){ - result = str8_lit("PROC-CUSTOM-TYPE"); - } - - else{ - switch (bind){ -#define X(N,C) case C: result = str8_lit(#N); break; - ELF_SymbolBinding_XList(X) -#undef X - } - } - - return(result); -} - -MR4TH_SYM_COMPTIME String8 -elf_str8_from_symbol_type(ELF_SymbolType type){ - String8 result = str8_lit("ERROR"); - - if (ELF_SymbolType_LOOS <= type && - type <= ELF_SymbolType_HIOS){ - result = str8_lit("OS-CUSTOM-TYPE"); - } - - else if (ELF_SymbolType_LOPROC <= type && - type <= ELF_SymbolType_HIPROC){ - result = str8_lit("PROC-CUSTOM-TYPE"); - } - - else{ - switch (type){ -#define X(N,C) case C: result = str8_lit(#N); break; - ELF_SymbolType_XList(X) -#undef X - } - } - - return(result); -} - -MR4TH_SYM_COMPTIME String8 -elf_str8_from_symbol_vis(ELF_SymbolVis vis){ - String8 result = str8_lit("ERROR"); - switch (vis){ -#define X(N,C) case C: result = str8_lit(#N); break; - ELF_SymbolVis_XList(X) -#undef X - } - return(result); -} diff --git a/src/binary/elf/mr4th_elf.dump.c b/src/binary/elf/mr4th_elf.dump.c deleted file mode 100644 index 8783a0d..0000000 --- a/src/binary/elf/mr4th_elf.dump.c +++ /dev/null @@ -1,567 +0,0 @@ -/* -** ELF File Dumper -*/ - - -#include "mr4th_base.h" -#include "mr4th_base.stdio.h" -#include "mr4th_keywords.h" - -#include "mr4th_elf.h" - -#include "mr4th_elf.dump.h" - -#include "mr4th_base.c" -#include "mr4th_elf.c" - -//////////////////////////////// -// ELF DUMP Arguments Functions - -function ELFDUMP_Arguments* -elfdump_args(Arena *arena, String8List *command_line_args){ - // parse - CMDLN *cmdln = cmdln_from_args(arena, command_line_args); - - // setup args - ELFDUMP_Arguments *args = push_array(arena, ELFDUMP_Arguments, 1); - - // get input file name - args->input_file_name = cmdln_input_from_idx(cmdln, 0); - - return(args); -} - -//////////////////////////////// -// Entry Point - -int main(int argc, char **argv){ - os_main_init(argc, argv); - - Arena *arena = arena_alloc(); - - // arguments - String8List command_line_args = os_command_line_arguments(); - ELFDUMP_Arguments *args = elfdump_args(arena, &command_line_args); - - // load input file - String8 input_data = os_file_read(arena, args->input_file_name); - - - ///////////////// - ///// PARSE ///// - ///////////////// - - // magic check - B32 elf_magic = 0; - ELF_Class elf_class = ELF_Class_NONE; - ELF_Encoding elf_encoding = ELF_Encoding_NONE; - U32 elf_version = 0; - ELF_OsAbiExtension elf_ext = ELF_OsAbiExtension_NONE; - - if (input_data.size >= ELF_NUM_IDENT){ - if (input_data.str[0] == ELF_Magic_Byte0 && - input_data.str[1] == ELF_Magic_Byte1 && - input_data.str[2] == ELF_Magic_Byte2 && - input_data.str[3] == ELF_Magic_Byte3){ - elf_magic = 1; - } - elf_class = input_data.str[ELF_IdentificationIdx_CLASS]; - elf_encoding = input_data.str[ELF_IdentificationIdx_DATA]; - elf_version = input_data.str[ELF_IdentificationIdx_VERSION]; - elf_ext = input_data.str[ELF_IdentificationIdx_OSABI]; - } - - B32 elf_header_good = 1; - if (!elf_magic || elf_class == 0 || elf_encoding == 0 || elf_version != 1){ - elf_header_good = 0; - } - - // elf header - ELF_Header32 *hdr32_raw = 0; - ELF_Header64 *hdr64_raw = 0; - if (elf_header_good){ - - // 32-bit - if (elf_class == ELF_Class_32){ - if (sizeof(*hdr32_raw) <= input_data.size){ - hdr32_raw = (ELF_Header32*)input_data.str; - } - } - - // 64-bit - if (elf_class == ELF_Class_64){ - if (sizeof(*hdr64_raw) <= input_data.size){ - hdr64_raw = (ELF_Header64*)input_data.str; - } - } - } - - // normalize header - ELF_Header64 hdr = {0}; - if (hdr32_raw != 0){ - elf_header64_from_header32(&hdr, hdr32_raw, elf_encoding); - } - else if (hdr64_raw != 0){ - elf_header64_from_header64(&hdr, hdr64_raw, elf_encoding); - } - else{ - elf_header_good = 0; - } - - // sections - ELF_Section32 *sec32_raw = 0; - ELF_Section64 *sec64_raw = 0; - if (elf_header_good){ - - U64 foff = hdr.section_table_foff; - U64 opl_foff = foff + hdr.section_count*hdr.section_size; - - if (opl_foff <= input_data.size){ - // 32-bit - if (elf_class == ELF_Class_32){ - sec32_raw = (ELF_Section32*)(input_data.str + foff); - } - - // 64-bit - if (elf_class == ELF_Class_64){ - sec64_raw = (ELF_Section64*)(input_data.str + foff); - } - } - } - - // normalize sections - B32 sections_good = 1; - ELF_Section64 *sections = 0; - if (sec32_raw != 0){ - sections = push_array(arena, ELF_Section64, hdr.section_count); - for (U32 i = 0; i < hdr.section_count; i += 1){ - elf_section64_from_section32(sections + i, sec32_raw + i, elf_encoding); - } - } - else if (sec64_raw != 0){ - sections = push_array(arena, ELF_Section64, hdr.section_count); - for (U32 i = 0; i < hdr.section_count; i += 1){ - elf_section64_from_section64(sections + i, sec64_raw + i, elf_encoding); - } - } - else{ - sections_good = 0; - } - - // segments - ELF_Segment32 *seg32_raw = 0; - ELF_Segment64 *seg64_raw = 0; - if (elf_header_good){ - - U64 foff = hdr.segment_table_foff; - U64 opl_foff = foff + hdr.segment_count*hdr.segment_size; - - if (opl_foff <= input_data.size){ - // 32-bit - if (elf_class == ELF_Class_32){ - seg32_raw = (ELF_Segment32*)(input_data.str + foff); - } - - // 64-bit - if (elf_class == ELF_Class_64){ - seg64_raw = (ELF_Segment64*)(input_data.str + foff); - } - } - } - - // normalize segments - B32 segments_good = 1; - ELF_Segment64 *segments = 0; - if (seg32_raw != 0){ - segments = push_array(arena, ELF_Segment64, hdr.segment_count); - for (U32 i = 0; i < hdr.segment_count; i += 1){ - elf_segment64_from_segment32(segments + i, seg32_raw + i, elf_encoding); - } - } - else if (seg64_raw != 0){ - segments = push_array(arena, ELF_Segment64, hdr.segment_count); - for (U32 i = 0; i < hdr.segment_count; i += 1){ - elf_segment64_from_segment64(segments + i, seg64_raw + i, elf_encoding); - } - } - else{ - segments_good = 0; - } - - // find symtab section - ELF_Section64 *symtab_section = 0; - for (U32 i = 0; i < hdr.section_count; i += 1){ - ELF_Section64 *section = sections + i; - if (section->type == ELF_SectionType_SYMTAB){ - symtab_section = section; - break; - } - } - - // find dynsym section - ELF_Section64 *dynsym_section = 0; - for (U32 i = 0; i < hdr.section_count; i += 1){ - ELF_Section64 *section = sections + i; - if (section->type == ELF_SectionType_DYNSYM){ - dynsym_section = section; - break; - } - } - - // decode symol tables - ELFDUMP_SymbolTable symbol_tables[2] = {0}; - - ELF_Section64 *symbol_table_sections[2]; - symbol_table_sections[0] = symtab_section; - symbol_table_sections[1] = dynsym_section; - - for (U32 k = 0; k < 2; k += 1){ - ELF_Section64 *symbol_section = symbol_table_sections[k]; - - ELF_Symbol32 *sym32_raw = 0; - ELF_Symbol64 *sym64_raw = 0; - U8 *string_table_first = 0; - U8 *string_table_opl = 0; - U32 symbol_count = 0; - - if (symbol_section != 0){ - - // extract symbol string table - if (symbol_section->link < hdr.section_count){ - ELF_Section64 *string_table = sections + symbol_section->link; - string_table_first = input_data.str + string_table->offset; - string_table_opl = string_table_first + string_table->size; - } - - // extract raw symbol array - U64 foff = symbol_section->offset; - U64 opl_foff = foff + symbol_section->size; - - U64 symbol_count_cap = 0; - - if (opl_foff <= input_data.size){ - // 32-bit - if (elf_class == ELF_Class_32){ - sym32_raw = (ELF_Symbol32*)(input_data.str + foff); - symbol_count_cap = symbol_section->size/sizeof(*sym32_raw); - } - - // 64-bit - if (elf_class == ELF_Class_64){ - sym64_raw = (ELF_Symbol64*)(input_data.str + foff); - symbol_count_cap = symbol_section->size/sizeof(*sym64_raw); - } - } - - // extract symbol count - symbol_count = symbol_section->info; - symbol_count = ClampTop(symbol_count, symbol_count_cap); - } - - // normalize symbol table - ELF_Symbol64 *symbols = 0; - if (sym32_raw != 0){ - symbols = push_array(arena, ELF_Symbol64, symbol_count); - for (U32 i = 0; i < symbol_count; i += 1){ - elf_symbol64_from_symbol32(symbols + i, sym32_raw + i, elf_encoding); - } - } - else if (sym64_raw != 0){ - symbols = push_array(arena, ELF_Symbol64, symbol_count); - for (U32 i = 0; i < symbol_count; i += 1){ - elf_symbol64_from_symbol64(symbols + i, sym64_raw + i, elf_encoding); - } - } - - // fill symbol table - if (symbols != 0){ - ELFDUMP_SymbolTable *symbol_table = &symbol_tables[k]; - symbol_table->symbols = symbols; - symbol_table->count = symbol_count; - symbol_table->string_table_first = string_table_first; - symbol_table->string_table_opl = string_table_opl; - } - } - - // relocations - ELFDUMP_Relocs *first_relocs = 0; - ELFDUMP_Relocs *last_relocs = 0; - - if (sections_good){ - for (U32 i = 0; i < hdr.section_count; i += 1){ - - ELF_RelocationAdd64 *relocs = 0; - U64 reloc_count = 0; - - ELF_Section64 *section = sections + i; - void *section_data = input_data.str + section->offset; - - switch (section->type){ - case ELF_SectionType_REL: - { - if (elf_class == ELF_Class_32){ - ELF_Relocation32 *reloc32 = (ELF_Relocation32*)section_data; - reloc_count = section->size/sizeof(*reloc32); - relocs = elf_reloca64_from_reloc32(arena, reloc32, reloc_count, elf_encoding); - } - else{ - ELF_Relocation64 *reloc64 = (ELF_Relocation64*)section_data; - reloc_count = section->size/sizeof(*reloc64); - relocs = elf_reloca64_from_reloc64(arena, reloc64, reloc_count, elf_encoding); - } - }break; - - case ELF_SectionType_RELA: - { - if (elf_class == ELF_Class_32){ - ELF_RelocationAdd32 *reloca32 = (ELF_RelocationAdd32*)section_data; - reloc_count = section->size/sizeof(*reloca32); - relocs = elf_reloca64_from_reloca32(arena, reloca32, reloc_count, elf_encoding); - } - else{ - ELF_RelocationAdd64 *reloca64 = (ELF_RelocationAdd64*)section_data; - reloc_count = section->size/sizeof(*reloca64); - relocs = elf_reloca64_from_reloca64(arena, reloca64, reloc_count, elf_encoding); - } - }break; - } - - if (reloc_count > 0){ - ELFDUMP_Relocs *r = push_array(arena, ELFDUMP_Relocs, 1); - SLLQueuePush(first_relocs, last_relocs, r); - r->section_idx = i; - r->symbol_table_section_idx = section->link; - r->target_section_idx = section->info; - r->relocs = relocs; - r->count = reloc_count; - } - } - } - - //////////////// - ///// DUMP ///// - //////////////// - - if (!elf_header_good){ - m4_printf("Unsuccessful parse\n"); - } - - if (elf_header_good){ - ArenaTemp scratch = arena_get_scratch(0, 0); - - // elf header - { - - for (U32 i = 0; i < ELF_NUM_IDENT; i += 1){ - String8 ident_string = elf_str8_from_identification_idx(i); - - String8 val_string = {0}; - switch (i){ - case ELF_IdentificationIdx_CLASS: - { - val_string = elf_str8_from_class(hdr.ident[i]); - }break; - - case ELF_IdentificationIdx_DATA: - { - val_string = elf_str8_from_encoding(hdr.ident[i]); - }break; - - case ELF_IdentificationIdx_OSABI: - { - val_string = elf_str8_from_osabi(hdr.ident[i]); - }break; - - default: - { - val_string = str8_pushf(scratch.arena, "0x%02x", hdr.ident[i]); - }break; - } - - m4_printf("elf_header.ident[%10S] = %S\n", &ident_string, &val_string); - } - - String8 type_string = elf_str8_from_file_type(hdr.type); - String8 machine_string = elf_str8_from_machine_type(hdr.machine); - - m4_printf("elf_header.type = %S\n", &type_string); - m4_printf("elf_header.machine = %S\n", &machine_string); - m4_printf("elf_header.version = %u\n", hdr.version); - m4_printf("elf_header.entry = 0x%08llx\n", hdr.entry); - m4_printf("elf_header.segment_table_foff = 0x%08llx\n", hdr.segment_table_foff); - m4_printf("elf_header.section_table_foff = 0x%08llx\n", hdr.section_table_foff); - // TODO(allen): flags pneumonics - m4_printf("elf_header.flags = 0x%08x\n", hdr.flags); - m4_printf("elf_header.header_size = %u\n", hdr.header_size); - m4_printf("elf_header.segment_size = %u\n", hdr.segment_size); - m4_printf("elf_header.segment_count = %u\n", hdr.segment_count); - m4_printf("elf_header.section_size = %u\n", hdr.section_size); - m4_printf("elf_header.section_count = %u\n", hdr.section_count); - m4_printf("elf_header.string_section_index = %u\n", hdr.string_section_index); - - m4_printf("\n"); - - arena_release_scratch(&scratch); - } - - // sections - if (sections_good){ - - // get the section name string table - U8 *string_table_first = 0; - U8 *string_table_opl = 0; - { - ELF_Section64 *string_table = 0; - if (hdr.string_section_index != ELF_SectionIndex_UNDEF && - hdr.string_section_index != ELF_SectionIndex_XINDEX){ - string_table = sections + hdr.string_section_index; - } - else if (hdr.string_section_index == ELF_SectionIndex_XINDEX){ - U32 string_section_index = sections[0].link; - string_table = sections + string_section_index; - } - - if (string_table != 0){ - string_table_first = input_data.str + string_table->offset; - string_table_opl = string_table_first + string_table->size; - } - } - - // dump each section - for (U32 i = 0; i < hdr.section_count; i += 1){ - ELF_Section64 *section = sections + i; - - String8 name = str8_cstring_capped(string_table_first + section->name, - string_table_opl); - String8 type_string = elf_str8_from_section_type(section->type); - String8 flags_string = - elf_str8_from_section_flags(scratch.arena, section->flags); - - m4_printf("section[%u] = {\n" - " name = \"%S\",\n" - " type = %S (0x%x),\n" - " flags = %S,\n" - " addr = 0x%08llx,\n" - " size = %$llu,\n" - " link = %u,\n" - " info = %u,\n" - " addralign = %llu,\n" - " entsize = %$llu,\n" - "}\n", - i, &name, - &type_string, section->type, - &flags_string, - section->addr, - section->size, - section->link, - section->info, - section->addralign, - section->entsize); - - } - - m4_printf("\n"); - - } - - // segments - if (segments_good){ - for (U32 i = 0; i < hdr.segment_count; i += 1){ - ELF_Segment64 *segment = segments + i; - - String8 type_string = elf_str8_from_segment_type(segment->type); - String8 flags_string = - elf_str8_from_segment_flags(scratch.arena, segment->flags); - - m4_printf("segment[%u] = {\n" - " type = %S (0x%x),\n" - " flags = %S,\n" - " offset = 0x%08llx,\n" - " vaddr = 0x%08llx,\n" - " paddr = 0x%08llx,\n" - " file_size = %$llu,\n" - " memory_size = %$llu,\n" - " align = %llu,\n" - "}\n", - i, - &type_string, segment->type, - &flags_string, - segment->offset, - segment->vaddr, - segment->paddr, - segment->file_size, - segment->memory_size, - segment->align); - } - - m4_printf("\n"); - } - - // symbol tables - String8 symbol_table_name[2]; - symbol_table_name[0] = str8_lit("symtab"); - symbol_table_name[1] = str8_lit("dynsym"); - - for (U32 k = 0; k < 2; k += 1){ - ELFDUMP_SymbolTable *symbol_table = &symbol_tables[k]; - - if (symbol_table->symbols != 0){ - for (U32 i = 0; i < symbol_table->count; i += 1){ - ELF_Symbol64 *symbol = symbol_table->symbols + i; - - String8 name = - str8_cstring_capped(symbol_table->string_table_first + symbol->name, - symbol_table->string_table_opl); - - ELF_SymbolBinding bind = ELF_Symbol_BindFromInfo(symbol->info); - ELF_SymbolType type = ELF_Symbol_TypeFromInfo(symbol->info); - - ELF_SymbolVis vis = ELF_Symbol_VisFromOther(symbol->other); - - String8 bind_string = elf_str8_from_symbol_binding(bind); - String8 type_string = elf_str8_from_symbol_type(type); - String8 vis_string = elf_str8_from_symbol_vis(vis); - - m4_printf("%S[%u] = {\n" - " name = \"%S\",\n" - " info = bind: %S, type: %S,\n" - " other = vis: %S,\n" - " section_index = %u,\n" - " value = %llu,\n" - " size = %$llu,\n" - "}\n", - &symbol_table_name[k], i, - &name, - &bind_string, &type_string, - &vis_string, - symbol->section_index, - symbol->value, - symbol->size); - } - - m4_printf("\n"); - } - } - - // relocations - for (ELFDUMP_Relocs *relocs = first_relocs; - relocs != 0; - relocs = relocs->next){ - m4_printf("relocations(%u) = {\n", relocs->section_idx); - - U32 count = relocs->count; - ELF_RelocationAdd64 *relocptr = relocs->relocs; - for (U32 i = 0; i < count; i += 1, relocptr += 1){ - m4_printf(" { offset = %llu,\n" - " info = %llu,\n" - " addend = %lld,\n" - " },\n", relocptr->offset, relocptr->info, relocptr->addend); - } - - m4_printf("}\n\n"); - } - - } - - return(0); -} diff --git a/src/binary/elf/mr4th_elf.dump.h b/src/binary/elf/mr4th_elf.dump.h deleted file mode 100644 index f7ff523..0000000 --- a/src/binary/elf/mr4th_elf.dump.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef MR4TH_ELF_DUMP_H -#define MR4TH_ELF_DUMP_H - -//////////////////////////////// -// ELF DUMP Arguments Types - -typedef struct ELFDUMP_Arguments{ - String8 input_file_name; -} ELFDUMP_Arguments; - -//////////////////////////////// -// ELF DUMP Types - -typedef struct ELFDUMP_SymbolTable{ - ELF_Symbol64 *symbols; - U32 count; - U8 *string_table_first; - U8 *string_table_opl; -} ELFDUMP_SymbolTable; - -typedef struct ELFDUMP_Relocs{ - struct ELFDUMP_Relocs *next; - U32 section_idx; - U32 symbol_table_section_idx; - U32 target_section_idx; - U32 count; - ELF_RelocationAdd64 *relocs; -} ELFDUMP_Relocs; - -//////////////////////////////// -// ELF DUMP Arguments Functions - -function ELFDUMP_Arguments* elfdump_args(Arena *arena, String8List *args); - -//////////////////////////////// -// Entry Point - -int main(int argc, char **argv); - -#endif //MR4TH_ELF_DUMP_H diff --git a/src/binary/pe/mr4th_pe.h b/src/binary/pe/mr4th_pe.h deleted file mode 100644 index b788a78..0000000 --- a/src/binary/pe/mr4th_pe.h +++ /dev/null @@ -1,831 +0,0 @@ -#ifndef MR4TH_PE_H -#define MR4TH_PE_H - -/* -** PE/COFF total layout summary: -** -** MS-DOS Stub [image only] -** ***************** -** ptr> PE Signature [image only] -** COFF Header -** Optional Header [image only] -** Section Table -*/ - -//////////////////////////////// -// Types: COFF / PE - -#pragma pack(push,1) - -// MS-DOS Stub - -// located at offset=0 in valid executables -#define PE_MSDOS_MAGIC 0x5A4D - -typedef struct PE_DosHeader{ - U16 magic; - U16 last_page_size; - U16 page_count; - U16 reloc_count; - U16 paragraph_header_size; - U16 min_paragraph; - U16 max_paragraph; - U16 init_ss; - U16 init_sp; - U16 checksum; - U16 init_ip; - U16 init_cs; - U16 reloc_table_file_off; - U16 overlay_number; - U16 reserved[4]; - U16 oem_id; - U16 oem_info; - U16 reserved2[10]; - U32 coff_file_offset; -} PE_DosHeader; - -// pe signature - -#define PE_SIGNATURE 0x00004550 - -// coff header - -#define PE_MachineType_XList(X)\ -X(UNKNOWN, 0x0)\ -X(ALPHA, 0x184)\ -X(ALPHA64, 0x284)\ -X(AM33, 0x1d3)\ -X(AMD64, 0x8664)\ -X(ARM, 0x1c0)\ -X(ARM64, 0xaa64)\ -X(ARMNT, 0x1c4)\ -X(EBC, 0xebc)\ -X(I386, 0x14c)\ -X(IA64, 0x200)\ -X(LOONGARCH32, 0x6232)\ -X(LOONGARCH64, 0x6264)\ -X(M32R, 0x9041)\ -X(MIPS16, 0x266)\ -X(MIPSFPU, 0x366)\ -X(MIPSFPU16, 0x466)\ -X(POWERPC, 0x1f0)\ -X(POWERPCFP, 0x1f1)\ -X(R4000, 0x166)\ -X(RISCV32, 0x5032)\ -X(RISCV64, 0x5064)\ -X(RISCV128, 0x5128)\ -X(SH3, 0x1a2)\ -X(SH3DSP, 0x1a3)\ -X(SH4, 0x1a6)\ -X(SH5, 0x1a8)\ -X(THUMB, 0x1c2)\ -X(WCEMIPSV2, 0x169) - -typedef U16 PE_MachineType; -enum{ -#define X(N,C) PE_MachineType_##N = C, - PE_MachineType_XList(X) -#undef X -}; - -#define PE_Flags_XList(X)\ -X(RELOCS_STRIPPED, 0x0001)\ -X(EXECUTABLE_IMAGE, 0x0002)\ -X(LINE_NUMS_STRIPPED, 0x0004)\ -X(LOCAL_SYMS_STRIPPED, 0x0008)\ -X(AGGRESSIVE_WS_TRIM, 0x0010)\ -X(LARGE_ADDRESS_AWARE, 0x0020)\ -X(RESERVED, 0x0040)\ -X(BYTES_REVERSED_LO, 0x0080)\ -X(32BIT_MACHINE, 0x0100)\ -X(DEBUG_STRIPPED, 0x0200)\ -X(REMOVABLE_RUN_FROM_SWAP, 0x0400)\ -X(NET_RUN_FROM_SWAP, 0x0800)\ -X(SYSTEM, 0x1000)\ -X(DLL, 0x2000)\ -X(UP_SYSTEM_ONLY, 0x4000)\ -X(BYTES_REVERSED_HI, 0x8000)\ - -typedef U16 PE_Flags; -enum{ -#define X(N,C) PE_Flag_##N = C, - PE_Flags_XList(X) -#undef X -}; - -typedef struct PE_CoffHeader{ - PE_MachineType machine_type; - U16 section_count; - U32 time_stamp; - U32 symbol_table_foff; - U32 symbol_count; - U16 optional_header_size; - PE_Flags flags; -} PE_CoffHeader; - -// optional headers - -#define PE_OptionalHeaderMagic_XList(X)\ -X(ROM, 0x107)\ -X(PE32, 0x10B)\ -X(PE32Plus, 0x20B) - -typedef U16 PE_OptionalHeaderMagic; -enum{ -#define X(N,C) PE_OptionalHeaderMagic_##N = C, - PE_OptionalHeaderMagic_XList(X) -#undef X -}; - -#define PE_WindowsSubsystem_XList(X)\ -X(UNKNOWN, 0)\ -X(NATIVE, 1)\ -X(WINDOWS_GUI, 2)\ -X(WINDOWS_CUI, 3)\ -X(OS2_CUI, 5)\ -X(POSIX_CUI, 7)\ -X(NATIVE_WINDOWS, 8)\ -X(WINDOWS_CE_GUI, 9)\ -X(EFI_APPLICATION, 10)\ -X(EFI_BOOT_SERVICE_DRIVER, 11)\ -X(EFI_RUNTIME_DRIVER, 12)\ -X(EFI_ROM, 13)\ -X(XBOX, 14)\ -X(WINDOWS_BOOT_APPLICATION, 16) - -typedef U16 PE_WindowsSubsystem; -enum{ -#define X(N,C) PE_WindowsSubsystem_##N = C, - PE_WindowsSubsystem_XList(X) -#undef X -}; - -#define PE_DLLFlags_XList(X)\ -X(RESERVED0, 0x0001)\ -X(RESERVED1, 0x0002)\ -X(RESERVED2, 0x0004)\ -X(RESERVED3, 0x0008)\ -X(HIGH_ENTROPY_VA, 0x0020)\ -X(DYNAMIC_BASE, 0x0040)\ -X(FORCE_INTEGRITY, 0x0080)\ -X(NX_COMPAT, 0x0100)\ -X(NO_ISOLATION, 0x0200)\ -X(NO_SEH, 0x0400)\ -X(NO_BIND, 0x0800)\ -X(APPCONTAINER, 0x1000)\ -X(WDM_DRIVER, 0x2000)\ -X(GUARD_CF, 0x4000)\ -X(TERMINAL_SERVER_AWARE, 0x8000) - -typedef U16 PE_DLLFlags; -enum{ -#define X(N,C) PE_DLLFlag_##N = C, - PE_DLLFlags_XList(X) -#undef X -}; - -typedef struct PE_OptionalHeader32{ - PE_OptionalHeaderMagic magic; - U8 linker_version_major; - U8 linker_version_minor; - U32 total_size_text; // 'text' means executable code here - U32 total_size_inited_data; - U32 total_size_zeroed_data; - U32 entry_point_voff; - U32 text_voff; - U32 data_voff; - U32 image_base_vaddr; - U32 section_alignment; - U32 file_alignment; - U16 os_version_major; - U16 os_version_minor; - U16 image_version_major; - U16 image_version_minor; - U16 subsystem_version_major; - U16 subsystem_version_minor; - U32 reserved0; - U32 image_size; - U32 headers_size; - U32 checksum; - PE_WindowsSubsystem subsystem; - PE_DLLFlags dll_flags; - U32 stack_size_reserve; - U32 stack_size_commit; - U32 heap_size_reserve; - U32 heap_size_commit; - U32 reserved1; - U32 data_directory_count; -} PE_OptionalHeader32; - -typedef struct PE_OptionalHeader32Plus{ - U16 magic; - U8 linker_version_major; - U8 linker_version_minor; - U32 total_size_text; // 'text' means executable code here - U32 total_size_inited_data; - U32 total_size_zeroed_data; - U32 entry_point_voff; - U32 text_voff; - U64 image_base_vaddr; - U32 section_alignment; - U32 file_alignment; - U16 os_version_major; - U16 os_version_minor; - U16 image_version_major; - U16 image_version_minor; - U16 subsystem_version_major; - U16 subsystem_version_minor; - U32 reserved0; - U32 image_size; - U32 headers_size; - U32 checksum; - PE_WindowsSubsystem subsystem; - PE_DLLFlags dll_flags; - U64 stack_size_reserve; - U64 stack_size_commit; - U64 heap_size_reserve; - U64 heap_size_commit; - U32 reserved1; - U32 data_directory_count; -} PE_OptionalHeader32Plus; - -typedef struct PE_DataDirectory{ - U32 voff; - U32 size; -} PE_DataDirectory; - -#define PE_DataDirectoryIdx_XList(X)\ -X(ExportTable, "Export Table", 0)\ -X(ImportTable, "Import Table", 1)\ -X(ResourceTable, "Resource Table", 2)\ -X(ExceptionTable, "Exception Table", 3)\ -X(CertificateTable, "Certificate Table", 4)\ -X(BaseRelocationTable, "Base Relocation Table", 5)\ -X(Debug, "Debug Directories", 6)\ -X(Architecture, "Architecture", 7)\ -X(GlobalPtr, "Global Ptr", 8)\ -X(TLSTable, "TLS Table", 9)\ -X(LoadConfigTable, "Load Config Table", 10)\ -X(BoundImport, "Bound Import", 11)\ -X(IAT, "IAT", 12)\ -X(DelayImportDescriptor, "Delay Import Descriptor", 13)\ -X(CLRRuntimeHeader, "CLR Runtime Header", 14)\ -X(Reserved, "Reserved", 15) - -typedef U32 PE_DataDirectoryIdx; -enum{ -#define X(N,D,C) PE_DataDirectoryIdx_##N = C, - PE_DataDirectoryIdx_XList(X) -#undef X - PE_DataDirectoryIdx_COUNT -}; - -// section table - -#define PE_SectionFlags_XList(X)\ -X(TYPE_NO_PAD, 0x00000008)\ -X(CNT_CODE, 0x00000020)\ -X(CNT_INITIALIZED_DATA, 0x00000040)\ -X(CNT_UNINITIALIZED_DATA, 0x00000080)\ -X(LNK_OTHER, 0x00000100)\ -X(LNK_INFO, 0x00000200)\ -X(LNK_REMOVE, 0x00000800)\ -X(LNK_COMDAT, 0x00001000)\ -X(GPREL, 0x00008000)\ -X(MEM_16BIT, 0x00020000)\ -X(MEM_LOCKED, 0x00040000)\ -X(MEM_PRELOAD, 0x00080000)\ -X(LNK_NRELOC_OVFL, 0x01000000)\ -X(MEM_DISCARDABLE, 0x02000000)\ -X(MEM_NOT_CACHED, 0x04000000)\ -X(MEM_NOT_PAGED, 0x08000000)\ -X(MEM_SHARED, 0x10000000)\ -X(MEM_EXECUTE, 0x20000000)\ -X(MEM_READ, 0x40000000)\ -X(MEM_WRITE, 0x80000000) - -#define PE_SectionAlignFromSectionFlags(f) ((f)&0x00F00000) - -#define PE_SectionAlign_XList(X)\ -X(1, 0x00100000)\ -X(2, 0x00200000)\ -X(4, 0x00300000)\ -X(8, 0x00400000)\ -X(16, 0x00500000)\ -X(32, 0x00600000)\ -X(64, 0x00700000)\ -X(128, 0x00800000)\ -X(256, 0x00900000)\ -X(512, 0x00A00000)\ -X(1024, 0x00B00000)\ -X(2048, 0x00C00000)\ -X(4096, 0x00D00000)\ -X(8192, 0x00E00000) - -typedef U32 PE_SectionFlags; -enum{ -#define X(N,C) PE_SectionFlag_##N = C, - PE_SectionFlags_XList(X) -#undef X - -#define X(N,C) PE_SectionAlign_##N = C, - PE_SectionAlign_XList(X) -#undef X -}; - -typedef struct PE_SectionHeader{ - U8 name[8]; - U32 vsize; - U32 voff; - U32 fsize; - U32 foff; - U32 relocations_foff; - U32 line_numbers_foff; - U16 relocation_count; - U16 line_number_count; - PE_SectionFlags flags; -} PE_SectionHeader; - -// coff relocations - -// TODO(allen): these are just the "x64" values -// generalize PE_CoffRelocationType to all machines -#define PE_CoffRelocationType_XList(X)\ -X(ABSOLUTE, 0x0000)\ -X(ADDR64, 0x0001)\ -X(ADDR32, 0x0002)\ -X(ADDR32NB, 0x0003)\ -X(REL32, 0x0004)\ -X(REL32_1, 0x0005)\ -X(REL32_2, 0x0006)\ -X(REL32_3, 0x0007)\ -X(REL32_4, 0x0008)\ -X(REL32_5, 0x0009)\ -X(SECTION, 0x000A)\ -X(SECREL, 0x000B)\ -X(SECREL7, 0x000C)\ -X(TOKEN, 0x000D)\ -X(SREL32, 0x000E)\ -X(PAIR, 0x000F)\ -X(SSPAN32, 0x0010) - -typedef U16 PE_CoffRelocationType; -enum{ -#define X(N,C) PE_CoffRelocationType_##N = C, - PE_CoffRelocationType_XList(X) -#undef X -}; - -typedef struct PE_CoffRelocation{ - U32 off; - U32 symbol_idx; - PE_CoffRelocationType type; -} PE_CoffRelocation; - -// coff symbol table - -#define PE_CoffSymbolStorageClass_XList(X)\ -X(END_OF_FUNCTION, 0xFF)\ -X(NULL, 0)\ -X(AUTOMATIC, 1)\ -X(EXTERNAL, 2)\ -X(STATIC, 3)\ -X(REGISTER, 4)\ -X(EXTERNAL_DEF, 5)\ -X(LABEL, 6)\ -X(UNDEFINED_LABEL, 7)\ -X(MEMBER_OF_STRUCT, 8)\ -X(ARGUMENT, 9)\ -X(STRUCT_TAG, 10)\ -X(MEMBER_OF_UNION, 11)\ -X(UNION_TAG, 12)\ -X(TYPE_DEFINITION, 13)\ -X(UNDEFINED_STATIC, 14)\ -X(ENUM_TAG, 15)\ -X(MEMBER_OF_ENUM, 16)\ -X(REGISTER_PARAM, 17)\ -X(BIT_FIELD, 18)\ -X(BLOCK, 100)\ -X(FUNCTION, 101)\ -X(END_OF_STRUCT, 102)\ -X(FILE, 103)\ -X(SECTION, 104)\ -X(WEAK_EXTERNAL, 105)\ -X(CLR_TOKEN, 107) - -typedef U8 PE_CoffSymbolStorageClass; -enum{ -#define X(N,C) PE_CoffSymbolStorageClass_##N = C, - PE_CoffSymbolStorageClass_XList(X) -#undef X -}; - -#define PE_CoffSymbolTypeLo_XList(X)\ -X(NULL, 0)\ -X(VOID, 1)\ -X(CHAR, 2)\ -X(SHORT, 3)\ -X(INT, 4)\ -X(LONG, 5)\ -X(FLOAT, 6)\ -X(DOUBLE, 7)\ -X(STRUCT, 8)\ -X(UNION, 9)\ -X(ENUM, 10)\ -X(MOE, 11)\ -X(BYTE, 12)\ -X(WORD, 13)\ -X(UINT, 14)\ -X(DWORD, 15) - -enum{ -#define X(N,C) PE_CoffSymbolTypeLo_##N = C, - PE_CoffSymbolTypeLo_XList(X) -#undef X -}; - -#define PE_CoffSymbolTypeHi_XList(X)\ -X(NULL, 0)\ -X(POINTER, 1)\ -X(FUNCTION, 2)\ -X(ARRAY, 3) - -enum{ -#define X(N,C) PE_CoffSymbolTypeHi_##N = C, - PE_CoffSymbolTypeHi_XList(X) -#undef X -}; - -typedef U16 PE_CoffSymbolType; - -typedef struct PE_CoffSymbolRecord{ - union{ - U8 raw[8]; - struct{ - U32 zero; - U32 string_table_off; - }; - } name; - U32 val; - S16 sec_number; - PE_CoffSymbolType type; - PE_CoffSymbolStorageClass storage_class; - U8 aux_symbol_count; -} PE_CoffSymbolRecord; - -// TODO(allen): COFF Line Numbers (Deprecated) - -// TODO(allen): Attribute Certificate Table - -// TODO(allen): Delay-Load Import Tables - -// export data - -typedef struct PE_ExportDirectoryTable{ - U32 reserved0; - U32 time_stamp; - U16 version_major; - U16 version_minor; - U32 binary_name_voff; - U32 ordinal_base; - U32 export_count; - U32 ordinal_count; - U32 export_voff; - U32 name_voff; - U32 ordinal_voff; - - /* - ** NOTE(allen): Additional Structure Information -** export : [ export_count] U32; -** name : [ordinal_count] U32; -** ordinal : [ordinal_count] U16; -** -** for i in [0,ordinal_count): -** name[i] : voff to a cstring -** export[ordinal[i]] : voff to a symbol OR voff to forwarding string -** (voffs in export range are fowarding strings) -** ordinal[i] : "unbiased ordinal" -** ordinal[i] + ordinal_base : "ordinal" - */ - -} PE_ExportDirectoryTable; - -// import data - -typedef struct PE_ImportDirectoryTable{ - U32 import_lookup_table_voff; - U32 time_stamp; - U32 first_forwarder_idx; - U32 name_voff; - U32 import_address_table_voff; - - /* - ** NOTE(allen): Additional Structure Information -** IF PE32: -** import_lookup_table : null-terminated [] U32; -** IF PE32Plus: -** import_lookup_table : null-terminated [] U64; -** -** WHEN lookup = import_lookup_table[i] -** if (HighBits1(lookup)){ - ** ordinal = LowBits16(lookup) -** } -** else{ -** hint_voff = LowBits31(lookup) -** hint : { U16, null-terminated-cstring } -** } - */ - -} PE_ImportDirectoryTable; - -// exception handling - -#if 0 -// NOTE(allen): untested - from documentation -typedef struct PE_ExceptionHandler_MIPS{ - U32 begin_vaddr; - U32 end_vaddr; - U32 handler_vaddr; - U32 handler_data; - U32 prolog_end_vaddr; -} PE_ExceptionHandler_MIPS; -#endif - -#if 0 -// NOTE(allen): untested - from documentation -typedef struct PE_ExceptionHandler_ARM{ - U32 begin_vaddr; - U32 info; -} PE_ExceptionHandler_ARM; - -#define PE_ExceptionHandler_ARMInfo_PrologLength(i) (((i) >> 0)&0xFF) -#define PE_ExceptionHandler_ARMInfo_FuncLength(i) (((i) >> 8)&0x3FFFFF) -#define PE_ExceptionHandler_ARMInfo_32Bit(i) (((i) >> 30)&0x1) -#define PE_ExceptionHandler_ARMInfo_ExceptionFlag(i) (((i) >> 31)&0x1) -#endif - -typedef struct PE_ExceptionHandler_X86{ - U32 begin_voff; - U32 end_voff; - U32 unwind_voff; -} PE_ExceptionHandler_X86; - -typedef struct PE_UnwindInfo{ - U8 header; - U8 prolog_size; - U8 code_count; - U8 frame; -} PE_UnwindInfo; - -#define PE_UnwindInfo_VersionFromHeader(x) ((x)&7) -#define PE_UnwindInfo_FlagsFromHeader(x) (((x)>>3)&0x1F) - -#define PE_UnwindInfoFlags_XList(X)\ -X(EHANDLER, (1 << 0))\ -X(UHANDLER, (1 << 1))\ -X(CHAINED, (1 << 2)) - -typedef U8 PE_UnwindInfoFlags; -enum{ -#define X(N,C) PE_UnwindInfoFlag_##N = C, - PE_UnwindInfoFlags_XList(X) -#undef X -}; - -#define PE_UnwindInfo_RegFromFrame(x) ((x)&0xF) -#define PE_UnwindInfo_OffFromFrame(x) (((x) >> 4)&0xF) - -#define PE_UnwindOpCode_XList(X)\ -X(PUSH_NONVOL, 0)\ -X(ALLOC_LARGE, 1)\ -X(ALLOC_SMALL, 2)\ -X(SET_FPREG, 3)\ -X(SAVE_NONVOL, 4)\ -X(SAVE_NONVOL_FAR, 5)\ -X(EPILOG, 6)\ -X(SPARE, 7)\ -X(SAVE_XMM128, 8)\ -X(SAVE_XMM128_FAR, 9)\ -X(PUSH_MACHFRAME, 10) - -typedef U8 PE_UnwindOpCode; -enum{ -#define X(N,C) PE_UnwindOpCode_##N = C, - PE_UnwindOpCode_XList(X) -#undef X -}; - -#define PE_UnwindCode_GetOffset(x) (((x) >> 0)&0xFF) -#define PE_UnwindCode_GetOpCode(x) (((x) >> 8)&0x0F) -#define PE_UnwindCode_GetOpInfo(x) (((x) >> 12)&0x0F) - -#define PE_UnwindCode_FromOffsetOpCodeInfo(off,opcode,opinfo) \ -(((off)&0xFF) | (((opcode)&0xF) << 8) | (((opinfo)&0xF) << 12)) - -#define PE_UnwindRegX64_XList(X)\ -X(RAX, 0)\ -X(RCX, 1)\ -X(RDX, 2)\ -X(RBX, 3)\ -X(RSP, 4)\ -X(RBP, 5)\ -X(RSI, 6)\ -X(RDI, 7)\ -X(R8, 8)\ -X(R9, 9)\ -X(R10, 10)\ -X(R11, 11)\ -X(R12, 12)\ -X(R13, 13)\ -X(R14, 14)\ -X(R15, 15) - -typedef U8 PE_UnwindRegX64; -enum{ -#define X(N,C) PE_UnwindRegX64_##N = C, - PE_UnwindRegX64_XList(X) -#undef X -}; - -// base relocations - -typedef struct PE_BaseRelocationBlock{ - U32 page_voff; - U32 block_size; -} PE_BaseRelocationBlock; - -typedef U16 PE_BaseRelocationTypeOffset; - -#define PE_BaseRelocationType_XList(X)\ -X(ABSOLUTE, 0)\ -X(HIGH, 1)\ -X(LOW, 2)\ -X(HIGHLOW, 3)\ -X(HIGHADJ, 4)\ -X(SPLIT1, 5)\ -X(SPLIT2, 7)\ -X(SPLIT3, 8)\ -X(MIPS_JMPADDR16, 9)\ -X(DIR64, 10) - -typedef U8 PE_BaseRelocationType; -enum{ -#define X(N,C) PE_BaseRelocationType_##N = C, - PE_BaseRelocationType_XList(X) -#undef X -}; - -// SPLIT1: MIPS -> JMPADDR, ARM -> MOV32, RISCV -> HIGH20 -// SPLIT2: THUMB -> MOV32, RISCV -> LOW12I -// SPLIT3: RISCV -> LOW12S, LOONGARCH32 -> MARK_LA, LOONGARCH64 -> MARK_LA - -#define PE_BaseRelocationTypeOffset_GetType(X) (((X) >> 12)&0xF) -#define PE_BaseRelocationTypeOffset_GetOff(X) (((X) >> 0)&0xFFF) - -// debug - -#define PE_DebugType_XList(X)\ -X(UNKNOWN, 0)\ -X(COFF, 1)\ -X(CODEVIEW, 2)\ -X(FPO, 3)\ -X(MISC, 4)\ -X(EXCEPTION, 5)\ -X(OMAP_TO_SRC, 7)\ -X(OMAP_FROM_SRC, 8)\ -X(BORLAND, 9)\ -X(RESERVED10, 10)\ -X(CLSID, 11)\ -X(VC_FEATURE, 12)\ -X(POGO, 13)\ -X(ILTCG, 14)\ -X(MPX, 15)\ -X(REPRO, 16)\ -X(EMBEDDED, 17)\ -X(CRYPTOHASH, 19)\ -X(EX_DLLCHARACTERISTICS, 20) - -typedef U32 PE_DebugType; -enum{ -#define X(N,C) PE_DebugType_##N = C, - PE_DebugType_XList(X) -#undef X -}; - -typedef struct PE_DebugDirectory{ - U32 reserved0; - U32 time_stamp; - U16 version_major; - U16 version_minor; - PE_DebugType type; - U32 size; - U32 voff; - U32 foff; -} PE_DebugDirectory; - -/* TODO(allen): -** [ ] Can we gather information about debug info types besides CODEVIEW ? -** [ ] What are the first 24 bytes of the CODEVIEW data range? -*/ - -// tls table - -typedef U32 PE_TlsDirectoryFlags; -#define PE_TLSDirectoryFlags_GetAlign(X) ((X)&0x00F00000) - -typedef struct PE_TLSDirectory32{ - U32 raw_data_start_vaddr; - U32 raw_data_end_vaddr; - U32 tls_index_vaddr; - U32 tls_callback_array_vaddr; - U32 zero_fill_size; - PE_TlsDirectoryFlags flags; -} PE_TLSDirectory32; - -typedef struct PE_TLSDirectory32Plus{ - U64 raw_data_start_vaddr; - U64 raw_data_end_vaddr; - U64 tls_index_vaddr; - U64 tls_callback_array_vaddr; - U32 zero_fill_size; - PE_TlsDirectoryFlags flags; -} PE_TLSDirectory32Plus; - -// load config table - -/* TODO(allen): pneumonics for load config that are missing -** [ ] global flags -** [ ] process affinity mask -** [ ] process heap flags -** [ ] control flow guard flags -*/ - -typedef struct PE_LoadConfig32{ - U32 reserved0; - U32 time_stamp; - U16 version_major; - U16 version_minor; - U32 global_flags_clear; - U32 global_flags_set; - U32 default_critical_section_timeout; - U32 decommit_free_block_threshold; - U32 decommit_total_free_threshold; - U32 lock_prefix_table_vaddr; - U32 maximum_allocation_size; - U32 virtual_memory_threshold; - U32 process_affinity_mask; - U32 process_heap_flags; - U16 csd_version; - U16 reserved1; - U32 reserved2; - U32 security_cookie_vaddr; - U32 seh_table_vaddr; - U32 seh_count; - U32 control_flow_guard_check_function_vaddr; - U32 control_flow_guard_dispatch_function_vaddr; - U32 control_flow_guard_function_table_vaddr; - U32 control_flow_guard_function_count; - U32 control_flow_guard_flags; - U8 code_integrity[12]; - U32 guard_address_taken_iat_table_vaddr; - U32 guard_address_taken_iat_count; - U32 guard_long_jump_target_table_vaddr; - U32 guard_long_jump_target_count; -} PE_LoadConfig32; - -typedef struct PE_LoadConfig64{ - U32 reserved0; - U32 time_stamp; - U16 version_major; - U16 version_minor; - U32 global_flags_clear; - U32 global_flags_set; - U32 default_critical_section_timeout; - U64 decommit_free_block_threshold; - U64 decommit_total_free_threshold; - U64 lock_prefix_table_vaddr; - U64 maximum_allocation_size; - U64 virtual_memory_threshold; - U64 process_affinity_mask; - U32 process_heap_flags; - U16 csd_version; - U16 reserved1; - U64 reserved2; - U64 security_cookie_vaddr; - U64 seh_table_vaddr; - U64 seh_count; - U64 control_flow_guard_check_function_vaddr; - U64 control_flow_guard_dispatch_function_vaddr; - U64 control_flow_guard_function_table_vaddr; - U64 control_flow_guard_function_count; - U32 control_flow_guard_flags; - U8 code_integrity[12]; - U64 guard_address_taken_iat_table_vaddr; - U64 guard_address_taken_iat_count; - U64 guard_long_jump_target_table_vaddr; - U64 guard_long_jump_target_count; -} PE_LoadConfig64; - -#pragma pack(pop) - -#endif //MR4TH_PE_H diff --git a/src/binary/pe/mr4th_pe.img_get_section.c b/src/binary/pe/mr4th_pe.img_get_section.c deleted file mode 100644 index a89784d..0000000 --- a/src/binary/pe/mr4th_pe.img_get_section.c +++ /dev/null @@ -1,68 +0,0 @@ -//////////////////////////////// -// PE Get Section Function - -static RangePtr -pe_img_get_section(void *image_raw, String8 name){ - U8 *image = (U8*)image_raw; - - // dos header - PE_DosHeader *dos_header = (PE_DosHeader*)(image); - if (dos_header->magic != PE_MSDOS_MAGIC){ - dos_header = 0; - } - - // coff header - PE_CoffHeader *coff_header = 0; - U64 after_coff_header_offset = 0; - if (dos_header != 0){ - U64 pe_signature_offset = dos_header->coff_file_offset; - U64 coff_header_offset = pe_signature_offset + sizeof(U32); - U32 pe_signature = *(U32*)(image + pe_signature_offset); - if (pe_signature == PE_SIGNATURE){ - coff_header = (PE_CoffHeader*)(image + coff_header_offset); - after_coff_header_offset = coff_header_offset + sizeof(*coff_header); - } - } - - // section table offset & count - U32 section_table_offset = 0; - U32 section_count = 0; - if (coff_header != 0){ - section_table_offset = ( after_coff_header_offset - + coff_header->optional_header_size); - section_count = coff_header->section_count; - } - - // scan section table - PE_SectionHeader *match = 0; - { - PE_SectionHeader *sec = (PE_SectionHeader*)(image + section_table_offset); - for (U32 i = 0; i < section_count; i += 1, sec += 1){ - U8 *secptr = sec->name; - U8 *secopl = sec->name + sizeof(sec->name); - U8 *keyptr = name.str; - U8 *keyopl = name.str + name.size; - B32 string_match = 1; - for (;*secptr != 0 && secptr < secopl && keyptr < keyopl; - secptr += 1, keyptr += 1){ - if (*secptr != *keyptr){ - string_match = 0; - break; - } - } - if (string_match){ - match = sec; - break; - } - } - } - - // fill result - RangePtr result = {0}; - if (match != 0){ - result.first = image + match->voff; - result.opl = result.first + match->vsize; - } - - return(result); -} diff --git a/src/binary/pe/mr4th_pe.img_get_section.h b/src/binary/pe/mr4th_pe.img_get_section.h deleted file mode 100644 index 20da32f..0000000 --- a/src/binary/pe/mr4th_pe.img_get_section.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef MR4TH_PE_IMG_GET_SECTION_H -#define MR4TH_PE_IMG_GET_SECTION_H - -//////////////////////////////// -// PE Get Section Function - -static RangePtr pe_img_get_section(void *image, String8 name); - -#endif //MR4TH_PE_IMG_GET_SECTION_H diff --git a/src/example1.h b/src/example1.h deleted file mode 100644 index d9324bf..0000000 --- a/src/example1.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef EXAMPLE1_H -#define EXAMPLE1_H - -//////////////////////////////// -// Setup a Symbol Set - -typedef struct EX1_Ctx{ - S64 foo; -} EX1_Ctx; - -typedef void EX1_Hook(EX1_Ctx *ctx); - -typedef struct EX1_Command{ - String8 name; - String8 description; - EX1_Hook *hook; -} EX1_Command; - -#define SYMBOL_SET_DEFINE EX1_CommandSymbols -#define EX1_CommandSymbols_Type EX1_Command -#define EX1_CommandSymbols_section ".ex1CMD" -#include "symbol_set.define.h" - -#define COMMAND_ID(N) SymbolID(EX1_CommandSymbols, N) -#define COMMAND_DECL(N) SymbolDeclare(EX1_CommandSymbols, N) -#define COMMAND_METADATA_FROM_ID(id) SymbolMetadataFromID(EX1_CommandSymbols, id) - -#define COMMAND_SCRIPT(N,desc) \ -void cmdhook_##N(EX1_Ctx *ctx); \ -SymbolDefine(EX1_CommandSymbols, N) = { str8_lit_const(#N), str8_lit_const(desc), cmdhook_##N };\ -void cmdhook_##N(EX1_Ctx *ctx) - -#endif //EXAMPLE1_H diff --git a/src/symbol_set.define.h b/src/symbol_set.define.h deleted file mode 100644 index a399112..0000000 --- a/src/symbol_set.define.h +++ /dev/null @@ -1,13 +0,0 @@ -static U64 SymbolCount(SYMBOL_SET_DEFINE) = 0; -static SYMBOL__TYPE(SYMBOL_SET_DEFINE) * SymbolBasePtr(SYMBOL_SET_DEFINE) = 0; -static SYMBOL__TYPE(SYMBOL_SET_DEFINE) SYMBOL__SYM(SYMBOL_SET_DEFINE,0); -#if COMPILER_CL -# pragma SYMBOL__CL_PRAGMA -#endif -BEFORE_MAIN(){ - void *image = os_this_image(); - RangePtr range = self_img_get_section(image, str8_lit(GLUE(SYMBOL_SET_DEFINE,_section))); - SymbolCount(SYMBOL_SET_DEFINE) = (range.opl - range.first)/sizeof(SYMBOL__TYPE(SYMBOL_SET_DEFINE)); - SymbolBasePtr(SYMBOL_SET_DEFINE) = (SYMBOL__TYPE(SYMBOL_SET_DEFINE)*)(range.first); -} -#undef SYMBOL_SET_DEFINE diff --git a/src/symbol_set.h b/src/symbol_set.h deleted file mode 100644 index 3b11ed7..0000000 --- a/src/symbol_set.h +++ /dev/null @@ -1,78 +0,0 @@ -/* -** Symbol Set -*/ - -#ifndef SYMBOL_SET_H -#define SYMBOL_SET_H - -//////////////////////////////// -// Copy-Pastable -#if 0 - -// To define a Symbol Set -#define SYMBOL_SET_DEFINE example_name -#define example_name_Type ExampleType -#define example_name_section ".exmpl" -#include "symbol_set.define.h" - -// Common wrappers to put around a Symbol Set (after defining it) -#define EXAMPLE_ID(N) SymbolID(example_name, N) -#define EXAMPLE_RAW(N) SymbolRaw(example_name, N) -#define EXAMPLE_DECL(N) SymbolDeclare(example_name, N) - -#endif - -//////////////////////////////// -// Symbol Set System - -// Symbol Set key terms: -// "count" - the total number of symbols in this set -// "baseptr" - the pointer to the base of the array of symbol data -// "oplptr" - the pointer to the spot one past the last element of the array -// "id" - the 1-based index of the symbol -// id=0 stands for the special 'nil' case -// id values in the range [1,count] stand for a symbol in the set -// "metadata" - a pointer to the slot in the array for this symbol -// "raw" - address of the metadata as an integer - -// user interface - -#define SymbolDeclare(E,N) \ -SECTION(E##_section) extern SYMBOL__TYPE(E) SYMBOL__SYM(E,N) - -#define SymbolDefine(E,N) DO_NOT_ELIMINATE(SYMBOL__SYM(E,N)) SymbolDeclare(E,N) -#define SymbolDefineNameless(E) SymbolDefine(E,GLUE(auto,__COUNTER__)) - -#define SymbolCount(E) GLUE(E, _count) -#define SymbolBasePtr(E) GLUE(E, _baseptr) -#define SymbolOplPtr(E) (SymbolBasePtr(E) + SymbolCount(E)) - -#define SymbolMetadata(E,N) (&SYMBOL__SYM(E,N)) -#define SymbolID(E,N) SymbolIDFromMetadata(E, SymbolMetadata(E,N)) - -#define SymbolIDFromMetadata(E,ptr) \ -((SymbolBasePtr(E) <= (ptr) && (ptr) < SymbolOplPtr(E))? \ -(U32)(1 + ((ptr) - SymbolBasePtr(E))):0) - -#define SymbolMetadataFromID(E,id) \ -((1 <= (id) && (id) <= SymbolCount(E))?\ -(SymbolBasePtr(E) + (id) - 1):(&SYMBOL__SYM(E,0))) - -#define SymbolRaw(E,N) IntFromPtr(SymbolMetadata(E,N)) -#define SymbolIDFromRaw(E,raw) SymbolIDFromMetadata(E,(SYMBOL__TYPE(E)*)(PtrFromInt(raw))) -#define SymbolRawFromID(E,id) IntFromPtr(SymbolMetadataFromID(E,id)) - -// internal - -#define SYMBOL__TYPE(E) GLUE(E,_Type) -#define SYMBOL__SYM(E,N) GLUE(E, GLUE(__, N)) - -#define SYMBOL__RAW_BASE(E) IntFromPtr(SymbolBasePtr(E)) -#define SYMBOL__RAW_OPL(E) (SYMBOL__RAW_BASE(E) + SymbolCount(E)*sizeof(SYMBOL__TYPE(E))) - -#define SYMBOL__BEFORE_MAIN_NAME GLUE(SYMBOL_SET_DEFINE,__init) -#define SYMBOL__CL_PRAGMA section(GLUE(SYMBOL_SET_DEFINE,_section),read,write) - - -#endif //SYMBOL_SET_H - diff --git a/symbol_set.ld_meta/mr4th/exefmt/elf/mr4th_elf.c b/symbol_set.ld_meta/mr4th/exefmt/elf/mr4th_elf.c new file mode 100644 index 0000000..fa701cc --- /dev/null +++ b/symbol_set.ld_meta/mr4th/exefmt/elf/mr4th_elf.c @@ -0,0 +1,480 @@ +//////////////////////////////// +// Functions: ELF + +// normalizing + +MR4TH_SYMBOL_STATIC U16 +elf_u16_decode(void *ptr, B32 flip){ + U16 result = 0; + if (flip){ + U8 *buf = (U8*)ptr; + result = buf[1] | (buf[0] << 8); + } + else{ + result = *(U16*)ptr; + } + return(result); +} + +MR4TH_SYMBOL_STATIC U32 +elf_u32_decode(void *ptr, B32 flip){ + U32 result = 0; + if (flip){ + U8 *buf = (U8*)ptr; + result = buf[3] | (buf[2] << 8) | (buf[1] << 16) | (buf[0] << 24); + } + else{ + result = *(U32*)ptr; + } + return(result); +} + +MR4TH_SYMBOL_STATIC U64 +elf_u64_decode(void *ptr, B32 flip){ + U64 result = 0; + if (flip){ + U8 *buf = (U8*)ptr; + result = ((((U64)buf[7]) << 0) | (((U64)buf[6]) << 8) | + (((U64)buf[5]) << 16) | (((U64)buf[4]) << 24) | + (((U64)buf[3]) << 32) | (((U64)buf[2]) << 40) | + (((U64)buf[1]) << 48) | (((U64)buf[0]) << 56)); + } + else{ + result = *(U64*)ptr; + } + return(result); +} + +MR4TH_SYMBOL_STATIC void +elf_header64_from_header32(ELF_Header64 *dst, ELF_Header32 *src, ELF_Encoding src_encoding){ + B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB); + B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN); + + MemoryCopyArray(dst->ident, src->ident); + dst->type = elf_u16_decode(&src->type, flip_order); + dst->machine = elf_u16_decode(&src->machine, flip_order); + dst->version = elf_u32_decode(&src->version, flip_order); + dst->entry = (U64)elf_u32_decode(&src->entry, flip_order); + dst->segment_table_foff = (U64)elf_u32_decode(&src->segment_table_foff, flip_order); + dst->section_table_foff = (U64)elf_u32_decode(&src->section_table_foff, flip_order); + dst->flags = elf_u32_decode(&src->flags, flip_order); + dst->header_size = elf_u16_decode(&src->header_size, flip_order); + dst->segment_size = elf_u16_decode(&src->segment_size, flip_order); + dst->segment_count = elf_u16_decode(&src->segment_count, flip_order); + dst->section_size = elf_u16_decode(&src->section_size, flip_order); + dst->section_count = elf_u16_decode(&src->section_count, flip_order); + dst->string_section_index = elf_u16_decode(&src->string_section_index, flip_order); +} + +MR4TH_SYMBOL_STATIC void +elf_header64_from_header64(ELF_Header64 *dst, ELF_Header64 *src, ELF_Encoding src_encoding){ + B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB); + B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN); + + if (!flip_order){ + MemoryCopyStruct(dst, src); + } + else{ + MemoryCopyArray(dst->ident, src->ident); + dst->type = elf_u16_decode(&src->type, 1); + dst->machine = elf_u16_decode(&src->machine, 1); + dst->version = elf_u32_decode(&src->version, 1); + dst->entry = elf_u32_decode(&src->entry, 1); + dst->segment_table_foff = elf_u32_decode(&src->segment_table_foff, 1); + dst->section_table_foff = elf_u32_decode(&src->section_table_foff, 1); + dst->flags = elf_u32_decode(&src->flags, 1); + dst->header_size = elf_u16_decode(&src->header_size, 1); + dst->segment_size = elf_u16_decode(&src->segment_size, 1); + dst->segment_count = elf_u16_decode(&src->segment_count, 1); + dst->section_size = elf_u16_decode(&src->section_size, 1); + dst->section_count = elf_u16_decode(&src->section_count, 1); + dst->string_section_index = elf_u16_decode(&src->string_section_index, 1); + } +} + +MR4TH_SYMBOL_STATIC void +elf_section64_from_section32(ELF_Section64 *dst, ELF_Section32 *src, ELF_Encoding src_encoding){ + B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB); + B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN); + + dst->name = elf_u32_decode(&src->name, flip_order); + dst->type = elf_u32_decode(&src->type, flip_order); + dst->flags = elf_u32_decode(&src->flags, flip_order); + dst->addr = elf_u32_decode(&src->addr, flip_order); + dst->offset = elf_u32_decode(&src->offset, flip_order); + dst->size = elf_u32_decode(&src->size, flip_order); + dst->link = elf_u32_decode(&src->link, flip_order); + dst->info = elf_u32_decode(&src->info, flip_order); + dst->addralign = elf_u32_decode(&src->addralign, flip_order); + dst->entsize = elf_u32_decode(&src->entsize, flip_order); +} + +MR4TH_SYMBOL_STATIC void +elf_section64_from_section64(ELF_Section64 *dst, ELF_Section64 *src, ELF_Encoding src_encoding){ + B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB); + B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN); + + if (!flip_order){ + MemoryCopyStruct(dst, src); + } + else{ + dst->name = elf_u32_decode(&src->name, 1); + dst->type = elf_u32_decode(&src->type, 1); + dst->flags = elf_u64_decode(&src->flags, 1); + dst->addr = elf_u64_decode(&src->addr, 1); + dst->offset = elf_u64_decode(&src->offset, 1); + dst->size = elf_u64_decode(&src->size, 1); + dst->link = elf_u32_decode(&src->link, 1); + dst->info = elf_u32_decode(&src->info, 1); + dst->addralign = elf_u64_decode(&src->addralign, 1); + dst->entsize = elf_u64_decode(&src->entsize, 1); + } +} + +MR4TH_SYMBOL_STATIC void +elf_segment64_from_segment32(ELF_Segment64 *dst, ELF_Segment32 *src, ELF_Encoding src_encoding){ + B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB); + B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN); + + dst->type = elf_u32_decode(&src->type, flip_order); + dst->offset = elf_u32_decode(&src->offset, flip_order); + dst->vaddr = elf_u32_decode(&src->vaddr, flip_order); + dst->paddr = elf_u32_decode(&src->paddr, flip_order); + dst->file_size = elf_u32_decode(&src->file_size, flip_order); + dst->memory_size = elf_u32_decode(&src->memory_size, flip_order); + dst->flags = elf_u32_decode(&src->flags, flip_order); + dst->align = elf_u32_decode(&src->align, flip_order); +} + +MR4TH_SYMBOL_STATIC void +elf_segment64_from_segment64(ELF_Segment64 *dst, ELF_Segment64 *src, ELF_Encoding src_encoding){ + B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB); + B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN); + + if (!flip_order){ + MemoryCopyStruct(dst, src); + } + else{ + dst->type = elf_u32_decode(&src->type, 1); + dst->flags = elf_u32_decode(&src->flags, 1); + dst->offset = elf_u64_decode(&src->offset, 1); + dst->vaddr = elf_u64_decode(&src->vaddr, 1); + dst->paddr = elf_u64_decode(&src->paddr, 1); + dst->file_size = elf_u64_decode(&src->file_size, 1); + dst->memory_size = elf_u64_decode(&src->memory_size, 1); + dst->align = elf_u64_decode(&src->align, 1); + } +} + +MR4TH_SYMBOL_STATIC void +elf_symbol64_from_symbol32(ELF_Symbol64 *dst, ELF_Symbol32 *src, ELF_Encoding src_encoding){ + B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB); + B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN); + + dst->name = elf_u32_decode(&src->name, flip_order); + dst->value = elf_u32_decode(&src->value, flip_order); + dst->size = elf_u32_decode(&src->size, flip_order); + dst->info = src->info; + dst->other = src->other; + dst->section_index = elf_u16_decode(&src->section_index, flip_order); +} + +MR4TH_SYMBOL_STATIC void +elf_symbol64_from_symbol64(ELF_Symbol64 *dst, ELF_Symbol64 *src, ELF_Encoding src_encoding){ + B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB); + B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN); + + if (!flip_order){ + MemoryCopyStruct(dst, src); + } + else{ + dst->name = elf_u32_decode(&src->name, 1); + dst->info = src->info; + dst->other = src->other; + dst->section_index = elf_u16_decode(&src->section_index, 1); + dst->value = elf_u64_decode(&src->value, 1); + dst->size = elf_u64_decode(&src->size, 1); + } +} + +MR4TH_SYMBOL_STATIC void +elf_reloca64_from_reloc32(ELF_RelocationAdd64 *dst, ELF_Relocation32 *src, ELF_Encoding src_encoding){ + B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB); + B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN); + dst->offset = elf_u32_decode(&src->offset, flip_order); + dst->info = elf_u32_decode(&src->info, flip_order); + dst->addend = 0; +} + +MR4TH_SYMBOL_STATIC void +elf_reloca64_from_reloc64(ELF_RelocationAdd64 *dst, ELF_Relocation64 *src, ELF_Encoding src_encoding){ + B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB); + B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN); + dst->offset = elf_u64_decode(&src->offset, flip_order); + dst->info = elf_u64_decode(&src->info, flip_order); + dst->addend = 0; +} + +MR4TH_SYMBOL_STATIC void +elf_reloca64_from_reloca32(ELF_RelocationAdd64 *dst, ELF_RelocationAdd32 *src, ELF_Encoding src_encoding){ + B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB); + B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN); + dst->offset = elf_u32_decode(&src->offset, flip_order); + dst->info = elf_u32_decode(&src->info, flip_order); + dst->addend = (S32)elf_u32_decode(&src->addend, flip_order); +} + +MR4TH_SYMBOL_STATIC void +elf_reloca64_from_reloca64(ELF_RelocationAdd64 *dst, ELF_RelocationAdd64 *src, ELF_Encoding src_encoding){ + B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB); + B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN); + dst->offset = elf_u64_decode(&src->offset, flip_order); + dst->info = elf_u64_decode(&src->info, flip_order); + dst->addend = (S64)elf_u64_decode(&src->addend, flip_order); +} + +// strings + +MR4TH_SYMBOL_STATIC String8 +elf_str8_from_identification_idx(ELF_IdentificationIdx idx){ + String8 result = str8_lit("ERROR"); + switch (idx){ +#define X(N,C) case C: result = str8_lit(#N); break; + ELF_IdentificationIdx_XList(X) +#undef X + } + return(result); +} + +MR4TH_SYMBOL_STATIC String8 +elf_str8_from_class(ELF_Class elf_class){ + String8 result = str8_lit("ERROR"); + switch (elf_class){ +#define X(N,C) case C: result = str8_lit(#N); break; + ELF_Class_XList(X) +#undef X + } + return(result); +} + +MR4TH_SYMBOL_STATIC String8 +elf_str8_from_encoding(ELF_Encoding elf_encoding){ + String8 result = str8_lit("ERROR"); + switch (elf_encoding){ +#define X(N,C) case C: result = str8_lit(#N); break; + ELF_Encoding_XList(X) +#undef X + } + return(result); +} + +MR4TH_SYMBOL_STATIC String8 +elf_str8_from_osabi(ELF_OsAbiExtension elf_osabi){ + String8 result = str8_lit("ERROR"); + switch (elf_osabi){ +#define X(N,C) case C: result = str8_lit(#N); break; + ELF_OsAbiExtension_XList(X) +#undef X + } + return(result); +} + +MR4TH_SYMBOL_STATIC String8 +elf_str8_from_file_type(ELF_FileType file_type){ + String8 result = str8_lit("ERROR"); + switch (file_type){ +#define X(N,C) case C: result = str8_lit(#N); break; + ELF_FileType_XList(X) +#undef X + } + return(result); +} + +MR4TH_SYMBOL_STATIC String8 +elf_str8_from_machine_type(ELF_FileType machine_type){ + String8 result = str8_lit("ERROR"); + switch (machine_type){ +#define X(N,C) case C: result = str8_lit(#N); break; + ELF_MachineType_XList(X) +#undef X + } + return(result); +} + +MR4TH_SYMBOL_STATIC String8 +elf_str8_from_section_type(ELF_SectionType section_type){ + String8 result = str8_lit("ERROR"); + + if (ELF_SectionType_LOOS <= section_type && + section_type <= ELF_SectionType_HIOS){ + result = str8_lit("OS-CUSTOM-TYPE"); + } + + else if (ELF_SectionType_LOPROC <= section_type && + section_type <= ELF_SectionType_HIPROC){ + result = str8_lit("PROC-CUSTOM-TYPE"); + } + + else if (ELF_SectionType_LOUSER <= section_type && + section_type <= ELF_SectionType_HIUSER){ + result = str8_lit("USER-CUSTOM-TYPE"); + } + + else{ + switch (section_type){ +#define X(N,C) case C: result = str8_lit(#N); break; + ELF_SectionType_XList(X) +#undef X + } + } + + return(result); +} + +MR4TH_SYMBOL_STATIC void +elf_str8list_from_section_flags(Arena *arena, String8List *out, U32 flags){ + U32 f = 1; + for (U32 i = 0; i < 20; i += 1){ + if ((flags & f) != 0){ + switch (f){ +#define X(N,C) case C: str8_list_push(arena, out, str8_lit(#N)); break; + ELF_SectionFlags_XList(X) +#undef X + } + } + f <<= 1; + } + if ((flags & ELF_SectionFlag_MASKOS) != 0){ + str8_list_pushf(arena, out, "OS-CUSTOM(%02x)", (flags >> 20)&0xFF); + } + if ((flags & ELF_SectionFlag_MASKPROC) != 0){ + str8_list_pushf(arena, out, "PROC-CUSTOM(%01x)", (flags >> 28)&0xF); + } +} + +MR4TH_SYMBOL_STATIC String8 +elf_str8_from_section_flags(Arena *arena, U32 flags){ + ArenaTemp scratch = arena_get_scratch(&arena, 1); + String8List list = {0}; + elf_str8list_from_section_flags(scratch.arena, &list, flags); + String8 result = str8_join_flags(arena, &list); + arena_release_scratch(&scratch); + return(result); +} + +MR4TH_SYMBOL_STATIC String8 +elf_str8_from_segment_type(ELF_SegmentType segment_type){ + String8 result = str8_lit("ERROR"); + + if (ELF_SegmentType_LOOS <= segment_type && + segment_type <= ELF_SegmentType_HIOS){ + result = str8_lit("OS-CUSTOM-TYPE"); + } + + else if (ELF_SegmentType_LOPROC <= segment_type && + segment_type <= ELF_SegmentType_HIPROC){ + result = str8_lit("PROC-CUSTOM-TYPE"); + } + + else{ + switch (segment_type){ +#define X(N,C) case C: result = str8_lit(#N); break; + ELF_SegmentType_XList(X) +#undef X + } + } + + return(result); +} + +MR4TH_SYMBOL_STATIC void +elf_str8list_from_segment_flags(Arena *arena, String8List *out, ELF_SegmentFlags flags){ + U32 f = 1; + for (U32 i = 0; i < 20; i += 1){ + if ((flags & f) != 0){ + switch (f){ +#define X(N,C) case C: str8_list_push(arena, out, str8_lit(#N)); break; + ELF_SegmentFlags_XList(X) +#undef X + } + } + f <<= 1; + } + if ((flags & ELF_SegmentFlag_MASKOS) != 0){ + str8_list_pushf(arena, out, "OS-CUSTOM(%02x)", (flags >> 20)&0xFF); + } + if ((flags & ELF_SegmentFlag_MASKPROC) != 0){ + str8_list_pushf(arena, out, "PROC-CUSTOM(%01x)", (flags >> 28)&0xF); + } +} + +MR4TH_SYMBOL_STATIC String8 +elf_str8_from_segment_flags(Arena *arena, ELF_SegmentFlags flags){ + ArenaTemp scratch = arena_get_scratch(&arena, 1); + String8List list = {0}; + elf_str8list_from_segment_flags(scratch.arena, &list, flags); + String8 result = str8_join_flags(arena, &list); + arena_release_scratch(&scratch); + return(result); +} + +MR4TH_SYMBOL_STATIC String8 +elf_str8_from_symbol_binding(ELF_SymbolBinding bind){ + String8 result = str8_lit("ERROR"); + + if (ELF_SymbolBinding_LOOS <= bind && + bind <= ELF_SymbolBinding_HIOS){ + result = str8_lit("OS-CUSTOM-TYPE"); + } + + else if (ELF_SymbolBinding_LOPROC <= bind && + bind <= ELF_SymbolBinding_HIPROC){ + result = str8_lit("PROC-CUSTOM-TYPE"); + } + + else{ + switch (bind){ +#define X(N,C) case C: result = str8_lit(#N); break; + ELF_SymbolBinding_XList(X) +#undef X + } + } + + return(result); +} + +MR4TH_SYMBOL_STATIC String8 +elf_str8_from_symbol_type(ELF_SymbolType type){ + String8 result = str8_lit("ERROR"); + + if (ELF_SymbolType_LOOS <= type && + type <= ELF_SymbolType_HIOS){ + result = str8_lit("OS-CUSTOM-TYPE"); + } + + else if (ELF_SymbolType_LOPROC <= type && + type <= ELF_SymbolType_HIPROC){ + result = str8_lit("PROC-CUSTOM-TYPE"); + } + + else{ + switch (type){ +#define X(N,C) case C: result = str8_lit(#N); break; + ELF_SymbolType_XList(X) +#undef X + } + } + + return(result); +} + +MR4TH_SYMBOL_STATIC String8 +elf_str8_from_symbol_vis(ELF_SymbolVis vis){ + String8 result = str8_lit("ERROR"); + switch (vis){ +#define X(N,C) case C: result = str8_lit(#N); break; + ELF_SymbolVis_XList(X) +#undef X + } + return(result); +} diff --git a/src/binary/elf/mr4th_elf.h b/symbol_set.ld_meta/mr4th/exefmt/elf/mr4th_elf.h similarity index 78% rename from src/binary/elf/mr4th_elf.h rename to symbol_set.ld_meta/mr4th/exefmt/elf/mr4th_elf.h index a60c8e1..3b39901 100644 --- a/src/binary/elf/mr4th_elf.h +++ b/symbol_set.ld_meta/mr4th/exefmt/elf/mr4th_elf.h @@ -302,6 +302,11 @@ X(AMDGPU, 224)\ X(RISCV, 243) typedef U16 ELF_MachineType; +enum{ +#define X(N,C) ELF_MachineType_##N = C, + ELF_MachineType_XList(X) +#undef X +}; // elf header @@ -660,46 +665,46 @@ typedef struct ELF_Segment64{ // normalizing -MR4TH_SYM_COMPTIME U16 elf_u16_decode(void *ptr, B32 flip); -MR4TH_SYM_COMPTIME U32 elf_u32_decode(void *ptr, B32 flip); -MR4TH_SYM_COMPTIME U64 elf_u64_decode(void *ptr, B32 flip); +MR4TH_SYMBOL_STATIC U16 elf_u16_decode(void *ptr, B32 flip); +MR4TH_SYMBOL_STATIC U32 elf_u32_decode(void *ptr, B32 flip); +MR4TH_SYMBOL_STATIC U64 elf_u64_decode(void *ptr, B32 flip); -MR4TH_SYM_COMPTIME void elf_header64_from_header32(ELF_Header64 *dst, ELF_Header32 *src, ELF_Encoding src_encoding); -MR4TH_SYM_COMPTIME void elf_header64_from_header64(ELF_Header64 *dst, ELF_Header64 *src, ELF_Encoding src_encoding); +MR4TH_SYMBOL_STATIC void elf_header64_from_header32(ELF_Header64 *dst, ELF_Header32 *src, ELF_Encoding src_encoding); +MR4TH_SYMBOL_STATIC void elf_header64_from_header64(ELF_Header64 *dst, ELF_Header64 *src, ELF_Encoding src_encoding); -MR4TH_SYM_COMPTIME void elf_section64_from_section32(ELF_Section64 *dst, ELF_Section32 *src, ELF_Encoding src_encoding); -MR4TH_SYM_COMPTIME void elf_section64_from_section64(ELF_Section64 *dst, ELF_Section64 *src, ELF_Encoding src_encoding); +MR4TH_SYMBOL_STATIC void elf_section64_from_section32(ELF_Section64 *dst, ELF_Section32 *src, ELF_Encoding src_encoding); +MR4TH_SYMBOL_STATIC void elf_section64_from_section64(ELF_Section64 *dst, ELF_Section64 *src, ELF_Encoding src_encoding); -MR4TH_SYM_COMPTIME void elf_segment64_from_segment32(ELF_Segment64 *dst, ELF_Segment32 *src, ELF_Encoding src_encoding); -MR4TH_SYM_COMPTIME void elf_segment64_from_segment64(ELF_Segment64 *dst, ELF_Segment64 *src, ELF_Encoding src_encoding); +MR4TH_SYMBOL_STATIC void elf_segment64_from_segment32(ELF_Segment64 *dst, ELF_Segment32 *src, ELF_Encoding src_encoding); +MR4TH_SYMBOL_STATIC void elf_segment64_from_segment64(ELF_Segment64 *dst, ELF_Segment64 *src, ELF_Encoding src_encoding); -MR4TH_SYM_COMPTIME void elf_symbol64_from_symbol32(ELF_Symbol64 *dst, ELF_Symbol32 *src, ELF_Encoding src_encoding); -MR4TH_SYM_COMPTIME void elf_symbol64_from_symbol64(ELF_Symbol64 *dst, ELF_Symbol64 *src, ELF_Encoding src_encoding); +MR4TH_SYMBOL_STATIC void elf_symbol64_from_symbol32(ELF_Symbol64 *dst, ELF_Symbol32 *src, ELF_Encoding src_encoding); +MR4TH_SYMBOL_STATIC void elf_symbol64_from_symbol64(ELF_Symbol64 *dst, ELF_Symbol64 *src, ELF_Encoding src_encoding); -MR4TH_SYM_COMPTIME ELF_RelocationAdd64* elf_reloca64_from_reloc32(Arena *arena, ELF_Relocation32 *src, U64 count, ELF_Encoding src_encoding); -MR4TH_SYM_COMPTIME ELF_RelocationAdd64* elf_reloca64_from_reloc64(Arena *arena, ELF_Relocation64 *src, U64 count, ELF_Encoding src_encoding); -MR4TH_SYM_COMPTIME ELF_RelocationAdd64* elf_reloca64_from_reloca32(Arena *arena, ELF_RelocationAdd32 *src, U64 count, ELF_Encoding src_encoding); -MR4TH_SYM_COMPTIME ELF_RelocationAdd64* elf_reloca64_from_reloca64(Arena *arena, ELF_RelocationAdd64 *src, U64 count, ELF_Encoding src_encoding); +MR4TH_SYMBOL_STATIC void elf_reloca64_from_reloc32(ELF_RelocationAdd64 *dst, ELF_Relocation32 *src, ELF_Encoding src_encoding); +MR4TH_SYMBOL_STATIC void elf_reloca64_from_reloc64(ELF_RelocationAdd64 *dst, ELF_Relocation64 *src, ELF_Encoding src_encoding); +MR4TH_SYMBOL_STATIC void elf_reloca64_from_reloca32(ELF_RelocationAdd64 *dst, ELF_RelocationAdd32 *src, ELF_Encoding src_encoding); +MR4TH_SYMBOL_STATIC void elf_reloca64_from_reloca64(ELF_RelocationAdd64 *dst, ELF_RelocationAdd64 *src, ELF_Encoding src_encoding); // strings -MR4TH_SYM_COMPTIME String8 elf_str8_from_identification_idx(ELF_IdentificationIdx idx); -MR4TH_SYM_COMPTIME String8 elf_str8_from_class(ELF_Class elf_class); -MR4TH_SYM_COMPTIME String8 elf_str8_from_encoding(ELF_Encoding elf_encoding); -MR4TH_SYM_COMPTIME String8 elf_str8_from_osabi(ELF_OsAbiExtension elf_osabi); -MR4TH_SYM_COMPTIME String8 elf_str8_from_file_type(ELF_FileType file_type); -MR4TH_SYM_COMPTIME String8 elf_str8_from_machine_type(ELF_FileType machine_type); +MR4TH_SYMBOL_STATIC String8 elf_str8_from_identification_idx(ELF_IdentificationIdx idx); +MR4TH_SYMBOL_STATIC String8 elf_str8_from_class(ELF_Class elf_class); +MR4TH_SYMBOL_STATIC String8 elf_str8_from_encoding(ELF_Encoding elf_encoding); +MR4TH_SYMBOL_STATIC String8 elf_str8_from_osabi(ELF_OsAbiExtension elf_osabi); +MR4TH_SYMBOL_STATIC String8 elf_str8_from_file_type(ELF_FileType file_type); +MR4TH_SYMBOL_STATIC String8 elf_str8_from_machine_type(ELF_FileType machine_type); -MR4TH_SYM_COMPTIME String8 elf_str8_from_section_type(ELF_SectionType section_type); -MR4TH_SYM_COMPTIME void elf_str8list_from_section_flags(Arena *arena, String8List *out, U32 flags); -MR4TH_SYM_COMPTIME String8 elf_str8_from_section_flags(Arena *arena, U32 flags); +MR4TH_SYMBOL_STATIC String8 elf_str8_from_section_type(ELF_SectionType section_type); +MR4TH_SYMBOL_STATIC void elf_str8list_from_section_flags(Arena *arena, String8List *out, U32 flags); +MR4TH_SYMBOL_STATIC String8 elf_str8_from_section_flags(Arena *arena, U32 flags); -MR4TH_SYM_COMPTIME String8 elf_str8_from_segment_type(ELF_SegmentType segment_type); -MR4TH_SYM_COMPTIME void elf_str8list_from_segment_flags(Arena *arena, String8List *out, ELF_SegmentFlags flags); -MR4TH_SYM_COMPTIME String8 elf_str8_from_segment_flags(Arena *arena, ELF_SegmentFlags flags); +MR4TH_SYMBOL_STATIC String8 elf_str8_from_segment_type(ELF_SegmentType segment_type); +MR4TH_SYMBOL_STATIC void elf_str8list_from_segment_flags(Arena *arena, String8List *out, ELF_SegmentFlags flags); +MR4TH_SYMBOL_STATIC String8 elf_str8_from_segment_flags(Arena *arena, ELF_SegmentFlags flags); -MR4TH_SYM_COMPTIME String8 elf_str8_from_symbol_binding(ELF_SymbolBinding bind); -MR4TH_SYM_COMPTIME String8 elf_str8_from_symbol_type(ELF_SymbolType type); -MR4TH_SYM_COMPTIME String8 elf_str8_from_symbol_vis(ELF_SymbolVis vis); +MR4TH_SYMBOL_STATIC String8 elf_str8_from_symbol_binding(ELF_SymbolBinding bind); +MR4TH_SYMBOL_STATIC String8 elf_str8_from_symbol_type(ELF_SymbolType type); +MR4TH_SYMBOL_STATIC String8 elf_str8_from_symbol_vis(ELF_SymbolVis vis); #endif //MR4TH_ELF_H diff --git a/symbol_set.ld_meta/mr4th/exefmt/elf/mr4th_elf.parse.c b/symbol_set.ld_meta/mr4th/exefmt/elf/mr4th_elf.parse.c new file mode 100644 index 0000000..2588a5e --- /dev/null +++ b/symbol_set.ld_meta/mr4th/exefmt/elf/mr4th_elf.parse.c @@ -0,0 +1,412 @@ +//////////////////////////////// +// Elf Parse Functions + +MR4TH_SYMBOL ELF_Parse* +elfp_begin(Arena *arena, String8 data){ + // magic check + B32 elf_magic = 0; + ELF_Class elf_class = ELF_Class_NONE; + ELF_Encoding elf_encoding = ELF_Encoding_NONE; + U32 elf_version = 0; + ELF_OsAbiExtension elf_ext = ELF_OsAbiExtension_NONE; + B32 good_headers = 0; + if (data.size >= ELF_NUM_IDENT){ + elf_class = data.str[ELF_IdentificationIdx_CLASS]; + elf_encoding = data.str[ELF_IdentificationIdx_DATA]; + elf_version = data.str[ELF_IdentificationIdx_VERSION]; + elf_ext = data.str[ELF_IdentificationIdx_OSABI]; + if (data.str[0] == ELF_Magic_Byte0 && + data.str[1] == ELF_Magic_Byte1 && + data.str[2] == ELF_Magic_Byte2 && + data.str[3] == ELF_Magic_Byte3 && + elf_version == 1 && + (elf_class == ELF_Class_32 || elf_class == ELF_Class_64) && + (elf_encoding == ELF_Encoding_2LSB || + elf_encoding == ELF_Encoding_2MSB)){ + switch (elf_class){ + case ELF_Class_32: { + if (data.size >= sizeof(ELF_Header32)){ good_headers = 1; } }break; + case ELF_Class_64: { + if (data.size >= sizeof(ELF_Header64)){ good_headers = 1; } }break; + } + } + } + + // header + void *header = 0; + U64 section_table_foff = 0; + U16 section_count = 0; + U16 section_size = 0; + U64 segment_table_foff = 0; + U16 segment_count = 0; + U16 segment_size = 0; + U16 string_section_index = 0; + + if (good_headers){ + switch (elf_class){ + case ELF_Class_32: { + if (sizeof(ELF_Header32) <= data.size){ + header = data.str; + ELF_Header32 *h = (ELF_Header32*)header; + section_table_foff = h->section_table_foff; + section_count = h->section_count; + section_size = h->section_size; + segment_table_foff = h->segment_table_foff; + segment_count = h->segment_count; + segment_size = h->segment_size; + string_section_index = h->string_section_index; + } + }break; + case ELF_Class_64: { + if (sizeof(ELF_Header64) <= data.size){ + header = data.str; + ELF_Header64 *h = (ELF_Header64*)header; + section_table_foff = h->section_table_foff; + section_count = h->section_count; + section_size = h->section_size; + segment_table_foff = h->segment_table_foff; + segment_count = h->segment_count; + segment_size = h->segment_size; + string_section_index = h->string_section_index; + } + }break; + } + } + + // sections + void *sections = 0; + if (section_count > 0 && section_table_foff + section_count*section_size <= data.size){ + sections = data.str + section_table_foff; + } + + // segments + void *segments = 0; + if (segment_count > 0 && segment_table_foff + segment_count*segment_size <= data.size){ + segments = data.str + segment_table_foff; + } + + // string table + U8 *section_strtable_first = 0; + U8 *section_strtable_opl = 0; + if (sections != 0){ + U32 index = 0; + if (string_section_index != ELF_SectionIndex_UNDEF && + string_section_index != ELF_SectionIndex_XINDEX){ + index = string_section_index; + } + else if (string_section_index == ELF_SectionIndex_XINDEX){ + switch (elf_class){ + case ELF_Class_32: index = ((ELF_Section32*)sections)->link; break; + case ELF_Class_64: index = ((ELF_Section64*)sections)->link; break; + } + } + if (index > 0){ + U64 offset = 0; + U64 size = 0; + switch (elf_class){ + case ELF_Class_32: + { + offset = ((ELF_Section32*)sections)[index].offset; + size = ((ELF_Section32*)sections)[index].size; + }break; + case ELF_Class_64: + { + offset = ((ELF_Section64*)sections)[index].offset; + size = ((ELF_Section64*)sections)[index].size; + }break; + } + if (offset < data.size && offset + size < data.size){ + section_strtable_first = data.str + offset; + section_strtable_opl = data.str + offset + size; + } + } + } + + // find symtab & dynsym sections + void *symbols[ELFP_SYMBOL_TABLE_COUNT] = {0}; + U32 symbol_count[ELFP_SYMBOL_TABLE_COUNT] = {0}; + U8 *symbol_strtable_first[ELFP_SYMBOL_TABLE_COUNT] = {0}; + U8 *symbol_strtable_opl[ELFP_SYMBOL_TABLE_COUNT] = {0}; + if (sections != 0){ + for (U32 i = 0; i < section_count; i += 1){ + ELF_SectionType type = 0; + U64 foff = 0; + U64 opl = 0; + switch (elf_class){ + case ELF_Class_32: { + ELF_Section32 *sec32 = (ELF_Section32*)sections + i; + type = sec32->type; + foff = sec32->offset; + opl = foff + sec32->size; + }break; + case ELF_Class_64: { + ELF_Section64 *sec64 = (ELF_Section64*)sections + i; + type = sec64->type; + foff = sec64->offset; + opl = foff + sec64->size; + }break; + } + U32 idx = ELFP_SYMBOL_TABLE_COUNT; + switch (type){ + case ELF_SectionType_SYMTAB: idx = ELFP_SYMBOL_TABLE_symtab; break; + case ELF_SectionType_DYNSYM: idx = ELFP_SYMBOL_TABLE_dynsym; break; + } + if (idx < ELFP_SYMBOL_TABLE_COUNT && + foff < data.size && opl < data.size){ + U32 link = 0; + //U32 info = 0; + U32 count = 0; + switch (elf_class){ + case ELF_Class_32: { + ELF_Section32 *sec32 = (ELF_Section32*)sections + i; + link = sec32->link; + //info = sec32->info; + count = sec32->size/sizeof(ELF_Symbol32); + }break; + case ELF_Class_64: { + ELF_Section64 *sec64 = (ELF_Section64*)sections + i; + link = sec64->link; + //info = sec64->info; + count = sec64->size/sizeof(ELF_Symbol64); + }break; + } + symbols[idx] = data.str + foff; + symbol_count[idx] = count; + if (link < section_count){ + U64 strtable_off = 0; + U64 strtable_size = 0; + switch (elf_class){ + case ELF_Class_32: { + ELF_Section32 *sec32 = (ELF_Section32*)sections + link; + strtable_off = sec32->offset; + strtable_size = sec32->size; + }break; + case ELF_Class_64: { + ELF_Section64 *sec64 = (ELF_Section64*)sections + link; + strtable_off = sec64->offset; + strtable_size = sec64->size; + }break; + } + if (strtable_off < data.size && + strtable_off + strtable_size < data.size){ + symbol_strtable_first[idx] = data.str + strtable_off; + symbol_strtable_opl[idx] = data.str + strtable_off + strtable_size; + } + } + } + } + } + + // fill result + ELF_Parse *elf = 0; + if (header != 0){ + elf = push_array(arena, ELF_Parse, 1); + elf->elf_class = elf_class; + elf->encoding = elf_encoding; + elf->section_count = section_count; + elf->segment_count = segment_count; + elf->header = header; + elf->size = data.size; + elf->sections = sections; + elf->segments = segments; + elf->section_strtable_first = section_strtable_first; + elf->section_strtable_opl = section_strtable_opl; + MemoryCopyArray(elf->symbols, symbols); + MemoryCopyArray(elf->symbol_count, symbol_count); + MemoryCopyArray(elf->symbol_strtable_first, symbol_strtable_first); + MemoryCopyArray(elf->symbol_strtable_opl, symbol_strtable_opl); + } + + return(elf); +} + +MR4TH_SYMBOL void +elfp_header_read(ELF_Parse *elf, ELF_Header64 *out){ + if (elf->header != 0){ + switch (elf->elf_class){ + case ELF_Class_32: { + elf_header64_from_header32(out, (ELF_Header32*)elf->header, elf->encoding); + }break; + case ELF_Class_64: { + elf_header64_from_header64(out, (ELF_Header64*)elf->header, elf->encoding); + }break; + } + } +} + +MR4TH_SYMBOL U32 +elfp_section_count(ELF_Parse *elf){ + U32 result = elf->section_count; + return(result); +} + +MR4TH_SYMBOL void +elfp_section_read(ELF_Parse *elf, U32 idx, ELF_Section64 *out){ + if (elf->sections != 0 && idx < elf->section_count){ + switch (elf->elf_class){ + case ELF_Class_32: { + elf_section64_from_section32(out, (ELF_Section32*)elf->sections + idx, elf->encoding); + }break; + case ELF_Class_64: { + elf_section64_from_section64(out, (ELF_Section64*)elf->sections + idx, elf->encoding); + }break; + } + } +} + +MR4TH_SYMBOL String8 +elfp_section_name(ELF_Parse *elf, U32 idx){ + String8 result = {0}; + if (elf->sections != 0 && idx < elf->section_count){ + U32 name = 0; + switch (elf->elf_class){ + case ELF_Class_32: name = ((ELF_Section32*)elf->sections)[idx].name; break; + case ELF_Class_64: name = ((ELF_Section64*)elf->sections)[idx].name; break; + } + result = str8_cstring_capped(elf->section_strtable_first + name, + elf->section_strtable_opl); + } + return(result); +} + +MR4TH_SYMBOL U32 +elfp_segment_count(ELF_Parse *elf){ + U32 result = elf->segment_count; + return(result); +} + +MR4TH_SYMBOL void +elfp_segment_read(ELF_Parse *elf, U32 idx, ELF_Segment64 *out){ + if (elf->segments != 0 && idx < elf->segment_count){ + switch (elf->elf_class){ + case ELF_Class_32: { + elf_segment64_from_segment32(out, (ELF_Segment32*)elf->segments + idx, elf->encoding); + }break; + case ELF_Class_64: { + elf_segment64_from_segment64(out, (ELF_Segment64*)elf->segments + idx, elf->encoding); + }break; + } + } +} + +MR4TH_SYMBOL U32 +elfp_symbol_count(ELF_Parse *elf, U32 table){ + U32 result = 0; + if (table < ELFP_SYMBOL_TABLE_COUNT){ + result = elf->symbol_count[table]; + } + return(result); +} + +MR4TH_SYMBOL void +elfp_symbol_read(ELF_Parse *elf, U32 table, U32 idx, ELF_Symbol64 *out){ + if (table < ELFP_SYMBOL_TABLE_COUNT){ + void *symbols = elf->symbols[table]; + switch (elf->elf_class){ + case ELF_Class_32: { + elf_symbol64_from_symbol32(out, (ELF_Symbol32*)symbols + idx, elf->encoding); + }break; + case ELF_Class_64: { + elf_symbol64_from_symbol64(out, (ELF_Symbol64*)symbols + idx, elf->encoding); + }break; + } + } +} + +MR4TH_SYMBOL String8 +elfp_symbol_name(ELF_Parse *elf, U32 table, U32 idx){ + String8 result = {0}; + if (table < ELFP_SYMBOL_TABLE_COUNT){ + void *s = elf->symbols[table]; + U8 *string_table_first = elf->symbol_strtable_first[table]; + U8 *string_table_opl = elf->symbol_strtable_opl[table]; + U32 name = 0; + switch (elf->elf_class){ + case ELF_Class_32: name = ((ELF_Symbol32*)s)[idx].name; break; + case ELF_Class_64: name = ((ELF_Symbol64*)s)[idx].name; break; + } + if (string_table_first != 0){ + result = str8_cstring_capped(string_table_first + name, + string_table_opl); + } + } + return(result); +} + +MR4TH_SYMBOL U32 +elfp_relocs_count(ELF_Parse *elf, U32 secidx){ + U32 result = 0; + if (elf->sections != 0 && secidx < elf->section_count){ + switch (elf->elf_class){ + case ELF_Class_32: { + ELF_Section32 *sec32 = ((ELF_Section32*)elf->sections) + secidx; + switch (sec32->type){ + case ELF_SectionType_REL: + result = sec32->size/sizeof(ELF_Relocation32); break; + case ELF_SectionType_RELA: + result = sec32->size/sizeof(ELF_RelocationAdd32); break; + } + }break; + case ELF_Class_64: { + ELF_Section64 *sec64 = ((ELF_Section64*)elf->sections) + secidx; + switch (sec64->type){ + case ELF_SectionType_REL: + result = sec64->size/sizeof(ELF_Relocation64); break; + case ELF_SectionType_RELA: + result = sec64->size/sizeof(ELF_RelocationAdd64); break; + } + }break; + } + } + return(result); +} + +MR4TH_SYMBOL void +elfp_relocs_read(ELF_Parse *elf, U32 secidx, U32 relocidx, ELF_RelocationAdd64 *out){ + if (elf->sections != 0 && secidx < elf->section_count){ + switch (elf->elf_class){ + case ELF_Class_32: { + ELF_Section32 *sec32 = ((ELF_Section32*)elf->sections) + secidx; + U64 opl = ClampTop(sec32->offset + sec32->size, elf->size); + U64 size = opl - sec32->offset; + switch (sec32->type){ + case ELF_SectionType_REL: { + ELF_Relocation32 *relocs = (ELF_Relocation32*)((U8*)elf->header + sec32->offset); + U32 count = sec32->size/sizeof(*relocs); + if (relocidx < count){ + elf_reloca64_from_reloc32(out, relocs + relocidx, elf->encoding); + } + }break; + case ELF_SectionType_RELA: { + ELF_RelocationAdd32 *relocs = (ELF_RelocationAdd32*)((U8*)elf->header + sec32->offset); + U32 count = sec32->size/sizeof(*relocs); + if (relocidx < count){ + elf_reloca64_from_reloca32(out, relocs + relocidx, elf->encoding); + } + }break; + } + }break; + case ELF_Class_64: { + ELF_Section64 *sec64 = ((ELF_Section64*)elf->sections) + secidx; + U64 opl = ClampTop(sec64->offset + sec64->size, elf->size); + U64 size = opl - sec64->offset; + switch (sec64->type){ + case ELF_SectionType_REL: { + ELF_Relocation64 *relocs = (ELF_Relocation64*)((U8*)elf->header + sec64->offset); + U32 count = sec64->size/sizeof(*relocs); + if (relocidx < count){ + elf_reloca64_from_reloc64(out, relocs + relocidx, elf->encoding); + } + }break; + case ELF_SectionType_RELA: { + ELF_RelocationAdd64 *relocs = (ELF_RelocationAdd64*)((U8*)elf->header + sec64->offset); + U32 count = sec64->size/sizeof(*relocs); + if (relocidx < count){ + elf_reloca64_from_reloca64(out, relocs + relocidx, elf->encoding); + } + }break; + } + }break; + } + } +} diff --git a/symbol_set.ld_meta/mr4th/exefmt/elf/mr4th_elf.parse.h b/symbol_set.ld_meta/mr4th/exefmt/elf/mr4th_elf.parse.h new file mode 100644 index 0000000..4a205d2 --- /dev/null +++ b/symbol_set.ld_meta/mr4th/exefmt/elf/mr4th_elf.parse.h @@ -0,0 +1,49 @@ +#ifndef MR4TH_ELF_PARSE_H +#define MR4TH_ELF_PARSE_H + +//////////////////////////////// +// Elf Parse Types + +#define ELFP_SYMBOL_TABLE_symtab 0 +#define ELFP_SYMBOL_TABLE_dynsym 1 +#define ELFP_SYMBOL_TABLE_COUNT 2 + +typedef struct ELF_Parse{ + ELF_Class elf_class; + ELF_Encoding encoding; + U32 section_count; + U32 segment_count; + void *header; + U64 size; + void *sections; + void *segments; + U8 *section_strtable_first; + U8 *section_strtable_opl; + void *symbols[ELFP_SYMBOL_TABLE_COUNT]; + U32 symbol_count[ELFP_SYMBOL_TABLE_COUNT]; + U8 *symbol_strtable_first[ELFP_SYMBOL_TABLE_COUNT]; + U8 *symbol_strtable_opl[ELFP_SYMBOL_TABLE_COUNT]; +} ELF_Parse; + +//////////////////////////////// +// Elf Parse Functions + +MR4TH_SYMBOL ELF_Parse* elfp_begin(Arena *arena, String8 data); + +MR4TH_SYMBOL void elfp_header_read(ELF_Parse *elf, ELF_Header64 *out); + +MR4TH_SYMBOL U32 elfp_section_count(ELF_Parse *elf); +MR4TH_SYMBOL void elfp_section_read(ELF_Parse *elf, U32 idx, ELF_Section64 *out); +MR4TH_SYMBOL String8 elfp_section_name(ELF_Parse *elf, U32 idx); + +MR4TH_SYMBOL U32 elfp_segment_count(ELF_Parse *elf); +MR4TH_SYMBOL void elfp_segment_read(ELF_Parse *elf, U32 idx, ELF_Segment64 *out); + +MR4TH_SYMBOL U32 elfp_symbol_count(ELF_Parse *elf, U32 table); +MR4TH_SYMBOL void elfp_symbol_read(ELF_Parse *elf, U32 table, U32 idx, ELF_Symbol64 *out); +MR4TH_SYMBOL String8 elfp_symbol_name(ELF_Parse *elf, U32 table, U32 idx); + +MR4TH_SYMBOL U32 elfp_relocs_count(ELF_Parse *elf, U32 secidx); +MR4TH_SYMBOL void elfp_relocs_read(ELF_Parse *elf, U32 secidx, U32 relocidx, ELF_RelocationAdd64 *out); + +#endif //MR4TH_ELF_PARSE_H diff --git a/symbol_set.ld_meta/mr4th/mr4th_base.c b/symbol_set.ld_meta/mr4th/mr4th_base.c new file mode 100644 index 0000000..3b005e1 --- /dev/null +++ b/symbol_set.ld_meta/mr4th/mr4th_base.c @@ -0,0 +1,5838 @@ +//////////////////////////////////////////////// +//////////////////////////////////////////////// +/////////// BASE IMPLEMENTATION //////////// +//////////////////////////////////////////////// +//////////////////////////////////////////////// + +//////////////////////////////// +// Functions: Numerical/Math + +// infinity + +MR4TH_SYMBOL B32 +is_inf_or_nan_F32(F32 x){ + U32 u = *(U32*)&x; + B32 result = ((u&0x7f800000) == 0x7f800000); + return(result); +} + +MR4TH_SYMBOL B32 +is_inf_or_nan_F64(F64 x){ + U64 u = *(U64*)&x; + B64 result = ((u&0x7ff0000000000000) == 0x7ff0000000000000); + return(result); +} + +// float signs, rounding, and modulus + +MR4TH_SYMBOL F32 +abs_F32(F32 x){ + union{ F32 f; U32 u; } r; + r.f = x; + r.u &= 0x7fffffff; + return(r.f); +} + +MR4TH_SYMBOL F32 +sign_F32(F32 x){ + union{ F32 f; U32 u; } r; + r.f = x; + F32 result = (r.u&0x80000000)?-1.f:1.f; + return(result); +} + +MR4TH_SYMBOL F32 +trunc_F32(F32 x){ + return((x < 0x1p23)?(F32)(S32)x:x); +} + +MR4TH_SYMBOL F32 +floor_F32(F32 x){ + F32 r = trunc_F32(x); + if (x < r){ + r -= 1.f; + } + return(r); +} + +MR4TH_SYMBOL F32 +ceil_F32(F32 x){ + F32 r = trunc_F32(x); + if (x > r){ + r += 1.f; + } + return(r); +} + +MR4TH_SYMBOL F32 +nearest_F32(F32 x){ + F32 r = floor_F32(x + 0.5f); + return(r); +} + +MR4TH_SYMBOL F32 +mod_F32(F32 x, F32 m){ + Assert(m > 0.f); + // + | - + F32 q = (x/m); // (x/m) | (x/m) + S32 q_s32 = (S32)q; // floor(x/m) | ceil(x/m) + F32 r = m*(F32)q_s32;// m*floor(x/m) | m*ceil(x/m) + // <= x | >= x + F32 d = x - r; + if (d < 0){ + d += m; + } + return(d); +} + +MR4TH_SYMBOL F32 +frac_F32(F32 x){ + // + | - + F32 q = (x); // (x) | (x) + S32 q_s32 = (S32)q; // floor(x) | ceil(x) + F32 r = (F32)q_s32; // floor(x) | ceil(x) + // <= x | >= x + F32 d = x - r; + return(d); +} + +MR4TH_SYMBOL F64 +abs_F64(F64 x){ + union{ F64 f; U64 u; } r; + r.f = x; + r.u &= 0x7fffffffffffffff; + return(r.f); +} + +MR4TH_SYMBOL F64 +sign_F64(F64 x){ + union{ F64 f; U32 u; } r; + r.f = x; + F64 result = (r.u&0x8000000000000000)?-1.:1.; + return(result); +} + +MR4TH_SYMBOL F64 +trunc_F64(F64 x){ + return((x < 0x1p52)?(F64)(S64)x:x); +} + +MR4TH_SYMBOL F64 +floor_F64(F64 x){ + F64 r = trunc_F64(x); + if (x < r){ + r -= 1.f; + } + return(r); +} + +MR4TH_SYMBOL F64 +ceil_F64(F64 x){ + F64 r = trunc_F64(x); + if (x > r){ + r += 1.f; + } + return(r); +} + +MR4TH_SYMBOL F64 +nearest_F64(F64 x){ + F64 r = floor_F64(x + 0.5f); + return(r); +} + +MR4TH_SYMBOL F64 +mod_F64(F64 x, F64 m){ + Assert(m > 0.f); + // + | - + F64 q = (x/m); // (x/m) | (x/m) + S64 q_s64 = (S64)q; // floor(x/m) | ceil(x/m) + F64 r = m*(F64)q_s64;// m*floor(x/m) | m*ceil(x/m) + // <= x | >= x + F64 d = x - r; + if (d < 0){ + d += m; + } + return(d); +} + +MR4TH_SYMBOL F64 +frac_F64(F64 x){ + // + | - + F64 q = (x); // (x) | (x) + S64 q_s64 = (S64)q; // floor(x) | ceil(x) + F64 r = (F64)q_s64; // floor(x) | ceil(x) + // <= x | >= x + F64 d = x - r; + return(d); +} + +// transcendental functions + +#include + +MR4TH_SYMBOL F32 +sin_F32(F32 x){ + return(sinf(x*tau_F32)); +} + +MR4TH_SYMBOL F32 +cos_F32(F32 x){ + return(cosf(x*tau_F32)); +} + +MR4TH_SYMBOL F32 +tan_F32(F32 x){ + return(tanf(x*tau_F32)); +} + +MR4TH_SYMBOL F32 +atan_F32(F32 x){ + return(atanf(x)/tau_F32); +} + +MR4TH_SYMBOL F32 +atan2_F32(F32 x, F32 y){ + return(atan2f(y, x)/tau_F32); +} + +MR4TH_SYMBOL F32 +sqrt_F32(F32 x){ + return(sqrtf(x)); +} + +MR4TH_SYMBOL F32 +inv_sqrt_F32(F32 x){ + return(1.f/sqrtf(x)); +} + +MR4TH_SYMBOL F32 +ln_F32(F32 x){ + return(logf(x)); +} + +MR4TH_SYMBOL F32 +pow_F32(F32 base, F32 x){ + return(powf(base, x)); +} + +MR4TH_SYMBOL F64 +sin_F64(F64 x){ + return(sin(x*tau_F64)); +} + +MR4TH_SYMBOL F64 +cos_F64(F64 x){ + return(cos(x*tau_F64)); +} + +MR4TH_SYMBOL F64 +tan_F64(F64 x){ + return(tan(x*tau_F64)); +} + +MR4TH_SYMBOL F64 +atan_F64(F64 x){ + return(atan(x)/tau_F64); +} + +MR4TH_SYMBOL F64 +atan2_F64(F64 x, F64 y){ + return(atan2(x, y)/tau_F64); +} + +MR4TH_SYMBOL F64 +sqrt_F64(F64 x){ + return(sqrt(x)); +} + +MR4TH_SYMBOL F64 +inv_sqrt_F64(F64 x){ + return(1.f/sqrt(x)); +} + +MR4TH_SYMBOL F64 +ln_F64(F64 x){ + return(log(x)); +} + +MR4TH_SYMBOL F64 +pow_F64(F64 base, F64 x){ + return(powf(base, x)); +} + +// linear interpolation + +MR4TH_SYMBOL F32 +lerp(F32 a, F32 t, F32 b){ + F32 x = a + (b - a)*t; + return(x); +} + +MR4TH_SYMBOL F32 +unlerp(F32 a, F32 x, F32 b){ + F32 t = 0.f; + if (a != b){ + t = (x - a)/(b - a); + } + return(t); +} + +// integer rounding + +MR4TH_SYMBOL U32 +next_pow2_U32(U32 x){ + U32 result = x; + if (result == 0){ + result = 1; + } + else{ + // TODO(allen): speed up with bit scan intrinsic + result -= 1; + result |= (result >> 1); + result |= (result >> 2); + result |= (result >> 4); + result |= (result >> 8); + result |= (result >> 16); + result += 1; + } + return(result); +} + +MR4TH_SYMBOL S32 +sign_extend_S32(S32 x, U32 bitidx){ + U32 shiftn = (31 - bitidx); + x = x << shiftn; + x = x >> shiftn; + return(x); +} + +MR4TH_SYMBOL U64 +next_pow2_U64(U32 x){ + U64 result = x; + if (result == 0){ + result = 1; + } + else{ + // TODO(allen): speed up with bit scan intrinsic + result -= 1; + result |= (result >> 1); + result |= (result >> 2); + result |= (result >> 4); + result |= (result >> 8); + result |= (result >> 16); + result |= (result >> 32); + result += 1; + } + return(result); +} + +MR4TH_SYMBOL S64 +sign_extend_S64(S64 x, U32 bitidx){ + U32 shiftn = (63 - bitidx); + x = x << shiftn; + x = x >> shiftn; + return(x); +} + +//////////////////////////////// +// Functions: Compound Types + +// 2d vectors + +MR4TH_SYMBOL V2F32 +v2_polar(F32 theta, F32 radius){ + V2F32 result = {radius*cos_F32(theta), radius*sin_F32(theta)}; + return(result); +} + +MR4TH_SYMBOL F32 +v2_angle_from_v(V2F32 v){ + F32 result = atan2_F32(v.x, v.y); + return(result); +} + +MR4TH_SYMBOL V2F32 +v2_unit(V2F32 v){ + F32 recip = inv_sqrt_F32(v.x*v.x + v.y*v.y); + V2F32 r = {0}; + r.x = v.x*recip; + r.y = v.y*recip; + return(r); +} + +MR4TH_SYMBOL V2F32 +v2_from_v2s32(V2S32 v){ + V2F32 r = {(F32)v.x, (F32)v.y}; + return(r); +} + +// 3d vectors + +MR4TH_SYMBOL V3F32 +v3_spherical(F32 theta_xz, F32 theta_yz, F32 radius){ + F32 sxz = sin_F32(theta_xz); + F32 syz = sin_F32(theta_yz); + F32 cxz = cos_F32(theta_xz); + F32 cyz = cos_F32(theta_yz); + + V3F32 result = {0}; + result.x = radius*sxz; + result.y = radius*syz; + result.z = radius*cxz*cyz; + return(result); +} + +MR4TH_SYMBOL V3F32 +v3_cross(V3F32 a, V3F32 b){ + V3F32 result = { + a.y*b.z - a.z*b.y, + a.z*b.x - a.x*b.z, + a.x*b.y - a.y*b.x, + }; + return(result); +} + +MR4TH_SYMBOL V3F32 +v3_unit(V3F32 v){ + F32 recip = inv_sqrt_F32(v.x*v.x + v.y*v.y + v.z*v.z); + V3F32 r = {0}; + r.x = v.x*recip; + r.y = v.y*recip; + r.z = v.z*recip; + return(r); +} + +// 4x4 matrix + +MR4TH_SYMBOL B32 +mat4x4_inv(F32 *in, F32 *out){ + /* credit: https://github.com/niswegmann/small-matrix-inverse/tree/master */ + + out[0] = + + in[ 5]*in[10]*in[15] + - in[ 5]*in[11]*in[14] + - in[ 9]*in[ 6]*in[15] + + in[ 9]*in[ 7]*in[14] + + in[13]*in[ 6]*in[11] + - in[13]*in[ 7]*in[10]; + + out[1] = + - in[ 1]*in[10]*in[15] + + in[ 1]*in[11]*in[14] + + in[ 9]*in[ 2]*in[15] + - in[ 9]*in[ 3]*in[14] + - in[13]*in[ 2]*in[11] + + in[13]*in[ 3]*in[10]; + + out[2] = + + in[ 1]*in[ 6]*in[15] + - in[ 1]*in[ 7]*in[14] + - in[ 5]*in[ 2]*in[15] + + in[ 5]*in[ 3]*in[14] + + in[13]*in[ 2]*in[ 7] + - in[13]*in[ 3]*in[ 6]; + + out[3] = + - in[ 1]*in[ 6]*in[11] + + in[ 1]*in[ 7]*in[10] + + in[ 5]*in[ 2]*in[11] + - in[ 5]*in[ 3]*in[10] + - in[ 9]*in[ 2]*in[ 7] + + in[ 9]*in[ 3]*in[ 6]; + + out[4] = + - in[ 4]*in[10]*in[15] + + in[ 4]*in[11]*in[14] + + in[ 8]*in[ 6]*in[15] + - in[ 8]*in[ 7]*in[14] + - in[12]*in[ 6]*in[11] + + in[12]*in[ 7]*in[10]; + + out[5] = + + in[ 0]*in[10]*in[15] + - in[ 0]*in[11]*in[14] + - in[ 8]*in[ 2]*in[15] + + in[ 8]*in[ 3]*in[14] + + in[12]*in[ 2]*in[11] + - in[12]*in[ 3]*in[10]; + + out[6] = + - in[ 0]*in[ 6]*in[15] + + in[ 0]*in[ 7]*in[14] + + in[ 4]*in[ 2]*in[15] + - in[ 4]*in[ 3]*in[14] + - in[12]*in[ 2]*in[ 7] + + in[12]*in[ 3]*in[ 6]; + + out[7] = + + in[ 0]*in[ 6]*in[11] + - in[ 0]*in[ 7]*in[10] + - in[ 4]*in[ 2]*in[11] + + in[ 4]*in[ 3]*in[10] + + in[ 8]*in[ 2]*in[ 7] + - in[ 8]*in[ 3]*in[ 6]; + + out[8] = + + in[ 4]*in[ 9]*in[15] + - in[ 4]*in[11]*in[13] + - in[ 8]*in[ 5]*in[15] + + in[ 8]*in[ 7]*in[13] + + in[12]*in[ 5]*in[11] + - in[12]*in[ 7]*in[ 9]; + + out[9] = + - in[ 0]*in[ 9]*in[15] + + in[ 0]*in[11]*in[13] + + in[ 8]*in[ 1]*in[15] + - in[ 8]*in[ 3]*in[13] + - in[12]*in[ 1]*in[11] + + in[12]*in[ 3]*in[ 9]; + + out[10] = + + in[ 0]*in[ 5]*in[15] + - in[ 0]*in[ 7]*in[13] + - in[ 4]*in[ 1]*in[15] + + in[ 4]*in[ 3]*in[13] + + in[12]*in[ 1]*in[ 7] + - in[12]*in[ 3]*in[ 5]; + + out[11] = + - in[ 0]*in[ 5]*in[11] + + in[ 0]*in[ 7]*in[ 9] + + in[ 4]*in[ 1]*in[11] + - in[ 4]*in[ 3]*in[ 9] + - in[ 8]*in[ 1]*in[ 7] + + in[ 8]*in[ 3]*in[ 5]; + + out[12] = + - in[ 4]*in[ 9]*in[14] + + in[ 4]*in[10]*in[13] + + in[ 8]*in[ 5]*in[14] + - in[ 8]*in[ 6]*in[13] + - in[12]*in[ 5]*in[10] + + in[12]*in[ 6]*in[ 9]; + + out[13] = + + in[ 0]*in[ 9]*in[14] + - in[ 0]*in[10]*in[13] + - in[ 8]*in[ 1]*in[14] + + in[ 8]*in[ 2]*in[13] + + in[12]*in[ 1]*in[10] + - in[12]*in[ 2]*in[ 9]; + + out[14] = + - in[ 0]*in[ 5]*in[14] + + in[ 0]*in[ 6]*in[13] + + in[ 4]*in[ 1]*in[14] + - in[ 4]*in[ 2]*in[13] + - in[12]*in[ 1]*in[ 6] + + in[12]*in[ 2]*in[ 5]; + + out[15] = + + in[ 0]*in[ 5]*in[10] + - in[ 0]*in[ 6]*in[ 9] + - in[ 4]*in[ 1]*in[10] + + in[ 4]*in[ 2]*in[ 9] + + in[ 8]*in[ 1]*in[ 6] + - in[ 8]*in[ 2]*in[ 5]; + + F32 det = in[0]*out[0] + in[1]*out[4] + in[2]*out[8] + in[3]*out[12]; + + B32 result = 0; + if (det != 0.f){ + F32 invdet = 1.f/det; + for (U32 i = 0; i < 16; i += 1){ + out[i] *= invdet; + } + result = 1; + } + + return(result); +} + +MR4TH_SYMBOL void +mat4x4_mul(F32 *a, F32 *b, F32 *out){ + out[ 0] = a[ 0]*b[ 0] + a[ 1]*b[ 4] + a[ 2]*b[ 8] + a[ 3]*b[12]; + out[ 1] = a[ 0]*b[ 1] + a[ 1]*b[ 5] + a[ 2]*b[ 9] + a[ 3]*b[13]; + out[ 2] = a[ 0]*b[ 2] + a[ 1]*b[ 6] + a[ 2]*b[10] + a[ 3]*b[14]; + out[ 3] = a[ 0]*b[ 3] + a[ 1]*b[ 7] + a[ 2]*b[11] + a[ 3]*b[15]; + out[ 4] = a[ 4]*b[ 0] + a[ 5]*b[ 4] + a[ 6]*b[ 8] + a[ 7]*b[12]; + out[ 5] = a[ 4]*b[ 1] + a[ 5]*b[ 5] + a[ 6]*b[ 9] + a[ 7]*b[13]; + out[ 6] = a[ 4]*b[ 2] + a[ 5]*b[ 6] + a[ 6]*b[10] + a[ 7]*b[14]; + out[ 7] = a[ 4]*b[ 3] + a[ 5]*b[ 7] + a[ 6]*b[11] + a[ 7]*b[15]; + out[ 8] = a[ 8]*b[ 0] + a[ 9]*b[ 4] + a[10]*b[ 8] + a[11]*b[12]; + out[ 9] = a[ 8]*b[ 1] + a[ 9]*b[ 5] + a[10]*b[ 9] + a[11]*b[13]; + out[10] = a[ 8]*b[ 2] + a[ 9]*b[ 6] + a[10]*b[10] + a[11]*b[14]; + out[11] = a[ 8]*b[ 3] + a[ 9]*b[ 7] + a[10]*b[11] + a[11]*b[15]; + out[12] = a[12]*b[ 0] + a[13]*b[ 4] + a[14]*b[ 8] + a[15]*b[12]; + out[13] = a[12]*b[ 1] + a[13]*b[ 5] + a[14]*b[ 9] + a[15]*b[13]; + out[14] = a[12]*b[ 2] + a[13]*b[ 6] + a[14]*b[10] + a[15]*b[14]; + out[15] = a[12]*b[ 3] + a[13]*b[ 7] + a[14]*b[11] + a[15]*b[15]; +} + +//////////////////////////////// +// Functions: Memory Operations + +MR4TH_SYMBOL void +memory_zero(void *ptr, U64 size){ + U64 z64 = size/8; + U64 z8 = size%8; + + U64 *p64 = (U64*)ptr; + for (;z64 > 0;){ + *p64 = 0; + p64 += 1; + z64 -= 1; + } + U8 *p8 = (U8*)p64; + for (;z8 > 0;){ + *p8 = 0; + p8 += 1; + z8 -= 1; + } +} + +MR4TH_SYMBOL void +memory_fill(void *ptr, U64 size, U8 fillbyte){ + U64 fillqword = fillbyte; + { + fillqword |= fillqword << 8; + fillqword |= fillqword << 16; + fillqword |= fillqword << 32; + } + + U64 z64 = size/8; + U64 z8 = size%8; + + U64 *p64 = (U64*)ptr; + for (;z64 > 0;){ + *p64 = fillqword; + p64 += 1; + z64 -= 1; + } + U8 *p8 = (U8*)p64; + for (;z8 > 0;){ + *p8 = fillbyte; + p8 += 1; + z8 -= 1; + } +} + +MR4TH_SYMBOL B32 +memory_match(void *a, void *b, U64 size){ + U64 z64 = size/8; + U64 z8 = size%8; + + U64 *a64 = (U64*)a; + U64 *b64 = (U64*)b; + for (;z64 > 0;){ + if (*a64 != *b64){ + return(0); + } + a64 += 1; + b64 += 1; + z64 -= 1; + } + + U8 *a8 = (U8*)a64; + U8 *b8 = (U8*)b64; + for (;z8 > 0;){ + if (*a8 != *b8){ + return(0); + } + a8 += 1; + b8 += 1; + z8 -= 1; + } + + return(1); +} + +MR4TH_SYMBOL void* +memory_move(void *dst, void *src, U64 size){ + U64 z64 = size/8; + U64 z8 = size%8; + + // backwards + if ((U8*)src < (U8*)dst){ + U8 *dst8 = ((U8*)dst) + size; + U8 *src8 = ((U8*)src) + size; + for (;z8 > 0;){ + dst8 -= 1; + src8 -= 1; + *dst8 = *src8; + z8 -= 1; + } + U64 *dst64 = (U64*)dst8; + U64 *src64 = (U64*)src8; + for (;z64 > 0;){ + dst64 -= 1; + src64 -= 1; + *dst64 = *src64; + z64 -= 1; + } + } + + // forwards + else if ((U8*)src > (U8*)dst){ + U64 *dst64 = (U64*)dst; + U64 *src64 = (U64*)src; + for (;z64 > 0;){ + *dst64 = *src64; + dst64 += 1; + src64 += 1; + z64 -= 1; + } + U8 *dst8 = (U8*)dst64; + U8 *src8 = (U8*)src64; + for (;z8 > 0;){ + *dst8 = *src8; + dst8 += 1; + src8 += 1; + z8 -= 1; + } + } + + return(dst); +} + +MR4TH_SYMBOL void +memory_swap(void *p0, void *p1, U64 size){ + Assert((U8*)p0 + size <= (U8*)p1 || (U8*)p1 + size <= (U8*)p0); + + U64 z64 = size/8; + U64 z8 = size%8; + + U64 *p0_64 = (U64*)p0; + U64 *p1_64 = (U64*)p1; + for (;z64 > 0;){ + Swap(U64, *p0_64, *p1_64); + p0_64 += 1; + p1_64 += 1; + z64 -= 1; + } + U8 *p0_8 = (U8*)p0_64; + U8 *p1_8 = (U8*)p1_64; + for (;z8 > 0;){ + Swap(U8, *p0_8, *p1_8); + p0_8 += 1; + p1_8 += 1; + z8 -= 1; + } +} + +//////////////////////////////// +// Functions: Symbolic Constants + +MR4TH_SYMBOL OperatingSystem +operating_system_from_context(void){ + OperatingSystem result = OperatingSystem_Null; +#if OS_WINDOWS + result = OperatingSystem_Windows; +#elif OS_LINUX + result = OperatingSystem_Linux; +#elif OS_MAC + result = OperatingSystem_Mac; +#endif + return(result); +} + +MR4TH_SYMBOL Architecture +architecture_from_context(void){ + Architecture result = Architecture_Null; +#if ARCH_X64 + result = Architecture_X64; +#elif ARCH_X86 + result = Architecture_X86; +#elif ARCH_ARM + result = Architecture_Arm; +#elif ARCH_ARM64 + result = Architecture_Arm64; +#endif + return(result); +} + +MR4TH_SYMBOL char* +string_from_operating_system(OperatingSystem os){ + char *result = "(null)"; + switch (os){ + case OperatingSystem_Windows: + { + result = "windows"; + }break; + case OperatingSystem_Linux: + { + result = "linux"; + }break; + case OperatingSystem_Mac: + { + result = "mac"; + }break; + } + return(result); +} + +MR4TH_SYMBOL char* +string_from_architecture(Architecture arch){ + char *result = "(null)"; + switch (arch){ + case Architecture_X64: + { + result = "x64"; + }break; + case Architecture_X86: + { + result = "x86"; + }break; + case Architecture_Arm: + { + result = "arm"; + }break; + case Architecture_Arm64: + { + result = "armm64"; + }break; + } + return(result); +} + +MR4TH_SYMBOL char* +string_from_month(Month month){ + char *result = "(null)"; + switch (month){ + case Month_Jan: + { + result = "jan"; + }break; + case Month_Feb: + { + result = "feb"; + }break; + case Month_Mar: + { + result = "mar"; + }break; + case Month_Apr: + { + result = "apr"; + }break; + case Month_May: + { + result = "may"; + }break; + case Month_Jun: + { + result = "jun"; + }break; + case Month_Jul: + { + result = "jul"; + }break; + case Month_Aug: + { + result = "aug"; + }break; + case Month_Sep: + { + result = "sep"; + }break; + case Month_Oct: + { + result = "oct"; + }break; + case Month_Nov: + { + result = "nov"; + }break; + case Month_Dec: + { + result = "dec"; + }break; + } + return(result); +} + +MR4TH_SYMBOL char* +string_from_day_of_week(DayOfWeek day_of_week){ + char *result = "(null)"; + switch (day_of_week){ + case DayOfWeek_Sunday: + { + result = "sunday"; + }break; + case DayOfWeek_Monday: + { + result = "monday"; + }break; + case DayOfWeek_Tuesday: + { + result = "tuesday"; + }break; + case DayOfWeek_Wednesday: + { + result = "wednesday"; + }break; + case DayOfWeek_Thursday: + { + result = "thursday"; + }break; + case DayOfWeek_Friday: + { + result = "friday"; + }break; + case DayOfWeek_Saturday: + { + result = "saturday"; + }break; + } + return(result); +} + +//////////////////////////////// +// Functions: Time + +MR4TH_SYMBOL DenseTime +dense_time_from_date_time(DateTime *in){ + U32 year_encoded = (U32)((S32)in->year + 0x8000); + DenseTime result = 0; + result += year_encoded; + result *= 12; + result += (in->mon - 1); + result *= 31; + result += (in->day - 1); + result *= 24; + result += in->hour; + result *= 60; + result += in->min; + result *= 61; + result += in->sec; + result *= 1000; + result += in->msec; + return(result); +} + +MR4TH_SYMBOL DateTime +date_time_from_dense_time(DenseTime in){ + DateTime result = {0}; + result.msec = in%1000; + in /= 1000; + result.sec = in%61; + in /= 61; + result.min = in%60; + in /= 60; + result.hour = in%24; + in /= 24; + result.day = (in%31) + 1; + in /= 31; + result.mon = (in%12) + 1; + in /= 12; + S32 year_encoded = (S32)in; + result.year = (year_encoded - 0x8000); + return(result); +} + +//////////////////////////////// +// Functions: Arena + +// arena core implementation + +#define MR4TH_MEM_INTERNAL_MIN_SIZE AlignUpPow2(sizeof(Arena), MR4TH_MEM_MAX_ALIGN) + +#if !MR4TH_PLUGIN_MODE + +// arena pre-requisites + +StaticAssert(sizeof(Arena) <= MR4TH_MEM_INITIAL_COMMIT, + mem_check_arena_size); + +StaticAssert(IsPow2OrZero(MR4TH_MEM_COMMIT_BLOCK_SIZE) && + MR4TH_MEM_COMMIT_BLOCK_SIZE != 0, + mem_check_commit_block_size); + +StaticAssert(IsPow2OrZero(MR4TH_MEM_MAX_ALIGN) && MR4TH_MEM_MAX_ALIGN != 0, + mem_check_max_align); + +// arena functions + +MR4TH_SYMBOL_MUST_SHARE Arena* +arena_new(U64 reserve_size, U64 alignment, B32 growing){ + ProfBeginFunc(); + + Arena *result = 0; + if (reserve_size >= MR4TH_MEM_INITIAL_COMMIT){ + void *memory = os_memory_reserve(reserve_size); + if (os_memory_commit(memory, MR4TH_MEM_INITIAL_COMMIT)){ + AsanPoison(memory, reserve_size); + AsanUnpoison(memory, MR4TH_MEM_INTERNAL_MIN_SIZE); + result = (Arena*)memory; + result->current = result; + result->prev = 0; + result->alignment = alignment; + result->default_reserve_size = reserve_size; + result->growing = growing; + result->base_pos = 0; + result->chunk_cap = reserve_size; + result->chunk_pos = MR4TH_MEM_INTERNAL_MIN_SIZE; + result->chunk_commit_pos = MR4TH_MEM_INITIAL_COMMIT; + } + } + Assert(result != 0); + + ProfEndFunc(); + return(result); +} + +MR4TH_SYMBOL_MUST_SHARE void +arena_release(Arena *arena){ + ProfBeginFunc(); + + Arena *ptr = arena->current; + for (;ptr != 0;){ + Arena *prev = ptr->prev; + AsanPoison(ptr, ptr->chunk_cap); + os_memory_release(ptr, ptr->chunk_cap); + ptr = prev; + } + + ProfEndFunc(); +} + +MR4TH_SYMBOL_MUST_SHARE void* +arena_push_no_zero(Arena *arena, U64 size){ + ProfBeginFunc(); + + void *result = 0; + Arena *current = arena->current; + + // allocate new chunk if necessary + if (arena->growing){ + U64 next_chunk_pos = AlignUpPow2(current->chunk_pos, arena->alignment); + next_chunk_pos += size; + if (next_chunk_pos > current->chunk_cap){ + U64 new_reserve_size = arena->default_reserve_size; + U64 enough_to_fit = size + MR4TH_MEM_INTERNAL_MIN_SIZE; + if (new_reserve_size < enough_to_fit){ + new_reserve_size = AlignUpPow2(enough_to_fit, KB(4)); + } + + void *memory = os_memory_reserve(new_reserve_size); + if (os_memory_commit(memory, MR4TH_MEM_INITIAL_COMMIT)){ + AsanPoison(memory, new_reserve_size); + AsanUnpoison(memory, MR4TH_MEM_INTERNAL_MIN_SIZE); + Arena *new_chunk = (Arena*)memory; + new_chunk->prev = current; + new_chunk->base_pos = current->base_pos + current->chunk_cap; + new_chunk->chunk_cap = new_reserve_size; + new_chunk->chunk_pos = MR4TH_MEM_INTERNAL_MIN_SIZE; + new_chunk->chunk_commit_pos = MR4TH_MEM_INITIAL_COMMIT; + current = arena->current = new_chunk; + } + } + } + + { + // if there is room in this chunk's reserve ... + U64 result_pos = AlignUpPow2(current->chunk_pos, arena->alignment); + U64 next_chunk_pos = result_pos + size; + if (next_chunk_pos <= current->chunk_cap){ + + // commit more memory if necessary + if (next_chunk_pos > current->chunk_commit_pos){ + U64 next_commit_pos_aligned = + AlignUpPow2(next_chunk_pos, MR4TH_MEM_COMMIT_BLOCK_SIZE); + U64 next_commit_pos = + ClampTop(next_commit_pos_aligned, current->chunk_cap); + U64 commit_size = next_commit_pos - current->chunk_commit_pos; + if (os_memory_commit((U8*)current + current->chunk_commit_pos, commit_size)){ + current->chunk_commit_pos = next_commit_pos; + } + } + + // if there is room in the commit range, return memory & advance pos + if (next_chunk_pos <= current->chunk_commit_pos){ + AsanUnpoison((U8*)current + current->chunk_pos, + next_chunk_pos - current->chunk_pos); + result = (U8*)current + result_pos; + current->chunk_pos = next_chunk_pos; + } + } + } + + ProfEndFunc(); + return(result); +} + +MR4TH_SYMBOL_MUST_SHARE void +arena_pop_to(Arena *arena, U64 pos){ + ProfBeginFunc(); + + U64 clamped_total_pos = ClampBot(pos, MR4TH_MEM_INTERNAL_MIN_SIZE); + + Arena *current = arena->current; + U64 total_pos = current->base_pos + current->chunk_pos; + if (clamped_total_pos < total_pos){ + // release all chunks that begin after this pos + for (; clamped_total_pos < current->base_pos; ){ + Arena *prev = current->prev; + AsanPoison(current, current->chunk_cap); + os_memory_release(current, current->chunk_cap); + current = prev; + } + + // update arena's current + { + arena->current = current; + } + + // update the chunk position of the chunk in + // the chain that contains this position. + { + U64 chunk_pos = clamped_total_pos - current->base_pos; + U64 clamped_chunk_pos = ClampBot(chunk_pos, MR4TH_MEM_INTERNAL_MIN_SIZE); + AsanPoison((U8*)current + clamped_chunk_pos, + current->chunk_pos - clamped_chunk_pos); + current->chunk_pos = clamped_chunk_pos; + } + } + + ProfEndFunc(); +} + +MR4TH_SYMBOL_MUST_SHARE U64 +arena_current_pos(Arena *arena){ + Arena *current = arena->current; + U64 result = current->base_pos + current->chunk_pos; + return(result); +} + +MR4TH_SYMBOL_MUST_SHARE U64 +arena_current_align(Arena *arena){ + return(arena->alignment); +} + +MR4TH_SYMBOL_MUST_SHARE void +arena_set_align(Arena *arena, U64 alignment){ + arena->alignment = alignment; +} + +MR4TH_SYMBOL_STATIC MR4TH_THREADVAR +Arena *arena__scratch_pool[MR4TH_MEM_SCRATCH_POOL_COUNT] = {0}; + +MR4TH_SYMBOL_MUST_SHARE ArenaTemp +arena_get_scratch(Arena **conflict_array, U32 count){ + ProfBeginFunc(); + + // init on first time + if (arena__scratch_pool[0] == 0){ + Arena **scratch_slot = arena__scratch_pool; + for (U64 i = 0; + i < MR4TH_MEM_SCRATCH_POOL_COUNT; + i += 1, scratch_slot += 1){ + *scratch_slot = arena_alloc(); + } + } + + // get non-conflicting arena + ArenaTemp result = {0}; + Arena **scratch_slot = arena__scratch_pool; + for (U64 i = 0; + i < MR4TH_MEM_SCRATCH_POOL_COUNT; + i += 1, scratch_slot += 1){ + B32 is_non_conflict = 1; + Arena **conflict_ptr = conflict_array; + for (U32 j = 0; j < count; j += 1, conflict_ptr += 1){ + if (*scratch_slot == *conflict_ptr){ + is_non_conflict = 0; + break; + } + } + if (is_non_conflict){ + result = arena_begin_temp(*scratch_slot); + break; + } + } + + ProfEndFunc(); + return(result); +} + +#endif /* arena core implementation */ + +// arena helpers + +#if !defined(MR4TH_MEM_DEFAULT_RESERVE_SIZE) +# define MR4TH_MEM_DEFAULT_RESERVE_SIZE GB(1) +#endif +#if !defined(MR4TH_MEM_DEFAULT_ALIGNMENT) +# define MR4TH_MEM_DEFAULT_ALIGNMENT sizeof(void*) +#endif + +MR4TH_SYMBOL Arena* +arena_alloc(void){ + Arena *result = arena_new(MR4TH_MEM_DEFAULT_RESERVE_SIZE, MR4TH_MEM_DEFAULT_ALIGNMENT, 1); + return(result); +} + +MR4TH_SYMBOL void* +arena_push(Arena *arena, U64 size){ + void *result = arena_push_no_zero(arena, size); + MemoryZero(result, size); + return(result); +} + +MR4TH_SYMBOL void +arena_pop_amount(Arena *arena, U64 amount){ + U64 pos = arena_current_pos(arena); + U64 new_pos = 0; + if (pos > amount){ + new_pos = pos - amount; + } + arena_pop_to(arena, new_pos); +} + +MR4TH_SYMBOL void +arena_align(Arena *arena, U64 pow2_align){ + Assert(IsPow2OrZero(pow2_align) && pow2_align != 0 && + pow2_align <= MR4TH_MEM_MAX_ALIGN); + U64 pos = arena_current_pos(arena); + U64 align_pos = AlignUpPow2(pos, pow2_align); + U64 z = align_pos - pos; + if (z > 0){ + arena_push(arena, z); + } +} + +MR4TH_SYMBOL ArenaTemp +arena_begin_temp(Arena *arena){ + U64 pos = arena_current_pos(arena); + ArenaTemp temp = {arena, pos}; + return(temp); +} + +MR4TH_SYMBOL void +arena_end_temp(ArenaTemp *temp){ + arena_pop_to(temp->arena, temp->pos); +} + +//////////////////////////////// +// Functions: Strings + +// characters + +MR4TH_SYMBOL B32 +char_is_whitespace(U8 c){ + return(c == ' ' || c == '\n' || c == '\t' || + c == '\r' || c == '\f' || c == '\v'); +} + +MR4TH_SYMBOL B32 +char_is_slash(U8 c){ + return(c == '/' || c == '\\'); +} + +MR4TH_SYMBOL B32 +char_is_digit(U8 c){ + return('0' <= c && c <= '9'); +} + +MR4TH_SYMBOL U8 +char_to_uppercase(U8 c){ + if ('a' <= c && c <= 'z'){ + c += 'A' - 'a'; + } + return(c); +} + +MR4TH_SYMBOL U8 +char_to_lowercase(U8 c){ + if ('A' <= c && c <= 'Z'){ + c += 'a' - 'A'; + } + return(c); +} + +// in-place constructors + +MR4TH_SYMBOL String8 +str8_range(U8 *first, U8 *opl){ + String8 result = {first, (U64)(opl - first)}; + return(result); +} + +MR4TH_SYMBOL String8 +str8_cstring(U8 *cstr){ + U8 *ptr = cstr; + for (;*ptr != 0; ptr += 1); + String8 result = str8_range(cstr, ptr); + return(result); +} + +MR4TH_SYMBOL String8 +str8_cstring_capped(U8 *cstr, U8 *opl){ + U8 *ptr = cstr; + for (;ptr < opl && *ptr != 0; ptr += 1); + String8 result = str8_range(cstr, ptr); + return(result); +} + +MR4TH_SYMBOL String16 +str16_cstring(U16 *cstr){ + U16 *ptr = cstr; + for (;*ptr != 0; ptr += 1); + String16 result = {cstr, (U64)(ptr - cstr)}; + return(result); +} + +// compound constructors + +MR4TH_SYMBOL void +str8_list_push(Arena *arena, String8List *list, String8 string){ + String8Node *node = push_array(arena, String8Node, 1); + node->string = string; + SLLQueuePush(list->first, list->last, node); + list->node_count += 1; + list->total_size += string.size; +} + +MR4TH_SYMBOL void +str8_list_push_front(Arena *arena, String8List *list, String8 string){ + String8Node *node = push_array(arena, String8Node, 1); + node->string = string; + SLLQueuePushFront(list->first, list->last, node); + list->node_count += 1; + list->total_size += string.size; +} + +MR4TH_SYMBOL String8List +str8_list_copy(Arena *arena, String8List *list){ + String8List result = {0}; + for (String8Node *node = list->first; + node != 0; + node = node->next){ + String8 string = str8_push_copy(arena, node->string); + str8_list_push(arena, &result, string); + } + return(result); +} + +MR4TH_SYMBOL String8 +str8_join(Arena *arena, String8List *list, StringJoin *join_optional){ + ProfBeginFunc(); + + // setup join parameters + MR4TH_SYMBOL_STATIC StringJoin dummy_join = {0}; + StringJoin *join = join_optional; + if (join == 0){ + join = &dummy_join; + } + + // compute total size + U64 size = (join->pre.size + + join->post.size + + ((list->node_count>0)? + (join->mid.size*(list->node_count - 1)):0) + + list->total_size); + + // begin string build + U8 *str = push_array(arena, U8, size + 1); + U8 *ptr = str; + + // write pre + MemoryCopy(ptr, join->pre.str, join->pre.size); + ptr += join->pre.size; + + B32 is_mid = 0; + for (String8Node *node = list->first; + node != 0; + node = node->next){ + // write mid + if (is_mid){ + MemoryCopy(ptr, join->mid.str, join->mid.size); + ptr += join->mid.size; + } + + // write node string + MemoryCopy(ptr, node->string.str, node->string.size); + ptr += node->string.size; + + is_mid = 1; + } + + // write post + MemoryCopy(ptr, join->post.str, join->post.size); + ptr += join->post.size; + + // write null + *ptr = 0; + + String8 result = CLiteral(String8){str, size}; + ProfEndFunc(); + return(result); +} + +MR4TH_SYMBOL String8List +str8_split(Arena *arena, String8 string, U8 *splits, U32 count){ + ProfBeginFunc(); + + String8List result = {0}; + + U8 *ptr = string.str; + U8 *word_first = ptr; + U8 *opl = string.str + string.size; + for (;ptr < opl; ptr += 1){ + // is this a split + U8 byte = *ptr; + B32 is_split_byte = 0; + for (U32 i = 0; i < count; i += 1){ + if (byte == splits[i]){ + is_split_byte = 1; + break; + } + } + + if (is_split_byte){ + // try to emit word, advance word first pointer + if (word_first < ptr){ + str8_list_push(arena, &result, str8_range(word_first, ptr)); + } + word_first = ptr + 1; + } + } + + // try to emit final word + if (word_first < ptr){ + str8_list_push(arena, &result, str8_range(word_first, ptr)); + } + + ProfEndFunc(); + return(result); +} + +MR4TH_SYMBOL String8 +str8_pushfv(Arena *arena, char *fmt, va_list args){ + ProfBeginFunc(); + + // in case we need to try a second time + va_list args2; + va_copy(args2, args); + + // try to build the string in 1024 bytes + U64 buffer_size = 1024; + U8 *buffer = push_array(arena, U8, buffer_size); + U64 actual_size = m4_vsnprintf((char*)buffer, buffer_size, fmt, args); + + String8 result = {0}; + if (actual_size < buffer_size){ + // if first try worked, put back what we didn't use and finish + arena_pop_amount(arena, buffer_size - actual_size - 1); + result = CLiteral(String8){buffer, actual_size}; + } + else{ + // if first try failed, reset and try again with correct size + arena_pop_amount(arena, buffer_size); + U8 *fixed_buffer = push_array(arena, U8, actual_size + 1); + U64 final_size = m4_vsnprintf((char*)fixed_buffer, actual_size + 1, fmt, args2); + result = CLiteral(String8){fixed_buffer, final_size}; + } + + // end args2 + va_end(args2); + + ProfEndFunc(); + return(result); +} + +MR4TH_SYMBOL String8 +str8_pushf(Arena *arena, char *fmt, ...){ + va_list args; + va_start(args, fmt); + String8 result = str8_pushfv(arena, fmt, args); + va_end(args); + return(result); +} + +MR4TH_SYMBOL void +str8_list_pushf(Arena *arena, String8List *list, char *fmt, ...){ + va_list args; + va_start(args, fmt); + String8 string = str8_pushfv(arena, fmt, args); + va_end(args); + str8_list_push(arena, list, string); +} + +MR4TH_SYMBOL String8 +str8_push_copy(Arena *arena, String8 string){ + String8 result = {0}; + result.str = push_array(arena, U8, string.size + 1); + result.size = string.size; + MemoryCopy(result.str, string.str, string.size); + result.str[result.size] = 0; + return(result); +} + +// substrings + +MR4TH_SYMBOL String8 +str8_prefix(String8 str, U64 size){ + U64 size_clamped = ClampTop(size, str.size); + String8 result = {str.str, size_clamped}; + return(result); +} + +MR4TH_SYMBOL String8 +str8_chop(String8 str, U64 amount){ + U64 amount_clamped = ClampTop(amount, str.size); + U64 remaining_size = str.size - amount_clamped; + String8 result = {str.str, remaining_size}; + return(result); +} + +MR4TH_SYMBOL String8 +str8_postfix(String8 str, U64 size){ + U64 size_clamped = ClampTop(size, str.size); + U64 skip_to = str.size - size_clamped; + String8 result = {str.str + skip_to, size_clamped}; + return(result); +} + +MR4TH_SYMBOL String8 +str8_skip(String8 str, U64 amount){ + U64 amount_clamped = ClampTop(amount, str.size); + U64 remaining_size = str.size - amount_clamped; + String8 result = {str.str + amount_clamped, remaining_size}; + return(result); +} + +MR4TH_SYMBOL String8 +str8_skip_chop_whitespace(String8 str){ + String8 result = str; + if (result.size > 0){ + U8 *sptr = str.str; + U8 *eptr = str.str + str.size - 1; + for (;sptr < eptr && char_is_whitespace(*sptr); sptr += 1); + for (;sptr < eptr && char_is_whitespace(*eptr); eptr -= 1); + if (char_is_whitespace(*sptr)){ + result.str = 0; + result.size = 0; + } + else{ + result = str8_range(sptr, eptr + 1); + } + } + return(result); +} + +// hash + +MR4TH_SYMBOL U64 +str8_hash(String8 str){ + U64 hash = 5381; + for (U8 *ptr = str.str, *opl = str.str + str.size; + ptr < opl; + ptr += 1){ + U8 c = *ptr; + hash = (hash*33)^c; + } + return(hash); +} + +// compare + +MR4TH_SYMBOL B32 +str8_match(String8 a, String8 b, StringMatchFlags flags){ + ProfBeginFunc(); + + B32 result = 0; + if ((flags & StringMatchFlag_PrefixMatch) != 0 || a.size == b.size){ + U64 size = a.size; + if ((flags & StringMatchFlag_PrefixMatch) != 0){ + size = Min(a.size, b.size); + } + + result = 1; + B32 no_case = ((flags & StringMatchFlag_NoCase) != 0); + for (U64 i = 0; i < size; i += 1){ + U8 ac = a.str[i]; + U8 bc = b.str[i]; + if (no_case){ + ac = char_to_uppercase(ac); + bc = char_to_uppercase(bc); + } + if (ac != bc){ + result = 0; + break; + } + } + } + + ProfEndFunc(); + return(result); +} + +// path helpers + +MR4TH_SYMBOL String8 +str8_chop_last_slash(String8 string){ + String8 result = string; + if (string.size > 0){ + // pos one past last slash + U64 pos = string.size; + for (S64 i = string.size - 1; i >= 0; i -= 1){ + if (char_is_slash(string.str[i])){ + pos = i; + break; + } + } + + // chop result string + result.size = pos; + } + return(result); +} + +MR4TH_SYMBOL String8 +str8_file_name_from_path(String8 full_file_name){ + String8 result = {0}; + if (full_file_name.size > 0){ + U8 *opl = full_file_name.str + full_file_name.size; + U8 *ptr = opl; + for (;ptr > full_file_name.str; ptr -= 1){ + U8 c = ptr[-1]; + if (c == '/' || c == '\\'){ + break; + } + } + result = str8_range(ptr, opl); + } + return(result); +} + +MR4TH_SYMBOL String8 +str8_base_name_from_file_name(String8 file_name){ + String8 result = {0}; + if (file_name.size > 0){ + U8 *opl = file_name.str + file_name.size; + U8 *ptr = file_name.str; + for (;ptr < opl; ptr += 1){ + U8 c = ptr[0]; + if (c == '.'){ + break; + } + } + result = str8_range(file_name.str, ptr); + } + return(result); +} + +// stylized constructors + +MR4TH_SYMBOL String8 +str8_join_flags(Arena *arena, String8List *list){ + StringJoin join = {0}; + join.mid = str8_lit(" | "); + String8 result = str8_join(arena, list, &join); + if (result.size == 0){ + result = str8_lit("0"); + } + return(result); +} + +// unicode + +MR4TH_SYMBOL StringDecode +str_decode_utf8(U8 *str, U32 cap){ + ProfBeginFunc(); + + MR4TH_SYMBOL_STATIC U8 length[] = { + 1, 1, 1, 1, // 000xx + 1, 1, 1, 1, + 1, 1, 1, 1, + 1, 1, 1, 1, + 0, 0, 0, 0, // 100xx + 0, 0, 0, 0, + 2, 2, 2, 2, // 110xx + 3, 3, // 1110x + 4, // 11110 + 0, // 11111 + }; + MR4TH_SYMBOL_STATIC U8 first_byte_mask[] = { 0, 0x7F, 0x1F, 0x0F, 0x07 }; + MR4TH_SYMBOL_STATIC U8 final_shift[] = { 0, 18, 12, 6, 0 }; + + StringDecode result = {0}; + if (cap > 0){ + result.codepoint = '#'; + result.size = 1; + + U8 byte = str[0]; + U8 l = length[byte >> 3]; + if (0 < l && l <= cap){ + U32 cp = (byte & first_byte_mask[l]) << 18; + switch (l){ + case 4: cp |= ((str[3] & 0x3F) << 0); + case 3: cp |= ((str[2] & 0x3F) << 6); + case 2: cp |= ((str[1] & 0x3F) << 12); + default: break; + } + cp >>= final_shift[l]; + + result.codepoint = cp; + result.size = l; + } + } + + ProfEndFunc(); + return(result); +} + +MR4TH_SYMBOL U32 +str_encode_utf8(U8 *dst, U32 codepoint){ + ProfBeginFunc(); + + U32 size = 0; + if (codepoint < (1 << 7)){ + dst[0] = codepoint; + size = 1; + } + else if (codepoint < (1 << 11)){ + dst[0] = 0xC0 | (codepoint >> 6); + dst[1] = 0x80 | (codepoint & 0x3F); + size = 2; + } + else if (codepoint < (1 << 16)){ + dst[0] = 0xE0 | (codepoint >> 12); + dst[1] = 0x80 | ((codepoint >> 6) & 0x3F); + dst[2] = 0x80 | (codepoint & 0x3F); + size = 3; + } + else if (codepoint < (1 << 21)){ + dst[0] = 0xF0 | (codepoint >> 18); + dst[1] = 0x80 | ((codepoint >> 12) & 0x3F); + dst[2] = 0x80 | ((codepoint >> 6) & 0x3F); + dst[3] = 0x80 | (codepoint & 0x3F); + size = 4; + } + else{ + dst[0] = '#'; + size = 1; + } + + ProfEndFunc(); + return(size); +} + +MR4TH_SYMBOL StringDecode +str_decode_utf16(U16 *str, U32 cap){ + ProfBeginFunc(); + + StringDecode result = {'#', 1}; + U16 x = str[0]; + if (x < 0xD800 || 0xDFFF < x){ + result.codepoint = x; + } + else if (cap >= 2){ + U16 y = str[1]; + if (0xD800 <= x && x < 0xDC00 && + 0xDC00 <= y && y < 0xE000){ + U16 xj = x - 0xD800; + U16 yj = y - 0xDc00; + U32 xy = (xj << 10) | yj; + result.codepoint = xy + 0x10000; + result.size = 2; + } + } + + ProfEndFunc(); + return(result); +} + +MR4TH_SYMBOL U32 +str_encode_utf16(U16 *dst, U32 codepoint){ + ProfBeginFunc(); + + U32 size = 0; + if (codepoint < 0x10000){ + dst[0] = codepoint; + size = 1; + } + else{ + U32 cpj = codepoint - 0x10000; + dst[0] = (cpj >> 10) + 0xD800; + dst[1] = (cpj & 0x3FF) + 0xDC00; + size = 2; + } + + ProfEndFunc(); + return(size); +} + +MR4TH_SYMBOL String32 +str32_from_str8(Arena *arena, String8 string){ + ProfBeginFunc(); + + U32 *memory = push_array(arena, U32, string.size + 1); + + U32 *dptr = memory; + U8 *ptr = string.str; + U8 *opl = string.str + string.size; + for (; ptr < opl;){ + StringDecode decode = str_decode_utf8(ptr, (U64)(opl - ptr)); + *dptr = decode.codepoint; + ptr += decode.size; + dptr += 1; + } + + *dptr = 0; + + U64 alloc_count = string.size + 1; + U64 string_count = (U64)(dptr - memory); + U64 unused_count = alloc_count - string_count - 1; + arena_pop_amount(arena, unused_count*sizeof(*memory)); + + String32 result = {memory, string_count}; + ProfEndFunc(); + return(result); +} + +MR4TH_SYMBOL String8 +str8_from_str32(Arena *arena, String32 string){ + ProfBeginFunc(); + + U8 *memory = push_array(arena, U8, string.size*4 + 1); + + U8 *dptr = memory; + U32 *ptr = string.str; + U32 *opl = string.str + string.size; + for (; ptr < opl;){ + U32 size = str_encode_utf8(dptr, *ptr); + ptr += 1; + dptr += size; + } + + *dptr = 0; + + U64 alloc_count = string.size*4 + 1; + U64 string_count = (U64)(dptr - memory); + U64 unused_count = alloc_count - string_count - 1; + arena_pop_amount(arena, unused_count*sizeof(*memory)); + + String8 result = {memory, string_count}; + ProfEndFunc(); + return(result); +} + +MR4TH_SYMBOL String16 +str16_from_str8(Arena *arena, String8 string){ + ProfBeginFunc(); + U16 *memory = push_array(arena, U16, string.size*2 + 1); + + U16 *dptr = memory; + U8 *ptr = string.str; + U8 *opl = string.str + string.size; + for (; ptr < opl;){ + StringDecode decode = str_decode_utf8(ptr, (U64)(opl - ptr)); + U32 enc_size = str_encode_utf16(dptr, decode.codepoint); + ptr += decode.size; + dptr += enc_size; + } + + *dptr = 0; + + U64 alloc_count = string.size*2 + 1; + U64 string_count = (U64)(dptr - memory); + U64 unused_count = alloc_count - string_count - 1; + arena_pop_amount(arena, unused_count*sizeof(*memory)); + + String16 result = {memory, string_count}; + ProfEndFunc(); + return(result); +} + +MR4TH_SYMBOL String8 +str8_from_str16(Arena *arena, String16 string){ + ProfBeginFunc(); + U8 *memory = push_array(arena, U8, string.size*3 + 1); + + U8 *dptr = memory; + U16 *ptr = string.str; + U16 *opl = string.str + string.size; + for (; ptr < opl;){ + StringDecode decode = str_decode_utf16(ptr, (U64)(opl - ptr)); + U16 enc_size = str_encode_utf8(dptr, decode.codepoint); + ptr += decode.size; + dptr += enc_size; + } + + *dptr = 0; + + U64 alloc_count = string.size*3 + 1; + U64 string_count = (U64)(dptr - memory); + U64 unused_count = alloc_count - string_count - 1; + arena_pop_amount(arena, unused_count*sizeof(*memory)); + + String8 result = {memory, string_count}; + ProfEndFunc(); + return(result); +} + +// numeric conversion + +MR4TH_SYMBOL B32 +str8_is_u64(String8 string, U32 radix){ + Assert(2 <= radix && radix <= 16); + + B32 result = 1; + { + U8 *ptr = string.str; + U8 *opl = string.str + string.size; + for (; ptr < opl; ptr += 1){ + U8 num = 0xFF; + + // extract + U8 s = *ptr - '0'; + if (s <= 9){ + num = s; + } + else{ + s = *ptr - 'A'; + if (s <= 5){ + num = 0xA + s; + } + else{ + s = *ptr - 'a'; + if (s <= 5){ + num = 0xA + s; + } + } + } + + // bad parse check + if (num >= radix){ + result = 0; + break; + } + } + } + + return(result); +} + +MR4TH_SYMBOL U64 +u64_from_str8(String8 string, U32 radix){ + Assert(2 <= radix && radix <= 16); + + U64 result = 0; + { + U8 *ptr = string.str; + U8 *opl = string.str + string.size; + for (; ptr < opl; ptr += 1){ + U8 num = 0xFF; + + // extract + U8 s = *ptr - '0'; + if (s <= 9){ + num = s; + } + else{ + s = *ptr - 'A'; + if (s <= 5){ + num = 0xA + s; + } + else{ + s = *ptr - 'a'; + if (s <= 5){ + num = 0xA + s; + } + } + } + + // bad parse check + if (num >= radix){ + result = 0; + break; + } + + // increment result + result *= radix; + result += num; + } + } + + return(result); +} + +MR4TH_SYMBOL U64 +u64_from_str8_c_syntax(String8 string){ + U8 *ptr = string.str; + U8 *opl = string.str + string.size; + + U32 radix = 10; + if (ptr < opl && *ptr == '0'){ + radix = 010; + ptr += 1; + if (ptr < opl && *ptr == 'x'){ + radix = 0x10; + ptr += 1; + } + else if (ptr < opl && *ptr == 'b'){ + radix = 2; + ptr += 1; + } + } + + U64 result = u64_from_str8(str8_range(ptr, opl), radix); + return(result); +} + +MR4TH_SYMBOL S64 +s64_from_str8_c_syntax(String8 string){ + U8 *ptr = string.str; + U8 *opl = string.str + string.size; + + B32 negative = 0; + if (ptr < opl){ + if (*ptr == '-'){ + negative = 1; + ptr += 1; + } + else if (*ptr == '+'){ + ptr += 1; + } + } + + S64 result = u64_from_str8_c_syntax(str8_range(ptr, opl)); + if (negative){ + result = -result; + } + + return(result); +} + +MR4TH_SYMBOL F64 +f64_from_str8(String8 string){ + F64 result = 0.f; + + U8 *ptr = string.str; + U8 *opl = string.str + string.size; + + B32 negative = 0; + if (ptr < opl){ + if (*ptr == '-'){ + negative = 1; + ptr += 1; + } + else if (*ptr == '+'){ + ptr += 1; + } + } + + for (; ptr < opl; ptr += 1){ + if (*ptr == '.'){ + ptr += 1; + break; + } + U32 x = 0; + if ('0' <= *ptr && *ptr <= '9'){ + x = *ptr - '0'; + } + result *= 10.f; + result += x; + } + + F32 mul = 0.1f; + for (; ptr < opl; ptr += 1){ + U32 x = 0; + if ('0' <= *ptr && *ptr <= '9'){ + x = *ptr - '0'; + } + result += x*mul; + mul /= 10.f; + } + + if (negative){ + result = -result; + } + + return(result); +} + +//////////////////////////////// +// Functions: Stream + +// stream core implementation + +#define STREAM_NODE_FROM_DATA(d) (STREAM_Node*)((U8*)(d) - sizeof(STREAM_Node)) + +#if !MR4TH_PLUGIN_MODE + +MR4TH_SYMBOL_MUST_SHARE STREAM* +stream_new(void){ + Arena *arena = arena_alloc(); + STREAM *stream = push_array(arena, STREAM, 1); + stream->arena = arena; + stream->clear_pos = arena_current_pos(arena); + return(stream); +} + +MR4TH_SYMBOL_MUST_SHARE void +stream_release(STREAM *stream){ + stream_clear(stream); +} + +MR4TH_SYMBOL_MUST_SHARE void +stream_clear(STREAM *stream){ + Arena *arena = stream->arena; + U64 clear_pos = stream->clear_pos; + arena_pop_to(arena, clear_pos); + MemoryZeroStruct(stream); + stream->arena = arena; + stream->clear_pos = clear_pos; +} + +MR4TH_SYMBOL_MUST_SHARE void +stream_write(STREAM *stream, String8 data){ + U64 data_pos = 0; + + for (;data_pos < data.size;){ + U64 remaining_size = data.size - data_pos; + + // try to get a node with buffer space from the stream + // (don't return a node with no space!) + STREAM_Node *node = 0; + { + if (stream->buffer_cap > 0){ + node = stream->last_node; + } + if (node != 0 && stream->buffer_cap <= node->size){ + node = 0; + } + } + + // if we didn't get a buffer, try the unused node + if (node == 0){ + if (stream->unused_node != 0){ + node = stream->unused_node; + stream->unused_node = 0; + stream->buffer_cap = node->size; + node->size = 0; + SLLQueuePush(stream->first_node, stream->last_node, node); + } + } + + // if we didn't get a buffer, make one now + if (node == 0){ + U64 alloc_size = sizeof(STREAM_Node) + remaining_size; + alloc_size = ClampBot(MR4TH_STREAM_ALLOC_SIZE, alloc_size); + + node = (STREAM_Node*)push_array_no_zero(stream->arena, U8, alloc_size); + SLLQueuePush(stream->first_node, stream->last_node, node); + node->size = 0; + + stream->buffer_cap = alloc_size - sizeof(STREAM_Node); + } + + // get buffer from the node + U8 *buffer = node->data + node->size; + U64 buffer_size = stream->buffer_cap - node->size; + + // calculate amount to copy + U64 copy_amount = ClampTop(remaining_size, buffer_size); + + // copy memory and increment counters + MemoryCopy(buffer, data.str + data_pos, copy_amount); + node->size += copy_amount; + stream->total_size += copy_amount; + data_pos += copy_amount; + } +} + +MR4TH_SYMBOL_MUST_SHARE U8* +stream_alloc(STREAM *stream, U64 size){ + // try to get a node with buffer space + // (don't return a node with no space) + STREAM_Node *node = 0; + { + if (stream->buffer_cap > 0){ + node = stream->last_node; + } + if (node != 0 && stream->buffer_cap <= node->size){ + node = 0; + } + } + + // if there is a node but it's not big enough... + if (node != 0 && stream->buffer_cap - node->size < size){ + U64 remaining_cap = stream->buffer_cap - node->size; + U64 unused_ptr = IntFromPtr(node->data) + node->size; + U64 unused_opl = IntFromPtr(node->data) + stream->buffer_cap; + U64 unused_ptr_aligned = AlignUpPow2(unused_ptr, sizeof(void*)); + if (unused_opl > unused_ptr_aligned && + unused_opl - unused_ptr_aligned >= sizeof(STREAM_Node) + 32){ + STREAM_Node *unused_node = (STREAM_Node*)PtrFromInt(unused_ptr_aligned); + U64 leftover_buffer_size = unused_opl - unused_ptr_aligned - sizeof(STREAM_Node); + unused_node->size = leftover_buffer_size; + SLLStackPush(stream->unused_node, unused_node); + } + node = 0; + } + + // if we don't have a node then setup a new node with sufficient space. + if (node == 0){ + U64 alloc_size = sizeof(STREAM_Node) + size; + alloc_size = ClampBot(MR4TH_STREAM_ALLOC_SIZE, alloc_size); + + node = (STREAM_Node*)push_array_no_zero(stream->arena, U8, alloc_size); + SLLQueuePush(stream->first_node, stream->last_node, node); + node->size = 0; + + stream->buffer_cap = alloc_size - sizeof(STREAM_Node); + } + + // return the buffer + U8 *result = node->data + node->size; + node->size += size; + stream->total_size += size; + return(result); +} + +MR4TH_SYMBOL_MUST_SHARE U64 +stream_total_size(STREAM *stream){ + return(stream->total_size); +} + +MR4TH_SYMBOL_MUST_SHARE STREAM_Handle* +stream_next_chunk(STREAM *stream, STREAM_Handle *handle, String8 *chunk_out){ + STREAM_Node *node = (STREAM_Node*)handle; + if (node == 0){ + node = stream->first_node; + } + else{ + node = node->next; + } + if (node != 0){ + chunk_out->size = node->size; + chunk_out->str = node->data; + } + else{ + chunk_out->size = 0; + chunk_out->str = 0; + } + return((STREAM_Handle*)node); +} + +#endif /* stream core implementation */ + +// stream helpers + +MR4TH_SYMBOL String8 +stream_read(Arena *arena, STREAM *stream){ + String8 result = {0}; + result.str = push_array(arena, U8, stream->total_size); + result.size = stream->total_size; + + U64 pos = 0; + for (STREAM_Node *node = stream->first_node; + node != 0; + node = node->next){ + MemoryCopy(result.str + pos, node->data, node->size); + pos += node->size; + } + return(result); +} + +MR4TH_SYMBOL void +stream_printfv(STREAM *stream, char *fmt, va_list args){ + // in case we need to try a second time + va_list args2; + va_copy(args2, args); + + // try to get a buffer from the stream + STREAM_Node *node = 0; + U8 *buffer = 0; + U64 buffer_size = 0; + if (stream->buffer_cap > 0){ + node = stream->last_node; + buffer = node->data + node->size; + buffer_size = stream->buffer_cap - node->size; + } + + // try to build the string in the current block + U64 actual_size = 0; + if (buffer_size > 0){ + actual_size = m4_vsnprintf((char*)buffer, buffer_size, fmt, args); + } + else{ + actual_size = m4_vsnprintf(0, 0, fmt, args); + } + + // if first try worked, increment block size + if (actual_size <= buffer_size){ + node->size += actual_size; + stream->total_size += actual_size; + } + + // if first try failed, reset and try again with correct size + else{ + // TODO(allen): super annoying extra buffer copy, need to fix. + ArenaTemp scratch = arena_get_scratch(0, 0); + U8 *temp_buffer = push_array(scratch.arena, U8,actual_size + 1); + U64 final_size = m4_vsnprintf((char*)temp_buffer, actual_size + 1, fmt, args2); + Assert(final_size == actual_size); + U8 *new_buffer = stream_alloc(stream, actual_size); + MemoryCopy(new_buffer, temp_buffer, actual_size); + arena_release_scratch(&scratch); + } + + // end args2 + va_end(args2); +} + +MR4TH_SYMBOL void +stream_printf(STREAM *stream, char *fmt, ...){ + va_list args; + va_start(args, fmt); + stream_printfv(stream, fmt, args); + va_end(args); +} + +//////////////////////////////// +// Functions: Buffer + +MR4TH_SYMBOL void +buffer_alloc(BUFFER *buffer, U64 size){ + MemoryZeroStruct(buffer); + U64 real_size = AlignUpPow2(size, KB(4)); + buffer->base = os_memory_reserve(real_size); + if (buffer->base != 0){ + buffer->size = real_size; + buffer->commit_pos = 0; + } +} + +MR4TH_SYMBOL void +buffer_release(BUFFER *buffer){ + os_memory_release(buffer->base, buffer->size); +} + +MR4TH_SYMBOL void +buffer_reset(BUFFER *buffer){ + if (buffer->commit_pos > 0){ + os_memory_decommit((U8*)buffer->base, buffer->commit_pos); + } + buffer->commit_pos = 0; +} + +MR4TH_SYMBOL B32 +buffer_commit_off__call(BUFFER *buffer, U64 off){ + B32 result = 0; + U64 new_commit_pos = AlignUpPow2(off, MR4TH_MEM_COMMIT_BLOCK_SIZE); + new_commit_pos = ClampTop(new_commit_pos, buffer->size); + if (os_memory_commit((U8*)buffer->base + buffer->commit_pos, new_commit_pos - buffer->commit_pos)){ + buffer->commit_pos = new_commit_pos; + result = 1; + } + return(result); +} + +//////////////////////////////// +// Types: Hash Table + +MR4TH_SYMBOL void +hash_buckets_init(BUFFER *buckets, U64 count){ + Assert(count > 0 && IsPow2OrZero(count)); + buffer_commit_off(buckets, sizeof(void*)*count); +} + +MR4TH_SYMBOL HASH_Node* +hash_buckets_first(BUFFER *buckets, U64 hash){ + U64 count = buckets->commit_pos/sizeof(void*); + U64 bidx = hash&(count - 1); + HASH_Node *result = ((HASH_Node**)buckets->base)[bidx]; + return(result); +} + +MR4TH_SYMBOL void +hash_buckets_expand(BUFFER *buckets, U64 capacity){ + static const U32 ALPHA_NUMERATOR = 7; + static const U32 ALPHA_DENOMINATOR = 8; + + U64 count = buckets->commit_pos/sizeof(void*); + U64 max = buckets->size/sizeof(void*); + U32 dbl_count = count*2; + if (capacity*ALPHA_DENOMINATOR > count*ALPHA_NUMERATOR && + dbl_count <= max){ + U64 old_count = count; + U64 new_count = count*4; + if (new_count > max){ + new_count = dbl_count; + } + buffer_commit_off(buckets, sizeof(void*)*new_count); + U64 mask = new_count - 1; + U64 rehash_mask = mask&~(U64)(old_count - 1); + HASH_Node **buckets_base = (HASH_Node**)buckets->base; + for (U32 i = 0; i < old_count; i += 1){ + for (HASH_Node **ptr = buckets_base + i; + *ptr != 0;){ + HASH_Node *node = *ptr; + if ((node->hash&rehash_mask) != 0){ + U64 bidx = node->hash&mask; + *ptr = (*ptr)->next; + SLLStackPush(buckets_base[bidx], node); + } + else{ + ptr = &node->next; + } + } + } + } +} + +MR4TH_SYMBOL void +hash_buckets_insert(Arena *arena, BUFFER *buckets, U64 hash, U64 val){ + U64 count = buckets->commit_pos/sizeof(void*); + U64 bucket_idx = hash&(count - 1); + HASH_Node *bucket = push_array(arena, HASH_Node, 1); + bucket->hash = hash; + bucket->val = val; + SLLStackPush(((HASH_Node**)buckets->base)[bucket_idx], bucket); +} + +//////////////////////////////// +// Functions: Log + +MR4TH_SYMBOL_MUST_SHARE MR4TH_THREADVAR +LOG_ThreadVars *log_vars = 0; + +MR4TH_SYMBOL void +log_accum_begin(LOG_LogToProc *proc, void *uptr){ + LOG_ThreadVars *vars = log_vars; + if (vars == 0){ + Arena *arena = arena_alloc(); + vars = log_vars = push_array(arena, LOG_ThreadVars, 1); + vars->arena = arena; + } + + U64 pos = arena_current_pos(vars->arena); + LOG_Node *node = push_array(vars->arena, LOG_Node, 1); + node->pos = pos; + node->logto = proc; + node->uptr = uptr; + SLLStackPush(vars->stack, node); +} + +MR4TH_SYMBOL B32 +log_gathering(void){ + LOG_ThreadVars *vars = log_vars; + return(vars != 0 && vars->stack != 0); +} + +MR4TH_SYMBOL void +log_emit(String8 message){ + LOG_ThreadVars *vars = log_vars; + if (vars != 0 && vars->stack != 0){ + LOG_Node *node = vars->stack; + String8 msg_copy = str8_push_copy(vars->arena, message); + str8_list_push(vars->arena, &node->log, msg_copy); + if (node->logto != 0){ + node->logto(node->uptr, msg_copy); + } + } +} + +MR4TH_SYMBOL void +log_emitf(char *fmt, ...){ + LOG_ThreadVars *vars = log_vars; + if (vars != 0 && vars->stack != 0){ + LOG_Node *node = vars->stack; + va_list args; + va_start(args, fmt); + String8 string = str8_pushfv(vars->arena, fmt, args); + va_end(args); + str8_list_push(vars->arena, &node->log, string); + if (node->logto != 0){ + node->logto(node->uptr, string); + } + } +} + +MR4TH_SYMBOL String8 +log_accum_end(Arena *arena){ + String8 result = {0}; + LOG_ThreadVars *vars = log_vars; + if (vars != 0){ + LOG_Node *node = vars->stack; + if (node != 0){ + result = str8_join(arena, &node->log, 0); + SLLStackPop(vars->stack); + arena_pop_to(vars->arena, node->pos); + } + } + return(result); +} + +//////////////////////////////// +// Functions: Errors + +// IMPORTANT: It is important that strings get pushed +// *on top* of the node that holds them within the arena. +// basically: Assert((U8*)node < (U8*)node->error.str); +// except that stops working if the arena is chained... + +MR4TH_SYMBOL_MUST_SHARE MR4TH_THREADVAR +ER_ThreadVars *er_vars = 0; + +MR4TH_SYMBOL void +er_accum_begin(void){ + ER_ThreadVars *vars = er_vars; + if (vars == 0){ + Arena *arena = arena_new(KB(64), 1, 0); + vars = er_vars = push_array(arena, ER_ThreadVars, 1); + vars->arena = arena; + } + + U64 pos = arena_current_pos(vars->arena); + ER_Node *node = push_array(vars->arena, ER_Node, 1); + if (node == 0){ + vars->over_stack += 1; + } + else{ + node->pos = pos; + SLLStackPush(vars->stack, node); + } +} + +MR4TH_SYMBOL void +er_emit(String8 error){ + ER_ThreadVars *vars = er_vars; + if (vars != 0 && + vars->over_stack == 0){ + ER_Node *node = vars->stack; + if (node != 0 && node->error.size == 0){ + node->error = str8_push_copy(vars->arena, error); + } + } +} + +MR4TH_SYMBOL void +er_emitf(char *fmt, ...){ + ER_ThreadVars *vars = er_vars; + if (vars != 0 && + vars->over_stack == 0){ + ER_Node *node = vars->stack; + if (node != 0 && node->error.size == 0){ + va_list args; + va_start(args, fmt); + String8 string = str8_pushfv(vars->arena, fmt, args); + va_end(args); + node->error = string; + } + } +} + +MR4TH_SYMBOL String8 +er_accum_end(Arena *arena){ + String8 result = {0}; + ER_ThreadVars *vars = er_vars; + if (vars != 0){ + if (vars->over_stack == 0){ + ER_Node *node = vars->stack; + if (node != 0){ + result = str8_push_copy(arena, node->error); + SLLStackPop(vars->stack); + arena_pop_to(vars->arena, node->pos); + } + } + else{ + vars->over_stack -= 1; + } + } + return(result); +} + + +//////////////////////////////////////////////// +//////////////////////////////////////////////// +//////// OS IMPLEMENTATION SHARED ////////// +//////////////////////////////////////////////// +//////////////////////////////////////////////// + +//////////////////////////////// +// Helper Implementation: File Handling + +MR4TH_SYMBOL B32 +os_file_write(String8 file_name, String8 data){ + String8Node node = {0}; + node.string = data; + B32 result = os_file_write_list(file_name, &node); + return(result); +} + +MR4TH_SYMBOL String8 +os__file_write_callback__list(void *udata){ + String8Node **ptr = (String8Node**)udata; + String8 result = {0}; + if (*ptr != 0){ + result = (*ptr)->string; + (*ptr) = (*ptr)->next; + } + return(result); +} + +MR4TH_SYMBOL B32 +os_file_write_list(String8 file_name, String8Node *first_node){ + B32 result = os_file_write_callback(file_name, &first_node, os__file_write_callback__list); + return(result); +} + +MR4TH_SYMBOL String8 +os__file_write_callback__stream(void *udata){ + STREAM *stream = *(STREAM**) ((void**)udata + 0); + STREAM_Handle**handle = (STREAM_Handle**)((void**)udata + 1); + String8 result = {0}; + *handle = stream_next_chunk(stream, *handle, &result); + return(result); +} + +MR4TH_SYMBOL B32 +os_file_write_stream(String8 file_name, STREAM *stream){ + void *udata[2] = { stream, 0 }; + B32 result = os_file_write_callback(file_name, udata, os__file_write_callback__stream); + return(result); +} + +//////////////////////////////// +// Helper Implementation: Libraries + +MR4TH_SYMBOL OS_VRange +os_this_image(void){ + OS_VRange result = {0}; + OS_Library *this_lib = os_lib_from_addr((void*)os_this_image); + if (this_lib != 0){ + result = os_lib_image_range(this_lib); + } + return(result); +} + + + +/******************************* +** Begin Base Win32 ** +*******************************/ + +#if OS_WINDOWS + +//////////////////////////////// +// Win32 Implementation: Global Variables + +MR4TH_SYMBOL_STATIC U64 w32_ticks_per_second = 1; + +MR4TH_SYMBOL_STATIC Arena *w32_perm_arena = 0; +MR4TH_SYMBOL_STATIC String8 w32_binary_path = {0}; +MR4TH_SYMBOL_STATIC String8 w32_user_path = {0}; +MR4TH_SYMBOL_STATIC String8 w32_temp_path = {0}; + +MR4TH_SYMBOL_STATIC String8List w32_cmd_line = {0}; + +//////////////////////////////// +// Win32 Implementation: Process Setup + +MR4TH_SYMBOL void +os_main_init(int argc, char **argv){ + ProfBeginFunc(); + + // setup precision time + LARGE_INTEGER perf_freq = {0}; + if (QueryPerformanceFrequency(&perf_freq)){ + w32_ticks_per_second = ((U64)perf_freq.HighPart << 32) | perf_freq.LowPart; + } + timeBeginPeriod(1); + + // arena + w32_perm_arena = arena_alloc(); + + // command line arguments + for (int i = 0; i < argc; i += 1){ + String8 arg = str8_cstring((U8*)argv[i]); + str8_list_push(w32_perm_arena, &w32_cmd_line, arg); + } + + // paths + ArenaTemp scratch = arena_get_scratch(0, 0); + + // binary path + { + DWORD cap = 2048; + U16 *buffer = 0; + DWORD size = 0; + for (U64 r = 0; r < 4; r += 1, cap *= 4){ + U16 *try_buffer = push_array(scratch.arena, U16, cap); + DWORD try_size = GetModuleFileNameW(0, (WCHAR*)try_buffer, cap); + if (try_size == cap && GetLastError() == ERROR_INSUFFICIENT_BUFFER){ + arena_end_temp(&scratch); + } + else{ + buffer = try_buffer; + size = try_size; + break; + } + } + + String8 full_path = str8_from_str16(scratch.arena, CLiteral(String16){buffer, size}); + String8 binary_path = str8_chop_last_slash(full_path); + w32_binary_path = str8_push_copy(w32_perm_arena, binary_path); + } + + // user data + { + HANDLE token = GetCurrentProcessToken(); + DWORD cap = 2048; + U16 *buffer = push_array(scratch.arena, U16, cap); + if (!GetUserProfileDirectoryW(token, (WCHAR*)buffer, &cap)){ + arena_end_temp(&scratch); + buffer = push_array(scratch.arena, U16, cap + 1); + if (GetUserProfileDirectoryW(token, (WCHAR*)buffer, &cap)){ + buffer = 0; + } + } + + if (buffer != 0){ + // the docs make it sound like we can only count on + // cap getting the size on failure; so we're just going to cstring + // this to be safe. + w32_user_path = str8_from_str16(w32_perm_arena, str16_cstring(buffer)); + } + } + + // temp data + { + DWORD cap = 2048; + U16 *buffer = push_array(scratch.arena, U16, cap); + DWORD size = GetTempPathW(cap, (WCHAR*)buffer); + if (size >= cap){ + arena_end_temp(&scratch); + buffer = push_array(scratch.arena, U16, size + 1); + size = GetTempPathW(size + 1, (WCHAR*)buffer); + } + + // size - 1, because this particular string function + // in the Win32 API is different from the others and it includes + // the trailing backslash. We want consistency, so the "- 1" removes it. + w32_temp_path = str8_from_str16(w32_perm_arena, CLiteral(String16){buffer, size - 1}); + } + + arena_release_scratch(&scratch); + + ProfEndFunc(); +} + +MR4TH_SYMBOL String8List +os_command_line_arguments(void){ + String8List result = w32_cmd_line; + return(result); +} + +MR4TH_SYMBOL void +os_exit_process(U32 code){ + ProfClose(); + ExitProcess(code); +} + +//////////////////////////////// +// Win32 Implementation: Memory Functions + +MR4TH_SYMBOL void* +os_memory_reserve(U64 size){ + void *result = VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE); + return(result); +} + +MR4TH_SYMBOL B32 +os_memory_commit(void *ptr, U64 size){ + B32 result = (VirtualAlloc(ptr, size, MEM_COMMIT, PAGE_READWRITE) != 0); + return(result); +} + +MR4TH_SYMBOL void +os_memory_decommit(void *ptr, U64 size){ + VirtualFree(ptr, size, MEM_DECOMMIT); +} + +MR4TH_SYMBOL void +os_memory_release(void *ptr, U64 size){ + VirtualFree(ptr, 0, MEM_RELEASE); +} + +MR4TH_SYMBOL B32 +os_memory_protect(void *ptr, U64 size, DataAccessFlags access_flags){ + DWORD protect = PAGE_NOACCESS; + switch (access_flags&0x7){ + case 0: protect = PAGE_NOACCESS; break; + case DataAccessFlag_Read: protect = PAGE_READONLY; break; + case DataAccessFlag_Write: + case DataAccessFlag_Read|DataAccessFlag_Write: protect = PAGE_READWRITE; break; + case DataAccessFlag_Execute: protect = PAGE_EXECUTE; break; + case DataAccessFlag_Read|DataAccessFlag_Execute: protect = PAGE_EXECUTE_READ; break; + case DataAccessFlag_Write|DataAccessFlag_Execute: + case DataAccessFlag_Read|DataAccessFlag_Write|DataAccessFlag_Execute: protect = PAGE_EXECUTE_READWRITE; break; + } + + DWORD dummy = 0; + B32 result = VirtualProtect(ptr, size, protect, &dummy); + return(result); +} + +//////////////////////////////// +// Win32 Implementation: File Handling + +MR4TH_SYMBOL String8 +os_file_read(Arena *arena, String8 file_name){ + ProfBeginFunc(); + + // get handle + ArenaTemp scratch = arena_get_scratch(&arena, 1); + String16 file_name16 = str16_from_str8(scratch.arena, file_name); + HANDLE file = CreateFileW((WCHAR*)file_name16.str, + GENERIC_READ, 0, 0, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, + 0); + + String8 result = {0}; + if (file != INVALID_HANDLE_VALUE){ + // get size + DWORD hi_size = 0; + DWORD lo_size = GetFileSize(file, &hi_size); + U64 total_size = (((U64)hi_size) << 32) | (U64)lo_size; + + // allocate buffer + ArenaTemp restore_point = arena_begin_temp(arena); + U8 *buffer = push_array_no_zero(arena, U8, total_size + 1); + + // read + U8 *ptr = buffer; + U8 *opl = buffer + total_size; + B32 success = 1; + for (;ptr < opl;){ + U64 total_to_read = (U64)(opl - ptr); + DWORD to_read = (DWORD)total_to_read; + if (total_to_read > max_U32){ + to_read = max_U32; + } + DWORD actual_read = 0; + if (!ReadFile(file, ptr, to_read, &actual_read, 0)){ + success = 0; + break; + } + ptr += actual_read; + } + + // set result or reset memory + if (success){ + buffer[total_size] = 0; + result.str = buffer; + result.size = total_size; + } + else{ + arena_end_temp(&restore_point); + } + + CloseHandle(file); + } + + arena_release_scratch(&scratch); + + ProfEndFunc(); + return(result); +} + +MR4TH_SYMBOL B32 +os_file_write_callback(String8 file_name, void *udata, OS_FileWriteCallback *callback){ + ProfBeginFunc(); + + // get handle + ArenaTemp scratch = arena_get_scratch(0, 0); + String16 file_name16 = str16_from_str8(scratch.arena, file_name); + HANDLE file = CreateFileW((WCHAR*)file_name16.str, + GENERIC_WRITE, 0, 0, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, + 0); + + B32 result = 0; + if (file != INVALID_HANDLE_VALUE){ + result = 1; + + for (;;){ + String8 chunk = callback(udata); + if (chunk.size == 0){ + break; + } + + U8 *ptr = chunk.str; + U8 *opl = chunk.str + chunk.size; + for (;ptr < opl;){ + U64 total_to_write = (U64)(opl - ptr); + DWORD to_write = total_to_write; + if (total_to_write > max_U32){ + to_write = max_U32; + } + DWORD actual_write = 0; + if (!WriteFile(file, ptr, to_write, &actual_write, 0)){ + result = 0; + goto dblbreak; + } + ptr += actual_write; + } + } + dblbreak:; + + // close file + CloseHandle(file); + } + + arena_release_scratch(&scratch); + + ProfEndFunc(); + return(result); +} + +MR4TH_SYMBOL FileProperties +os_file_properties(String8 file_name){ + ProfBeginFunc(); + + // convert name + ArenaTemp scratch = arena_get_scratch(0, 0); + String16 file_name16 = str16_from_str8(scratch.arena, file_name); + + // get attribs and convert to properties + FileProperties result = {0}; + WIN32_FILE_ATTRIBUTE_DATA attribs = {0}; + if (GetFileAttributesExW((WCHAR*)file_name16.str, GetFileExInfoStandard, + &attribs)){ + result.size = ((U64)attribs.nFileSizeHigh << 32) | (U64)attribs.nFileSizeLow; + result.flags = w32_prop_flags_from_attribs(attribs.dwFileAttributes); + result.create_time = w32_dense_time_from_file_time(&attribs.ftCreationTime); + result.modify_time = w32_dense_time_from_file_time(&attribs.ftLastWriteTime); + result.access = w32_access_from_attributes(attribs.dwFileAttributes); + } + + arena_release_scratch(&scratch); + + ProfEndFunc(); + return(result); +} + +MR4TH_SYMBOL B32 +os_file_delete(String8 file_name){ + ProfBeginFunc(); + + // convert name + ArenaTemp scratch = arena_get_scratch(0, 0); + String16 file_name16 = str16_from_str8(scratch.arena, file_name); + // delete file + B32 result = DeleteFileW((WCHAR*)file_name16.str); + arena_release_scratch(&scratch); + + ProfEndFunc(); + return(result); +} + +MR4TH_SYMBOL B32 +os_file_rename(String8 og_name, String8 new_name){ + ProfBeginFunc(); + + // convert name + ArenaTemp scratch = arena_get_scratch(0, 0); + String16 og_name16 = str16_from_str8(scratch.arena, og_name); + String16 new_name16 = str16_from_str8(scratch.arena, new_name); + // rename file + B32 result = MoveFileW((WCHAR*)og_name16.str, (WCHAR*)new_name16.str); + arena_release_scratch(&scratch); + + ProfEndFunc(); + return(result); +} + +MR4TH_SYMBOL B32 +os_file_make_directory(String8 path){ + ProfBeginFunc(); + + // convert name + ArenaTemp scratch = arena_get_scratch(0, 0); + String16 path16 = str16_from_str8(scratch.arena, path); + // make directory + B32 result = CreateDirectoryW((WCHAR*)path16.str, 0); + arena_release_scratch(&scratch); + + ProfEndFunc(); + return(result); +} + +MR4TH_SYMBOL B32 +os_file_delete_directory(String8 path){ + ProfBeginFunc(); + + // convert name + ArenaTemp scratch = arena_get_scratch(0, 0); + String16 path16 = str16_from_str8(scratch.arena, path); + // make directory + B32 result = RemoveDirectoryW((WCHAR*)path16.str); + arena_release_scratch(&scratch); + + ProfEndFunc(); + return(result); +} + +MR4TH_SYMBOL OS_FileIter +os_file_iter_init(Arena *arena, String8 path){ + ProfBeginFunc(); + + // convert name + String8Node nodes[2]; + String8List list; + + nodes[0].string = path; + nodes[0].next = nodes + 1; + nodes[1].string = str8_lit("\\*"); + nodes[1].next = 0; + + list.first = nodes + 0; + list.last = nodes + 1; + list.node_count = 2; + list.total_size = path.size + 2; + + ArenaTemp scratch = arena_get_scratch(&arena, 1); + String8 path_star = str8_join(scratch.arena, &list, 0); + // TODO(allen): Better unicode conversions here + String16 path16 = str16_from_str8(scratch.arena, path_star); + + // store into iter + OS_FileIter result = {0}; + W32_FileIter *w32_iter = (W32_FileIter*)&result; + w32_iter->handle = FindFirstFileW((WCHAR*)path16.str, &w32_iter->find_data); + arena_release_scratch(&scratch); + + ProfEndFunc(); + return(result); +} + +MR4TH_SYMBOL B32 +os_file_iter_next(Arena *arena, OS_FileIter *iter, + String8 *name, FileProperties *props){ + ProfBeginFunc(); + + B32 result = 0; + + W32_FileIter *w32_iter = (W32_FileIter*)iter; + if (w32_iter->handle != 0 && + w32_iter->handle != INVALID_HANDLE_VALUE){ + for (;!w32_iter->done;){ + // check for . and .. + WCHAR *file_name = w32_iter->find_data.cFileName; + B32 is_dot = (file_name[0] == '.' && file_name[1] == 0); + B32 is_dotdot = (file_name[0] == '.' && file_name[1] == '.' && + file_name[2] == 0); + + // setup to emit + B32 emit = (!is_dot && !is_dotdot); + WIN32_FIND_DATAW data = {0}; + if (emit){ + MemoryCopyStruct(&data, &w32_iter->find_data); + } + + // increment the iterator + if (!FindNextFileW(w32_iter->handle, &w32_iter->find_data)){ + w32_iter->done = 1; + } + + // do the emit if we saved one earlier + if (emit){ + *name = str8_from_str16(arena, str16_cstring((U16*)data.cFileName)); + props->size = ((U64)data.nFileSizeHigh << 32) | (U64)data.nFileSizeLow; + props->flags = w32_prop_flags_from_attribs(data.dwFileAttributes); + props->create_time = w32_dense_time_from_file_time(&data.ftCreationTime); + props->modify_time = w32_dense_time_from_file_time(&data.ftLastWriteTime); + props->access = w32_access_from_attributes(data.dwFileAttributes); + result = 1; + break; + } + } + } + + ProfEndFunc(); + return(result); +} + +MR4TH_SYMBOL void +os_file_iter_end(OS_FileIter *iter){ + ProfBeginFunc(); + + W32_FileIter *w32_iter = (W32_FileIter*)iter; + if (w32_iter->handle != 0 && + w32_iter->handle != INVALID_HANDLE_VALUE){ + FindClose(w32_iter->handle); + } + + ProfEndFunc(); +} + +MR4TH_SYMBOL String8 +os_file_path(Arena *arena, OS_SystemPath path){ + ProfBeginFunc(); + + String8 result = {0}; + + switch (path){ + case OS_SystemPath_CurrentDirectory: + { + ArenaTemp scratch = arena_get_scratch(&arena, 1); + DWORD cap = 2048; + U16 *buffer = push_array(scratch.arena, U16, cap); + DWORD size = GetCurrentDirectoryW(cap, (WCHAR*)buffer); + if (size >= cap){ + arena_end_temp(&scratch); + buffer = push_array(scratch.arena, U16, size + 1); + size = GetCurrentDirectoryW(size + 1, (WCHAR*)buffer); + } + result = str8_from_str16(arena, CLiteral(String16){buffer, size}); + arena_release_scratch(&scratch); + }break; + + case OS_SystemPath_Binary: + { + result = str8_push_copy(arena, w32_binary_path); + }break; + + case OS_SystemPath_UserData: + { + result = str8_push_copy(arena, w32_user_path); + }break; + + case OS_SystemPath_TempData: + { + result = str8_push_copy(arena, w32_temp_path); + }break; + } + + ProfEndFunc(); + return(result); +} + +MR4TH_SYMBOL void +os_set_current_directory(String8 path){ + ArenaTemp scratch = arena_get_scratch(0, 0); + + String16 path16 = str16_from_str8(scratch.arena, path); + SetCurrentDirectoryW((WCHAR*)path16.str); + + arena_release_scratch(&scratch); +} + + +//////////////////////////////// +// Win32 Implementation: Time + +MR4TH_SYMBOL DateTime +os_now_universal_time(void){ + SYSTEMTIME system_time = {0}; + GetSystemTime(&system_time); + DateTime result = w32_date_time_from_system_time(&system_time); + return(result); +} + +MR4TH_SYMBOL DateTime +os_local_time_from_universal(DateTime *univ_date_time){ + SYSTEMTIME univ_system_time = w32_system_time_from_date_time(univ_date_time); + FILETIME univ_file_time = {0}; + SystemTimeToFileTime(&univ_system_time, &univ_file_time); + FILETIME local_file_time = {0}; + FileTimeToLocalFileTime(&univ_file_time, &local_file_time); + SYSTEMTIME local_system_time = {0}; + FileTimeToSystemTime(&local_file_time, &local_system_time); + DateTime result = w32_date_time_from_system_time(&local_system_time); + return(result); +} + +MR4TH_SYMBOL DateTime +os_universal_time_from_local(DateTime *local_date_time){ + SYSTEMTIME local_system_time = w32_system_time_from_date_time(local_date_time); + FILETIME local_file_time = {0}; + SystemTimeToFileTime(&local_system_time, &local_file_time); + FILETIME univ_file_time = {0}; + LocalFileTimeToFileTime(&local_file_time, &univ_file_time); + SYSTEMTIME univ_system_time = {0}; + FileTimeToSystemTime(&univ_file_time, &univ_system_time); + DateTime result = w32_date_time_from_system_time(&univ_system_time); + return(result); +} + +MR4TH_SYMBOL U32 +os_time_stamp_32_from_date_time(DateTime *date_time){ + SYSTEMTIME system_time = w32_system_time_from_date_time(date_time); + FILETIME file_time = {0}; + SystemTimeToFileTime(&system_time, &file_time); + U64 file_time64 = ( (U64) file_time.dwLowDateTime + + ((U64)(file_time.dwHighDateTime) << 32ll) + ); + U32 result = (U32) ((file_time64 - 116444736000000000ll)/10000000ll); + return(result); +} + + +MR4TH_SYMBOL U64 +os_now_ticks(void){ + U64 result = 0; + LARGE_INTEGER perf_counter = {0}; + if (QueryPerformanceCounter(&perf_counter)){ + result = ((U64)perf_counter.HighPart << 32) | perf_counter.LowPart; + } + return(result); +} + +MR4TH_SYMBOL void +os_get_microseconds_per_tick(U64 *usecs_out, U64 *ticks_out){ + *usecs_out = Million(1); + *ticks_out = w32_ticks_per_second; +} + +MR4TH_SYMBOL void +os_sleep_milliseconds(U32 t){ + Sleep(t); +} + +//////////////////////////////// +// Win32 Implementation: Libraries + +MR4TH_SYMBOL OS_Library* +os_lib_load(String8 path){ + ArenaTemp scratch = arena_get_scratch(0, 0); + String16 path16 = str16_from_str8(scratch.arena, path); + OS_Library *result = (OS_Library*)(LoadLibraryW((WCHAR*)path16.str)); + arena_release_scratch(&scratch); + return(result); +} + +MR4TH_SYMBOL VoidFunc* +os_lib_get_proc(OS_Library *lib, char *name){ + HMODULE module = (HMODULE)(lib); + VoidFunc *result = (VoidFunc*)(GetProcAddress(module, name)); + return(result); +} + +MR4TH_SYMBOL void +os_lib_release(OS_Library *lib){ + HMODULE module = (HMODULE)(lib); + FreeLibrary(module); +} + +MR4TH_SYMBOL OS_Library* +os_lib_from_addr(void *addr){ + HMODULE module = 0; + GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, + (LPWSTR)addr, &module); + return((OS_Library*)module); +} + +MR4TH_SYMBOL OS_VRange +os_lib_image_range(OS_Library *lib){ + OS_VRange result = {0}; + HANDLE process = GetCurrentProcess(); + MODULEINFO module_info = {0}; + if (GetModuleInformation(process, (HMODULE)lib, + &module_info, sizeof(module_info))){ + result.addr = (U8*)module_info.lpBaseOfDll; + result.size = module_info.SizeOfImage; + } + return(result); +} + +//////////////////////////////// +// Win32 Implementation: Entropy + +MR4TH_SYMBOL void +os_get_entropy(void *data, U64 size){ + HCRYPTPROV prov = 0; + CryptAcquireContextW(&prov, 0, 0, PROV_DSS, CRYPT_VERIFYCONTEXT); + CryptGenRandom(prov, size, (BYTE*)data); + CryptReleaseContext(prov, 0); +} + +//////////////////////////////// +// Win32 Functions: Specialized Init for WinMain + +MR4TH_SYMBOL_STATIC HINSTANCE w32_instance = 0; + +MR4TH_SYMBOL void +w32_WinMain_init(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPSTR lpCmdLine, + int nShowCmd){ + int argc = __argc; + char **argv = __argv; + w32_instance = hInstance; + os_main_init(argc, argv); +} + +MR4TH_SYMBOL HINSTANCE +w32_get_instance(void){ + if (w32_instance == 0){ + w32_instance = GetModuleHandle(0); + } + return(w32_instance); +} + +//////////////////////////////// +// Win32 Functions: Time Helpers + +MR4TH_SYMBOL DateTime +w32_date_time_from_system_time(SYSTEMTIME *in){ + DateTime result = {0}; + result.year = in->wYear; + result.mon = (U8)in->wMonth; + result.day = in->wDay; + result.hour = in->wHour; + result.min = in->wMinute; + result.sec = in->wSecond; + result.msec = in->wMilliseconds; + return(result); +} + +MR4TH_SYMBOL SYSTEMTIME +w32_system_time_from_date_time(DateTime *in){ + SYSTEMTIME result = {0}; + result.wYear = in->year; + result.wMonth = in->mon; + result.wDay = in->day; + result.wHour = in->hour; + result.wMinute = in->min; + result.wSecond = in->sec; + result.wMilliseconds = in->msec; + return(result); +} + +MR4TH_SYMBOL DenseTime +w32_dense_time_from_file_time(FILETIME *file_time){ + SYSTEMTIME system_time = {0}; + FileTimeToSystemTime(file_time, &system_time); + DateTime date_time = w32_date_time_from_system_time(&system_time); + DenseTime result = dense_time_from_date_time(&date_time); + return(result); +} + +//////////////////////////////// +// Win32 Functions: File Helpers + +MR4TH_SYMBOL FilePropertyFlags +w32_prop_flags_from_attribs(DWORD attribs){ + FilePropertyFlags result = 0; + if (attribs & FILE_ATTRIBUTE_DIRECTORY){ + result |= FilePropertyFlag_IsFolder; + } + return(result); +} + +MR4TH_SYMBOL DataAccessFlags +w32_access_from_attributes(DWORD attribs){ + DataAccessFlags result = DataAccessFlag_Read|DataAccessFlag_Execute; + if (!(attribs & FILE_ATTRIBUTE_READONLY)){ + result |= DataAccessFlag_Write; + } + return(result); +} + +#endif /* OS_WINDOWS */ + +/******************************* +** End Base Win32 ** +*******************************/ + + + +/******************************* +** Begin Base Linux ** +*******************************/ + +#if OS_LINUX + +//////////////////////////////// +// Linux Implementation: Global Variables + +MR4TH_SYMBOL_STATIC Arena *lnx_perm_arena = 0; +MR4TH_SYMBOL_STATIC String8 lnx_binary_path = {0}; +MR4TH_SYMBOL_STATIC String8 lnx_user_path = {0}; +MR4TH_SYMBOL_STATIC String8 lnx_temp_path = {0}; + +MR4TH_SYMBOL_STATIC String8List lnx_cmd_line = {0}; + +//////////////////////////////// +// Linux Implementation: Process Setup + +MR4TH_SYMBOL void +os_main_init(int argc, char **argv){ + ProfBeginFunc(); + + // arena + lnx_perm_arena = arena_alloc(); + + // command line arguments + for (int i = 0; i < argc; i += 1){ + String8 arg = str8_cstring((U8*)argv[i]); + str8_list_push(lnx_perm_arena, &lnx_cmd_line, arg); + } + + // paths + ArenaTemp scratch = arena_get_scratch(0, 0); + + // binary path + { + B32 got_final_result = 0; + U8 *buffer = 0; + int size = 0; + for (U64 cap = PATH_MAX, r = 0; r < 4; cap *= 2, r += 1){ + arena_end_temp(&scratch); + buffer = push_array_no_zero(scratch.arena, U8, cap); + size = readlink("/proc/self/exe", (char*)buffer, cap); + if (size < cap){ + got_final_result = 1; + break; + } + } + + if (got_final_result && size > 0){ + String8 full_name = str8(buffer, (U64)size); + String8 name_chopped = str8_chop_last_slash(full_name); + lnx_binary_path = str8_push_copy(lnx_perm_arena, name_chopped); + } + } + + // user data path + { + char *home = getenv("HOME"); + lnx_user_path = str8_cstring((U8*)home); + } + + // temp data path + { + lnx_temp_path = str8_lit("/var/tmp"); + } + + arena_release_scratch(&scratch); + + ProfEndFunc(); +} + +MR4TH_SYMBOL String8List +os_command_line_arguments(void){ + String8List result = lnx_cmd_line; + return(result); +} + +MR4TH_SYMBOL void +os_exit_process(U32 code){ + ProfClose(); + exit((int)code); +} + +//////////////////////////////// +// Linux Implementation: Memory Functions + +MR4TH_SYMBOL void* +os_memory_reserve(U64 size){ + void *result = mmap(0, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + if (result == MAP_FAILED){ + result = 0; + } + return(result); +} + +MR4TH_SYMBOL B32 +os_memory_commit(void *ptr, U64 size){ + B32 result = 0; + if (mprotect(ptr, size, PROT_READ|PROT_WRITE) == 0){ + result = 1; + } + return(result); +} + +MR4TH_SYMBOL void +os_memory_decommit(void *ptr, U64 size){ + madvise(ptr, size, MADV_DONTNEED); + mprotect(ptr, size, PROT_NONE); +} + +MR4TH_SYMBOL void +os_memory_release(void *ptr, U64 size){ + munmap(ptr, size); +} + +MR4TH_SYMBOL B32 +os_memory_protect(void *ptr, U64 size, DataAccessFlags access_flags){ + int prot = PROT_NONE; + if ((access_flags&DataAccessFlag_Read) == 0){ + prot |= PROT_READ; + } + if ((access_flags&DataAccessFlag_Write) == 0){ + prot |= PROT_WRITE; + } + if ((access_flags&DataAccessFlag_Execute) == 0){ + prot |= PROT_EXEC; + } + + B32 result = (mprotect(ptr, size, prot)); + return(result); +} + +//////////////////////////////// +// Linux Implementation: File Handling + +MR4TH_SYMBOL String8 +os_file_read(Arena *arena, String8 file_name){ + ProfBeginFunc(); + + // get handle + ArenaTemp scratch = arena_get_scratch(&arena, 1); + // (this ensures file_name8 has a null terminator) + String8 file_name8 = str8_push_copy(scratch.arena, file_name); + int file = open((char*)file_name8.str, O_RDONLY); + + String8 result = {0}; + if (file >= 0){ + // get size + struct stat file_stat = {0}; + U64 size = 0; + if (fstat(file, &file_stat) == 0){ + size = file_stat.st_size; + } + + // allocate buffer + if (size > 0){ + ArenaTemp restore_point = arena_begin_temp(arena); + U8 *buffer = push_array_no_zero(arena, U8, size + 1); + + // read + U8 *ptr = buffer; + U8 *opl = buffer + size; + B32 success = 1; + for (;ptr < opl;){ + U64 total_to_read = (U64)(opl - ptr); + U32 to_read = (U32)total_to_read; + if (total_to_read > max_U32){ + to_read = max_U32; + } + int read_result = read(file, buffer, to_read); + if (read_result < 0){ + success = 0; + break; + } + else if (read_result == 0){ + break; + } + ptr += read_result; + } + + // set result or reset memory + if (success){ + U64 final_size = (U64)(ptr - buffer); + final_size = ClampTop(final_size, size); + buffer[final_size] = 0; + result.str = buffer; + result.size = final_size; + } + else{ + arena_end_temp(&restore_point); + } + } + + // close file + close(file); + } + + arena_release_scratch(&scratch); + + ProfEndFunc(); + return(result); +} + +MR4TH_SYMBOL B32 +os_file_write_callback(String8 file_name, void *udata, OS_FileWriteCallback *callback){ + ProfBeginFunc(); + + // get handle + ArenaTemp scratch = arena_get_scratch(0, 0); + String8 file_name8 = str8_push_copy(scratch.arena, file_name); + int file = open((char*)file_name8.str, O_CREAT|O_WRONLY|O_TRUNC, 0666); + + B32 result = 0; + if (file >= 0){ + result = 1; + + for (;;){ + String8 chunk = callback(udata); + if (chunk.size == 0){ + break; + } + + U8 *ptr = chunk.str; + U8 *opl = chunk.str + chunk.size; + for (;ptr < opl;){ + U64 total_to_write = (U64)(opl - ptr); + U32 to_write = total_to_write; + if (total_to_write > (U32)SSIZE_MAX){ + to_write = (U32)SSIZE_MAX; + } + int write_result = write(file, ptr, to_write); + + if (write_result <= 0){ + result = 0; + goto dblbreak; + } + ptr += write_result; + } + + } + dblbreak:; + + // close file + close(file); + } + + arena_release_scratch(&scratch); + + ProfEndFunc(); + return(result); +} + +MR4TH_SYMBOL FileProperties +os_file_properties(String8 file_name){ + ProfBeginFunc(); + + // convert name + ArenaTemp scratch = arena_get_scratch(0, 0); + // (this ensures file_name8 has a null terminator) + String8 file_name8 = str8_push_copy(scratch.arena, file_name); + int file = open((char*)file_name8.str, O_RDONLY); + + // get file stat and convert to properties + FileProperties result = {0}; + struct stat file_stat = {0}; + if (fstat(file, &file_stat) == 0){ + result = lnx_file_properties_from_stat(&file_stat); + } + + arena_release_scratch(&scratch); + + ProfEndFunc(); + return(result); +} + +MR4TH_SYMBOL B32 +os_file_delete(String8 file_name){ + ProfBeginFunc(); + + ArenaTemp scratch = arena_get_scratch(0, 0); + String8 file_name8 = str8_push_copy(scratch.arena, file_name); + + B32 result = 0; + if (remove((char*)file_name8.str) >= 0){ + result = 1; + } + + arena_end_temp(&scratch); + + ProfEndFunc(); + return(result); +} + +MR4TH_SYMBOL B32 +os_file_rename(String8 og_name, String8 new_name){ + ProfBeginFunc(); + + ArenaTemp scratch = arena_get_scratch(0, 0); + String8 og_name8 = str8_push_copy(scratch.arena, og_name); + String8 new_name8 = str8_push_copy(scratch.arena, new_name); + + int rename_result = rename((char*)og_name8.str, (char*)new_name8.str); + B32 result = (rename_result >= 0); + + arena_release_scratch(&scratch); + + ProfEndFunc(); + return(result); +} + +MR4TH_SYMBOL B32 +os_file_make_directory(String8 path){ + ProfBeginFunc(); + + ArenaTemp scratch = arena_get_scratch(0, 0); + String8 path8 = str8_push_copy(scratch.arena, path); + + B32 result = 0; + if (mkdir((char*)path8.str, 0755) >= 0){ + result = 1; + } + + arena_release_scratch(&scratch); + + ProfEndFunc(); + return(result); +} + +MR4TH_SYMBOL B32 +os_file_delete_directory(String8 path){ + ProfBeginFunc(); + + ArenaTemp scratch = arena_get_scratch(0, 0); + String8 path8 = str8_push_copy(scratch.arena, path); + + B32 result = 0; + if (rmdir((char*)path8.str) >= 0){ + result = 1; + } + + arena_release_scratch(&scratch); + + ProfEndFunc(); + return(result); +} + +MR4TH_SYMBOL OS_FileIter +os_file_iter_init(Arena *arena, String8 path){ + ProfBeginFunc(); + + String8 path8 = str8_push_copy(arena, path); + + OS_FileIter result = {0}; + LNX_FileIter *lnx_iter = (LNX_FileIter*)&result; + lnx_iter->dir = opendir((char*)path8.str); + lnx_iter->path = path8; + + ProfEndFunc(); + return(result); +} + +MR4TH_SYMBOL B32 +os_file_iter_next(Arena *arena, OS_FileIter *iter, + String8 *name, FileProperties *props){ + B32 result = 0; + LNX_FileIter *lnx_iter = (LNX_FileIter*)iter; + + String8 path = lnx_iter->path; + + for (;lnx_iter->dir != 0;){ + struct dirent *dp = readdir(lnx_iter->dir); + result = (dp != 0); + if (!result){ + break; + } + + String8 file_name = str8_cstring((U8*)dp->d_name); + + struct stat st = {0}; + int stat_result = 0; + { + ArenaTemp scratch = arena_get_scratch(&arena, 1); + + String8 full_path = {0}; + full_path.size = path.size + 1 + file_name.size; + full_path.str = push_array_no_zero(scratch.arena, U8, full_path.size + 1); + MemoryCopy(full_path.str, path.str, path.size); + full_path.str[path.size] = '/'; + MemoryCopy(full_path.str + path.size + 1, file_name.str, file_name.size); + full_path.str[full_path.size] = 0; + + stat_result = stat((char*)full_path.str, &st); + + arena_release_scratch(&scratch); + } + + B32 filtered = 0; + if (file_name.str != 0 && + ((file_name.str[0] == '.' && file_name.str[1] == 0) || + (file_name.str[0] == '.' && file_name.str[1] == '.' && file_name.str[2] == 0))){ + filtered = 1; + } + + if (!filtered){ + *name = str8_push_copy(arena, file_name); + if (stat_result >= 0){ + *props = lnx_file_properties_from_stat(&st); + } + result = 1; + break; + } + } + + return(result); +} + +MR4TH_SYMBOL void +os_file_iter_end(OS_FileIter *iter){ + ProfBeginFunc(); + + LNX_FileIter *lnx_iter = (LNX_FileIter*)iter; + if (lnx_iter->dir != 0){ + closedir(lnx_iter->dir); + lnx_iter->dir = 0; + } + + ProfEndFunc(); +} + +MR4TH_SYMBOL String8 +os_file_path(Arena *arena, OS_SystemPath path){ + ProfBeginFunc(); + + String8 result = {0}; + + switch (path){ + case OS_SystemPath_CurrentDirectory: + { + char *cwd = getcwd(0, 0); + result = str8_push_copy(arena, str8_cstring((U8*)cwd)); + free(cwd); + }break; + + case OS_SystemPath_Binary: + { + result = str8_push_copy(arena, lnx_binary_path); + }break; + + case OS_SystemPath_UserData: + { + result = str8_push_copy(arena, lnx_user_path); + }break; + + case OS_SystemPath_TempData: + { + result = str8_push_copy(arena, lnx_temp_path); + }break; + } + + ProfEndFunc(); + return(result); +} + +MR4TH_SYMBOL void +os_set_current_directory(String8 path){ + ArenaTemp scratch = arena_get_scratch(0, 0); + String8 path8 = str8_push_copy(scratch.arena, path); + chdir((char*)path8.str); + arena_release_scratch(&scratch); +} + + +//////////////////////////////// +// Linux Implementation: Time + +MR4TH_SYMBOL DateTime +os_now_universal_time(void){ + time_t t = time(0); + struct tm tm = {0}; + struct tm *tmptr = gmtime_r(&t, &tm); + DateTime result = {0}; + if (tmptr != 0){ + result = lnx_date_time_from_tm(&tm); + } + return(result); +} + +MR4TH_SYMBOL DateTime +os_local_time_from_universal(DateTime *date_time){ + struct tm univ_tm = lnx_tm_from_date_time(date_time); + univ_tm.tm_isdst = -1; + time_t univ_time_t = timegm(&univ_tm); + struct tm local_tm = {0}; + localtime_r(&univ_time_t, &local_tm); + DateTime result = lnx_date_time_from_tm(&local_tm); + return(result); +} + +MR4TH_SYMBOL DateTime +os_universal_time_from_local(DateTime *date_time){ + struct tm local_tm = lnx_tm_from_date_time(date_time); + local_tm.tm_isdst = -1; + time_t univ_time_t = mktime(&local_tm); + struct tm univ_tm = {0}; + gmtime_r(&univ_time_t, &univ_tm); + DateTime result = lnx_date_time_from_tm(&univ_tm); + return(result); +} + +MR4TH_SYMBOL U32 +os_time_stamp_32_from_date_time(DateTime *date_time){ + struct tm tm = lnx_tm_from_date_time(date_time); + time_t t = timegm(&tm); + return((U32)t); +} + +MR4TH_SYMBOL U64 +os_now_ticks(void){ + struct timespec t = {0}; + clock_gettime(CLOCK_MONOTONIC, &t); + U64 result = t.tv_sec*Million(1) + t.tv_nsec/Thousand(1); + return(result); +} + +MR4TH_SYMBOL void +os_get_microseconds_per_tick(U64 *usecs_out, U64 *ticks_out){ + *usecs_out = 1; + *ticks_out = 1; +} + +MR4TH_SYMBOL void +os_sleep_milliseconds(U32 t){ + usleep(t*Thousand(1)); +} + +//////////////////////////////// +// Linux Implementation: Libraries + +MR4TH_SYMBOL OS_Library* +os_lib_load(String8 path){ + ArenaTemp scratch = arena_get_scratch(0, 0); + String8 path8 = str8_push_copy(scratch.arena, path); + OS_Library *result = (OS_Library*)dlopen((char*)path8.str, RTLD_LAZY|RTLD_LOCAL); + arena_release_scratch(&scratch); + return(result); +} + +MR4TH_SYMBOL VoidFunc* +os_lib_get_proc(OS_Library *lib, char *name){ + VoidFunc *proc = (VoidFunc*)dlsym((void*)lib, name); + return(proc); +} + +MR4TH_SYMBOL void +os_lib_release(OS_Library *lib){ + dlclose((void*)lib); +} + +MR4TH_SYMBOL OS_Library* +os_lib_from_addr(void *addr){ + UAddr addrx = IntFromPtr(addr); + ArenaTemp scratch = arena_get_scratch(0, 0); + + // NOTE(allen): + // Implementing this on Linux is a lot less solidly + // supported than it is on Windows. + // + // As far as I can tell, reading "/proc/self/maps" is the + // only portable way to get at the information. The fact + // that this information is in only available in this text + // based unaccelerated structured is unfortunate. + // + // But even worse is the fact that, even with this info, + // to actually get one of the opaque handles to the + // library, we have to call dlopen again -- no other API + // appears to exist which can return the required information. + // + // But that also means that the library gets a reference + // count from this call and so we have to then call dlclose + // on the handle just to decrement that reference counter. + // + // I don't even know yet if this handles the base executable + // of the program correctly. + + // read "/proc/self/maps" + B32 got_result = 0; + U8 *buffer = 0; + int size = 0; + for (U64 cap = KB(4), r = 0; r < 6; cap *= 64, r += 1){ + arena_end_temp(&scratch); + buffer = push_array_no_zero(scratch.arena, U8, cap); + size = readlink("/proc/self/maps", (char*)buffer, cap); + if (size < cap){ + got_result = 1; + break; + } + } + + // parse contents from "/proc/self/maps" + String8 contents = str8(buffer, (U64)size); + String8List lines = str8_split(scratch.arena, contents, (U8*)"\n", 1); + for (String8Node *node = lines.first; + node != 0; + node = node->next){ + String8 line = node->string; + U8 *opl = line.str + line.size; + + + U8 *dash_ptr = line.str; + for (;dash_ptr < opl && *dash_ptr != '-'; dash_ptr += 1); + + U8 *addr_max_opl = dash_ptr; + for (;dash_ptr < opl && *dash_ptr != ' '; dash_ptr += 1); + + U8 *slash_ptr = addr_max_opl; + for (;slash_ptr < opl && *slash_ptr != '/'; slash_ptr += 1); + + U64 addr_min = u64_from_str8(str8_range(line.str, dash_ptr), 0x10); + U64 addr_max = u64_from_str8(str8_range(dash_ptr + 1, addr_max_opl), 0x10); + String8 path = str8_range(slash_ptr, opl); + + + } + + push_array_no_zero(scratch.arena, U8, size); + + arena_release_scratch(&scratch); + + OS_Library *library = 0; + return(library); +} + +MR4TH_SYMBOL OS_VRange +os_lib_image_range(OS_Library *lib){ + OS_VRange result = {0}; + // TODO(allen): + return(result); +} + +//////////////////////////////// +// Linux Implementation: Entropy + +MR4TH_SYMBOL void +os_get_entropy(void *data, U64 size){ + getrandom(data, size, 0); +} + +//////////////////////////////// +// Linux Functions: Time Helpers + +MR4TH_SYMBOL DateTime +lnx_date_time_from_tm(struct tm *tm){ + DateTime result = {0}; + result.year = tm->tm_year + 1900; + result.mon = tm->tm_mon + 1; + result.day = tm->tm_mday; + result.hour = tm->tm_hour; + result.min = tm->tm_min; + result.sec = tm->tm_sec; + result.msec = 0; + return(result); +} + +MR4TH_SYMBOL DenseTime +lnx_dense_time_from_time_t(time_t time){ + struct tm system_time = *gmtime(&time); + DateTime date_time = lnx_date_time_from_tm(&system_time); + DenseTime result = dense_time_from_date_time(&date_time); + return(result); +} + +MR4TH_SYMBOL struct tm +lnx_tm_from_date_time(DateTime *date_time){ + struct tm result = {0}; + result.tm_sec = date_time->sec; + result.tm_min = date_time->min; + result.tm_hour = date_time->hour; + result.tm_mday = date_time->day; + result.tm_mon = date_time->mon - 1; + result.tm_year = date_time->year; + return(result); +} + +//////////////////////////////// +// Linux Functions: File Helpers + +MR4TH_SYMBOL FilePropertyFlags +lnx_prop_flags_from_mode(mode_t mode){ + FilePropertyFlags result = 0; + if ((mode&S_IFDIR) != 0){ + result |= FilePropertyFlag_IsFolder; + } + return(result); +} + +MR4TH_SYMBOL DataAccessFlags +lnx_access_from_mode(mode_t mode){ + DataAccessFlags result = 0; + if ((mode&(S_IRUSR|S_IRGRP)) != 0){ + result |= DataAccessFlag_Read; + } + if ((mode&(S_IWUSR|S_IWGRP)) != 0){ + result |= DataAccessFlag_Write; + } + if ((mode&(S_IXUSR|S_IXGRP)) != 0){ + result |= DataAccessFlag_Execute; + } + return(result); +} + +MR4TH_SYMBOL FileProperties +lnx_file_properties_from_stat(struct stat *st){ + FileProperties result = {0}; + result.size = st->st_size; + result.flags = lnx_prop_flags_from_mode(st->st_mode); + result.create_time = lnx_dense_time_from_time_t(st->st_ctime); + result.modify_time = lnx_dense_time_from_time_t(st->st_mtime); + // TODO(allen): research: + // how do these flags from fstat behave? + // S_IRUSR, S_IWUSR, S_IXUSR + // S_IRGRP, S_IWGRP, S_IXGRP + // S_IROTH, S_IWOTH, S_IXOTH + result.access = lnx_access_from_mode(st->st_mode); + return(result); +} + +MR4TH_SYMBOL String8 +lnx_proc_file_read(Arena *arena, char *path){ + ArenaTemp scratch = arena_get_scratch(&arena, 1); + + int file = open(path, O_RDONLY); + + String8List list = {0}; + if (file >= 0){ + S64 cap = KB(4); + for (;;){ + U8 *buffer = push_array_no_zero(scratch.arena, U8, cap); + int size = read(file, buffer, cap); + if (size > 0){ + str8_list_push(scratch.arena, &list, str8(buffer, (U64)size)); + } + if (size < cap){ + break; + } + cap *= 4; + } + } + + String8 result = str8_join(arena, &list, 0); + + arena_release_scratch(&scratch); + + return(result); +} + +#endif /* OS_LINUX */ + +/******************************* +** End Base Linux ** +*******************************/ + + +//////////////////////////////////////////////// +//////////////////////////////////////////////// +///////////////// M4 Printf //////////////// +//////////////////////////////////////////////// +//////////////////////////////////////////////// + +// stb_sprintf.h STB_SPRINTF_IMPLEMENTATION + +#define stbsp__uint32 U32 +#define stbsp__int32 S32 +#define stbsp__uint64 U64 +#define stbsp__int64 S64 +#define stbsp__uint16 U16 +#define stbsp__uintptr UAddr + +// NOTE: modification - removed STB_SPRINTF_MSVC_MODE -- compatibility mode for behavior from MSVC2013 and earlier + +#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 + +// 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 + +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 m4_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__MEMORY_SIZES 256 +#define STBSP__HALFWIDTH 512 + +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 m4_vsprintfcb(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va) +{ + ArenaTemp scratch = arena_get_scratch(0, 0); + + static char hex[] = "0123456789abcdefxp"; + static char hexu[] = "0123456789ABCDEFXP"; + static char spaces[] = + " " + " " + " " + " "; + char *bf = buf; + char const *f = fmt; + int tlen = 0; + + for (;;){ + stbsp__int32 field_width; + stbsp__int32 precision; + stbsp__int32 tz; + stbsp__uint32 flags; + + // 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 + field_width = 0; + precision = -1; + flags = 0; + tz = 0; + + // flags + for (;;) { + switch (f[0]) { + // if we have left justify + case '-': + flags |= STBSP__LEFTJUST; + ++f; + continue; + // if we have leading plus + case '+': + flags |= STBSP__LEADINGPLUS; + ++f; + continue; + // if we have leading space + case ' ': + flags |= STBSP__LEADINGSPACE; + ++f; + continue; + // if we have leading 0x + case '#': + flags |= STBSP__LEADING_0X; + ++f; + continue; + // if we have thousand commas + case '\'': + flags |= STBSP__TRIPLET_COMMA; + ++f; + continue; + // if we have memory sizes + case '$': + flags |= STBSP__MEMORY_SIZES; + ++f; + continue; + // if we have leading zero + case '0': + flags |= STBSP__LEADINGZERO; + ++f; + goto flags_done; + default: goto flags_done; + } + } + flags_done: + + // get the field width + if (f[0] == '*'){ + field_width = va_arg(va, stbsp__uint32); + ++f; + } + else{ + while ((f[0] >= '0') && (f[0] <= '9')) { + field_width = field_width*10 + f[0] - '0'; + f++; + } + } + + // get the precision + if (f[0] == '.'){ + ++f; + if (f[0] == '*'){ + precision = va_arg(va, stbsp__uint32); + ++f; + } + else{ + precision = 0; + while ((f[0] >= '0') && (f[0] <= '9')){ + precision = precision*10 + f[0] - '0'; + f++; + } + } + } + + // handle integer size overrides + switch (f[0]){ + // are we halfwidth? + case 'h': + flags |= STBSP__HALFWIDTH; + ++f; + if (f[0] == 'h') + ++f; // QUARTERWIDTH + break; + // are we 64-bit (unix style) + case 'l': + flags |= ((sizeof(long) == 8) ? STBSP__INTMAX : 0); + ++f; + if (f[0] == 'l') { + flags |= STBSP__INTMAX; + ++f; + } + break; + // are we 64-bit on intmax? (c99) + case 'j': + flags |= (sizeof(size_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + // are we 64-bit on size_t or ptrdiff_t? (c99) + case 'z': + flags |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + case 't': + flags |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + // are we 64-bit (msft style) + case 'I': + if ((f[1] == '6') && (f[2] == '4')) { + flags |= STBSP__INTMAX; + f += 3; + } else if ((f[1] == '3') && (f[2] == '2')) { + f += 3; + } else { + flags |= ((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; + stbsp__uint32 n; + stbsp__uint32 cs; + stbsp__uint64 n64; + double fv; + stbsp__int32 dp; + char const *sn; + + // NOTE(allen): MODIFICATION + // %S prints str (parameter: String8 str) + case 'S': + { + String8 *str = va_arg(va, String8*); + s = (char*)str->str; + l = (precision >= 0 && precision < str->size) ? precision : (U32)str->size; + lead[0] = 0; + tail[0] = 0; + precision = 0; + dp = 0; + cs = 0; + goto scopy; + } + + // NOTE(allen): MODIFICATION + // %N prints n spaces (parameter: int n) + case 'N': + { + // TODO(allen): there's probably a better way to set this + // up so that it doesn't rely on the buffer of spaces - + // perhaps a way to apply the space filling in the left justify path? + l = va_arg(va, int); + l = (sizeof(spaces) - 1 < l) ? (sizeof(spaces) - 1) : l; + l = (precision >= 0 && precision < l) ? precision : l; + s = spaces; + lead[0] = 0; + tail[0] = 0; + precision = 0; + dp = 0; + cs = 0; + goto scopy; + } + + // NOTE(allen): MODIFICATION + // %R prints n copies of c (parameter: int n, int c) + case 'R': + {char c; + l = va_arg(va, int); + c = (char)va_arg(va, int); + l = (precision >= 0 && precision < l) ? precision : l; + s = push_array_no_zero(scratch.arena, char, l); + for (char *sptr = s; sptr < s + l; sptr += 1){ + *sptr = (char)c; + } + lead[0] = 0; + tail[0] = 0; + precision = 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, (precision >= 0) ? precision : ~0u); + lead[0] = 0; + tail[0] = 0; + precision = 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; + precision = 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; + + case 'A': // hex float + case 'a': // hex float + { + h = (f[0] == 'A') ? hexu : hex; + fv = va_arg(va, double); + if (precision == -1){ + precision = 6; // default is 6 + } + // read the double into a string + if (stbsp__real_to_parts((stbsp__int64 *)&n64, &dp, fv)){ + flags |= STBSP__NEGATIVE; + } + + s = num + 64; + + stbsp__lead_sign(flags, lead); + + if (dp == -1023){ + dp = (n64) ? -1022 : 0; + } + else{ + n64 |= (((stbsp__uint64)1) << 52); + } + n64 <<= (64 - 56); + if (precision < 15){ + n64 += ((((stbsp__uint64)8) << 56) >> (precision*4)); + } + // add leading chars + + lead[1 + lead[0]] = '0'; + lead[2 + lead[0]] = 'x'; + lead[0] += 2; + + *s++ = h[(n64 >> 60) & 15]; + n64 <<= 4; + if (precision){ + *s++ = stbsp__period; + } + sn = s; + + // print the bits + n = precision; + if (n > 13){ + n = 13; + } + if (precision > (stbsp__int32)n){ + tz = precision - n; + } + precision = 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 (precision == -1){ + precision = 6; + } + else if (precision == 0){ + precision = 1; // default is 6 + } + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, (precision - 1) | 0x80000000)){ + flags |= STBSP__NEGATIVE; + } + + // clamp the precision and delete extra zeros after clamp + n = precision; + if (l > (stbsp__uint32)precision){ + l = precision; + } + while ((l > 1) && (precision) && (sn[l - 1] == '0')){ + --precision; + --l; + } + + // should we use %e + if ((dp <= -4) || (dp > (stbsp__int32)n)) { + if (precision > (stbsp__int32)l){ + precision = l - 1; + } + else if (precision){ + --precision; // when using %e, there is one digit before the decimal + } + goto doexpfromg; + } + // this is the insane action to get the precision to match %g semantics for %f + if (dp > 0){ + precision = (dp < (stbsp__int32)l) ? l - dp : 0; + } + else{ + precision = -dp + ((precision > (stbsp__int32)l) ? (stbsp__int32) l : precision); + } + goto dofloatfromg; + } + + case 'E': // float + case 'e': // float + { + h = (f[0] == 'E') ? hexu : hex; + fv = va_arg(va, double); + if (precision == -1){ + precision = 6; // default is 6 + } + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, precision | 0x80000000)){ + flags |= STBSP__NEGATIVE; + } + goto doexpfromg; + } + + doexpfromg: + { + tail[0] = 0; + stbsp__lead_sign(flags, lead); + if (dp == STBSP__SPECIAL) { + s = (char *)sn; + cs = 0; + precision = 0; + goto scopy; + } + s = num + 64; + // handle leading chars + *s++ = sn[0]; + + if (precision){ + *s++ = stbsp__period; + } + + // handle after decimal + if ((l - 1) > (stbsp__uint32)precision){ + l = precision + 1; + } + for (n = 1; n < l; n++){ + *s++ = sn[n]; + } + // trailing zeros + tz = precision - (l - 1); + precision = 0; + // dump expo + tail[1] = h[0xe]; + dp -= 1; + if (dp < 0){ + tail[2] = '-'; + dp = -dp; + } + else{ + tail[2] = '+'; + } + n = (dp >= 100) ? 5 : 4; + 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); + goto doafloat; + } + + doafloat: + { + // do kilos + if (flags & STBSP__MEMORY_SIZES) { + double divisor = 1024.f; + while (flags < 0x4000000) { + if ((fv < divisor) && (fv > -divisor)) + break; + fv /= divisor; + flags += 0x1000000; + } + } + if (precision == -1){ + precision = 6; // default is 6 + } + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, precision)){ + flags |= STBSP__NEGATIVE; + } + } + + dofloatfromg: + { + tail[0] = 0; + stbsp__lead_sign(flags, lead); + if (dp == STBSP__SPECIAL) { + s = (char *)sn; + cs = 0; + precision = 0; + goto scopy; + } + s = num + 64; + + // handle the three decimal varieties + if (dp <= 0) { + stbsp__int32 i; + // handle 0.000*000xxxx + *s++ = '0'; + if (precision){ + *s++ = stbsp__period; + } + n = -dp; + if ((stbsp__int32)n > precision){ + n = precision; + } + 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) > precision){ + l = precision - n; + } + i = l; + while (i) { + *s++ = *sn++; + --i; + } + tz = precision - (n + l); + cs = 1 + (3 << 24); // how many tens did we write (for commas below) + } + else{ + cs = (flags & STBSP__TRIPLET_COMMA) ? ((600 - (stbsp__uint32)dp) % 3) : 0; + if ((stbsp__uint32)dp >= l) { + // handle xxxx000*000.0 + n = 0; + for (;;) { + if ((flags & 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 ((flags & 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 ((flags & 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 (precision) { + *s++ = stbsp__period; + tz = precision; + } + } else { + // handle xxxxx.xxxx000*000 + n = 0; + for (;;) { + if ((flags & 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 (precision){ + *s++ = stbsp__period; + } + if ((l - dp) > (stbsp__uint32)precision){ + l = precision + dp; + } + while (n < l) { + *s++ = sn[n]; + ++n; + } + tz = precision - (l - dp); + } + } + precision = 0; + + // handle k,m,g,t + if (flags & STBSP__MEMORY_SIZES) { + tail[0] = 0; + { + char idx = 1; + tail[idx] = ' '; + idx++; + tail[idx] = " KMGT"[flags >> 24]; + idx++; + tail[idx] = 'b'; + tail[0] = idx; + } + } + goto flt_lead; + } + + flt_lead: + { + // get the length that we copied + l = (stbsp__uint32)(s - (num + 64)); + s = num + 64; + goto scopy; + } + + case 'B': // upper binary + case 'b': // lower binary + { + h = (f[0] == 'B') ? hexu : hex; + lead[0] = 0; + if (flags & 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 (flags & STBSP__LEADING_0X) { + lead[0] = 1; + lead[1] = '0'; + } + l = (3 << 4) | (3 << 8); + goto radixnum; + } + + case 'p': // pointer + { + flags |= (sizeof(void *) == 8) ? STBSP__INTMAX : 0; + precision = sizeof(void *) * 2; + flags &= ~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 (flags & STBSP__LEADING_0X) { + lead[0] = 2; + lead[1] = '0'; + lead[2] = h[16]; + } + goto radixnum; + } + + radixnum: + { + // get the number + if (flags & 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 (precision == 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) < precision))){ + break; + } + if (flags & 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 (flags & STBSP__INTMAX) { + stbsp__int64 i64 = va_arg(va, stbsp__int64); + n64 = (stbsp__uint64)i64; + if ((f[0] != 'u') && (i64 < 0)) { + n64 = (stbsp__uint64)-i64; + flags |= 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; + flags |= STBSP__NEGATIVE; + } + } + + if (flags & STBSP__MEMORY_SIZES) { + if (n64 < 1024){ + precision = 0; + } + else if (precision == -1){ + precision = 1; + } + fv = (double)(stbsp__int64)n64; + goto doafloat; + } + + // 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 ((flags & 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 ((flags & 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 ((flags & STBSP__TRIPLET_COMMA) && (l++ == 3)){ + l = 0; + *--s = stbsp__comma; + --o; + } + else{ + *--s = '0'; + } + } + } + + tail[0] = 0; + stbsp__lead_sign(flags, 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 (precision < 0){ + precision = 0; + } + goto scopy; + } + + default: // unknown, just copy code + { + s = num + STBSP__NUMSZ - 1; + *s = f[0]; + l = 1; + field_width = flags = 0; + lead[0] = 0; + tail[0] = 0; + precision = 0; + dp = 0; + cs = 0; + goto scopy; + } + + scopy: + { + // get field_width=leading/trailing space, precision=leading zeros + if (precision < (stbsp__int32)l){ + precision = l; + } + n = precision + lead[0] + tail[0] + tz; + if (field_width < (stbsp__int32)n){ + field_width = n; + } + field_width -= n; + precision -= l; + + // handle right justify and leading zeros + if ((flags & STBSP__LEFTJUST) == 0){ + // if leading zeros, everything is in precision + if (flags & STBSP__LEADINGZERO){ + precision = (field_width > precision) ? field_width : precision; + field_width = 0; + } + // if no leading zeros, then no commas + else{ + flags &= ~STBSP__TRIPLET_COMMA; + } + } + + // copy the spaces and/or zeros + if (field_width + precision) { + stbsp__int32 i; + stbsp__uint32 c; + + // copy leading spaces (or when doing %8.4d stuff) + if ((flags & STBSP__LEFTJUST) == 0) + while (field_width > 0) { + stbsp__cb_buf_clamp(i, field_width); + field_width -= 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 = (flags & STBSP__TRIPLET_COMMA) ? ((stbsp__uint32)(c - ((precision + cs) % (c + 1)))) : 0; + while (precision > 0) { + stbsp__cb_buf_clamp(i, precision); + precision -= i; + if ((flags & 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 ((flags & 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 (flags & STBSP__LEFTJUST){ + if (field_width > 0){ + while (field_width){ + stbsp__int32 i; + stbsp__cb_buf_clamp(i, field_width); + field_width -= 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); + } + } + } + + arena_end_temp(&scratch); + }break; + } + ++f; + } + endfmt: + + if (!callback) + *bf = 0; + else + stbsp__flush_cb(); + + done: + arena_release_scratch(&scratch); + 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 m4_sprintf(char *buf, char const *fmt, ...) +{ + int result; + va_list va; + va_start(va, fmt); + result = m4_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 m4_vsnprintf( char * buf, int count, char const * fmt, va_list va ) +{ + stbsp__context c; + + if ( (count == 0) && !buf ) + { + c.length = 0; + + m4_vsprintfcb( stbsp__count_clamp_callback, &c, c.tmp, fmt, va ); + } + else + { + int l; + + c.buf = buf; + c.count = count; + c.length = 0; + + m4_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 m4_snprintf(char *buf, int count, char const *fmt, ...) +{ + int result; + va_list va; + va_start(va, fmt); + + result = m4_vsnprintf(buf, count, fmt, va); + va_end(va); + + return result; +} + +STBSP__PUBLICDEF int m4_vsprintf(char *buf, char const *fmt, va_list va) +{ + return m4_vsprintfcb(0, 0, buf, fmt, va); +} + +// ======================================================================= +// low level float utility functions + +// 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 + +// clean up +#undef stbsp__uint16 +#undef stbsp__uint32 +#undef stbsp__int32 +#undef stbsp__uint64 +#undef stbsp__int64 +#undef STBSP__UNALIGNED + diff --git a/symbol_set.ld_meta/mr4th/mr4th_base.h b/symbol_set.ld_meta/mr4th/mr4th_base.h new file mode 100644 index 0000000..75af415 --- /dev/null +++ b/symbol_set.ld_meta/mr4th/mr4th_base.h @@ -0,0 +1,2035 @@ +/* +** +** TODO: instructions will go here +** +*/ + +#if !defined(MR4TH_BASE_H) +#define MR4TH_BASE_H 1 + +//////////////////////////////////////////////// +//////////////////////////////////////////////// +////////////////// MACROS ////////////////// +//////////////////////////////////////////////// +//////////////////////////////////////////////// + +//////////////////////////////// +// Build Mode Defaults & Checks + +#if !defined(MR4TH_DISABLE_UNITY_BUILD) +# define MR4TH_DISABLE_UNITY_BUILD 0 +#endif +#if !defined(MR4TH_CORE_MODE) +# define MR4TH_CORE_MODE 0 +#endif +#if !defined(MR4TH_PLUGIN_MODE) +# define MR4TH_PLUGIN_MODE 0 +#endif +#if !defined(MR4TH_STANDALONE_MODE) +# define MR4TH_STANDALONE_MODE 0 +#endif +#if !defined(MR4TH_PROFILING_PROVIDER) +# define MR4TH_PROFILING_PROVIDER 0 +#endif + +#if !MR4TH_CORE_MODE && !MR4TH_PLUGIN_MODE && !MR4TH_STANDALONE_MODE +# undef MR4TH_STANDALONE_MODE +# define MR4TH_STANDALONE_MODE 1 +#endif + +#if (MR4TH_CORE_MODE && MR4TH_PLUGIN_MODE) +# error conflicing mr4th build mode +#endif +#if (MR4TH_CORE_MODE && MR4TH_STANDALONE_MODE) +# error conflicing mr4th build mode +#endif +#if (MR4TH_PLUGIN_MODE && MR4TH_STANDALONE_MODE) +# error conflicing mr4th build mode +#endif + +//////////////////////////////// +// Context Cracking + +// development settings +#if !defined(MR4TH_ASSERTS) +# define MR4TH_ASSERTS 0 +#endif +#if !defined(MR4TH_SANITIZER) +# define MR4TH_SANITIZER 0 +#endif +#if !defined(MR4TH_PROFILING_MANUAL) +# define MR4TH_PROFILING_MANUAL 0 +#endif +#if !defined(MR4TH_PROFILING_AUTO) +# define MR4TH_PROFILING_AUTO 0 +#endif + +#if defined(MR4TH_PROFILING_USER) +# error the user should not set MR4TH_PROFILING_USER, instead set MR4TH_PROFILING_MANUAL or MR4TH_PROFILING_AUTO +#endif + +#if MR4TH_PROFILING_MANUAL || MR4TH_PROFILING_AUTO +# define MR4TH_PROFILING_USER 1 +#else +# define MR4TH_PROFILING_USER 0 +#endif + +// untangle compiler, os, & architecture +#if defined(__clang__) +# define COMPILER_CLANG 1 + +# if defined(_WIN32) +# define OS_WINDOWS 1 +# elif defined(__gnu_linux__) +# define OS_LINUX 1 +# elif defined(__APPLE__) && defined(__MACH__) +# define OS_MAC 1 +# else +# error missing OS detection +# endif + +# if defined(__amd64__) +# define ARCH_X64 1 +// TODO(allen): verify this works on clang +# elif defined(__i386__) +# define ARCH_X86 1 +// TODO(allen): verify this works on clang +# elif defined(__arm__) +# define ARCH_ARM 1 +// TODO(allen): verify this works on clang +# elif defined(__aarch64__) +# define ARCH_ARM64 1 +# else +# error missing ARCH detection +# endif + +#elif defined(_MSC_VER) +# define COMPILER_CL 1 + +# if defined(_WIN32) +# define OS_WINDOWS 1 +# else +# error missing OS detection +# endif + +# if defined(_M_AMD64) +# define ARCH_X64 1 +# elif defined(_M_I86) +# define ARCH_X86 1 +# elif defined(_M_ARM) +# define ARCH_ARM 1 +// TODO(allen): ARM64? +# else +# error missing ARCH detection +# endif + +#elif defined(__GNUC__) +# define COMPILER_GCC 1 + +# if defined(_WIN32) +# define OS_WINDOWS 1 +# elif defined(__gnu_linux__) +# define OS_LINUX 1 +# elif defined(__APPLE__) && defined(__MACH__) +# define OS_MAC 1 +# else +# error missing OS detection +# endif + +# if defined(__amd64__) +# define ARCH_X64 1 +# elif defined(__i386__) +# define ARCH_X86 1 +# elif defined(__arm__) +# define ARCH_ARM 1 +# elif defined(__aarch64__) +# define ARCH_ARM64 1 +# else +# error missing ARCH detection +# endif + +#else +# error no context cracking for this compiler +#endif + +#if !defined(COMPILER_CL) +# define COMPILER_CL 0 +#endif +#if !defined(COMPILER_CLANG) +# define COMPILER_CLANG 0 +#endif +#if !defined(COMPILER_GCC) +# define COMPILER_GCC 0 +#endif +#if !defined(OS_WINDOWS) +# define OS_WINDOWS 0 +#endif +#if !defined(OS_LINUX) +# define OS_LINUX 0 +#endif +#if !defined(OS_MAC) +# define OS_MAC 0 +#endif +#if !defined(ARCH_X64) +# define ARCH_X64 0 +#endif +#if !defined(ARCH_X86) +# define ARCH_X86 0 +#endif +#if !defined(ARCH_ARM) +# define ARCH_ARM 0 +#endif +#if !defined(ARCH_ARM64) +# define ARCH_ARM64 0 +#endif + + +// language +#if defined(__cplusplus) +# define LANG_CXX 1 +#else +# define LANG_C 1 +#endif + +#if !defined(LANG_CXX) +# define LANG_CXX 0 +#endif +#if !defined(LANG_C) +# define LANG_C 0 +#endif + + +// language version: C +#if LANG_C +# if !defined(__STDC_VERSION__) +# define LANGVER_C90 1 +# elif (__STDC_VERSION__ == 199901) +# define LANGVER_C99 1 +# elif (__STDC_VERSION__ == 201112) +# define LANGVER_C11 1 +# elif (__STDC_VERSION__ == 201710) +# define LANGVER_C17 1 +# elif (__STDC_VERSION__ == 202000) +# define LANGVER_C23 1 +# else +# error unrecorgnized C version +# endif +#endif + +#if !defined(LANGVER_C90) +# define LANGVER_C90 0 +#endif +#if !defined(LANGVER_C99) +# define LANGVER_C99 0 +#endif +#if !defined(LANGVER_C11) +# define LANGVER_C11 0 +#endif +#if !defined(LANGVER_C17) +# define LANGVER_C17 0 +#endif +#if !defined(LANGVER_C23) +# define LANGVER_C23 0 +#endif + + +// language version: C++ +#if LANG_CXX +# if (__cplusplus == 199711) +# define LANGVER_CXX98 1 +# elif (__cplusplus == 201103) +# define LANGVER_CXX11 1 +# elif (__cplusplus == 201402) +# define LANGVER_CXX14 1 +# elif (__cplusplus == 201703) +# define LANGVER_CXX17 1 +# elif (__cplusplus == 202002) +# define LANGVER_CXX20 1 +# elif (__cplusplus == 202302) +# define LANGVER_CXX23 1 +# else +# error unrecorgnized C++ version +# endif +#endif + +#if !defined(LANGVER_CXX98) +# define LANGVER_CXX98 0 +#endif +#if !defined(LANGVER_CXX11) +# define LANGVER_CXX11 0 +#endif +#if !defined(LANGVER_CXX14) +# define LANGVER_CXX14 0 +#endif +#if !defined(LANGVER_CXX17) +# define LANGVER_CXX17 0 +#endif +#if !defined(LANGVER_CXX20) +# define LANGVER_CXX20 0 +#endif +#if !defined(LANGVER_CXX23) +# define LANGVER_CXX23 0 +#endif + +// determine intrinsics mode +#if OS_WINDOWS +# if COMPILER_CL || COMPILER_CLANG +# define INTRINSICS_MICROSOFT 1 +# endif +#endif +#if OS_LINUX +# if COMPILER_CLANG || COMPILER_GCC +# define INTRINSICS_BUILT_IN 1 +# endif +#endif + +#if !defined(INTRINSICS_MICROSOFT) +# define INTRINSICS_MICROSOFT 0 +#endif +#if !defined(INTRINSICS_BUILT_IN) +# define INTRINSICS_BUILT_IN 0 +#endif + +// setup pointer size macro +#if ARCH_X64 || ARCH_ARM64 +# define ARCH_ADDRSIZE 64 +#else +# define ARCH_ADDRSIZE 32 +#endif + + +#define ARCH_LITTLE_ENDIAN 1 +#define ARCH_BIG_ENDIAN 0 + +// TODO(allen): target image format + +//////////////////////////////// +// Macros: Symbol Markers + +// linkage, storage, & other "attribute" like things + +#define MR4TH_SYMBOL_STATIC static + +#if LANG_CXX +# define MR4TH_SYMBOL_LINK extern "C" +#else +# define MR4TH_SYMBOL_LINK extern +#endif + +#if OS_WINDOWS +# define MR4TH_SYMBOL_EXPORT __declspec(dllexport) +#elif OS_LINUX || OS_MAC +# define MR4TH_SYMBOL_EXPORT __attribute__((visibility("default"))) +#else +# error MR4TH_SYMBOL_EXPORT not defined for this OS +#endif + +#if OS_WINDOWS +# define MR4TH_SYM_IMPORT __declspec(dllimport) +#elif OS_LINUX || OS_MAC +# define MR4TH_SYM_IMPORT +#else +# error MR4TH_SYMBOL_IMPORT not defined for this OS +#endif + +#if COMPILER_CL +# define MR4TH_THREADVAR __declspec(thread) +#elif COMPILER_CLANG || COMPILER_GCC +# define MR4TH_THREADVAR __thread +#else +# error MR4TH_THREADVAR not defined for this compiler +#endif + +#if COMPILER_CLANG || COMPILER_GCC +# if OS_MAC +# define MR4TH_SEC_R(N) __attribute__((__section__("__TEXT,"N))) +# define MR4TH_SEC_RW(N) __attribute__((__section__("__READ,"N))) +# else +# define MR4TH_SEC_R(N) __attribute__((__section__(N))) +# define MR4TH_SEC_RW(N) __attribute__((__section__(N))) +# endif +#elif COMPILER_CL +# define MR4TH_SEC_R(N) __declspec(allocate(N)) +# define MR4TH_SEC_RW(N) __declspec(allocate(N)) +#else +# error MR4TH_SEC_R/MR4TH_SEC_RW not defined for this compiler/OS +#endif + +// for CL users: #pragma section(
,read,write) + +#if COMPILER_CLANG || COMPILER_GCC +#if OS_WINDOWS +# define MR4TH_READ_ONLY MR4TH_SEC_R(".rdata") const +#else +# define MR4TH_READ_ONLY MR4TH_SEC_R(".rodata") const +#endif +#elif COMPILER_CL +# pragma section(".m4rdata",read) +# define MR4TH_READ_ONLY MR4TH_SEC_R(".m4rdata") +#else +# define MR4TH_READ_ONLY +# error MR4TH_READ_ONLY not defined for this compiler +#endif + +// symbol kinds + +#if MR4TH_DISABLE_UNITY_BUILD +# define MR4TH_SYMBOL MR4TH_SYMBOL_LINK +#else +# define MR4TH_SYMBOL MR4TH_SYMBOL_STATIC +#endif + +#if MR4TH_CORE_MODE +# define MR4TH_SYMBOL_MUST_SHARE MR4TH_SYMBOL_EXPORT +#elif MR4TH_PLUGIN_MODE +# define MR4TH_SYMBOL_MUST_SHARE MR4TH_SYMBOL_IMPORT +#else +# define MR4TH_SYMBOL_MUST_SHARE MR4TH_SYMBOL +#endif + +// align-as + +#if COMPILER_CL +# define MR4TH_ALIGN_AS_LIT(n) __declspec(align(n)) +#elif COMPILER_CLANG || COMPILER_GCC +# define MR4TH_ALIGN_AS_LIT(n) __attribute__(( __aligned__(n) )) +#else +# error MR4TH_ALIGN_AS_LIT not defined for this compiler +#endif + +// do-not-eliminate + +#if OS_WINDOWS + +# define MR4TH_DO_NOT_ELIMINATE__S0(N) __pragma(comment(linker,"/include:" #N)) +# define MR4TH_DO_NOT_ELIMINATE__S1(N) MR4TH_DO_NOT_ELIMINATE__S0(N) +# define MR4TH_DO_NOT_ELIMINATE(N) MR4TH_DO_NOT_ELIMINATE__S1(N) + +#else + +# define MR4TH_DO_NOT_ELIMINATE(N) + +#endif + +// before-main abstraction + +#if OS_WINDOWS + +# pragma section(".CRT$XCU", read) + +# if LANG_CXX + +# define MR4TH_BEFORE_MAIN_(n) \ +static void n(void); \ +__declspec(allocate(".CRT$XCU")) \ +__pragma(comment(linker,"/include:" #n "__")) \ +extern "C" void (*n##__)(void); \ +void (*n##__)(void) = n; \ +static void n(void) + +# else + +# define MR4TH_BEFORE_MAIN_(n) \ +static void n(void); \ +__declspec(allocate(".CRT$XCU")) \ +__pragma(comment(linker,"/include:" #n "__")) \ +void (*n##__)(void) = n; \ +static void n(void) + +# endif + +# define MR4TH_BEFORE_MAIN(n) MR4TH_BEFORE_MAIN_(n) + +#elif OS_LINUX + +# define MR4TH_BEFORE_MAIN(n) \ +__attribute__((constructor)) static void n(void) + +#else +# error MR4TH_BEFORE_MAIN missing for this OS +#endif + +// sanitizer + +#if COMPILER_CLANG && MR4TH_SANITIZER +# include +#endif + +#if MR4TH_SANITIZER +# define AsanPoison(p,z) __asan_poison_memory_region((p),(z)) +# define AsanUnpoison(p,z) __asan_unpoison_memory_region((p),(z)) +#else +# define AsanPoison(p,z) +# define AsanUnpoison(p,z) +#endif + + +//////////////////////////////// +// Macros: Common Expressions + +// macro writing utilities + +#define Stmnt(S) do{ S }while(0) + +#define Stringify_(S) #S +#define Stringify(S) Stringify_(S) +#define Glue_(A,B) A##B +#define Glue(A,B) Glue_(A,B) + +// assert + +#if !defined(AssertBreak) +# define AssertBreak() (*(volatile int*)0 = 0) +#endif + +#if MR4TH_ASSERTS +# define Assert(c) Stmnt( if (!(c)){ AssertBreak(); } ) +#else +# define Assert(c) +#endif + +#define StaticAssert(c,l) typedef U8 Glue(l,__LINE__) [(c)?1:-1] + +// memory operations + +#define MemoryZero(p,z) memory_zero((p), (z)) +#define MemoryZeroStruct(p) MemoryZero((p), sizeof(*(p))) +#define MemoryZeroArray(p) MemoryZero((p), sizeof(p)) +#define MemoryZeroRange(f,o) MemoryZero((f), (U8*)(o) - (U8*)(f)) + +#define MemoryMatch(a,b,z) (memory_match((a),(b),(z))) + +#define MemoryCopy(d,s,z) memory_move((d), (s), (z)) +#define MemoryCopyStruct(d,s) MemoryCopy((d),(s),Min(sizeof(*(d)),sizeof(*(s)))) +#define MemoryCopyArray(d,s) MemoryCopy((d),(s),Min(sizeof(s),sizeof(d))) + +#define MemorySwap(a,b,z) (memory_swap((a),(b),(z))) + +// misc expressions + +#define ArrayCount(a) (sizeof(a)/sizeof(*(a))) +#define ArrayOpl(a) (a + ArrayCount(a)) + +#define IntFromPtr(p) (UAddr)(p) +#define PtrFromInt(n) (void*)((UAddr)(n)) +#define PtrDif(a,b) ((U8*)(a) - (U8*)(b)) + +#define PtrInArray(p,a) ((a) <= (p) && (p) < ArrayOpl(a)) + +#define Member(T,m) (((T*)0)->m) +#define AddrOfMember(T,m) (void*)(&Member(T,m)) +#define OffsetOfMember(T,m) IntFromPtr(&Member(T,m)) +#define OffsetOfMemberV(s,m) PtrDif(&s->m, s) +#define MemberAddr(T,m) AddrOfMember(T,m) +#define MemberOff(T,m) OffsetOfMember(T,m) + +#define Min(a,b) (((a)<(b))?(a):(b)) +#define Max(a,b) (((a)>(b))?(a):(b)) +#define Clamp(a,x,b) (((x)<(a))?(a):((b)<(x))?(b):(x)) +#define ClampTop(a,b) Min(a,b) +#define ClampBot(a,b) Max(a,b) + +#define Swap(T,a,b) Stmnt( T t_ = (a); (a) = (b); (b) = t_; ) + +#define SignedIntFromCompare(a,b) (S32)( ((b)<(a)) - ((a)<(b)) ) + +#define AlignUpPow2(x,p) (((x) + (p) - 1)&~((p) - 1)) +#define AlignDownPow2(x,p) ((x)&~((p) - 1)) +#define IsPow2OrZero(x) (((x)&((x)-1)) == 0) + +#define CeilIntDiv(n,d) (((n) + (d) - 1)/(d)) + +#define KB(x) ((U64)(x) << 10llu) +#define MB(x) ((U64)(x) << 20llu) +#define GB(x) ((U64)(x) << 30llu) +#define TB(x) ((U64)(x) << 40llu) + +#define Thousand(x) ((x)*1000llu) +#define Million(x) ((x)*1000000llu) +#define Billion(x) ((x)*1000000000llu) +#define Trillion(x) ((x)*1000000000000llu) + +#define AsciiID4(a,b,c,d) (((d) << 24) | ((c) << 16) | ((b) << 8) | (a)) + +#if LANG_CXX +# define CLiteral(T) T +#else +# define CLiteral(T) (T) +#endif + +//////////////////////////////// +// Macros: Linked Lists + +#define DLLPushBack_NPZ(f,l,n,next,prev,nil)\ +(((f) == (nil))?\ +((f)=(l)=(n),(n)->next=(n)->prev=(nil)):\ +((n)->prev=(l),(l)->next=(n),(l)=(n),(n)->next=(nil))) + +#define DLLPushBack(f,l,n) DLLPushBack_NPZ(f,l,n,next,prev,0) +#define DLLPushFront(f,l,n) DLLPushBack_NPZ(l,f,n,prev,next,0) + +#define DLLInsert_NPZ(f,l,p,n,next,prev,nil) \ +(((p) != (l))?\ +((n)->next = (p)->next,\ +(n)->prev = (p),\ +(p)->next->prev = (n),\ +(p)->next = (n))\ +:((n)->next = (nil),\ +(n)->prev = (l),\ +(l)->next = (n),\ +(l) = (n))) + +#define DLLInsert(f,l,p,n) DLLInsert_NPZ(f,l,p,n,next,prev,0) + +#define DLLRemove_NPZ(f,l,n,next,prev,nil)\ +((f)==(n)?\ +((f)==(l)?\ +((f)=(l)=(nil)):\ +((f)=(f)->next,(f)->prev=(nil))):\ +(l)==(n)?\ +((l)=(l)->prev,(l)->next=(nil)):\ +((n)->next->prev=(n)->prev,\ +(n)->prev->next=(n)->next)) + +#define DLLRemove(f,l,n) DLLRemove_NPZ(f,l,n,next,prev,0) + +#define SLLQueuePush_NZ(f,l,n,next,nil) (((f)==(nil)?\ +(f)=(l)=(n):\ +((l)->next=(n),(l)=(n))),\ +(n)->next=(nil)) +#define SLLQueuePush(f,l,n) SLLQueuePush_NZ(f,l,n,next,0) + +#define SLLQueuePushFront_NZ(f,l,n,next,nil) ((f)==(nil)?\ +((f)=(l)=(n),(n)->next=(nil)):\ +((n)->next=(f),(f)=(n))) +#define SLLQueuePushFront(f,l,n) SLLQueuePushFront_NZ(f,l,n,next,0) + +#define SLLQueuePop_NZ(f,l,next,nil) ((f)==(l)?\ +(f)=(l)=(nil):\ +((f)=(f)->next)) +#define SLLQueuePop(f,l) SLLQueuePop_NZ(f,l,next,0) + +#define SLLStackPush_N(f,n,next) ((n)->next=(f),(f)=(n)) +#define SLLStackPush(f,n) SLLStackPush_N(f,n,next) + +#define SLLStackPop_NZ(f,next,nil) ((f)==(nil)?(nil):\ +((f)=(f)->next)) +#define SLLStackPop(f) SLLStackPop_NZ(f,next,0) + +//////////////////////////////// +// Macros: Static Configurations + +#if !defined(MR4TH_MEM_COMMIT_BLOCK_SIZE) +# define MR4TH_MEM_COMMIT_BLOCK_SIZE MB(64) +#endif + +#if !defined(MR4TH_MEM_INITIAL_COMMIT) +# define MR4TH_MEM_INITIAL_COMMIT KB(4) +#endif + +#if !defined(MR4TH_MEM_MAX_ALIGN) +# define MR4TH_MEM_MAX_ALIGN 64 +#endif + +#if !defined(MR4TH_MEM_SCRATCH_POOL_COUNT) +# define MR4TH_MEM_SCRATCH_POOL_COUNT 2 +#endif + +#if !defined(MR4TH_STREAM_ALLOC_SIZE) +# define MR4TH_STREAM_ALLOC_SIZE MB(16) +#endif + + +//////////////////////////////////////////////// +//////////////////////////////////////////////// +////////////////// TYPES /////////////////// +//////////////////////////////////////////////// +//////////////////////////////////////////////// + +//////////////////////////////// +// Types: Basic + +#include +#include +#include + +typedef int8_t S8; +typedef int16_t S16; +typedef int32_t S32; +typedef int64_t S64; +typedef uint8_t U8; +typedef uint16_t U16; +typedef uint32_t U32; +typedef uint64_t U64; +typedef S8 B8; +typedef S16 B16; +typedef S32 B32; +typedef S64 B64; +typedef float F32; +typedef double F64; + +#if ARCH_ADDRSIZE == 32 +typedef U32 UAddr; +typedef S32 SAddr; +#elif ARCH_ADDRSIZE == 64 +typedef U64 UAddr; +typedef S64 SAddr; +#else +# error UAddr and SAddr not defined for this architecture +#endif + +typedef void VoidFunc(void); + +//////////////////////////////// +// Types: Symbolic Constants + +typedef enum Axis{ + Axis_X = 0, + Axis_Y = 1, + Axis_Z = 2, + Axis_W = 3 +} Axis; + +typedef enum Side{ + Side_Min = 0, + Side_Max = 1 +} Side; + +typedef enum OperatingSystem{ + OperatingSystem_Null = 0, + OperatingSystem_Windows = 1, + OperatingSystem_Linux = 2, + OperatingSystem_Mac = 3, + OperatingSystem_COUNT +} OperatingSystem; + +typedef enum Architecture{ + Architecture_Null = 0, + Architecture_X64 = 1, + Architecture_X86 = 2, + Architecture_Arm = 3, + Architecture_Arm64 = 4, + Architecture_COUNT +} Architecture; + +typedef enum Month{ + Month_Jan = 1, + Month_Feb = 2, + Month_Mar = 3, + Month_Apr = 4, + Month_May = 5, + Month_Jun = 6, + Month_Jul = 7, + Month_Aug = 8, + Month_Sep = 9, + Month_Oct = 10, + Month_Nov = 11, + Month_Dec = 12, +} Month; + +typedef enum DayOfWeek{ + DayOfWeek_Sunday = 0, + DayOfWeek_Monday = 1, + DayOfWeek_Tuesday = 2, + DayOfWeek_Wednesday = 3, + DayOfWeek_Thursday = 4, + DayOfWeek_Friday = 5, + DayOfWeek_Saturday = 6 +} DayOfWeek; + +typedef U32 DataAccessFlags; +enum{ + DataAccessFlag_Read = (1 << 0), + DataAccessFlag_Write = (1 << 1), + DataAccessFlag_Execute = (1 << 2), +}; + +//////////////////////////////// +// Types: Compound Types + +typedef struct V2F32{ + F32 x; + F32 y; +} V2F32; + +typedef struct V3F32{ + F32 x; + F32 y; + F32 z; +} V3F32; + +typedef struct V4F32{ + F32 x; + F32 y; + F32 z; + F32 w; +} V4F32; + +typedef struct RangeF32{ + F32 min; + F32 max; +} RangeF32; + +typedef struct RectF32{ + union{ + struct{ + F32 x0; + F32 y0; + F32 x1; + F32 y1; + }; + struct{ + V2F32 p0; + V2F32 p1; + }; + V2F32 p[2]; + }; +} RectF32; + +typedef struct V2S32{ + union{ + struct{ + S32 x; + S32 y; + }; + S32 v[2]; + }; +} V2S32; + +typedef struct V3S32{ + S32 x; + S32 y; + S32 z; +} V3S32; + +typedef struct V4S32{ + S32 x; + S32 y; + S32 z; + S32 w; +} V4S32; + +typedef struct RangeS32{ + S32 min; + S32 max; +} RangeS32; + +typedef struct RectS32{ + union{ + struct{ + S32 x0; + S32 y0; + S32 x1; + S32 y1; + }; + struct{ + V2S32 p0; + V2S32 p1; + }; + V2S32 p[2]; + }; +} RectS32; + +//////////////////////////////// +// Types: Time + +typedef U64 DenseTime; + +typedef struct DateTime{ + U16 msec; // [0,999] + U8 sec; // [0,60] + U8 min; // [0,59] + U8 hour; // [0,23] + U8 day; // [1,31] + U8 mon; // [1,12] + S16 year; // 1 = 1 CE; 2020 = 2020 CE; 0 = 1 BCE; -100 = 101 BCE; etc. +} DateTime; + +//////////////////////////////// +// Types: File Properties + +typedef U32 FilePropertyFlags; +enum{ + FilePropertyFlag_IsFolder = (1 << 0), +}; + +typedef struct FileProperties{ + U64 size; + FilePropertyFlags flags; + DenseTime create_time; + DenseTime modify_time; + DataAccessFlags access; +} FileProperties; + + +//////////////////////////////// +// Types: Arena + +typedef struct Arena{ + struct Arena *current; + struct Arena *prev; + U64 default_reserve_size; + U16 alignment; + B8 growing; + U8 filler[5]; + U64 base_pos; + U64 chunk_cap; + U64 chunk_pos; + U64 chunk_commit_pos; +} Arena; + +typedef struct ArenaTemp{ + Arena *arena; + U64 pos; +} ArenaTemp; + + +//////////////////////////////// +// Types: String + +typedef struct String8{ + U8 *str; + U64 size; +} String8; + +typedef struct String8Node{ + struct String8Node *next; + String8 string; +} String8Node; + +typedef struct String8List{ + String8Node *first; + String8Node *last; + U64 node_count; + U64 total_size; +} String8List; + +typedef struct StringJoin{ + String8 pre; + String8 mid; + String8 post; +} StringJoin; + +typedef U32 StringMatchFlags; +enum{ + StringMatchFlag_NoCase = (1 << 0), + StringMatchFlag_PrefixMatch = (1 << 1), +}; + +typedef struct String16{ + U16 *str; + U64 size; +} String16; + +typedef struct String32{ + U32 *str; + U64 size; +} String32; + +#define str8_expand(s) (int)((s).size), ((s).str) + +typedef struct StringDecode{ + U32 codepoint; + U32 size; +} StringDecode; + + +//////////////////////////////// +// Types: Stream + +typedef struct STREAM_Node{ + struct STREAM_Node *next; + U64 size; + U8 data[0]; +} STREAM_Node; + +typedef struct STREAM{ + Arena *arena; + U64 clear_pos; + STREAM_Node *first_node; + STREAM_Node *last_node; + U64 buffer_cap; + U64 total_size; + STREAM_Node *unused_node; +} STREAM; + +typedef void STREAM_Handle; + + +//////////////////////////////// +// Types: Buffer + +typedef struct BUFFER{ + void *base; + U64 size; + U64 commit_pos; +} BUFFER; + +#define BUFFER_TYPED(T, name) union{ BUFFER buffer; T *ptr; } name + + +//////////////////////////////// +// Types: Hash Table + +typedef struct HASH_Node{ + struct HASH_Node *next; + U64 hash; + U64 val; +} HASH_Node; + + +//////////////////////////////// +// Types: Log + +typedef void LOG_LogToProc(void *uptr, String8 str); + +typedef struct LOG_Node{ + struct LOG_Node *next; + U64 pos; + String8List log; + LOG_LogToProc *logto; + void *uptr; +} LOG_Node; + +typedef struct LOG_ThreadVars{ + Arena *arena; + LOG_Node *stack; +} LOG_ThreadVars; + + +//////////////////////////////// +// Types: Error + +typedef struct ER_Node{ + struct ER_Node *next; + U64 pos; + String8 error; +} ER_Node; + +typedef struct ER_ThreadVars{ + Arena *arena; + ER_Node *stack; + U64 over_stack; +} ER_ThreadVars; + + +//////////////////////////////////////////////// +//////////////////////////////////////////////// +//////////////// CONSTANTS ///////////////// +//////////////////////////////////////////////// +//////////////////////////////////////////////// + +#define min_S8 ((S8) 0x80) +#define min_S16 ((S16)0x8000) +#define min_S32 ((S32)0x80000000) +#define min_S64 ((S64)0x8000000000000000llu) + +#define max_S8 ((S8) 0x7f) +#define max_S16 ((S16)0x7fff) +#define max_S32 ((S32)0x7fffffff) +#define max_S64 ((S64)0x7fffffffffffffffllu) + +#define max_U8 0xff +#define max_U16 0xffff +#define max_U32 0xffffffff +#define max_U64 0xffffffffffffffffllu + +#define machine_epsilon_F32 1.1920929e-7f +#define pi_F32 3.14159265359f +#define tau_F32 6.28318530718f +#define e_F32 2.71828182846f +#define gold_big_F32 1.61803398875f +#define gold_small_F32 0.61803398875f + +#define machine_epsilon_F64 2.220446e-16 +#define pi_F64 3.14159265359 +#define tau_F64 6.28318530718 +#define e_F64 2.71828182846 +#define gold_big_F64 1.61803398875 +#define gold_small_F64 0.61803398875 + +#define inf_F32_as_U32 0x7f800000 +#define neg_inf_F32_as_U32 0xff800000 +#define inf_F64_as_U64 0x7ff0000000000000 +#define neg_inf_F64_as_U64 0xfff0000000000000 + +#define set_inf_F32(x) (*(U32*)(&(x)) = inf_F32_as_U32) +#define set_neg_inf_F32(x) (*(U32*)(&(x)) = neg_inf_F32_as_U32) +#define set_inf_F64(x) (*(U64*)(&(x)) = inf_F64_as_U64) +#define set_neg_inf_F64(x) (*(U64*)(&(x)) = neg_inf_F64_as_U64) + + +//////////////////////////////////////////////// +//////////////////////////////////////////////// +//////////////// INTRINSICS //////////////// +//////////////////////////////////////////////// +//////////////////////////////////////////////// + +#if INTRINSICS_MICROSOFT + +# include +# define intrinsic_rdtsc() __rdtsc() +# define intrinsic_maxbit(o,x) _BitScanReverse64((o),(x)) + +#elif INTRINSICS_BUILT_IN + +# define intrinsic_rdtsc() __rdtsc() +# define intrinsic_maxbit(o,x) (*(o) = __builtin_clzll(x),(x)!=0) + +#endif + + +//////////////////////////////////////////////// +//////////////////////////////////////////////// +/////////////// DECLARATIONS /////////////// +//////////////////////////////////////////////// +//////////////////////////////////////////////// + +//////////////////////////////// +// Functions: Numerical/Math + +// float infinity/NaN + +MR4TH_SYMBOL B32 is_inf_or_nan_F32(F32 x); +MR4TH_SYMBOL B32 is_inf_or_nan_F64(F64 x); + +// float signs, rounding, and modulus + +MR4TH_SYMBOL F32 abs_F32(F32 x); +MR4TH_SYMBOL F32 sign_F32(F32 x); + +MR4TH_SYMBOL F32 trunc_F32(F32 x); +MR4TH_SYMBOL F32 floor_F32(F32 x); +MR4TH_SYMBOL F32 ceil_F32(F32 x); +MR4TH_SYMBOL F32 nearest_F32(F32 x); +MR4TH_SYMBOL F32 mod_F32(F32 x, F32 m); +MR4TH_SYMBOL F32 frac_F32(F32 x); + +MR4TH_SYMBOL F64 abs_F64(F64 x); +MR4TH_SYMBOL F64 sign_F64(F64 x); + +MR4TH_SYMBOL F64 trunc_F64(F64 x); +MR4TH_SYMBOL F64 floor_F64(F64 x); +MR4TH_SYMBOL F64 ceil_F64(F64 x); +MR4TH_SYMBOL F64 nearest_F64(F64 x); +MR4TH_SYMBOL F64 mod_F64(F64 x, F64 m); +MR4TH_SYMBOL F64 frac_F64(F64 x); + +// transcendental functions + +MR4TH_SYMBOL F32 sin_F32(F32 x); +MR4TH_SYMBOL F32 cos_F32(F32 x); +MR4TH_SYMBOL F32 tan_F32(F32 x); +MR4TH_SYMBOL F32 atan_F32(F32 x); +MR4TH_SYMBOL F32 atan2_F32(F32 x, F32 y); + +MR4TH_SYMBOL F32 sqrt_F32(F32 x); +MR4TH_SYMBOL F32 inv_sqrt_F32(F32 x); +MR4TH_SYMBOL F32 ln_F32(F32 x); +MR4TH_SYMBOL F32 pow_F32(F32 base, F32 x); + +MR4TH_SYMBOL F64 sin_F64(F64 x); +MR4TH_SYMBOL F64 cos_F64(F64 x); +MR4TH_SYMBOL F64 tan_F64(F64 x); +MR4TH_SYMBOL F64 atan_F64(F64 x); +MR4TH_SYMBOL F64 atan2_F64(F64 x, F64 y); + +MR4TH_SYMBOL F64 sqrt_F64(F64 x); +MR4TH_SYMBOL F64 inv_sqrt_F64(F64 x); +MR4TH_SYMBOL F64 ln_F64(F64 x); +MR4TH_SYMBOL F64 pow_F64(F64 base, F64 x); + +// linear interpolation + +MR4TH_SYMBOL F32 lerp(F32 a, F32 t, F32 b); +MR4TH_SYMBOL F32 unlerp(F32 a, F32 x, F32 b); + +// integer rounding & truncating + +MR4TH_SYMBOL U32 next_pow2_U32(U32 x); +MR4TH_SYMBOL S32 sign_extend_S32(S32 x, U32 bitidx); + +MR4TH_SYMBOL U64 next_pow2_U64(U32 x); +MR4TH_SYMBOL S64 sign_extend_S64(S64 x, U32 bitidx); + +//////////////////////////////// +// Functions: Compound Types + +// vector macros + +#define v2(x,y) CLiteral(V2F32){(x),(y)} +#define v3(x,y,z) CLiteral(V3F32){(x),(y),(z)} +#define v4(x,y,z,w) CLiteral(V4F32){(x),(y),(z),(w)} + +#define v2s32(x,y) CLiteral(V2S32){(x),(y)} +#define v3s32(x,y,z) CLiteral(V2S32){(x),(y),(z)} +#define v4s32(x,y,z,w) CLiteral(V2S32){(x),(y),(z),(w)} + +#define v2_expanded(v) ((v).x), ((v).y) +#define v3_expanded(v) ((v).x), ((v).y), ((v).z) +#define v4_expanded(v) ((v).x), ((v).y), ((v).z), ((v).w) +#define rect_expanded(r) ((r).x0), ((r).y0), ((r).x1), ((r).y1) + +#define v2_exop_scalar(sc, op, v) ((sc) op ((v).x)), ((sc) op ((v).y)) +#define v3_exop_scalar(sc, op, v) ((sc) op ((v).x)), ((sc) op ((v).y)), ((sc) op ((v).z)) +#define v4_exop_scalar(sc, op, v) ((sc) op ((v).x)), ((sc) op ((v).y)), ((sc) op ((v).z)), ((sc) op ((v).w)) + +#define v2_exop(v1, op, v2) ((v1).x op ((v2).x)), ((v1).y op ((v2).y)) +#define v3_exop(v1, op, v2) ((v1).x op ((v2).x)), ((v1).y op ((v2).y)), ((v1).z op ((v2).z)) +#define v4_exop(v1, op, v2) ((v1).x op ((v2).x)), ((v1).y op ((v2).y)), ((v1).z op ((v2).z)), ((v1).w op ((v2).w)) + +#define v2_op_scalar(sc, op, v) CLiteral(V2F32){ v2_exop_scalar(sc,op,v) } +#define v3_op_scalar(sc, op, v) CLiteral(V3F32){ v3_exop_scalar(sc,op,v) } +#define v4_op_scalar(sc, op, v) CLiteral(V4F32){ v4_exop_scalar(sc,op,v) } + +#define v2_op(v1, op, v2) CLiteral(V2F32){ v2_exop(v1,op,v2) } +#define v3_op(v1, op, v2) CLiteral(V3F32){ v3_exop(v1,op,v2) } +#define v4_op(v1, op, v2) CLiteral(V4F32){ v4_exop(v1,op,v2) } + +#define sc_lerp(s1, l, s2) ((s1) + (l)*((s2) - (s1))) +#define v2_lerp(v1, l, v2) v2(sc_lerp((v1).x, l, (v2).x),\ +sc_lerp((v1).y, l, (v2).y) ) +#define v3_lerp(v1, l, v2) v3(sc_lerp((v1).x, l, (v2).x),\ +sc_lerp((v1).y, l, (v2).y),\ +sc_lerp((v1).z, l, (v2).z) ) +#define v4_lerp(v1, l, v2) v4(sc_lerp((v1).x, l, (v2).x),\ +sc_lerp((v1).y, l, (v2).y),\ +sc_lerp((v1).z, l, (v2).z),\ +sc_lerp((v1).w, l, (v2).w) ) + +#define v2_dim_from_rect(r) CLiteral(V2F32){ (r).x1 - (r).x0, (r).y1 - (r).y0 } + +// range macros + +#define rangef32(min,max) CLiteral(RangeF32){(min),(max)} + +// 2d vectors + +MR4TH_SYMBOL V2F32 v2_polar(F32 theta, F32 radius); +MR4TH_SYMBOL F32 v2_angle_from_v(V2F32 v); + +MR4TH_SYMBOL V2F32 v2_unit(V2F32 v); + +MR4TH_SYMBOL V2F32 v2_from_v2s32(V2S32 v); + +// 3d vectors + +MR4TH_SYMBOL V3F32 v3_spherical(F32 theta_xz, F32 theta_yz, F32 radius); +MR4TH_SYMBOL V3F32 v3_cross(V3F32 a, V3F32 b); + +MR4TH_SYMBOL V3F32 v3_unit(V3F32 v); + +// 4x4 matrix + +MR4TH_SYMBOL B32 mat4x4_inv(F32 *in, F32 *out); +MR4TH_SYMBOL void mat4x4_mul(F32 *a, F32 *b, F32 *out); + +//////////////////////////////// +// Functions: Memory Operations + +MR4TH_SYMBOL void memory_zero(void *ptr, U64 size); +MR4TH_SYMBOL void memory_fill(void *ptr, U64 size, U8 fillbyte); +MR4TH_SYMBOL B32 memory_match(void *a, void *b, U64 size); +MR4TH_SYMBOL void*memory_move(void *a, void *b, U64 size); + +//////////////////////////////// +// Functions: Symbolic Constants + +MR4TH_SYMBOL OperatingSystem operating_system_from_context(void); +MR4TH_SYMBOL Architecture architecture_from_context(void); + +MR4TH_SYMBOL char* string_from_operating_system(OperatingSystem os); +MR4TH_SYMBOL char* string_from_architecture(Architecture arch); +MR4TH_SYMBOL char* string_from_month(Month month); +MR4TH_SYMBOL char* string_from_day_of_week(DayOfWeek day_of_week); + +//////////////////////////////// +// Functions: Time + +MR4TH_SYMBOL DenseTime dense_time_from_date_time(DateTime *date_time); +MR4TH_SYMBOL DateTime date_time_from_dense_time(DenseTime dense_time); + +//////////////////////////////// +// Functions: Arenas + +// arena core + +MR4TH_SYMBOL_MUST_SHARE Arena* arena_new(U64 reserve_size, U64 alignment, B32 growing); +MR4TH_SYMBOL_MUST_SHARE void arena_release(Arena *arena); +MR4TH_SYMBOL_MUST_SHARE void* arena_push_no_zero(Arena *arena, U64 size); +MR4TH_SYMBOL_MUST_SHARE void arena_pop_to(Arena *arena, U64 pos); +MR4TH_SYMBOL_MUST_SHARE U64 arena_current_pos(Arena *arena); +MR4TH_SYMBOL_MUST_SHARE U64 arena_current_align(Arena *arena); +MR4TH_SYMBOL_MUST_SHARE void arena_set_align(Arena *arena, U64 alignment); + +MR4TH_SYMBOL_MUST_SHARE ArenaTemp arena_get_scratch(Arena **conflict_array, U32 count); + +// arena helpers + +MR4TH_SYMBOL Arena* arena_alloc(void); +MR4TH_SYMBOL void* arena_push(Arena *arena, U64 size); +MR4TH_SYMBOL void arena_pop_amount(Arena *arena, U64 amount); +MR4TH_SYMBOL void arena_align(Arena *arena, U64 pow2_align); + +#define push_array(a,T,c) (T*)arena_push((a), sizeof(T)*(c)) +#define push_array_no_zero(a,T,c) (T*)arena_push_no_zero((a), sizeof(T)*(c)) + +#define push_array_copy(a,T,c,p) (T*)(MemoryCopy(push_array(a,T,c), (p), sizeof(T)*(c))) + +MR4TH_SYMBOL ArenaTemp arena_begin_temp(Arena *arena); +MR4TH_SYMBOL void arena_end_temp(ArenaTemp *temp); + +#define arena_release_scratch(temp) arena_end_temp(temp) + +//////////////////////////////// +// Functions: Strings + +// characters + +MR4TH_SYMBOL B32 char_is_whitespace(U8 c); +MR4TH_SYMBOL B32 char_is_slash(U8 c); +MR4TH_SYMBOL B32 char_is_digit(U8 c); + +MR4TH_SYMBOL U8 char_to_uppercase(U8 c); +MR4TH_SYMBOL U8 char_to_lowercase(U8 c); + +// in-place constructors + +#define str8(p,z) CLiteral(String8){(U8*)(p), (z)} + +MR4TH_SYMBOL String8 str8_range(U8 *first, U8 *opl); +MR4TH_SYMBOL String8 str8_cstring(U8 *cstr); +MR4TH_SYMBOL String8 str8_cstring_capped(U8 *cstr, U8 *opl); + +MR4TH_SYMBOL String16 str16_cstring(U16 *cstr); + +// literal constructors + +#define str8_lit(s) CLiteral(String8){(U8*)(s), sizeof(s) - 1} +#define str8_lit_const(s) {(U8*)(s), sizeof(s) - 1} + +// compound constructors + +MR4TH_SYMBOL void str8_list_push(Arena *arena, String8List *list, String8 string); +MR4TH_SYMBOL void str8_list_push_front(Arena *arena, String8List *list, String8 string); + +MR4TH_SYMBOL String8List str8_list_copy(Arena *arena, String8List *list); +MR4TH_SYMBOL String8 str8_join(Arena *arena, String8List *list, StringJoin *optional_join); +MR4TH_SYMBOL String8List str8_split(Arena *arena, String8 string, U8 *split_characters, U32 count); + +MR4TH_SYMBOL String8 str8_pushfv(Arena *arena, char *fmt, va_list args); +MR4TH_SYMBOL String8 str8_pushf(Arena *arena, char *fmt, ...); +MR4TH_SYMBOL void str8_list_pushf(Arena *arena, String8List *list, char *fmt, ...); + +MR4TH_SYMBOL String8 str8_push_copy(Arena *arena, String8 string); + +// NOTE(allen): use to turn `char *fmt, ...` into a String8, the function should have +// the fmt string just before the ... +#define Str8FromFmt(str,arena,fmt) do{\ +va_list args_; va_start(args_, (fmt)); *(str) = str8_pushfv((arena), (fmt), args_); va_end(args_); \ +}while(0) + +// substrings + +MR4TH_SYMBOL String8 str8_prefix(String8 str, U64 size); +MR4TH_SYMBOL String8 str8_chop(String8 str, U64 amount); +MR4TH_SYMBOL String8 str8_postfix(String8 str, U64 size); +MR4TH_SYMBOL String8 str8_skip(String8 str, U64 amount); + +MR4TH_SYMBOL String8 str8_skip_chop_whitespace(String8 str); + +// hash + +MR4TH_SYMBOL U64 str8_hash(String8 str); + +// compare + +MR4TH_SYMBOL B32 str8_match(String8 a, String8 b, StringMatchFlags flags); + +// path helpers + +MR4TH_SYMBOL String8 str8_chop_last_slash(String8 string); +MR4TH_SYMBOL String8 str8_file_name_from_path(String8 full_file_name); +MR4TH_SYMBOL String8 str8_base_name_from_file_name(String8 file_name); + +// stylized constructors + +MR4TH_SYMBOL String8 str8_join_flags(Arena *arena, String8List *list); + +// unicode + +MR4TH_SYMBOL StringDecode str_decode_utf8(U8 *str, U32 cap); +MR4TH_SYMBOL U32 str_encode_utf8(U8 *dst, U32 codepoint); +MR4TH_SYMBOL StringDecode str_decode_utf16(U16 *str, U32 cap); +MR4TH_SYMBOL U32 str_encode_utf16(U16 *dst, U32 codepoint); + +MR4TH_SYMBOL String32 str32_from_str8(Arena *arena, String8 string); +MR4TH_SYMBOL String8 str8_from_str32(Arena *arena, String32 string); +MR4TH_SYMBOL String16 str16_from_str8(Arena *arena, String8 string); +MR4TH_SYMBOL String8 str8_from_str16(Arena *arena, String16 string); + +// numeric conversion + +MR4TH_SYMBOL B32 str8_is_u64(String8 string, U32 radix); + +MR4TH_SYMBOL U64 u64_from_str8(String8 string, U32 radix); +MR4TH_SYMBOL U64 u64_from_str8_c_syntax(String8 string); +MR4TH_SYMBOL S64 s64_from_str8_c_syntax(String8 string); +MR4TH_SYMBOL F64 f64_from_str8(String8 string); + +//////////////////////////////// +// Functions: Stream + +// stream core + +MR4TH_SYMBOL_MUST_SHARE STREAM* stream_new(void); +MR4TH_SYMBOL_MUST_SHARE void stream_release(STREAM *stream); + +MR4TH_SYMBOL_MUST_SHARE void stream_clear(STREAM *stream); +MR4TH_SYMBOL_MUST_SHARE void stream_write(STREAM *stream, String8 data); +MR4TH_SYMBOL_MUST_SHARE U8* stream_alloc(STREAM *stream, U64 size); + +MR4TH_SYMBOL_MUST_SHARE U64 stream_total_size(STREAM *stream); +MR4TH_SYMBOL_MUST_SHARE STREAM_Handle* stream_next_chunk(STREAM *stream, STREAM_Handle *handle, String8 *chunk_out); + +// stream helpers + +MR4TH_SYMBOL String8 stream_read(Arena *arena, STREAM *stream); + +MR4TH_SYMBOL void stream_printfv(STREAM *stream, char *fmt, va_list args); +MR4TH_SYMBOL void stream_printf(STREAM *stream, char *fmt, ...); + +//////////////////////////////// +// Functions: Buffer + +MR4TH_SYMBOL void buffer_alloc(BUFFER *buffer, U64 reserve_size); +MR4TH_SYMBOL void buffer_release(BUFFER *buffer); +MR4TH_SYMBOL void buffer_reset(BUFFER *buffer); + +MR4TH_SYMBOL B32 buffer_commit_off__call(BUFFER *buffer, U64 off); + +#define buffer_commit_off(b, off) \ +((((b)->commit_pos < (off) && (off) <= (b)->size)? \ +(buffer_commit_off__call((b),(off))):((off) <= (b)->size))) + +#define buffer_typed_commit_idx(b, idx) \ +buffer_commit_off(&(b)->buffer, (idx)*sizeof(*(b)->ptr)) + +//////////////////////////////// +// Types: Hash Table + +MR4TH_SYMBOL void hash_buckets_init(BUFFER *buckets, U64 size); +MR4TH_SYMBOL HASH_Node* hash_buckets_first(BUFFER *buckets, U64 hash); +MR4TH_SYMBOL void hash_buckets_expand(BUFFER *buckets, U64 capacity); +MR4TH_SYMBOL void hash_buckets_insert(Arena *arena, BUFFER *buckets, U64 hash, U64 val); + +//////////////////////////////// +// Functions: Log + +MR4TH_SYMBOL void log_accum_begin(LOG_LogToProc *proc, void *uptr); +MR4TH_SYMBOL B32 log_gathering(void); +MR4TH_SYMBOL void log_emit(String8 message); +MR4TH_SYMBOL void log_emitf(char *fmt, ...); +MR4TH_SYMBOL String8 log_accum_end(Arena *arena); + +//////////////////////////////// +// Functions: Errors + +MR4TH_SYMBOL void er_accum_begin(void); +MR4TH_SYMBOL void er_emit(String8 error); +MR4TH_SYMBOL void er_emitf(char *fmt, ...); +MR4TH_SYMBOL String8 er_accum_end(Arena *arena); + +#define er_emit_lit(s) er_emit(str8_lit(s)) + + +//////////////////////////////////////////////// +//////////////////////////////////////////////// +///////// DECLARATIONS PROFILING /////////// +//////////////////////////////////////////////// +//////////////////////////////////////////////// + +//////////////////////////////// +// Profiling: Implementable Interface + +#if MR4TH_PROFILING_USER || MR4TH_PROFILING_PROVIDER + +MR4TH_SYMBOL_LINK void prof_open(char *name); +MR4TH_SYMBOL_LINK void prof_close(void); +MR4TH_SYMBOL_LINK void prof_thread_begin(void); +MR4TH_SYMBOL_LINK void prof_thread_end(void); +MR4TH_SYMBOL_LINK void prof_thread_flush(void); +MR4TH_SYMBOL_LINK void prof_begin(char *name, U32 len); +MR4TH_SYMBOL_LINK void prof_end(void); + +#endif + +//////////////////////////////// +// Profiling: User Interface + +#if MR4TH_PROFILING_USER +# define ProfOpen(n) prof_open((char*)(n)) +# define ProfClose() prof_close() +# define ProfThreadBegin() prof_thread_begin() +# define ProfThreadEnd() prof_thread_end() +# define ProfThreadFlush() prof_thread_flush() +# define ProfBegin(n) prof_begin((n), sizeof(n) - 1) +# define ProfEnd() prof_end() +#else +# define ProfOpen(n) +# define ProfClose() +# define ProfThreadBegin() +# define ProfThreadEnd() +# define ProfThreadFlush() +# define ProfBegin(n) +# define ProfEnd() +#endif + +#if MR4TH_PROFILING_MANUAL +# define ProfBeginManual(n) ProfBegin(n) +# define ProfEndManual() ProfEnd() +#else +# define ProfBeginManual(n) +# define ProfEndManual() +#endif + +#define ProfBeginFunc() ProfBeginManual(__FUNCTION__) +#define ProfEndFunc() ProfEndManual() + + + +//////////////////////////////////////////////// +//////////////////////////////////////////////// +/////// DECLARATIONS OS ABSTRACTION //////// +//////////////////////////////////////////////// +//////////////////////////////////////////////// + +//////////////////////////////// +// Shared Types + +typedef struct OS_FileIter{ + U8 v[640]; +} OS_FileIter; + +typedef enum OS_SystemPath{ + OS_SystemPath_CurrentDirectory = 0, + OS_SystemPath_Binary = 1, + OS_SystemPath_UserData = 2, + OS_SystemPath_TempData = 3, + OS_SystemPath_COUNT +} OS_SystemPath; + +typedef void OS_Library; + +typedef struct OS_VRange{ + void *addr; + U64 size; +} OS_VRange; + +//////////////////////////////// +// Implementable Functions: Process Setup + +MR4TH_SYMBOL void os_main_init(int argc, char **argv); +MR4TH_SYMBOL String8List os_command_line_arguments(void); +MR4TH_SYMBOL void os_exit_process(U32 code); + +//////////////////////////////// +// Implementable Functions: Memory Functions + +MR4TH_SYMBOL void* os_memory_reserve(U64 size); +MR4TH_SYMBOL B32 os_memory_commit(void *ptr, U64 size); +MR4TH_SYMBOL void os_memory_decommit(void *ptr, U64 size); +MR4TH_SYMBOL void os_memory_release(void *ptr, U64 size); + +MR4TH_SYMBOL B32 os_memory_protect(void *ptr, U64 size, DataAccessFlags access_flags); + +//////////////////////////////// +// Implementable Functions: File Handling + +MR4TH_SYMBOL String8 os_file_read(Arena *arena, String8 file_name); + +typedef String8 OS_FileWriteCallback(void *udata); +MR4TH_SYMBOL B32 os_file_write_callback(String8 file_name, void *udata, OS_FileWriteCallback *callback); + +MR4TH_SYMBOL FileProperties os_file_properties(String8 file_name); + +MR4TH_SYMBOL B32 os_file_delete(String8 file_name); +MR4TH_SYMBOL B32 os_file_rename(String8 og_name, String8 new_name); +MR4TH_SYMBOL B32 os_file_make_directory(String8 path); +MR4TH_SYMBOL B32 os_file_delete_directory(String8 path); + +MR4TH_SYMBOL OS_FileIter os_file_iter_init(Arena *arena, String8 path); +MR4TH_SYMBOL B32 os_file_iter_next(Arena *arena, OS_FileIter *iter, + String8 *name_out, FileProperties *prop_out); +MR4TH_SYMBOL void os_file_iter_end(OS_FileIter *iter); + +MR4TH_SYMBOL String8 os_file_path(Arena *arena, OS_SystemPath path); + +MR4TH_SYMBOL void os_set_current_directory(String8 path); + +//////////////////////////////// +// Implementable Functions: Time + +MR4TH_SYMBOL DateTime os_now_universal_time(void); +MR4TH_SYMBOL DateTime os_local_time_from_universal(DateTime *date_time); +MR4TH_SYMBOL DateTime os_universal_time_from_local(DateTime *date_time); + +MR4TH_SYMBOL U32 os_time_stamp_32_from_date_time(DateTime *date_time); + +MR4TH_SYMBOL U64 os_now_ticks(void); +MR4TH_SYMBOL void os_get_microseconds_per_tick(U64 *usecs_out, U64 *ticks_out); +MR4TH_SYMBOL void os_sleep_milliseconds(U32 t); + +//////////////////////////////// +// Implementable Functions: Libraries + +MR4TH_SYMBOL OS_Library* os_lib_load(String8 path); +MR4TH_SYMBOL VoidFunc* os_lib_get_proc(OS_Library *lib, char *name); +MR4TH_SYMBOL void os_lib_release(OS_Library *lib); + +MR4TH_SYMBOL OS_Library* os_lib_from_addr(void *addr); +MR4TH_SYMBOL OS_VRange os_lib_image_range(OS_Library *lib); + +//////////////////////////////// +// Implementable Functions: Entropy + +MR4TH_SYMBOL void os_get_entropy(void *data, U64 size); + +//////////////////////////////// +// Helper Functions: File Handling + +MR4TH_SYMBOL B32 os_file_write(String8 file_name, String8 data); +MR4TH_SYMBOL B32 os_file_write_list(String8 file_name, String8Node *first_node); +MR4TH_SYMBOL B32 os_file_write_stream(String8 file_name, STREAM *stream); + +//////////////////////////////// +// Helper Functions: Libraries + +MR4TH_SYMBOL OS_VRange os_this_image(void); + + + +/******************************* +** Begin Base Win32 ** +*******************************/ + +#if OS_WINDOWS + +//////////////////////////////// +// Win32 Includes + +#include +#include +#include + +//////////////////////////////// +// Win32 Types + +typedef struct W32_FileIter{ + HANDLE handle; + WIN32_FIND_DATAW find_data; + B32 done; +} W32_FileIter; +StaticAssert(sizeof(W32_FileIter) <= sizeof(OS_FileIter), w32_fileiter); + +//////////////////////////////// +// Win32 Functions: Specialized Init for WinMain + +MR4TH_SYMBOL void w32_WinMain_init(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPSTR lpCmdLine, + int nShowCmd); + +MR4TH_SYMBOL HINSTANCE w32_get_instance(void); + +//////////////////////////////// +// Win32 Functions: Time Helpers + +MR4TH_SYMBOL DateTime w32_date_time_from_system_time(SYSTEMTIME *in); +MR4TH_SYMBOL SYSTEMTIME w32_system_time_from_date_time(DateTime *in); +MR4TH_SYMBOL DenseTime w32_dense_time_from_file_time(FILETIME *file_time); + +//////////////////////////////// +// Win32 Functions: File Helpers + +MR4TH_SYMBOL FilePropertyFlags w32_prop_flags_from_attribs(DWORD attribs); +MR4TH_SYMBOL DataAccessFlags w32_access_from_attributes(DWORD attribs); + +//////////////////////////////// +// Win32 Helper Macro + +#define W32_PROC_ADDR(v,m,s) (*(PROC*)(&(v))) = GetProcAddress((m),(s)) + +#endif /* OS_WINDOWS */ + +/******************************* +** End Base Win32 ** +*******************************/ + + + +/******************************* +** Begin Base Linux ** +*******************************/ + +#if OS_LINUX + +//////////////////////////////// +// Linux Includes + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//////////////////////////////// +// Linux Types + +typedef struct LNX_FileIter{ + DIR *dir; + struct dirent *dp; + String8 path; +} LNX_FileIter; +StaticAssert(sizeof(LNX_FileIter) <= sizeof(OS_FileIter), lnx_fileiter); + +//////////////////////////////// +// Linux Functions: Time Helpers + +MR4TH_SYMBOL DateTime lnx_date_time_from_tm(struct tm *tm); +MR4TH_SYMBOL DenseTime lnx_dense_time_from_time_t(time_t time); +MR4TH_SYMBOL struct tm lnx_tm_from_date_time(DateTime *date_time); + +//////////////////////////////// +// Linux Functions: File Helpers + +MR4TH_SYMBOL FilePropertyFlags lnx_prop_flags_from_mode(mode_t mode); +MR4TH_SYMBOL DataAccessFlags lnx_access_from_mode(mode_t mode); +MR4TH_SYMBOL FileProperties lnx_file_properties_from_stat(struct stat *st); + +MR4TH_SYMBOL String8 lnx_proc_file_read(Arena *arena, char *path); + +#endif /* OS_LINUX */ + +/******************************* +** End Base Linux ** +*******************************/ + + +//////////////////////////////////////////////// +//////////////////////////////////////////////// +///////////////// M4 Printf //////////////// +//////////////////////////////////////////////// +//////////////////////////////////////////////// + +#if 0 +// TODO(allen): develop this idea further + +typedef struct M4_FormatterFields{ + S32 precision; +} M4_FormatterFields; + +typedef void M4_Formatter(STREAM *stream, M4_FormatterFields *fields, va_list va); + +typedef struct M4_FormatTable{ + M4_Formatter *formatters[26]; +} M4_FormatTable; + +MR4TH_SYMBOL m4_set_format_table(M4_FormatTable *table); + +#endif + + +// stb_sprintf.h 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. +NOTE(allen): MODIFICATION - I've hard coded that this copy *does* use floats + +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 // 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); + +STBSP__PUBLICDEC int m4_vsprintf(char *buf, char const *fmt, va_list va); +STBSP__PUBLICDEC int m4_vsnprintf(char *buf, int count, char const *fmt, va_list va); +STBSP__PUBLICDEC int m4_sprintf(char *buf, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(2,3); +STBSP__PUBLICDEC int m4_snprintf(char *buf, int count, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(3,4); + +STBSP__PUBLICDEC int m4_vsprintfcb(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va); +STBSP__PUBLICDEC void m4_set_separators(char comma, char period); + +/* +** NOTE(allen): sprintf modifications notes +** +** Legend for specifier allocation: +** [ ] - specifier is free for use +** [#] - used by stb_sprintf default +** [$] - used by built-in custom printing logic +** +** Specifier allocation: +** a[#] - hex float +** A[#] - hex float +** b[#] - binary +** B[#] - binary +** c[#] - char +** C[ ] +** d[#] - integer +** D[ ] +** e[#] - float +** E[#] - float +** f[#] - float +** F[ ] +** g[#] - float +** G[#] - float +** h[#] - half width +** H[ ] +** i[#] - integer +** I[#] - size[Microsoft-Style] +** j[#] - size[size_t] +** J[ ] +** k[ ] +** K[ ] +** l[#] - size[long]/size[long long] +** L[ ] +** m[ ] +** M[ ] +** n[#] - write-bytes +** N[$] - indentation +** o[#] - octal +** O[ ] +** p[ ] +** P[ ] +** q[ ] +** Q[ ] +** r[ ] +** R[$] +** s[#] - cstr +** S[$] - String8 +** t[#] - size[ptrdiff_t] +** T[ ] +** u[#] - unsigned integer +** U[ ] +** v[ ] +** V[ ] +** w[ ] +** W[ ] +** x[#] - hex +** X[#] - hex +** y[ ] +** Y[ ] +** z[#] - size[size_t] +** Z[ ] +** 0[#] - leading zeroes +** 1[ ] +** 2[ ] +** 3[ ] +** 4[ ] +** 5[ ] +** 6[ ] +** 7[ ] +** 8[ ] +** 9[ ] +** $[$] - memory size (modified from stb_sprintf.h) +** ?[ ] +** +*/ + +#endif /* MR4TH_BASE_H */ + +/* +** TODO: +** [ ] m4_printf plugins experiment +** +** [ ] unicode support +** [ ] high performance encode/decode factorization +** [ ] unicode string conversions should take string lists "Better unicode conversions here" +** [ ] math implementation replacements +** [ ] clib elimination option +** [x] manually implement memset,memcmp,memmove +** [x] basic C implementations +** [ ] high performance implementations (hand written assembly perhaps? -- or at least use vector intrinsics) +** [ ] necessary stubs for clib elimination +** [ ] Test out the usage process +** [ ] default (unity build standalone) +** [ ] standalone multi-unit +** [ ] core & plugin +** [ ] Write instructions +** [ ] Write examples +** +** [ ] Multi-threading primitives +** [ ] Thread-safe STREAM +*/ diff --git a/symbol_set.ld_meta/mr4th/mr4th_cmdln.c b/symbol_set.ld_meta/mr4th/mr4th_cmdln.c new file mode 100644 index 0000000..e74f2fc --- /dev/null +++ b/symbol_set.ld_meta/mr4th/mr4th_cmdln.c @@ -0,0 +1,294 @@ +//////////////////////////////// +// Functions: Command Line Parsing + +MR4TH_SYMBOL MR4TH_READ_ONLY +CMDLN_Params cmdln__params_nil = {0}; + +#define cmdln_params_nil (CMDLN_Params*)(&cmdln__params_nil) + +MR4TH_SYMBOL CMDLN* +cmdln_from_args(Arena *arena, String8List *args){ + CMDLN *cmdln = push_array(arena, CMDLN, 1); + cmdln->raw = str8_list_copy(arena, args); + + String8Node *node = cmdln->raw.first; + + // first string is 'program' + if (node != 0){ + cmdln->program = node->string; + node = node->next; + } + + // consume string nodes + B32 forced_input = 0; + for (;node != 0;){ + String8 string = str8_skip_chop_whitespace(node->string); + node = node->next; + + // check if argument is a flag + B32 is_flag = 0; + if (!forced_input){ + is_flag = (string.size != 0 && string.str[0] == '-'); + } + + // parse flag + if (is_flag){ + + // long flag + B32 is_long_flag = (string.size > 1 && string.str[1] == '-'); + if (is_long_flag){ + + // end 'normal' mode + B32 double_dash = (string.size == 2); + if (double_dash){ + forced_input = 1; + } + + // parse long flag + if (!double_dash){ + String8 flag_whole = str8_skip(string, 2); + + // parameter delimter + U64 delim = flag_whole.size; + for (U8 *ptr = flag_whole.str, *opl = flag_whole.str + flag_whole.size; + ptr < opl; ptr += 1){ + if (*ptr == '=' || *ptr == ':'){ + delim = (U64)(ptr - flag_whole.str); + break; + } + } + + // split flag at delimiter + String8 flag_name = str8_prefix(flag_whole, delim); + String8 flag_param = str8_skip(flag_whole, delim + 1); + + // if have a param delimiter at end + // then use the next argument as the flag_param + if (delim == flag_whole.size - 1){ + if (node != 0){ + flag_param = node->string; + node = node->next; + } + } + + // parse parameters + CMDLN_Params *params = cmdln_params_from_string(arena, flag_param); + + // store flag node + { + CMDLN_Node *cmdlnnode = push_array(arena, CMDLN_Node, 1); + SLLQueuePush(cmdln->first, cmdln->last, cmdlnnode); + cmdlnnode->string = flag_name; + cmdlnnode->params = params; + cmdln->flag_count += 1; + } + } + } + + // short flags + if (!is_long_flag){ + String8 short_flags = str8_skip(string, 1); + + U8 *flag = short_flags.str; + U8 *flagopl = short_flags.str + short_flags.size; + for (; flag < flagopl; flag += 1){ + U8 *flagptr = flag; + + // check for parameters + String8 flag_param = {0}; + if (flag + 1 < flagopl && + (flag[1] == '=' || flag[1] == ':')){ + flag_param = str8_range(flag + 2, flagopl); + if (flag_param.size == 0){ + if (node != 0){ + flag_param = node->string; + node = node->next; + } + } + + // kill the flag loop after finding parameters + flag = flagopl; + } + + // parse parameters + CMDLN_Params *params = cmdln_params_from_string(arena, flag_param); + + // store flag node + { + CMDLN_Node *cmdlnnode = push_array(arena, CMDLN_Node, 1); + SLLQueuePush(cmdln->first, cmdln->last, cmdlnnode); + cmdlnnode->string = CLiteral(String8){flagptr, 1}; + cmdlnnode->params = params; + cmdln->flag_count += 1; + } + } + } + } + + // parse input + if (!is_flag){ + String8 input_string = string; + + // store input node + { + CMDLN_Node *cmdlnnode = push_array(arena, CMDLN_Node, 1); + SLLQueuePush(cmdln->first, cmdln->last, cmdlnnode); + cmdlnnode->string = input_string; + cmdln->input_count += 1; + } + } + } + + // pointer arrays + cmdln->inputs = push_array(arena, CMDLN_Node*, cmdln->input_count); + cmdln->flags = push_array(arena, CMDLN_Node*, cmdln->flag_count); + { + CMDLN_Node **inputptr = cmdln->inputs; + CMDLN_Node **flagptr = cmdln->flags; + for (CMDLN_Node *node = cmdln->first; + node != 0; + node = node->next){ + if (node->params == 0){ + *inputptr = node; + inputptr += 1; + } + else{ + *flagptr = node; + flagptr += 1; + } + } + } + + return(cmdln); +} + +MR4TH_SYMBOL CMDLN_Params* +cmdln_params_from_string(Arena *arena, String8 flag_param){ + CMDLN_Params *params = cmdln_params_nil; + if (flag_param.size > 0){ + params = push_array(arena, CMDLN_Params, 1); + params->raw = flag_param; + params->list = str8_split(arena, flag_param, (U8*)",", 1); + } + return(params); +} + +MR4TH_SYMBOL U64 +cmdln_input_count(CMDLN *cmdln){ + return(cmdln->input_count); +} + +MR4TH_SYMBOL String8 +cmdln_input_from_idx(CMDLN *cmdln, U64 idx){ + String8 result = {0}; + if (idx < cmdln->input_count){ + result = cmdln->inputs[idx]->string; + } + return(result); +} + +MR4TH_SYMBOL U64 +cmdln_flag_count(CMDLN *cmdln){ + return(cmdln->flag_count); +} + +MR4TH_SYMBOL CMDLN_Flag* +cmdln_flag_from_idx(CMDLN *cmdln, U64 idx){ + CMDLN_Flag *result = 0; + if (idx < cmdln->flag_count){ + result = cmdln->flags[idx]; + } + return(result); +} + +MR4TH_SYMBOL CMDLN_Params* +cmdln_get_params(CMDLN *cmdln, String8 flagstr, char abbrev){ + CMDLN_Params *result = 0; + for (CMDLN_Node *node = cmdln->first; + node != 0; + node = node->next){ + if (node->params != 0){ + if (str8_match(flagstr, node->string, 0) || + (node->string.size == 1 && node->string.str[0] == (U8)abbrev)){ + result = node->params; + break; + } + } + } + return(result); +} + +MR4TH_SYMBOL B32 +cmdln_has_flag(CMDLN *cmdln, String8 flagstr, char abbrev){ + CMDLN_Params *params = cmdln_get_params(cmdln, flagstr, abbrev); + B32 result = (params != 0); + return(result); +} + +MR4TH_SYMBOL String8 +cmdln_get_str8(CMDLN *cmdln, String8 flagstr, char abbrev){ + CMDLN_Params *params = cmdln_get_params(cmdln, flagstr, abbrev); + String8 result = {0}; + if (params != 0){ + result = params->raw; + } + return(result); +} + +MR4TH_SYMBOL S64 +cmdln_get_s64(CMDLN *cmdln, String8 flagstr, char abbrev){ + String8 str = cmdln_get_str8(cmdln, flagstr, abbrev); + S64 result = cmdln_s64_from_str8(str); + return(result); +} + +MR4TH_SYMBOL F64 +cmdln_get_f64(CMDLN *cmdln, String8 flagstr, char abbrev){ + String8 str = cmdln_get_str8(cmdln, flagstr, abbrev); + F64 result = cmdln_f64_from_str8(str); + return(result); +} + +MR4TH_SYMBOL S64 +cmdln_s64_from_str8(String8 valstr){ + S64 result = s64_from_str8_c_syntax(valstr); + return(result); +} + +MR4TH_SYMBOL F64 +cmdln_f64_from_str8(String8 valstr){ + F64 result = f64_from_str8(valstr); + return(result); +} + +MR4TH_SYMBOL void +cmdln_dump(Arena *arena, String8List *out, CMDLN *cmdln, U32 indent){ + // raw + str8_list_pushf(arena, out, "%Nraw:\n", indent); + for (String8Node *node = cmdln->raw.first; + node != 0; + node = node->next){ + str8_list_pushf(arena, out, "%N%S\n", indent + 1, node->string); + } + + // program + str8_list_pushf(arena, out, "%Nprogram: %S\n", indent, cmdln->program); + + // input nodes + str8_list_pushf(arena, out, "%Nnodes:\n", indent); + for (CMDLN_Node *cmdlnnode = cmdln->first; + cmdlnnode != 0; + cmdlnnode = cmdlnnode->next){ + if (cmdlnnode->params == 0){ + str8_list_pushf(arena, out, "%N[input] %S\n", indent + 1, cmdlnnode->string); + } + else{ + str8_list_pushf(arena, out, "%N[flag ] %S\n", indent + 1, cmdlnnode->string); + for (String8Node *node = cmdlnnode->params->list.first; + node != 0; + node = node->next){ + str8_list_pushf(arena, out, "%N[param] %S\n", indent + 2, node->string); + } + } + } +} diff --git a/symbol_set.ld_meta/mr4th/mr4th_cmdln.h b/symbol_set.ld_meta/mr4th/mr4th_cmdln.h new file mode 100644 index 0000000..686ae84 --- /dev/null +++ b/symbol_set.ld_meta/mr4th/mr4th_cmdln.h @@ -0,0 +1,64 @@ +#ifndef MR4TH_CMDLN_H +#define MR4TH_CMDLN_H + +//////////////////////////////// +// Types: Command Line Parsing + +typedef struct CMDLN_Params{ + String8 raw; + String8List list; +} CMDLN_Params; + +typedef struct CMDLN_Node{ + struct CMDLN_Node *next; + String8 string; + CMDLN_Params *params; + // (params == 0 ) -> 'input' (not a flag) + // (params != 0 ) -> flag + // (params == nil) -> flag has no parameters +} CMDLN_Node; + +typedef CMDLN_Node CMDLN_Flag; + +typedef struct CMDLN{ + String8List raw; + String8 program; + CMDLN_Node *first; + CMDLN_Node *last; + U64 input_count; + U64 flag_count; + CMDLN_Node **inputs; + CMDLN_Node **flags; +} CMDLN; + +//////////////////////////////// +// Functions: Command Line Parsing + +MR4TH_SYMBOL CMDLN* cmdln_from_args(Arena *arena, String8List *args); +MR4TH_SYMBOL CMDLN_Params* cmdln_params_from_string(Arena *arena, String8 flag_param); + +MR4TH_SYMBOL U64 cmdln_input_count(CMDLN *cmdln); +MR4TH_SYMBOL String8 cmdln_input_from_idx(CMDLN *cmdln, U64 idx); + +MR4TH_SYMBOL U64 cmdln_flag_count(CMDLN *cmdln); +MR4TH_SYMBOL CMDLN_Flag* cmdln_flag_from_idx(CMDLN *cmdln, U64 idx); + +MR4TH_SYMBOL CMDLN_Params* cmdln_get_params(CMDLN *cmdln, String8 flagstr, char abbrev); +MR4TH_SYMBOL B32 cmdln_has_flag(CMDLN *cmdln, String8 flagstr, char abbrev); +MR4TH_SYMBOL String8 cmdln_get_str8(CMDLN *cmdln, String8 flagstr, char abbrev); +MR4TH_SYMBOL S64 cmdln_get_s64(CMDLN *cmdln, String8 flagstr, char abbrev); +MR4TH_SYMBOL F64 cmdln_get_f64(CMDLN *cmdln, String8 flagstr, char abbrev); + +MR4TH_SYMBOL S64 cmdln_s64_from_str8(String8 valstr); +MR4TH_SYMBOL F64 cmdln_f64_from_str8(String8 valstr); + +MR4TH_SYMBOL void cmdln_dump(Arena *arena, String8List *out, CMDLN *cmdln, U32 indent); + +/* TODO: +** [ ] fuzz +** [ ] built in parser diagnostics +** [ ] built in misuse feedback +** [ ] help structurer +*/ + +#endif //MR4TH_CMDLN_H diff --git a/symbol_set.ld_meta/mr4th/mr4th_stdio.c b/symbol_set.ld_meta/mr4th/mr4th_stdio.c new file mode 100644 index 0000000..93e595e --- /dev/null +++ b/symbol_set.ld_meta/mr4th/mr4th_stdio.c @@ -0,0 +1,56 @@ +//////////////////////////////// +// Functions: File Handle Printf + +MR4TH_SYMBOL void +m4_printf(char *fmt, ...){ + ArenaTemp scratch = arena_get_scratch(0, 0); + va_list args; + va_start(args, fmt); + String8 str = str8_pushfv(scratch.arena, fmt, args); + va_end(args); + fwrite(str.str, 1, str.size, stdout); + arena_release_scratch(&scratch); +} + +MR4TH_SYMBOL void +m4_fprintf(FILE *file, char *fmt, ...){ + ArenaTemp scratch = arena_get_scratch(0, 0); + va_list args; + va_start(args, fmt); + String8 str = str8_pushfv(scratch.arena, fmt, args); + va_end(args); + fwrite(str.str, 1, str.size, file); + arena_release_scratch(&scratch); +} + +MR4TH_SYMBOL void +m4_print_str8list(String8List *out){ + m4_fprint_str8list(stdout, out); +} + +MR4TH_SYMBOL void +m4_fprint_str8list(FILE *file, String8List *out){ + for (String8Node *node = out->first; + node != 0; + node = node->next){ + fwrite(node->string.str, node->string.size, 1, file); + } +} + +// TODO(allen): integrate here +MR4TH_SYMBOL_STATIC void +stream_fprint(FILE *file, STREAM *stream){ + for (STREAM_Node *node = stream->first_node; + node != 0; + node = node->next){ + fwrite(node->data, 1, node->size, file); + } +} + +//////////////////////////////// +// LogToProc for (FILE*) + +MR4TH_SYMBOL void +cstd_logto_file_handle(void *uptr, String8 str){ + fwrite(str.str, 1, str.size, (FILE*)uptr); +} diff --git a/symbol_set.ld_meta/mr4th/mr4th_stdio.h b/symbol_set.ld_meta/mr4th/mr4th_stdio.h new file mode 100644 index 0000000..f76ab4b --- /dev/null +++ b/symbol_set.ld_meta/mr4th/mr4th_stdio.h @@ -0,0 +1,23 @@ +#ifndef MR4TH_STDIO_H +#define MR4TH_STDIO_H + +#include + +//////////////////////////////// +// Functions: File Handle Printf + +MR4TH_SYMBOL void m4_printf(char *fmt, ...); +MR4TH_SYMBOL void m4_fprintf(FILE *file, char *fmt, ...); + +MR4TH_SYMBOL void m4_print_str8list(String8List *out); +MR4TH_SYMBOL void m4_fprint_str8list(FILE *file, String8List *out); + +// TODO(allen): integrate to this layer +MR4TH_SYMBOL void stream_fprint(FILE *file, STREAM *stream); + +//////////////////////////////// +// LogToProc for (FILE*) + +MR4TH_SYMBOL void cstd_logto_file_handle(void *uptr, String8 str); + +#endif //MR4TH_STDIO_H diff --git a/symbol_set.ld_meta/symbol_set.ld_meta.c b/symbol_set.ld_meta/symbol_set.ld_meta.c new file mode 100644 index 0000000..122fc0f --- /dev/null +++ b/symbol_set.ld_meta/symbol_set.ld_meta.c @@ -0,0 +1,146 @@ +/* +** Symbol Set Link Script Generator for ld +*/ + +#include "mr4th_base.h" +#include "mr4th_cmdln.h" +#include "exefmt/elf/mr4th_elf.h" +#include "exefmt/elf/mr4th_elf.parse.h" + +#include "mr4th_stdio.h" + +#include "mr4th_base.c" +#include "mr4th_cmdln.c" +#include "exefmt/elf/mr4th_elf.c" +#include "exefmt/elf/mr4th_elf.parse.c" + +#include "mr4th_stdio.c" + +//////////////////////////////// +// Entry Point + +int main(int argc, char **argv){ + os_main_init(argc, argv); + + Arena *arena = arena_alloc(); + + // arguments + String8List command_line_args = os_command_line_arguments(); + CMDLN *cmdln = cmdln_from_args(arena, &command_line_args); + + String8 output_file_name = cmdln_get_str8(cmdln, str8_lit("out"), 'o'); + if (output_file_name.size == 0){ + output_file_name = str8_lit("sy.ld"); + } + + // symbol set names + String8List symbol_set_names_list = {0}; + BUFFER buckets; + buffer_alloc(&buckets, GB(64)); + hash_buckets_init(&buckets, 512); + + for (U32 input_idx = 0;; input_idx += 1){ + // load input file + String8 input_file_name = cmdln_input_from_idx(cmdln, input_idx); + if (input_file_name.size == 0){ + break; + } + String8 input_data = os_file_read(arena, input_file_name); + + // parse + ELF_Parse *parse = elfp_begin(arena, input_data); + if (parse != 0){ + + // process sections + U32 section_count = elfp_section_count(parse); + for (U32 i = 0; i < section_count; i += 1){ + String8 secname = elfp_section_name(parse, i); + if (secname.size >= 4 && + str8_match(secname, str8_lit(".sy."), StringMatchFlag_PrefixMatch)){ + String8 syname = str8_skip(secname, 4); + U64 hash = str8_hash(syname); + B32 match = 0; + for (HASH_Node *hash_node = hash_buckets_first(&buckets, hash); + hash_node != 0; + hash_node = hash_node->next){ + if (hash_node->hash == hash){ + String8Node *node = (String8Node*)PtrFromInt(hash_node->val); + if (str8_match(node->string, syname, 0)){ + match = 1; + break; + } + } + } + + if (!match){ + str8_list_push(arena, &symbol_set_names_list, syname); + hash_buckets_expand(&buckets, symbol_set_names_list.node_count); + hash_buckets_insert(arena, &buckets, hash, + IntFromPtr(symbol_set_names_list.last)); + } + } + } + + // process symbols + U32 symbol_count = elfp_symbol_count(parse, ELFP_SYMBOL_TABLE_symtab); + for (U32 i = 0; i < symbol_count; i += 1){ + String8 name = elfp_symbol_name(parse, ELFP_SYMBOL_TABLE_symtab, i); + + String8 syname = {0}; + if (str8_match(name, str8_lit("syfirst__"), StringMatchFlag_PrefixMatch)){ + syname = str8_skip(name, 9); + } + else if (str8_match(name, str8_lit("syopl__"), StringMatchFlag_PrefixMatch)){ + syname = str8_skip(name, 7); + } + + if (syname.size != 0){ + U64 hash = str8_hash(syname); + B32 match = 0; + for (HASH_Node *hash_node = hash_buckets_first(&buckets, hash); + hash_node != 0; + hash_node = hash_node->next){ + if (hash_node->hash == hash){ + String8Node *node = (String8Node*)PtrFromInt(hash_node->val); + if (str8_match(node->string, syname, 0)){ + match = 1; + break; + } + } + } + + if (!match){ + str8_list_push(arena, &symbol_set_names_list, syname); + hash_buckets_expand(&buckets, symbol_set_names_list.node_count); + hash_buckets_insert(arena, &buckets, hash, + IntFromPtr(symbol_set_names_list.last)); + } + } + } + + } + + } + + // print output linker script + STREAM *stream = stream_new(); + + stream_printf(stream, + "SECTIONS {\n" + " .sy : {\n"); + + for (String8Node *node = symbol_set_names_list.first; + node != 0; node = node->next){ + stream_printf(stream, " syfirst__%S = .; *(.sy.%S); syopl__%S = .;\n", + &node->string, &node->string, &node->string); + } + + stream_printf(stream, + " }\n" + "}\n" + "INSERT AFTER .data;\n"); + + os_file_write_stream(output_file_name, stream); + + return(0); +} diff --git a/symbol_set/symbol_set.base.h b/symbol_set/symbol_set.base.h new file mode 100644 index 0000000..a1b1a00 --- /dev/null +++ b/symbol_set/symbol_set.base.h @@ -0,0 +1,269 @@ +#if !defined(SY__BASE_H) +#define SY__BASE_H + +//////////////////////////////// +// Context Cracking + +// untangle compiler, os, & architecture +#if defined(__clang__) +# define SY__COMPILER_CLANG 1 + +# if defined(_WIN32) +# define SY__OS_WINDOWS 1 +# elif defined(__gnu_linux__) +# define SY__OS_LINUX 1 +# elif defined(__APPLE__) && defined(__MACH__) +# define SY__OS_MAC 1 +# else +# error missing OS detection +# endif + +# if defined(__amd64__) +# define SY__ARCH_X64 1 +// TODO(allen): verify this works on clang +# elif defined(__i386__) +# define SY__ARCH_X86 1 +// TODO(allen): verify this works on clang +# elif defined(__arm__) +# define SY__ARCH_ARM 1 +// TODO(allen): verify this works on clang +# elif defined(__aarch64__) +# define SY__ARCH_ARM64 1 +# else +# error missing SY__ARCH detection +# endif + +#elif defined(_MSC_VER) +# define SY__COMPILER_CL 1 + +# if defined(_WIN32) +# define SY__OS_WINDOWS 1 +# else +# error missing OS detection +# endif + +# if defined(_M_AMD64) +# define SY__ARCH_X64 1 +# elif defined(_M_I86) +# define SY__ARCH_X86 1 +# elif defined(_M_ARM) +# define SY__ARCH_ARM 1 +// TODO(allen): ARM64? +# else +# error missing SY__ARCH detection +# endif + +#elif defined(__GNUC__) +# define SY__COMPILER_GCC 1 + +# if defined(_WIN32) +# define SY__OS_WINDOWS 1 +# elif defined(__gnu_linux__) +# define SY__OS_LINUX 1 +# elif defined(__APPLE__) && defined(__MACH__) +# define SY__OS_MAC 1 +# else +# error missing OS detection +# endif + +# if defined(__amd64__) +# define SY__ARCH_X64 1 +# elif defined(__i386__) +# define SY__ARCH_X86 1 +# elif defined(__arm__) +# define SY__ARCH_ARM 1 +# elif defined(__aarch64__) +# define SY__ARCH_ARM64 1 +# else +# error missing SY__ARCH detection +# endif + +#else +# error no context cracking for this compiler +#endif + +#if !defined(SY__COMPILER_CL) +# define SY__COMPILER_CL 0 +#endif +#if !defined(SY__COMPILER_CLANG) +# define SY__COMPILER_CLANG 0 +#endif +#if !defined(SY__COMPILER_GCC) +# define SY__COMPILER_GCC 0 +#endif +#if !defined(SY__OS_WINDOWS) +# define SY__OS_WINDOWS 0 +#endif +#if !defined(SY__OS_LINUX) +# define SY__OS_LINUX 0 +#endif +#if !defined(SY__OS_MAC) +# define SY__OS_MAC 0 +#endif +#if !defined(SY__ARCH_X64) +# define SY__ARCH_X64 0 +#endif +#if !defined(SY__ARCH_X86) +# define SY__ARCH_X86 0 +#endif +#if !defined(SY__ARCH_ARM) +# define SY__ARCH_ARM 0 +#endif +#if !defined(SY__ARCH_ARM64) +# define SY__ARCH_ARM64 0 +#endif + + +// language +#if defined(__cplusplus) +# define SY__LANGCXX 1 +#else +# define SY__LANGC 1 +#endif + +#if !defined(SY__LANGCXX) +# define SY__LANGCXX 0 +#endif +#if !defined(SY__LANGC) +# define SY__LANGC 0 +#endif + +// setup pointer size macro +#if SY__ARCH_X64 || SY__ARCH_ARM64 +# define SY__ARCH_ADDRSIZE 64 +#else +# define SY__ARCH_ADDRSIZE 32 +#endif + +//////////////////////////////// +// GLUE(a,b) STRIFY(s) + +#define SY__GLUE__(a,b) a##b +#define SY__GLUE_(a,b) SY__GLUE__(a,b) +#define SY__GLUE(a,b) SY__GLUE_(a,b) + +#define SY__STRIFY__(s) #s +#define SY__STRIFY_(s) SY__STRIFY__(s) +#define SY__STRIFY(s) SY__STRIFY_(s) + +//////////////////////////////// +// SECTION() + +#if SY__COMPILER_CLANG || SY__COMPILER_GCC +# if SY__OS_MAC +# define SY__SEC_R(N) __attribute__((__section__("__TEXT,"N))) +# define SY__SEC_RW(N) __attribute__((__section__("__READ,"N))) +# else +# define SY__SEC_R(N) __attribute__((__section__(N))) +# define SY__SEC_RW(N) __attribute__((__section__(N))) +# endif +#elif SY__COMPILER_CL +# define SY__SEC_R(N) __declspec(allocate(N)) +# define SY__SEC_RW(N) __declspec(allocate(N)) +#else +# error SY__SEC_R/SY__SEC_RW not defined for this compiler/OS +#endif + +// for CL users: #pragma section(
,read,write) + +//////////////////////////////// +// BEFORE_MAIN(name){ <...> } + +#if SY__OS_WINDOWS + +# pragma section(".CRT$XCU", read) + +# if SY__LANG_CXX + +# define SY__BEFORE_MAIN_(n) \ +static void n(void); \ +__declspec(allocate(".CRT$XCU")) \ +__pragma(comment(linker,"/include:" #n "__")) \ +extern "C" void (*n##__)(void); \ +void (*n##__)(void) = n; \ +static void n(void) + +# else + +# define SY__BEFORE_MAIN_(n) \ +static void n(void); \ +__declspec(allocate(".CRT$XCU")) \ +__pragma(comment(linker,"/include:" #n "__")) \ +void (*n##__)(void) = n; \ +static void n(void) + +# endif + +# define SY__BEFORE_MAIN(n) SY__BEFORE_MAIN_(n) + +#elif SY__OS_LINUX + +# define SY__BEFORE_MAIN(n) \ +__attribute__((constructor)) static void n(void) + +#else +# error SY__BEFORE_MAIN missing for this OS +#endif + +//////////////////////////////// +// DO_NOT_ELIMINATE + +#if SY__OS_WINDOWS + +# define SY__DO_NOT_ELIMINATE__S0(N) __pragma(comment(linker,"/include:" #N)) +# define SY__DO_NOT_ELIMINATE__S1(N) SY__DO_NOT_ELIMINATE__S0(N) +# define SY__DO_NOT_ELIMINATE(N) SY__DO_NOT_ELIMINATE__S1(N) + +#else + +# define SY__DO_NOT_ELIMINATE(N) + +#endif + +//////////////////////////////// +// ALIGN_AS_LIT + +#if SY__COMPILER_CL +# define SY__ALIGN_AS_LIT(n) __declspec(align(n)) +#elif SY__COMPILER_CLANG || SY__COMPILER_GCC +# define SY__ALIGN_AS_LIT(n) __attribute__(( __aligned__(n) )) +#else +# error SY__ALIGN_AS_LIT not defined for this compiler +#endif + +//////////////////////////////// +// ROUND_UP_POW2 + +#define SY__ROUND_UP_POW2(x,p) (((x) + (p) - 1)&~((p) - 1)) + +//////////////////////////////// +// Types + +#if !defined(SY__TYPES) +# include +typedef int8_t SY__S8; +typedef int16_t SY__S16; +typedef int32_t SY__S32; +typedef int64_t SY__S64; +typedef uint8_t SY__U8; +typedef uint16_t SY__U16; +typedef uint32_t SY__U32; +typedef uint64_t SY__U64; +typedef SY__S8 SY__B8; +typedef SY__S16 SY__B16; +typedef SY__S32 SY__B32; +typedef SY__S64 SY__B64; +typedef float SY__F32; +typedef double SY__F64; +# if SY__ARCH_ADDRSIZE == 32 +typedef SY__U32 SY__UAddr; +typedef SY__S32 SY__SAddr; +# elif SY__ARCH_ADDRSIZE == 64 +typedef SY__U64 SY__UAddr; +typedef SY__S64 SY__SAddr; +# else +# error SY__UAddr and SY__SAddr not defined for this architecture +# endif +#endif + +#endif diff --git a/symbol_set/symbol_set.define.h b/symbol_set/symbol_set.define.h new file mode 100644 index 0000000..931cb5f --- /dev/null +++ b/symbol_set/symbol_set.define.h @@ -0,0 +1,36 @@ +#if SY__OS_LINUX + +extern SyType(SYMBOL_SET_DEFINE) SY__FIRST(SYMBOL_SET_DEFINE); +extern SyType(SYMBOL_SET_DEFINE) SY__OPL(SYMBOL_SET_DEFINE); +// (these symbols are resolved in the linker script) + +#elif SY__OS_WINDOWS + +#if SY__COMPILER_CL +# pragma section(SY__GLUE(SYMBOL_SET_DEFINE,_section),read,write) +#endif + +#if defined(SY__MAIN) + +SyType(SYMBOL_SET_DEFINE) *SY__FIRST(SYMBOL_SET_DEFINE) = 0; +SyType(SYMBOL_SET_DEFINE) *SY__OPL(SYMBOL_SET_DEFINE) = 0; + +SY__BEFORE_MAIN(SY__GLUE(SYMBOL_SET_DEFINE,__init)){ + int size = sizeof(SY__GLUE(SYMBOL_SET_DEFINE,_section)) - 1; + sy__section_init(SY__GLUE(SYMBOL_SET_DEFINE,_section), size, + (void**)&SY__FIRST(SYMBOL_SET_DEFINE), + (void**)&SY__OPL(SYMBOL_SET_DEFINE)); +} + +#else + +extern SyType(SYMBOL_SET_DEFINE) *SY__FIRST(SYMBOL_SET_DEFINE); +extern SyType(SYMBOL_SET_DEFINE) *SY__OPL(SYMBOL_SET_DEFINE); + +#endif + +#else +# error symbol_set.define not implemented for this OS +#endif + +#undef SYMBOL_SET_DEFINE diff --git a/symbol_set/symbol_set.h b/symbol_set/symbol_set.h new file mode 100644 index 0000000..e5e6f94 --- /dev/null +++ b/symbol_set/symbol_set.h @@ -0,0 +1,216 @@ +#if !defined(SY__H) +#define SY__H + +#include "symbol_set.base.h" + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~ Copy-Pastable Setup ~~~~~~~~~~~~~~~~~~~~~~~~~ +~~ To define a Symbol Set: +~~ copy the code below +~~ replace ZZZ: to name the symbol set +~~ replace Typezz: to set the type of the symbol set +~~ replace secz: to name of the data section of the symbol set +~~ pick and complete the sections with "..."s +*/ + +#if 0 + +#define SYMBOL_SET_DEFINE ZZZ +#define ZZZ_Type Typezz +#define ZZZ_section ".sy.secz" +#define ZZZ_marker secz +#include "symbol_set.define.h" + +// basic struct def with a name +#define ZZZ_DEF(N,...) SyDefine(ZZZ, N) = { ... } + +// def with attached function body +// replace funczz: to prefix namespace unique functions +// fill ... sections appropriately +#define ZZZ_DEF(N,...) \ +static void funczz##N(void*); \ +SyDefine(ZZZ, N) = { funczz##N, ... }; \ +static void funczz##N(void *ptr) + +// nameless def +#define ZZZ_DEF(...) SyDefineUnnamed(ZZZ) = { ... } + +#endif + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~ Loop Boilerplates ~~~~~~~~~~~~~~~~~*/ + +#if 0 +{ + for (SyEach(ZZZ, zzz_ptr)){ + zzz_ptr-> + } +} +{ + for (SyEachID(ZZZ, zzz_id)){ + [zzz_id] + } +} +#endif + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~ User Primitives ~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#define SyType(S) SY__GLUE(S,_Type) + +#define SyDeclare(S,N) SY__ALIGN_AS_LIT(8) SY__SEC_RW(S##_section) SyType(S) SY__SYMBOL(S,N) +#define SyDefine(S,N) SY__DO_NOT_ELIMINATE(SY__SYMBOL(S,N)) SyDeclare(S,N) +#define SyDefineUnnamed(S) SyDefine(S,SY__GLUE(unnamed,__COUNTER__)) + +#define SyAddress(S,N) (&SY__SYMBOL(S,N)) +#define SyID(S,N) SyIDFromAddress_Unchecked(S,SyAddress(S,N)) +#define SyRaw(S,N) (SY__UAddr)(SyAddress(S,N)) + +#if SY__OS_WINDOWS +# define SyFirst(S) SY__FIRST(S) +# define SyOpl(S) SY__OPL(S) +#elif SY__OS_LINUX +# define SyFirst(S) (&SY__FIRST(S)) +# define SyOpl(S) (&SY__OPL(S)) +#else +# error missing implementation set locator for this OS +#endif + +#define SyStride(S) SY__ROUND_UP_POW2(sizeof(SyType(S)), 8) + +#define SyCount(S) (((SY__U8*)SyOpl(S) - (SY__U8*)SyFirst(S))/SyStride(S)) +#define SyNext(S,addr) (SyType(S)*)((SY__U8*)addr + SyStride(S)) + +#define SyEach(S,var) SyType(S)*var=SyFirst(S); varmagic == SY__PE_MSDOS_MAGIC){ + SY__U32 *pe_signature = (SY__U32*)(base + dos->coff_file_offset); + if (*pe_signature == SY__PE_SIGNATURE){ + struct SY__PE_CoffHeader *coff = (struct SY__PE_CoffHeader*)(pe_signature + 1); + SY__U32 sections_offset = (dos->coff_file_offset + sizeof(*pe_signature) + + sizeof(*coff) + coff->optional_header_size); + SY__U32 section_count = coff->section_count; + struct SY__PE_SectionHeader* + section = (struct SY__PE_SectionHeader*)(base + sections_offset); + for (SY__U32 i = 0; i < section_count; i += 1, section += 1){ + SY__B32 match = 1; + for (SY__U32 j = 0; j < 8; j += 1){ + if (name[j] != section->name[j]){ + match = 0; + break; + } + if (name[j] == 0){ + break; + } + } + if (match){ + *first_out = base + section->voff; + *opl_out = base + section->voff + section->vsize; + break; + } + } + } + } + } +} + +#endif /* SY__OS_WINDOWS */ + +#endif //SY__MAIN_DEFINITIONS diff --git a/symbol_set/symbol_set.init.h b/symbol_set/symbol_set.init.h new file mode 100644 index 0000000..0126a7a --- /dev/null +++ b/symbol_set/symbol_set.init.h @@ -0,0 +1,40 @@ +#if !defined(SY__INIT_H) +#define SY__INIT_H + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~ SY Init Definitions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +typedef void SY_InitFunc(void*); +typedef struct SY_InitRecord{ + SY_InitFunc *func; +} SY_InitRecord; + +#define SYMBOL_SET_DEFINE SY_INIT +#define SY_INIT_Type SY_InitRecord +#define SY_INIT_section ".sy.init" +#define SY_INIT_marker init +#include "mr4th_symbol_set.define.h" + +#define SY_INIT_DEF(N,T) \ +static void syinit__##N(T*); \ +SyDefine(SY_INIT, N) = { syinit__##N }; \ +static void syinit__##N(T*ptr) + +void sy__run_init(void); + +#endif //SY__INIT_H + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~ Function Definitions ~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#if SY__MAIN && !defined(SY__INIT_DEFINITIONS) +#define SY__INIT_DEFINITIONS 1 + +void sy__run_init(void){ + for (SyEach(SY_INIT, init)){ + init->func(); + } +} + +#endif //SY__INIT_DEFINITIONS + diff --git a/todo.txt b/todo.txt index f67d371..8279f80 100644 --- a/todo.txt +++ b/todo.txt @@ -3,19 +3,18 @@ Example Writing: [ ] Think up better introductory examples [ ] Example of symbol sets with instances that don't need names -[ ] Example of more complex data initialization via the hook -[ ] Example of symbol set reuse across multiple programs - - Metaprogramming without needing input - -Research and Provide Practical Use Tips: +Practical Use Tips: [ ] See the symbol set in the debugger [ ] Best ideas for solving serialization systems problem -[ ] Shader management -[ ] IDs for profiler blocks [ ] How does this hold up when multiple DLLs or SOs are involved? +Real Uses: + +[ ] Command Line Options +[ ] Shader management +[ ] IDs for profiler blocks Vetting: @@ -31,40 +30,11 @@ OSs: Win32, Linux, Mac All combinations of CL, LINK, CLANG work so far so long as the above options are used. } - Linux{ [ ] GCC CLANG [ ] GCC GCC [ ] CLANG GCC [ ] CLANG CLANG } + Linux{ [ ] GCC CLANG [ ] GCC GCC [ ] CLANG GCC [x] CLANG CLANG } Mac { [ ] CLANG CLANG } [ ] Check configurations for link time optimization removal of symbols -[ ] Vet __ImageBase trick on Windows linkers - - - -Development: - -[x] experiment with __ImageBase pseudo variable (linker variable) -[ ] Small as possible binary parser for "selfimg" purposes - [x] PE - [ ] Elf - [ ] Macho - - -"Mark II": - -[ ] Object file editing for improved memory layout & symbol id references -[ ] Setup count and base pointer symbols -[ ] Learn how to put the symbol data into .data and relink everything -[ ] Setup id symbols & editing to give them values -[ ] Eliminate the "raw" version of symbols - -[ ] Do I want to maintain a pair of versions one with object file editing - and one without? - -Other Upgrade Research: - -[ ] Could Symbol Sets syntax be organized in such a way that it's just a data - section and type wrapped in a macro? - Support Tools: