V1 file read done
This commit is contained in:
parent
42aed25b51
commit
76fbeff084
@ -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
|
||||
|
211
demo.str_cache.c
211
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 <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
|
||||
|
Loading…
x
Reference in New Issue
Block a user