From d7c39e076a46ad2227b162a632ea644081636313 Mon Sep 17 00:00:00 2001 From: Lyssieth Date: Sat, 7 Dec 2024 04:19:02 +0200 Subject: [PATCH] Rename TrackedString -> SmartString --- src/util/smartString.zig | 142 +++++++++++++++++++++++++++++++++++++ src/util/trackedString.zig | 81 --------------------- src/util/utils.zig | 4 +- 3 files changed, 144 insertions(+), 83 deletions(-) create mode 100644 src/util/smartString.zig delete mode 100644 src/util/trackedString.zig diff --git a/src/util/smartString.zig b/src/util/smartString.zig new file mode 100644 index 0000000..f28c4dc --- /dev/null +++ b/src/util/smartString.zig @@ -0,0 +1,142 @@ +const std = @import("std"); + +pub const SmartString = struct { + data: []const u8, + kind: AllocKind, + + pub fn alloc(value: []const u8, allocator: std.mem.Allocator) !SmartString { + return .{ + .data = try allocator.dupe(u8, value), + .kind = .{ .Allocated = allocator }, + }; + } + + pub fn constant(comptime value: []const u8) SmartString { + return .{ + .data = value, + .kind = .{ .Constant = {} }, + }; + } + + /// See also: `cloneAlloc` + pub fn clone(self: SmartString) !SmartString { + if (!self.kind.isAllocated()) + return error.NotAllocated; // should use `cloneAlloc` instead + + return try SmartString.alloc(self.data, self.kind.Allocated); + } + + /// Clones the string to a new allocator. + pub fn cloneAlloc(self: SmartString, allocator: std.mem.Allocator) !SmartString { + return try SmartString.alloc(self.data, allocator); + } + + pub fn deinit(self: *SmartString) void { + switch (self.*.kind) { + .Constant => { + self.*.data = undefined; + self.*.kind = .{ .Dead = {} }; + }, + + .Allocated => |allocator| { + allocator.free(self.data); + self.*.data = undefined; + self.*.kind = .{ .Dead = {} }; + }, + + .Dead => { + if (std.debug.runtime_safety) { + std.debug.panic("Double free of SmartString", .{}); + } + }, + } + } +}; + +pub const AllocKind = union(enum) { + Constant: void, + Allocated: std.mem.Allocator, + Dead: void, + + inline fn isDead(self: AllocKind) bool { + return switch (self) { + .Dead => true, + else => false, + }; + } + + inline fn isAllocated(self: AllocKind) bool { + return switch (self) { + .Allocated => true, + else => false, + }; + } + + inline fn isConstant(self: AllocKind) bool { + return switch (self) { + .Constant => true, + else => false, + }; + } + + fn eql(self: AllocKind, other: AllocKind) bool { + if (@as(std.meta.Tag(AllocKind), self) != @as(std.meta.Tag(AllocKind), other)) + return false; + + return switch (self) { + .Allocated => |a| a.ptr == other.Allocated.ptr, + else => true, + }; + } +}; + +const t = std.testing; + +test "the different kinds work" { + const a = t.allocator; + + var strOne = try SmartString.alloc("hello, world", a); + defer strOne.deinit(); + + try t.expectEqualStrings("hello, world", strOne.data); + try t.expectEqual(AllocKind{ .Allocated = a }, strOne.kind); + + var strTwo = SmartString.constant("hello, world"); + defer strTwo.deinit(); + + try t.expectEqualStrings("hello, world", strTwo.data); + try t.expectEqual(AllocKind{ .Constant = {} }, strTwo.kind); + + try t.expectEqualStrings(strOne.data, strTwo.data); + try t.expect(!strOne.kind.eql(strTwo.kind)); +} + +test "allocKind eql works" { + const a = AllocKind{ .Dead = {} }; + const b = AllocKind{ .Constant = {} }; + + try t.expect(!a.eql(b)); + try t.expect(!b.eql(a)); + + const c = AllocKind{ .Allocated = t.allocator }; + const d = AllocKind{ .Allocated = t.allocator }; + + try t.expect(c.eql(d)); + try t.expect(!d.eql(a)); + try t.expect(!d.eql(b)); +} + +test "clone works" { + const a = t.allocator; + + var strOne = try SmartString.alloc("hello, world", a); + defer strOne.deinit(); + + var strTwo = try strOne.clone(); + defer strTwo.deinit(); + + try t.expectEqualStrings("hello, world", strOne.data); + try t.expectEqualStrings("hello, world", strTwo.data); + + try t.expect(strOne.kind.eql(strTwo.kind)); +} diff --git a/src/util/trackedString.zig b/src/util/trackedString.zig deleted file mode 100644 index 132ceae..0000000 --- a/src/util/trackedString.zig +++ /dev/null @@ -1,81 +0,0 @@ -const std = @import("std"); - -pub const TrackedString = struct { - data: []const u8, - kind: AllocKind, - - pub fn alloc(value: []const u8, allocator: std.mem.Allocator) !TrackedString { - return .{ - .data = try allocator.dupe(u8, value), - .kind = .{ .Allocated = allocator }, - }; - } - - pub fn constant(comptime value: []const u8) TrackedString { - return .{ - .data = value, - .kind = .{ .Constant = {} }, - }; - } - - pub fn deinit(self: *TrackedString) void { - switch (self.*.kind) { - .Constant => { - self.*.data = undefined; - self.*.kind = .{ .Dead = {} }; - }, - - .Allocated => |allocator| { - allocator.free(self.data); - self.*.data = undefined; - self.*.kind = .{ .Dead = {} }; - }, - - .Dead => {}, - } - } -}; - -pub const AllocKind = union(enum) { - Constant: void, - Allocated: std.mem.Allocator, - Dead: void, - - fn eql(self: AllocKind, other: AllocKind) bool { - return switch (self) { - .Constant => switch (other) { - .Constant => true, - else => false, - }, - .Allocated => |a| switch (other) { - .Allocated => |b| std.meta.eql(a, b), - else => false, - }, - .Dead => switch (other) { - .Dead => true, - else => false, - }, - }; - } -}; - -const t = std.testing; - -test "the different kinds work" { - const a = t.allocator; - - var strOne = try TrackedString.alloc("hello, world", a); - defer strOne.deinit(); - - try t.expectEqualStrings("hello, world", strOne.data); - try t.expectEqual(AllocKind{ .Allocated = a }, strOne.kind); - - var strTwo = TrackedString.constant("hello, world"); - defer strTwo.deinit(); - - try t.expectEqualStrings("hello, world", strTwo.data); - try t.expectEqual(AllocKind{ .Constant = {} }, strTwo.kind); - - try t.expectEqualStrings(strOne.data, strTwo.data); - try t.expect(!strOne.kind.eql(strTwo.kind)); -} diff --git a/src/util/utils.zig b/src/util/utils.zig index 383a77e..af79677 100644 --- a/src/util/utils.zig +++ b/src/util/utils.zig @@ -1,5 +1,5 @@ -const str = @import("./trackedString.zig"); -pub const TrackedString = str.TrackedString; +const str = @import("./smartString.zig"); +pub const TrackedString = str.SmartString; comptime { const std = @import("std");