Skip to content

Instantly share code, notes, and snippets.

@marvhus
Last active June 10, 2024 14:51
Show Gist options
  • Save marvhus/7ec8bacb3b0024becec39ab73516d24b to your computer and use it in GitHub Desktop.
Save marvhus/7ec8bacb3b0024becec39ab73516d24b to your computer and use it in GitHub Desktop.

Ref

A small Jai library for making it easy to share pointers/references to the same data, while making it easy to get rid of the pointer/reference when it is no longer needed.

How to use

Creating a Ref variable

You just create a variable using with the Ref(T) type, where you replace T with your type. For example Ref(int).

Initializing

Then you initialize it with init(*ref_variable) where ref_variable is your variable, and it will initialize the value to all 0s.

Getting the value

And you can now get a pointer to the inner value using get(ref_variable), so you can read it, or even set it using something like get(ref_variable).* = 10;.

Copying the reference

Aditionally, you can copy it without re-allocating anything by using the copy(ref_variable) function. So, you can do something like this other_ref := copy(ref_variable);.

Deinitializing

Then you can get rid of it using the deinit(*ref_variable) function, and if it's the last one, it will also dealocate the internal pointer it uses to syncronice all the references. Though you would have to free any other memory in the value, for example if you have a reference to a pointer, or something containing a pointer.
If you want to do this, you could look at the internal counter, like this: ref_variable.counted.count, and if it's 1 it will be deallocated on the next call to deinit if you don't copy it in the meantime.
So you can make a custom deinit function:

my_deinit :: (ref: *Ref(Foo)) {
    if ref.counted.count == 1 then deinit(get(ref));
    deinit(ref);
}

Last tested on

$ jai -version
Version: beta 0.1.091, built on 8 June 2024.
//
// Copyright (c) 2024 Martin Husby
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
// For inquiries, please contact: martin@marvhus.xyz
//
#module_parameters (TEST_REF_MODULE := false);
Ref :: struct (T: Type) {
Counted :: struct {
value: T;
count: int;
}
counted: *Counted;
}
// Initialize ref.
init :: (using ref: *Ref($T)) {
assert(counted == null, "Attemted to init a Ref that is already initialized.");
counted = alloc(size_of(Ref(T).Counted));
counted.count = 1;
value : T = ---;
counted.value = value;
}
// Deinitialize ref.
deinit :: (using ref: *Ref($T)) {
assert(counted != null, "Attempted to deinit an unititialized Ref.");
counted.count -= 1;
if counted.count < 1 then free(counted);
counted = null; // so we can't deinit the same ref several times.
}
// Copy another ref.
copy :: inline (using ref: Ref($T)) -> Ref(T) {
assert(counted != null, "Attempted to copy an uninitialized Ref.");
counted.count += 1;
return Ref(T).{counted};
}
// Get pointer to value.
get :: inline (using ref: Ref($T)) -> *T {
assert(counted != null, "Attempted to get value for uninitialized Red.");
return *counted.value;
}
#if TEST_REF_MODULE #run {
ref : Ref(int);
init(*ref);
assert(ref.counted != null);
assert(ref.counted.count == 1);
get(ref).* = 1;
assert(ref.counted.value == 1);
ref2 := copy(ref);
assert(ref.counted.count == 2);
loop := 8;
for 1..loop {
get(ref2).* += 1;
assert(ref.counted.value == 1 + it);
}
assert(ref.counted.value == 1 + loop);
deinit(*ref);
assert(ref.counted == null);
assert(ref2.counted.count == 1);
deinit(*ref2);
assert(ref2.counted == null);
}
#scope_module
#import "Basic";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment