Skip to content

Instantly share code, notes, and snippets.

@denisdefreyne
Last active November 4, 2025 19:48
Show Gist options
  • Select an option

  • Save denisdefreyne/9f954ca824925dd8f0fefdfad48d12ab to your computer and use it in GitHub Desktop.

Select an option

Save denisdefreyne/9f954ca824925dd8f0fefdfad48d12ab to your computer and use it in GitHub Desktop.
const std = @import("std");
const Self = @This();
ref_count: u8 = 1,
allocator: std.mem.Allocator,
content: union(enum) {
owned: struct {
data: []const u8,
},
owned_z: struct {
data: [:0]const u8,
},
constant_slice: []const u8,
reference_slice: struct {
data: []const u8,
referenced: *Self,
},
},
pub fn newFromOwned(allocator: std.mem.Allocator, data: []const u8) error{OutOfMemory}!*Self {
const self = try allocator.create(Self);
self.* = .{
.allocator = allocator,
.content = .{ .owned = .{
.data = data,
} },
};
return self;
}
pub fn newFromOwnedZ(allocator: std.mem.Allocator, data: [:0]const u8) error{OutOfMemory}!*Self {
const self = try allocator.create(Self);
self.* = .{
.allocator = allocator,
.content = .{ .owned_z = .{
.data = data,
} },
};
return self;
}
pub fn newFromConstantSlice(allocator: std.mem.Allocator, data: []const u8) error{OutOfMemory}!*Self {
const self = try allocator.create(Self);
self.* = .{
.allocator = allocator,
.content = .{ .constant_slice = data },
};
return self;
}
pub fn newFromReferenceSlice(allocator: std.mem.Allocator, data: []const u8, referenced: *Self) error{OutOfMemory}!*Self {
const self = try allocator.create(Self);
referenced.retain();
self.* = .{
.allocator = allocator,
.content = .{ .reference_slice = .{
.data = data,
.referenced = referenced,
} },
};
return self;
}
test "alloc + init + retain + release - owned" {
const wrapped = try std.testing.allocator.dupe(u8, "hi");
const s = try Self.newFromOwned(std.testing.allocator, wrapped);
defer s.release();
try std.testing.expectEqualStrings("hi", s.getSlice());
}
test "alloc + init + retain + release - owned with sentinel" {
const wrapped = try std.testing.allocator.dupeZ(u8, "hi");
const s = try Self.newFromOwnedZ(std.testing.allocator, wrapped);
defer s.release();
try std.testing.expectEqualStrings("hi", s.getSlice());
}
test "alloc + init + retain + release - constant slice" {
const s = try Self.newFromConstantSlice(std.testing.allocator, "hi");
defer s.release();
try std.testing.expectEqualStrings("hi", s.getSlice());
}
test "alloc + init + retain + release - reference slice" {
const wrapped = try std.testing.allocator.dupe(u8, "helloworld");
const s1 = try Self.newFromOwned(std.testing.allocator, wrapped);
defer s1.release();
const s2 = try Self.newFromReferenceSlice(std.testing.allocator, s1.getSlice()[4..7], s1);
defer s2.release();
try std.testing.expectEqualStrings("owo", s2.getSlice());
}
pub fn getSlice(self: *const Self) []const u8 {
return switch (self.content) {
.owned => |s| s.data,
.owned_z => |s| s.data,
.constant_slice => |s| s,
.reference_slice => |s| s.data,
};
}
pub fn retain(self: *Self) void {
self.ref_count += 1;
}
pub fn release(self: *Self) void {
self.ref_count -= 1;
if (self.ref_count == 0) {
self.destroy(self.allocator);
}
}
fn destroy(self: *Self, allocator: std.mem.Allocator) void {
// dealloc content
switch (self.content) {
.owned => |s| {
self.allocator.free(s.data);
},
.owned_z => |s| {
self.allocator.free(s.data);
},
.constant_slice => {
// do nothing
},
.reference_slice => |s| {
s.referenced.release();
},
}
// dealloc self
allocator.destroy(self);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment