From 76fbeff0841597d39d3672fa8c7e01ea234ee18b Mon Sep 17 00:00:00 2001 From: Ed_ Date: Sun, 4 May 2025 14:52:05 -0400 Subject: [PATCH] V1 file read done --- build.ps1 | 1 + demo.str_cache.c | 211 +++++++++++++++++++++++++++++++++++++---------- 2 files changed, 167 insertions(+), 45 deletions(-) diff --git a/build.ps1 b/build.ps1 index 3773f28..d8d7616 100644 --- a/build.ps1 +++ b/build.ps1 @@ -71,6 +71,7 @@ $compiler_args += $flag_nologo # Constraints on interpeting all files as C code $compiler_args += $flag_all_c +$compiler_args += $flag_c11 # Constraints on C program code-gen $compiler_args += $flag_exceptions_disabled $compiler_args += $flag_RTTI_disabled diff --git a/demo.str_cache.c b/demo.str_cache.c index c9c0efe..cbd0eab 100644 --- a/demo.str_cache.c +++ b/demo.str_cache.c @@ -1,13 +1,18 @@ /* A introduction to C11 with a str cache demo. Attempting to showcase better conventions and constructs in C; Discovered to me as of 2025 from scouring the internet. + +"C is old and flawed, but your use of it is most likely more flawed. You must have calouses to write with barbed syntax & semantics." */ /* The below will be implemented within this single file. Because of this, definitions will be kept on a need-to-have basis to target only one vendor target and toolchain. We will not use nearly any libraries and will be targeting only Windows 11 x64 using MSVC. + Even so the constructs defined and their dependencies can be properly abstracted into a ergonomic library for multiple targets with enough time and pain. +The difference is just more preprocess conditionals, and how far a library is trying to support a larger range of targets and their age discrpancy. +The more minimal the less cruft. Definitions are defined linearly on the file on-demand as needed. Since the file is to be read linearly. This will cause non-categorical organization so it will be more difficult to sift through if you wanted @@ -66,7 +71,7 @@ So we'll setup the the minimum for that when dealing with immutable constructs. #include #include -#include +// #include typedef unsigned __int8 U8; typedef signed __int8 S8; @@ -80,6 +85,17 @@ typedef signed __int64 S64; typedef size_t USIZE; typedef ptrdiff_t SSIZE; + +enum { + false, + true, + true_overflow, +}; + +typedef S8 B8; +typedef S16 B16; +typedef S32 B32; + // Functional style cast #define cast(type, data) ((type)(data)) @@ -101,7 +117,7 @@ struct Str8 { }; // String iterals in C include null-terminators, we aren't interested in preserving that. -#define lit(string_literal) (Str8){ string_literal, size_of(string_literal) - 1 }; +#define lit(string_literal) (Str8){ string_literal, size_of(string_literal) - 1 } // For now this string can visualized using a debugger. // #define DEMO__STR_SLICE @@ -132,17 +148,7 @@ typedef struct Opts__read_file_contents Opts__read_file_contents; void api_file_read_contents(FileOpResult* result, Str8 path, Opts__read_file_contents* opts); FileOpResult file__read_contents ( Str8 path, Opts__read_file_contents* opts); -#define file_read_contents(path, opts) file__read_contents(path, & (Opts__read_file_contents){0} ) - -/* -The above is a pattern that can be provided so that whether or not the result is formatted and provided to the user via the stack is entirely optional. -It also allows for default parameters to be defined conviently. -*/ - -// Now for our "Version 1" - -#define DEMO__FILE_READ_CONTENTS_V1 -#ifdef DEMO__FILE_READ_CONTENTS_V1 +#define file_read_contents(path, ...) file__read_contents(path, & (Opts__read_file_contents){__VA_ARGS__} ) /* The file contents will be returned in bytes. @@ -163,20 +169,34 @@ struct SliceMem { SSIZE len; }; -struct FileOpResult -{ - // For now we'll just have the content - SliceByte content; -}; - -struct Opts__read_file_contents -{ - // For now we'll just have the backing memory provided as a slice. - SliceMem backing; -}; +/* +The above is a pattern that can be provided so that whether or not the result is formatted and provided to the user via the stack is entirely optional. +It also allows for default parameters to be defined conviently. +*/ // We'll utilize the ReadFile procedure within the WinAPI: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile -#include "fileapi.h" +#define NOMINMAX +#define WIN32_LEAN_AND_MEAN +#define WIN32_MEAN_AND_LEAN +#define VC_EXTRALEAN +#include +#include +#include +#include +#include +#include +#pragma comment(lib, "user32") +#pragma comment(lib, "winmm") +#pragma comment(lib, "shell32") +#pragma comment(lib, "advapi32") +#pragma comment(lib, "rpcrt4") +#pragma comment(lib, "shlwapi") +#pragma comment(lib, "comctl32") +#pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") // this is required for loading correct comctl32 dll file +#undef NOMINMAX +#undef WIN32_LEAN_AND_MEAN +#undef WIN32_MEAN_AND_LEAN +#undef VC_EXTRALEAN #if 0 BOOL ReadFile( [in] HANDLE hFile, @@ -213,51 +233,142 @@ typedef U8 FMem_KB [ ]; */ typedef U8 FMem_16KB [ KILOBTYES(16) ]; +typedef U8 FMem_64KB [ KILOBTYES(64) ]; #define typeof __typeof__ #define fmem_slice(mem) (SliceMem) { mem, size_of(mem) } // We'll be using an intrinsic for copying memory: -void* memory_copy(void* dest, const void* src, size_t count) +void* memory_copy(void* dest, void const* src, SSIZE length) { - if (dest == NULL || src == NULL || count == 0) { - return NULL; + if (dest == nullptr || src == nullptr || length == 0) { + return nullptr; } - - __movsb((unsigned char*)dest, (const unsigned char*)src, count); + // https://learn.microsoft.com/en-us/cpp/intrinsics/movsb?view=msvc-170 + __movsb((unsigned char*)dest, (const unsigned char*)src, length); return dest; } +// Often we'll want to check validity of a slice: +#define slice_assert(slice) do { \ + assert(slice.ptr != nullptr); \ + assert(slice.len > 0); \ +} while(0) + +void slice_copy(SliceMem dest, SliceMem src) { + assert(dest.len >= src.len); + slice_assert(dest); + slice_assert(src); + memory_copy(dest.ptr, src.ptr, src.len); +} + // Assumes memory is zeroed. -char const* str8_to_cstr_capped(Str8 content, SliceMem mem) -{ +char const* str8_to_cstr_capped(Str8 content, SliceMem mem) { assert(mem.len >= content.len); - memory_copy(mem.ptr, content.ptr, content.ptr); + memory_copy(mem.ptr, content.ptr, content.len); return mem.ptr; } +// To support zeroing slices we'll utilize an intrinisc. +B32 memory_zero(void* dest, SSIZE length) { + if (dest == nullptr || length <= 0) { + return false; + } + __stosd((unsigned long*)dest, 0, length); + return true; +} + +void slice_zero(SliceMem mem) { + slice_assert(mem); + memory_zero(mem.ptr, mem.len); +} + +// Now for our "Version 1" + +#define DEMO__FILE_READ_CONTENTS_V1 +#ifdef DEMO__FILE_READ_CONTENTS_V1 + +struct FileOpResult +{ + // For now we'll just have the content + SliceByte content; +}; + +struct Opts__read_file_contents +{ + // For now we'll just have the backing memory provided as a slice. + SliceMem backing; + // And whether we should zero the backing. + B32 zero_backing; +}; + void api_file_read_contents(FileOpResult* result, Str8 path, Opts__read_file_contents* opts) { + assert(result != nullptr); + assert(opts != nullptr); + slice_assert(path); + // Backing is required at this point + slice_assert(opts->backing); + FMem_16KB scratch = {0}; char const* path_cstr = str8_to_cstr_capped(path, fmem_slice(scratch) ); HANDLE id_file = CreateFileA( path_cstr, - + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + B32 open_failed = id_file == INVALID_HANDLE_VALUE; + if (open_failed) { + DWORD error_code = GetLastError(); + assert(error_code != 0); + return; + } - // BOOL op_result = ReadFile( - // id_file, - // buffer, - // to_read, - // read_amount, - // nullptr - // ); + LARGE_INTEGER file_size = {0}; + DWORD get_size_failed = ! GetFileSizeEx(id_file, & file_size); + if (get_size_failed) { + assert(get_size_failed == INVALID_FILE_SIZE); + return; + } - return - /* - TODO(Ed): You are here - */ + // Because we are currently using fixed size memory, we need to confirm that we can hold this content. + B32 not_enough_backing = opts->backing.len < file_size.QuadPart; + if (not_enough_backing) { + assert(not_enough_backing); + // Otherwise we don't provide a result. + result->content = (SliceByte){0}; + return; + } + + if (opts->zero_backing) { + slice_zero(opts->backing); + } + + DWORD amount_read = 0; + BOOL read_result = ReadFile( + id_file, + opts->backing.ptr, + file_size.QuadPart, + & amount_read, + nullptr + ); + CloseHandle(id_file); + + B32 read_failed = ! read_result; + read_failed |= amount_read != file_size.QuadPart; + if (read_failed) { + assert(read_failed); + return; + } + + result->content.ptr = opts->backing.ptr; + result->content.len = file_size.QuadPart; + return; } #endif DEMO__FILE_READ_CONTENTS_V1 @@ -269,3 +380,13 @@ FileOpResult file__read_contents(Str8 path, Opts__read_file_contents* opts) { api_file_read_contents(& result, path, opts); return result; } + +// And now to put it all together into a test run in the debugger. Content should be properly formatted if the code is correct. +#ifdef DEMO__FILE_READ_CONTENTS_V1 +int main() +{ + FMem_64KB read_mem = {0}; + FileOpResult res = file_read_contents(lit("demo.str_cache.c"), .backing = fmem_slice(read_mem) ); + return 0; +} +#endif