_ _ _ _ _ _ _
_( )__ _( )__ _( )__ _( )__ _( )__ _( )__ _( )__
_| _| _| _| _| _| _| _|
(_ _ (_ _ (_ _ (_ _ (_ _ (_ _ (_ _ (_
|__( )_|__( )_|__( )_|__( )_|__( )_|__( )_|__( )_|
|_ |_ |_ |_ |_ |_ |_ |_
_) _ _) _ _) _ _) _ _) _ _) _ _) _ _)
|__( )_|__( )_|__( )_|__( )_|__( )_|__( )_|__( )_|
_| _| _| _| _| _| _| _|
(_ _ (_ _ (_ _ (_ _ (_ _ (_ _ (_ _ (_
|__( )_|__( )_|__( )_|__( )_|__( )_|__( )_|__( )_|
|_ |_ |_ |_ |_ |_ |_ |_
_) _ _) _ _) _ _) _ _) _ _) _ _) _mx _)
|__( )_|__( )_|__( )_|__( )_|__( )_|__( )_|__( )_|
My university dissertation, Preventing Software Vulnerabilities: Detecting Uninitialised Memory Access in C/C++, as the title suggests was a journey into how the system-level programming languages C and C++ (but mostly C however) allocate memory on the heap and how this can lead to many unwanted issues like Out of Bounds read/writes, free-after-use and leaking memory when not freeing pointers with free().
The dissertation taught me a lot and the biggest take away from it, as I wrote in my conclusion is:
I’ve come to see how C, despite its power and performance, is beginning to show its age. At over 50 years old, its design reflects the needs of a very different era in computing. One of the biggest challenges I faced was dealing with memory safety—something C handles poorly by design.
Further mentioning:
I’ve seen how more modern languages are tackling these issues head-on. C++17’s unique_ptr and std::allocator provide safer ways to manage memory, and languages like Rust, Odin, and Zig go even further. Rust’s borrow checker, Odin’s context-based allocator, and Zig’s built-in safety tools all offer features I found myself wishing C had.
Yeah, I am not sure what I was on when I wrote that (that is a lie, I know exactly what it was: sugar-free Monsters, green tea and Kinder Happy Hippos), wanting these specific features are not easy to implement and back-port to all versions of C. This has been tried and tested in the past to some certain varying degrees of success and is in-fact what the creator of Odin, gingerBill had tried a couple of years ago...
This friction is fundamental to the creation of Odin and is something I felt myself yearning for. Since then, to put it shortly, I have fallen in love with Odin. I may just end up writing more about the features (or lack thereof) of this language in another write up.
Taken from Odin's site:
The main purpose of the implicit context system is for the ability to intercept third-party code and libraries and modify their functionality.
The context is essentially a struct that is passed implicitly by a pointer into every scope. This can be found in base/runtime/core.odin.
Context :: struct {
allocator: Allocator,
temp_allocator: Allocator,
assertion_failure_proc: Assertion_Failure_Proc,
logger: Logger,
random_generator: Random_Generator,
user_ptr: rawptr,
user_index: int,
// Internal use only
_internal: rawptr,
}
Within every scope, this local variable can be accessed. These fields are all used to intercept third-party code (code you did not write). Most commonly accessed fields however are the allocator and temp_allocator fields.
Most often use case of the allocator field is to be able to pass a specific type of allocation (where that be a arena, ring buffer et al.) to a procedure that allocates memory in a dynamic way. Good API design is to use allocator := context.allocator as the last field in a procedure definition like below:
generate_blog_md_file :: proc(path: string, allocator := context.allocator) {
blog_list := make([dynamic]string)
sb := strings.builder_make(allocator)
blog_md_file := strings.builder_make(allocator)
...
}
As you can see, I explicitly pass allocator to procedures such as strings.builder_make() as these are dynamically allocate memory.
Note: This isn't necessary, the procedures themselves also contain the same definition:
builder_make_len :: proc(len: int, allocator := context.allocator, loc := #caller_location) -> (res: Builder, err: runtime.Allocator_Error) #optional_allocator_error {
return Builder{buf=make([dynamic]byte, len, allocator, loc) or_return }, nil
}
Doing so allows us to use a custom allocator when we call the procedure in the desired location like so:
generate_blog_md_file(directory, arena_alloc)
Where arena_alloc is a custom arena allocator. Now, all dynamic allocations are made within an arena I defined earlier in the program. How cool!
Now, I could have also just used temp_allocator as a argument to the procedure. By default, this temporary allocator is an growing arena anyway! Or even better, just completely ignored the last argument entirely and let context.allocator be passed implicitly!
generate_blog_md_file(directory)
You can also use context within C code. Of course Odin doesn't have direct access to C code like Zig does - but it does have bindings! Within your own bindings, you can set context to runtime.default_context() to allow functions within your code (such as fmt.printfln, which needs a context parameter) to use the context system. Using the default will override any custom features you have tacked onto the context struct - if you want to use, say a custom number generator or a fancy tracking allocator, you can just use context = custom_context like in this example taken from Karl Zylinski's book:
custom_context: runtime.Context
my_callback :: proc "c" (ts: my_lib.TestStruct) {
context = custom_context
fmt.println("In the callback")
fmt.println(ts)
}
main :: proc() {
custom_context = context
my_lib.set_callback(my_callback)
ts := my_lib.TestStruct {
num = 7,
flt_num = 23.12,
}
my_lib.do_stuff(ts)
}
This field, like all the others in fact, is overridable and allows you to insert a custom assertion failure for third-party code. Incredibly useful if you again have a custom way of handling assert() errors e.g. stack traces. Not something I have come across or used as of yet, but the ability to have it straight away with no confusing syntax is lovely!
Another overridable variable that allows you to pass better random number generator. Incredibly useful if you want to avoid C's rand() and srand() functions, which are dreadful!
Within the latest master branch of Zig, we have seen the introduction of a juicy main! This is done by passing a struct defined in std.process (documentation can be found here) as one of the parameters in the main function.
const std = @import("std");
pub fn main(init: std.process.Init) !void {
try std.Io.File.stdout().writeStreamingAll(init.io, "Hello, World!\n");
}
Taken from here, we can see that there is two versions of a juicy main:
And within the Init struct, there is the Allocator field! This can be used to setup allocators and other such useful goodies. This implementation is resplendent in its features. Most of my Zig programs main() functions is usually just a bunch of set ups, plumbing and buffers - this feature is welcomed!
When passing an allocator in your program, Karl Zylinski mentions in his Odin book (buy it here!):
Having an allocator parameter on a procedure that allocates memory is good documentation... It gives the programmer a hint that the return value will be allocated.
And that's how I view the allocator parameter as well! I enjoy languages who are explicit (be that the type system, return values or the allocators) in their design and Odin pulls this off perfectly! Funnily enough, when writing my dissertation, it was an idea floating in the back of my head as a solution to tracking memory in C programs - an abstract concept which was passed around that had tracking properties attached to it. This idea didn't really take off as much as my other implementation.
I think that was because of C's fundamental language design. Building off C isn't enough these days and we must take the good with the so-called bad with C in the future when developing newer languages that are "C-killers". It is important to note that Odin is not advertised as a "C-killer", but the C alternative for the Joy of Programming.
Odin has many other fantastic features I may end up writing more about in the future. I thoroughly enjoy writing anything and everything in this wonderful language - with it now giving me motivation than ever to pick up more hobbyist and recreational programming! This is also my first proper blog on something I find interesting! I wish I had more time and energy in my days to write more about these topics, but the slow release allows me to dig deep into a topic and write about it informally.
Thank you :)
Note: No AI was used in the creation of this blog and/or site, I'd like to stay away from slop. All written by me!