From aeacdda36647fe9f59fea92d1a2f7c99f5787b95 Mon Sep 17 00:00:00 2001 From: Vincent Rischmann Date: Mon, 21 Dec 2020 02:09:18 +0100 Subject: add the readPointer method --- sqlite.zig | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) (limited to 'sqlite.zig') diff --git a/sqlite.zig b/sqlite.zig index 015dd64..10695ba 100644 --- a/sqlite.zig +++ b/sqlite.zig @@ -248,10 +248,10 @@ pub fn Iterator(comptime Type: type) type { // If the array is too small for the data an error will be returned. fn readArray(self: *Self, comptime ArrayType: type, _i: usize) error{ArrayTooSmall}!ArrayType { const i = @intCast(c_int, _i); - const array_type_info = @typeInfo(ArrayType); + const type_info = @typeInfo(ArrayType); var ret: ArrayType = undefined; - switch (array_type_info) { + switch (type_info) { .Array => |arr| { comptime if (arr.sentinel == null) { @compileError("cannot populate array of " ++ @typeName(arr.child) ++ ", arrays must have a sentinel"); @@ -356,6 +356,27 @@ pub fn Iterator(comptime Type: type) type { } } + fn readPointer(self: *Self, comptime PointerType: type, i: usize, options: anytype) !PointerType { + const type_info = @typeInfo(PointerType); + + var ret: PointerType = undefined; + switch (type_info) { + .Pointer => |ptr| { + switch (ptr.size) { + .One => unreachable, + .Slice => switch (ptr.child) { + u8 => ret = try self.readBytes(PointerType, i, .Text, options), + else => @compileError("cannot read pointer of type " ++ @typeName(PointerType)), + }, + else => @compileError("cannot read pointer of type " ++ @typeName(PointerType)), + } + }, + else => @compileError("cannot read pointer of type " ++ @typeName(PointerType)), + } + + return ret; + } + // readStruct reads an entire sqlite row into a struct. // // Each field correspond to a column; its position in the struct determines the column used for it. @@ -385,7 +406,6 @@ pub fn Iterator(comptime Type: type) type { const field_type_info = @typeInfo(field.field_type); const ret = switch (field.field_type) { - []const u8, []u8 => try self.readBytes(field.field_type, i, .Blob, options), Blob => try self.readBytes(Blob, i, .Blob, options), Text => try self.readBytes(Text, i, .Text, options), else => switch (field_type_info) { @@ -394,6 +414,7 @@ pub fn Iterator(comptime Type: type) type { .Bool => try self.readBool(i, options), .Void => {}, .Array => try self.readArray(field.field_type, i), + .Pointer => try self.readPointer(field.field_type, i, options), else => @compileError("cannot populate field " ++ field.name ++ " of type " ++ @typeName(field.field_type)), }, }; -- cgit v1.2.3 From 9221d3056be82bb18d601df790aebfae30d850a7 Mon Sep 17 00:00:00 2001 From: Vincent Rischmann Date: Mon, 21 Dec 2020 02:35:40 +0100 Subject: implement reading of sentineled slices --- sqlite.zig | 75 +++++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 25 deletions(-) (limited to 'sqlite.zig') diff --git a/sqlite.zig b/sqlite.zig index 10695ba..dedd480 100644 --- a/sqlite.zig +++ b/sqlite.zig @@ -232,6 +232,10 @@ pub fn Iterator(comptime Type: type) type { debug.assert(columns == 1); return try self.readArray(Type, 0); }, + .Pointer => { + debug.assert(columns == 1); + return try self.readPointer(Type, 0, options); + }, .Struct => { std.debug.assert(columns == TypeInfo.Struct.fields.len); return try self.readStruct(options); @@ -300,6 +304,25 @@ pub fn Iterator(comptime Type: type) type { Text, }; + // dupeWithSentinel is like dupe/dupeZ but allows for any sentinel value. + fn dupeWithSentinel(comptime SliceType: type, allocator: *mem.Allocator, data: []const u8) !SliceType { + const type_info = @typeInfo(SliceType); + switch (type_info) { + .Pointer => |ptr_info| { + if (ptr_info.sentinel) |sentinel| { + const slice = try allocator.alloc(u8, data.len + 1); + mem.copy(u8, slice, data); + slice[data.len] = sentinel; + + return slice[0..data.len :sentinel]; + } else { + return try allocator.dupe(u8, data); + } + }, + else => @compileError("cannot dupe type " ++ @typeName(SliceType)), + } + } + // readBytes reads a sqlite BLOB or TEXT column. // // The mode controls which sqlite function is used to retrieve the data: @@ -312,46 +335,47 @@ pub fn Iterator(comptime Type: type) type { // The options must contain an `allocator` field which will be used to create a copy of the data. fn readBytes(self: *Self, comptime BytesType: type, _i: usize, comptime mode: ReadBytesMode, options: anytype) !BytesType { const i = @intCast(c_int, _i); + const type_info = @typeInfo(BytesType); var ret: BytesType = switch (BytesType) { Text, Blob => .{ .data = "" }, - else => "", // TODO(vincent): I think with a []u8 this will crash if the caller attempts to modify it... + else => try dupeWithSentinel(BytesType, options.allocator, ""), }; switch (mode) { .Blob => { const data = c.sqlite3_column_blob(self.stmt, i); - if (data == null) return ret; + if (data == null) { + return switch (BytesType) { + Text, Blob => .{ .data = try options.allocator.dupe(u8, "") }, + else => try dupeWithSentinel(BytesType, options.allocator, ""), + }; + } const size = @intCast(usize, c.sqlite3_column_bytes(self.stmt, i)); const ptr = @ptrCast([*c]const u8, data)[0..size]; - return switch (BytesType) { - []const u8, []u8 => try options.allocator.dupe(u8, ptr), - Blob => blk: { - var tmp: Blob = undefined; - tmp.data = try options.allocator.dupe(u8, ptr); - break :blk tmp; - }, - else => @compileError("cannot read blob into type " ++ @typeName(BytesType)), - }; + if (BytesType == Blob) { + return Blob{ .data = try options.allocator.dupe(u8, ptr) }; + } + return try dupeWithSentinel(BytesType, options.allocator, ptr); }, .Text => { const data = c.sqlite3_column_text(self.stmt, i); - if (data == null) return ret; + if (data == null) { + return switch (BytesType) { + Text, Blob => .{ .data = try options.allocator.dupe(u8, "") }, + else => try dupeWithSentinel(BytesType, options.allocator, ""), + }; + } const size = @intCast(usize, c.sqlite3_column_bytes(self.stmt, i)); const ptr = @ptrCast([*c]const u8, data)[0..size]; - return switch (BytesType) { - []const u8, []u8 => try options.allocator.dupe(u8, ptr), - Text => blk: { - var tmp: Text = undefined; - tmp.data = try options.allocator.dupe(u8, ptr); - break :blk tmp; - }, - else => @compileError("cannot read text into type " ++ @typeName(BytesType)), - }; + if (BytesType == Text) { + return Text{ .data = try options.allocator.dupe(u8, ptr) }; + } + return try dupeWithSentinel(BytesType, options.allocator, ptr); }, } } @@ -926,16 +950,17 @@ test "sqlite: read a single text value" { try db.init(testing.allocator, .{ .mode = dbMode() }); try addTestData(&db); - // TODO(vincent): implement the following - // [:0]const u8 - // [:0]u8 - const types = &[_]type{ // Slices []const u8, []u8, + [:0]const u8, + [:0]u8, + [:0xAD]const u8, + [:0xAD]u8, // Array [8:0]u8, + [8:0xAD]u8, // Specific text or blob Text, Blob, -- cgit v1.2.3