V1 file read done

This commit is contained in:
Edward R. Gonzalez 2025-05-04 14:52:05 -04:00
parent 42aed25b51
commit 76fbeff084
2 changed files with 167 additions and 45 deletions

View File

@ -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

View File

@ -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 <wmmintrin.h>
#include <assert.h>
#include <stdbool.h>
// #include <stdbool.h>
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 <windows.h>
#include <windowsx.h>
#include <timeapi.h>
#include <tlhelp32.h>
#include <Shlobj.h>
#include <processthreadsapi.h>
#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_<size>KB [ <U8 amount> ];
*/
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