diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-01-31 15:31:15 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-01-31 15:47:23 +0900 |
| commit | 617ddc62aa4d3153850362526069b85bfaf5e59e (patch) | |
| tree | 6940c577bbecdbf7856ef3c37ab43b256d33da59 /src/jv/value.zig | |
| parent | 6be43138338fbe4623c1cd62cf71138873af3a7a (diff) | |
| download | zgjq-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.zig | 229 |
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); + } } }; |
