aboutsummaryrefslogtreecommitdiff
path: root/src/ref_count.zig
blob: 2cc5c09fd300d3b98060c419d3605544d5e30d00 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
const std = @import("std");

pub fn RefCount(comptime TypeName: type, comptime deinit_on_zero: bool) type {
    return struct {
        const AllocatorType = if (deinit_on_zero) std.mem.Allocator else void;

        value: Type,
        count: i32 = 1,
        allocator: AllocatorType = undefined,

        pub inline fn ref(this: *@This()) void {
            this.count += 1;
        }

        /// Create a new reference counted value.
        pub inline fn init(
            value: Type,
            allocator: std.mem.Allocator,
        ) !*@This() {
            var ptr = try allocator.create(@This());
            ptr.create(value, allocator);
            return ptr;
        }

        /// Get the value & increment the reference count.
        pub inline fn get(this: *@This()) *Type {
            std.debug.assert(this.count >= 0);

            this.count += 1;
            return this.leak();
        }

        /// Get the value without incrementing the reference count.
        pub inline fn leak(this: *@This()) *Type {
            return &this.value;
        }

        pub inline fn getRef(this: *@This()) *@This() {
            this.count += 1;
            return this;
        }

        pub inline fn create(
            this: *@This(),
            value: Type,
            allocator: AllocatorType,
        ) void {
            this.* = .{
                .value = value,
                .allocator = allocator,
                .count = 1,
            };
        }

        pub inline fn deinit(this: *@This()) void {
            if (comptime @hasDecl(Type, "deinit")) {
                this.value.deinit();
            }

            if (comptime deinit_on_zero) {
                var allocator = this.allocator;
                allocator.destroy(this);
            }
        }

        pub inline fn deref(this: *@This()) void {
            this.count -= 1;

            std.debug.assert(this.count >= 0);

            if (comptime deinit_on_zero) {
                if (this.count <= 0) {
                    this.deinit();
                }
            }
        }

        pub const Type = TypeName;
    };
}