Skip to content

Commit

Permalink
Merge pull request #143 from getty-zig/lifetimes-visitString-input
Browse files Browse the repository at this point in the history
Introduce lifetime parameter to visitString
  • Loading branch information
ibokuri authored Sep 22, 2023
2 parents 2118bce + bfb4d8f commit 767c0cd
Show file tree
Hide file tree
Showing 17 changed files with 286 additions and 75 deletions.
18 changes: 12 additions & 6 deletions src/de/blocks/slice.zig
Original file line number Diff line number Diff line change
Expand Up @@ -44,25 +44,31 @@ test "deserialize - slice, string" {

const tests = .{
.{
.name = "no sentinel",
.name = "no sentinel (copied)",
.tokens = &.{.{ .String = "abc" }},
.want = @as([]u8, &no_sentinel),
},
.{
.name = "no sentinel, const",
.name = "no sentinel, const (borrowed)",
.tokens = &.{.{ .String = "abc" }},
.want = @as([]const u8, &no_sentinel),
},
.{
.name = "sentinel",
.tokens = &.{.{ .String = "abc" }},
.want = @as([:0]u8, &sentinel),
.name = "sentinel (input has a sentinel)",
.tokens = &.{.{ .StringZ = "abc" }},
.want = @as([:0]const u8, &sentinel),
},
.{
.name = "sentinel, const",
.name = "sentinel, const (input has no sentinel)",
.tokens = &.{.{ .String = "abc" }},
.want = @as([:0]const u8, &sentinel),
},
.{
.name = "sentinel (input has no sentinel, const -> mut)",
.tokens = &.{.{ .String = "abc" }},
.want = @as([:0]u8, &sentinel),
},
// TODO: Add test case for []u8 input to []const u8 output with and without sentinel.
};

inline for (tests) |t| {
Expand Down
7 changes: 7 additions & 0 deletions src/de/de.zig
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ pub const de = struct {
/// An implementation of `getty.de.Seed` that ignores values.
pub const Ignored = @import("impls/seed/ignored.zig").Ignored;

/// The lifetime of the Getty String passed to a visitor's `visitString`
/// method.
pub const StringLifetime = @import("lifetime.zig").StringLifetime;

/// The lifetime of an access method's return value.
pub const ValueLifetime = @import("lifetime.zig").ValueLifetime;

////////////////////////////////////////////////////////////////////////////////
// Namespaces
////////////////////////////////////////////////////////////////////////////////
Expand Down
5 changes: 3 additions & 2 deletions src/de/impls/deserializer/content.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const getty_deserialize = @import("../../deserialize.zig").deserialize;
const getty_error = @import("../../error.zig").Error;
const MapAccessInterface = @import("../../interfaces/map_access.zig").MapAccess;
const SeqAccessInterface = @import("../../interfaces/seq_access.zig").SeqAccess;
const StringLifetime = @import("../../lifetime.zig").StringLifetime;
const UnionAccessInterface = @import("../../interfaces/union_access.zig").UnionAccess;
const VariantAccessInterface = @import("../../interfaces/variant_access.zig").VariantAccess;
const VisitorInterface = @import("../../interfaces/visitor.zig").Visitor;
Expand Down Expand Up @@ -79,7 +80,7 @@ pub const ContentDeserializer = struct {
const int = v.to(@TypeOf(visitor).Value) catch unreachable;
break :blk try visitor.visitInt(ally, De, int);
},
.String => |v| try visitor.visitString(ally, De, v),
.String => |v| try visitor.visitString(ally, De, v, .managed),
else => error.InvalidType,
};
}
Expand Down Expand Up @@ -141,7 +142,7 @@ pub const ContentDeserializer = struct {

fn deserializeString(self: Self, ally: std.mem.Allocator, visitor: anytype) getty_error!@TypeOf(visitor).Value {
return switch (self.content) {
.String => |v| try visitor.visitString(ally, De, v),
.String => |v| try visitor.visitString(ally, De, v, .managed),
else => error.InvalidType,
};
}
Expand Down
32 changes: 23 additions & 9 deletions src/de/impls/visitor/array.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const std = @import("std");

const StringLifetime = @import("../../lifetime.zig").StringLifetime;
const VisitorInterface = @import("../../interfaces/visitor.zig").Visitor;

pub fn Visitor(comptime Array: type) type {
Expand All @@ -17,7 +18,12 @@ pub fn Visitor(comptime Array: type) type {

const Value = Array;

fn visitSeq(_: Self, ally: std.mem.Allocator, comptime Deserializer: type, seq: anytype) Deserializer.Err!Value {
fn visitSeq(
_: Self,
ally: std.mem.Allocator,
comptime Deserializer: type,
seq: anytype,
) Deserializer.Err!Value {
var array: Value = undefined;
var seen: usize = 0;

Expand All @@ -42,17 +48,25 @@ pub fn Visitor(comptime Array: type) type {
return array;
}

fn visitString(_: Self, _: std.mem.Allocator, comptime Deserializer: type, input: anytype) Deserializer.Err!Value {
if (Child == u8) {
var string: Value = undefined;
fn visitString(
_: Self,
_: std.mem.Allocator,
comptime Deserializer: type,
input: anytype,
_: StringLifetime,
) Deserializer.Err!Value {
if (Child != u8) {
return error.InvalidType;
}

var arr: Value = undefined;

if (input.len == string.len) {
@memcpy(&string, input);
return string;
}
if (input.len != arr.len) {
return error.InvalidLength;
}

return error.InvalidType;
@memcpy(&arr, input);
return arr;
}

const Child = std.meta.Child(Value);
Expand Down
21 changes: 16 additions & 5 deletions src/de/impls/visitor/content.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const std = @import("std");
const Content = @import("../../content.zig").Content;
const ContentMultiArrayList = @import("../../content.zig").ContentMultiArrayList;
const getty_deserialize = @import("../../deserialize.zig").deserialize;
const StringLifetime = @import("../../lifetime.zig").StringLifetime;
const VisitorInterface = @import("../../interfaces/visitor.zig").Visitor;

const Visitor = @This();
Expand Down Expand Up @@ -87,11 +88,21 @@ fn visitSome(_: @This(), ally: std.mem.Allocator, deserializer: anytype) @TypeOf
} };
}

fn visitString(_: @This(), ally: std.mem.Allocator, comptime Deserializer: type, input: anytype) Deserializer.Err!Content {
const output = try ally.alloc(u8, input.len);
std.mem.copy(u8, output, input);

return .{ .String = output };
fn visitString(
_: @This(),
ally: std.mem.Allocator,
comptime Deserializer: type,
input: anytype,
lt: StringLifetime,
) Deserializer.Err!Content {
switch (lt) {
.heap => return .{ .String = @as([]const u8, input) },
.stack, .managed => {
const copy = try ally.alloc(u8, input.len);
std.mem.copy(u8, copy, input);
return .{ .String = copy };
},
}
}

fn visitUnion(_: @This(), ally: std.mem.Allocator, comptime Deserializer: type, ua: anytype, va: anytype) Deserializer.Err!Content {
Expand Down
16 changes: 14 additions & 2 deletions src/de/impls/visitor/enum.zig
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const std = @import("std");

const getAttributes = @import("../../attributes.zig").getAttributes;
const StringLifetime = @import("../../lifetime.zig").StringLifetime;
const VisitorInterface = @import("../../interfaces/visitor.zig").Visitor;

pub fn Visitor(comptime Enum: type) type {
Expand All @@ -18,7 +19,12 @@ pub fn Visitor(comptime Enum: type) type {

const Value = Enum;

fn visitInt(_: Self, _: std.mem.Allocator, comptime Deserializer: type, input: anytype) Deserializer.Err!Value {
fn visitInt(
_: Self,
_: std.mem.Allocator,
comptime Deserializer: type,
input: anytype,
) Deserializer.Err!Value {
@setEvalBranchQuota(10_000);

const fields = std.meta.fields(Value);
Expand Down Expand Up @@ -47,7 +53,13 @@ pub fn Visitor(comptime Enum: type) type {
return e;
}

fn visitString(_: Self, _: std.mem.Allocator, comptime Deserializer: type, input: anytype) Deserializer.Err!Value {
fn visitString(
_: Self,
_: std.mem.Allocator,
comptime Deserializer: type,
input: anytype,
_: StringLifetime,
) Deserializer.Err!Value {
@setEvalBranchQuota(10_000);

const fields = std.meta.fields(Value);
Expand Down
7 changes: 6 additions & 1 deletion src/de/impls/visitor/ignored.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const std = @import("std");

const StringLifetime = @import("../../lifetime.zig").StringLifetime;
const VisitorInterface = @import("../../interfaces/visitor.zig").Visitor;

pub fn Visitor(comptime Ignored: type) type {
Expand All @@ -17,7 +18,7 @@ pub fn Visitor(comptime Ignored: type) type {
.visitNull = visitNothing,
.visitSeq = visitSeq,
.visitSome = visitSome,
.visitString = visitAny,
.visitString = visitString,
.visitUnion = visitUnion,
.visitVoid = visitNothing,
},
Expand Down Expand Up @@ -53,6 +54,10 @@ pub fn Visitor(comptime Ignored: type) type {
return .{};
}

fn visitString(_: Self, _: std.mem.Allocator, comptime Deserializer: type, _: anytype, _: StringLifetime) Deserializer.Err!Value {
return .{};
}

fn visitUnion(_: Self, _: std.mem.Allocator, comptime Deserializer: type, _: anytype, _: anytype) Deserializer.Err!Value {
return .{};
}
Expand Down
9 changes: 8 additions & 1 deletion src/de/impls/visitor/net_address.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const std = @import("std");

const StringLifetime = @import("../../lifetime.zig").StringLifetime;
const VisitorInterface = @import("../../interfaces/visitor.zig").Visitor;

pub fn Visitor(comptime NetAddress: type) type {
Expand All @@ -16,7 +17,13 @@ pub fn Visitor(comptime NetAddress: type) type {

const Value = NetAddress;

fn visitString(_: Self, _: std.mem.Allocator, comptime Deserializer: type, input: anytype) Deserializer.Err!Value {
fn visitString(
_: Self,
_: ?std.mem.Allocator,
comptime Deserializer: type,
input: anytype,
_: StringLifetime,
) Deserializer.Err!Value {
const max_ipv6_chars = 47;
const max_port_chars = 6;

Expand Down
12 changes: 10 additions & 2 deletions src/de/impls/visitor/pointer.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ const std = @import("std");
const blocks = @import("../../blocks.zig");
const find_db = @import("../../find.zig").find_db;
const has_attributes = @import("../../../attributes.zig").has_attributes;
const StringLifetime = @import("../../lifetime.zig").StringLifetime;

const VisitorInterface = @import("../../interfaces/visitor.zig").Visitor;

pub fn Visitor(comptime Pointer: type) type {
Expand Down Expand Up @@ -103,12 +105,18 @@ pub fn Visitor(comptime Pointer: type) type {
return value;
}

fn visitString(_: Self, ally: std.mem.Allocator, comptime Deserializer: type, input: anytype) Deserializer.Err!Value {
fn visitString(
_: Self,
ally: std.mem.Allocator,
comptime Deserializer: type,
input: anytype,
lt: StringLifetime,
) Deserializer.Err!Value {
const value = try ally.create(Child);
errdefer ally.destroy(value);

var cv = ChildVisitor(Deserializer){};
value.* = try cv.visitor().visitString(ally, Deserializer, input);
value.* = try cv.visitor().visitString(ally, Deserializer, input, lt);

return value;
}
Expand Down
26 changes: 17 additions & 9 deletions src/de/impls/visitor/semantic_version.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const std = @import("std");

const StringLifetime = @import("../../lifetime.zig").StringLifetime;
const VisitorInterface = @import("../../interfaces/visitor.zig").Visitor;

const Visitor = @This();
Expand All @@ -14,18 +15,25 @@ pub usingnamespace VisitorInterface(

const Value = std.SemanticVersion;

fn visitString(_: Visitor, ally: std.mem.Allocator, comptime Deserializer: type, input: anytype) Deserializer.Err!Value {
fn visitString(
_: Visitor,
ally: std.mem.Allocator,
comptime Deserializer: type,
input: anytype,
lt: StringLifetime,
) Deserializer.Err!Value {
var ver = std.SemanticVersion.parse(input) catch return error.InvalidValue;

if (ver.pre == null and ver.build == null) {
return ver;
}
switch (lt) {
.heap => {},
.stack, .managed => {
if (ver.pre == null and ver.build == null) {
return ver;
}

if (ver.pre) |pre| {
ver.pre = try ally.dupe(u8, pre);
}
if (ver.build) |build| {
ver.build = try ally.dupe(u8, build);
if (ver.pre) |pre| ver.pre = try ally.dupe(u8, pre);
if (ver.build) |build| ver.build = try ally.dupe(u8, build);
},
}

return ver;
Expand Down
38 changes: 34 additions & 4 deletions src/de/impls/visitor/slice.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const std = @import("std");

const StringLifetime = @import("../../lifetime.zig").StringLifetime;
const VisitorInterface = @import("../../interfaces/visitor.zig").Visitor;

pub fn Visitor(comptime Slice: type) type {
Expand Down Expand Up @@ -33,17 +34,46 @@ pub fn Visitor(comptime Slice: type) type {
return try list.toOwnedSlice();
}

fn visitString(_: Self, ally: std.mem.Allocator, comptime Deserializer: type, input: anytype) Deserializer.Err!Value {
// input is only used directly if it the following are true:
//
// 1. input is a Heap value.
// 2. The sentinel values of input and Value match.
// 3. Either constness of input and Value match or are compatible
// (i.e., Value is const and input is not const).
//
// Otherwise, input is copied into a new slice.
fn visitString(
_: Self,
ally: std.mem.Allocator,
comptime Deserializer: type,
input: anytype,
lt: StringLifetime,
) Deserializer.Err!Value {
if (Child != u8) {
return error.InvalidType;
}

const sentinel = @typeInfo(Value).Pointer.sentinel;
const v_info = @typeInfo(Value).Pointer;

const output = try ally.alloc(u8, input.len + @intFromBool(sentinel != null));
switch (lt) {
.heap => {
const i_info = @typeInfo(@TypeOf(input)).Pointer;

const sentinels_match = (v_info.sentinel == null) == (i_info.sentinel == null);
const constness_match = v_info.is_const == i_info.is_const;
const constness_compat = v_info.is_const and !i_info.is_const;

if (sentinels_match and (constness_match or constness_compat)) {
return @as(Value, input);
}
},
.stack, .managed => {},
}

const output = try ally.alloc(u8, input.len + @intFromBool(v_info.sentinel != null));
std.mem.copy(u8, output, input);

if (sentinel) |s| {
if (v_info.sentinel) |s| {
const sentinel_char = @as(*const u8, @ptrCast(s)).*;
output[input.len] = sentinel_char;
return output[0..input.len :sentinel_char];
Expand Down
Loading

0 comments on commit 767c0cd

Please sign in to comment.