Skip to content

Commit

Permalink
Convert PyString append functions to void (#6)
Browse files Browse the repository at this point in the history
Instead of returning a new object, the CPython API effectively decref's
the LHS and places a new string in its place.
This PR therefore changes our append functions to also mutate the
py.PyString's internal pointer. It makes it more obvious that there's
nothing to decref.
  • Loading branch information
gatesn authored Sep 1, 2023
1 parent 572f048 commit 4feba07
Showing 1 changed file with 31 additions and 16 deletions.
47 changes: 31 additions & 16 deletions pydust/src/types/str.zig
Original file line number Diff line number Diff line change
Expand Up @@ -21,35 +21,38 @@ pub const PyString = extern struct {
return .{ .obj = .{ .py = unicode } };
}

pub inline fn append(self: PyString, other: PyString) !PyString {
return self.appendObj(other.obj);
pub fn append(self: PyString, other: PyString) !void {
try self.appendObj(other.obj);
}

pub fn appendObj(self: PyString, other: PyObject) !PyString {
pub fn appendSlice(self: *PyString, str: [:0]const u8) !void {
const other = try fromSlice(str);
defer other.decref();
try self.appendObj(other.obj);
}

fn appendObj(self: *PyString, other: PyObject) !void {
// This function effectively decref's the left-hand side.
// The semantics therefore sort of imply mutation, and so we expose the same in our API.
var self_ptr: ?*ffi.PyObject = self.obj.py;
ffi.PyUnicode_Append(@ptrCast(&self_ptr), other.py);
ffi.PyUnicode_Append(&self_ptr, other.py);
if (self_ptr) |ptr| {
return .{ .obj = .{ .py = ptr } };
self.obj.py = ptr;
} else {
// If set to null, then it failed.
return PyError.Propagate;
}
}

pub fn appendSlice(self: PyString, str: [:0]const u8) !PyString {
const other = try fromSlice(str);
defer other.decref();
return self.appendObj(other.obj);
}

pub fn asOwnedSlice(self: PyString) ![:0]const u8 {
defer self.decref();
return try self.asSlice();
}

pub fn asSlice(self: PyString) ![:0]const u8 {
var size: i64 = 0;
const buffer: [*]const u8 = ffi.PyUnicode_AsUTF8AndSize(self.obj.py, &size) orelse return PyError.Propagate;
return @ptrCast(buffer[0..@intCast(size + 1)]);
const buffer: [*:0]const u8 = ffi.PyUnicode_AsUTF8AndSize(self.obj.py, &size) orelse return PyError.Propagate;
return buffer[0..@as(usize, @intCast(size)) :0];
}

pub fn incref(self: PyString) void {
Expand All @@ -61,14 +64,26 @@ pub const PyString = extern struct {
}
};

const testing = std.testing;

test "PyString" {
py.initialize();
defer py.finalize();

var ps = try PyString.fromSlice("Hello");
const a = "Hello";
const b = ", world!";

var ps = try PyString.fromSlice(a);
defer ps.decref();

ps = try ps.appendSlice(", world!");
try ps.appendSlice(b);

var ps_slice = try ps.asSlice();
try std.testing.expectEqualStrings("Hello, world!", ps_slice[0 .. ps_slice.len - 1]);

// Null-terminated strings have len == non-null bytes, but are guaranteed to have a null byte
// when indexed by their length.
try testing.expectEqual(a.len + b.len, ps_slice.len);
try testing.expectEqual(@as(u8, 0), ps_slice[ps_slice.len]);

try testing.expectEqualStrings("Hello, world!", ps_slice);
}

0 comments on commit 4feba07

Please sign in to comment.