aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/jv/value.zig
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-01-31 15:31:15 +0900
committernsfisis <nsfisis@gmail.com>2026-01-31 15:47:23 +0900
commit617ddc62aa4d3153850362526069b85bfaf5e59e (patch)
tree6940c577bbecdbf7856ef3c37ab43b256d33da59 /src/jv/value.zig
parent6be43138338fbe4623c1cd62cf71138873af3a7a (diff)
downloadzgjq-617ddc62aa4d3153850362526069b85bfaf5e59e.tar.gz
zgjq-617ddc62aa4d3153850362526069b85bfaf5e59e.tar.zst
zgjq-617ddc62aa4d3153850362526069b85bfaf5e59e.zip
use reference counting to manage JSON value lifetime
Diffstat (limited to 'src/jv/value.zig')
-rw-r--r--src/jv/value.zig229
1 files changed, 139 insertions, 90 deletions
diff --git a/src/jv/value.zig b/src/jv/value.zig
index ffdec44..eb36c99 100644
--- a/src/jv/value.zig
+++ b/src/jv/value.zig
@@ -1,159 +1,208 @@
const std = @import("std");
-const Allocator = std.mem.Allocator;
+const Rc = @import("./rc.zig").Rc;
-pub const Value = struct {
- const _Internal = std.json.Value;
- _internal: _Internal,
-
- pub const Kind = enum { null, bool, integer, float, string, array, object, number_string };
+pub const ValueKind = enum {
+ null,
+ bool,
+ integer,
+ float,
+ string,
+ array,
+ object,
+};
- pub fn kind(self: Value) Kind {
- return switch (self._internal) {
- .null => .null,
- .bool => .bool,
- .integer => .integer,
- .float => .float,
- .string => .string,
- .array => .array,
- .object => .object,
- .number_string => .number_string,
- };
- }
+pub const Value = union(ValueKind) {
+ null: void,
+ bool: bool,
+ integer: i64,
+ float: f64,
+ string: []const u8,
+ array: Array,
+ object: Object,
- pub const @"null": Value = .{ ._internal = .null };
- pub const @"true": Value = .{ ._internal = .{ .bool = true } };
- pub const @"false": Value = .{ ._internal = .{ .bool = false } };
+ pub const @"true": Value = .{ .bool = true };
+ pub const @"false": Value = .{ .bool = false };
pub fn initBool(b: bool) Value {
- return .{ ._internal = .{ .bool = b } };
+ return .{ .bool = b };
}
pub fn initInteger(i: i64) Value {
- return .{ ._internal = .{ .integer = i } };
+ return .{ .integer = i };
}
pub fn initFloat(f: f64) Value {
- return .{ ._internal = .{ .float = f } };
+ return .{ .float = f };
}
pub fn initString(s: []const u8) Value {
- return .{ ._internal = .{ .string = s } };
+ return .{ .string = s };
}
pub fn initArray(arr: Array) Value {
- return .{ ._internal = .{ .array = arr._internal } };
+ return .{ .array = arr };
}
pub fn initObject(obj: Object) Value {
- return .{ ._internal = .{ .object = obj._internal } };
+ return .{ .object = obj };
+ }
+
+ pub fn kind(self: Value) ValueKind {
+ return self;
+ }
+
+ pub fn clone(self: Value) Value {
+ return switch (self) {
+ .array => |a| .{ .array = a.clone() },
+ .object => |o| .{ .object = o.clone() },
+ else => self,
+ };
}
- pub fn deinit(self: *Value, allocator: Allocator) void {
- switch (self._internal) {
- .string => |s| allocator.free(s),
- .array => |*a| a.deinit(),
- .object => |*o| o.deinit(),
+ pub fn deinit(self: Value, allocator: std.mem.Allocator) void {
+ switch (self) {
+ .array => |a| a.deinit(allocator),
+ .object => |o| o.deinit(allocator),
else => {},
}
}
- pub fn boolean(self: Value) bool {
- return self._internal.bool;
+ pub fn arrayAppend(self: *Value, allocator: std.mem.Allocator, item: Value) !void {
+ try self.array.append(allocator, item);
}
- pub fn integer(self: Value) i64 {
- return self._internal.integer;
+ pub fn jsonStringify(self: Value, jws: anytype) !void {
+ switch (self) {
+ .null => try jws.write(null),
+ .bool => |b| try jws.write(b),
+ .integer => |i| try jws.write(i),
+ .float => |f| try jws.write(f),
+ .string => |s| try jws.write(s),
+ .array => |a| {
+ try jws.beginArray();
+ for (0..a.len()) |i| {
+ try a.get(i).jsonStringify(jws);
+ }
+ try jws.endArray();
+ },
+ .object => |o| {
+ try jws.beginObject();
+ var it = o.iterator();
+ while (it.next()) |entry| {
+ try jws.objectField(entry.key_ptr.*);
+ try entry.value_ptr.jsonStringify(jws);
+ }
+ try jws.endObject();
+ },
+ }
}
+};
- pub fn float(self: Value) f64 {
- return self._internal.float;
- }
+pub const Array = struct {
+ const Inner = std.ArrayList(Value);
+
+ rc: Rc(Inner),
- pub fn string(self: Value) []const u8 {
- return self._internal.string;
+ pub fn init(allocator: std.mem.Allocator) !Array {
+ return .{ .rc = try Rc(Inner).init(allocator, .{}) };
}
- pub fn array(self: Value) Array {
- return .{ ._internal = self._internal.array };
+ pub fn clone(self: Array) Array {
+ return .{ .rc = self.rc.retain() };
}
- pub fn object(self: Value) Object {
- return .{ ._internal = self._internal.object };
+ pub fn deinit(self: Array, allocator: std.mem.Allocator) void {
+ if (self.rc.isUnique()) {
+ for (self.rc.get().items) |item| {
+ item.deinit(allocator);
+ }
+ self.rc.get().deinit(allocator);
+ }
+ self.rc.release(allocator);
}
- pub fn arrayGet(self: Value, idx: usize) Value {
- const items = self._internal.array.items;
+ pub fn get(self: Array, idx: usize) Value {
+ const items = self.rc.get().items;
if (idx < items.len) {
- return .{ ._internal = items[idx] };
+ return items[idx];
}
return Value.null;
}
- pub fn arrayLen(self: Value) usize {
- return self._internal.array.items.len;
+ pub fn len(self: Array) usize {
+ return self.rc.get().items.len;
}
- pub fn arrayAppend(self: *Value, item: Value) !void {
- try self._internal.array.append(item._internal);
+ pub fn append(self: *Array, allocator: std.mem.Allocator, value: Value) !void {
+ try self.ensureUnique(allocator);
+ try self.rc.get().append(allocator, value);
}
- pub fn objectGet(self: Value, key: []const u8) ?Value {
- if (self._internal.object.get(key)) |v| {
- return .{ ._internal = v };
+ fn ensureUnique(self: *Array, allocator: std.mem.Allocator) !void {
+ if (!self.rc.isUnique()) {
+ const old_items = self.rc.get().items;
+ var new_inner: Inner = .{};
+ try new_inner.ensureTotalCapacity(allocator, old_items.len);
+ for (old_items) |item| {
+ new_inner.appendAssumeCapacity(item.clone());
+ }
+ self.rc.release(allocator);
+ self.rc = try Rc(Inner).init(allocator, new_inner);
}
- return null;
- }
-
- pub fn objectSet(self: *Value, key: []const u8, val: Value) !void {
- try self._internal.object.put(key, val._internal);
}
};
-pub const Array = struct {
- const _Internal = std.json.Array;
- _internal: _Internal,
+pub const Object = struct {
+ const Inner = std.StringArrayHashMapUnmanaged(Value);
- pub fn init(allocator: Allocator) Array {
- return .{ ._internal = _Internal.init(allocator) };
- }
+ rc: Rc(Inner),
- pub fn deinit(self: *Array) void {
- self._internal.deinit();
+ pub fn init(allocator: std.mem.Allocator) !Object {
+ return .{ .rc = try Rc(Inner).init(allocator, .{}) };
}
- pub fn get(self: Array, idx: usize) Value {
- return .{ ._internal = self._internal.items[idx] };
+ pub fn clone(self: Object) Object {
+ return .{ .rc = self.rc.retain() };
}
- pub fn len(self: Array) usize {
- return self._internal.items.len;
+ pub fn deinit(self: Object, allocator: std.mem.Allocator) void {
+ if (self.rc.isUnique()) {
+ for (self.rc.get().values()) |v| {
+ v.deinit(allocator);
+ }
+ self.rc.get().deinit(allocator);
+ }
+ self.rc.release(allocator);
}
- pub fn append(self: *Array, value: Value) !void {
- try self._internal.append(value._internal);
+ pub fn get(self: Object, key: []const u8) ?Value {
+ return self.rc.get().get(key);
}
-};
-
-pub const Object = struct {
- const _Internal = std.json.ObjectMap;
- _internal: _Internal,
- pub fn init(allocator: Allocator) Object {
- return .{ ._internal = _Internal.init(allocator) };
+ pub fn set(self: *Object, allocator: std.mem.Allocator, key: []const u8, value: Value) !void {
+ try self.ensureUnique(allocator);
+ try self.rc.get().put(allocator, key, value);
}
- pub fn deinit(self: *Object) void {
- self._internal.deinit();
+ pub fn count(self: Object) usize {
+ return self.rc.get().count();
}
- pub fn get(self: Object, key: []const u8) ?Value {
- if (self._internal.get(key)) |v| {
- return .{ ._internal = v };
- }
- return null;
+ pub fn iterator(self: Object) Inner.Iterator {
+ return self.rc.get().iterator();
}
- pub fn set(self: *Object, key: []const u8, value: Value) !void {
- try self._internal.put(key, value._internal);
+ fn ensureUnique(self: *Object, allocator: std.mem.Allocator) !void {
+ if (!self.rc.isUnique()) {
+ const old = self.rc.get();
+ var new_inner: Inner = .{};
+ try new_inner.ensureTotalCapacity(allocator, old.count());
+ var it = old.iterator();
+ while (it.next()) |entry| {
+ new_inner.putAssumeCapacity(entry.key_ptr.*, entry.value_ptr.clone());
+ }
+ self.rc.release(allocator);
+ self.rc = try Rc(Inner).init(allocator, new_inner);
+ }
}
};