added SmolStr

This commit is contained in:
Moritz Gmeiner 2025-04-08 00:33:12 +02:00
commit 7490faa589
6 changed files with 131 additions and 49 deletions

View file

@ -1,3 +1,3 @@
# zig-template # zig-smolstr
Template for zig repositories String with inlining support, based on [smol_str](https://github.com/rust-analyzer/smol_str)

View file

@ -10,57 +10,28 @@ pub fn build(b: *std.Build) void {
.optimize = optimize, .optimize = optimize,
}); });
const exe_mod = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
exe_mod.addImport("zig_template_lib", lib_mod);
const lib = b.addLibrary(.{ const lib = b.addLibrary(.{
.linkage = .static, .linkage = .static,
.name = "zig_template", .name = "smol_str",
.root_module = lib_mod, .root_module = lib_mod,
}); });
const exe = b.addExecutable(.{
.name = "zig_template",
.root_module = exe_mod,
});
b.installArtifact(lib); b.installArtifact(lib);
b.installArtifact(exe);
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| {
run_cmd.addArgs(args);
}
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
const lib_unit_tests = b.addTest(.{ const lib_unit_tests = b.addTest(.{
.root_module = lib_mod, .root_module = lib_mod,
}); });
const exe_unit_tests = b.addTest(.{
.root_module = exe_mod,
});
const ext_tests = b.addTest(.{ const ext_tests = b.addTest(.{
.root_source_file = b.path("tests/root.zig"), .root_source_file = b.path("tests/root.zig"),
}); });
ext_tests.root_module.addImport("smol_str", lib.root_module);
const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests); const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
const run_ext_tests = b.addRunArtifact(ext_tests); const run_ext_tests = b.addRunArtifact(ext_tests);
const test_step = b.step("test", "Run unit tests"); const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&run_lib_unit_tests.step); test_step.dependOn(&run_lib_unit_tests.step);
test_step.dependOn(&run_exe_unit_tests.step);
test_step.dependOn(&run_ext_tests.step); test_step.dependOn(&run_ext_tests.step);
} }

View file

@ -6,11 +6,11 @@
// //
// It is redundant to include "zig" in this name because it is already // It is redundant to include "zig" in this name because it is already
// within the Zig package namespace. // within the Zig package namespace.
.name = .zig_template, .name = .smol_str,
// This is a [Semantic Version](https://semver.org/). // This is a [Semantic Version](https://semver.org/).
// In a future version of Zig it will be used for package deduplication. // In a future version of Zig it will be used for package deduplication.
.version = "0.0.0", .version = "0.1.0",
// Together with name, this represents a globally unique package // Together with name, this represents a globally unique package
// identifier. This field is generated by the Zig toolchain when the // identifier. This field is generated by the Zig toolchain when the
@ -24,7 +24,7 @@
// original project's identity. Thus it is recommended to leave the comment // original project's identity. Thus it is recommended to leave the comment
// on the following line intact, so that it shows up in code reviews that // on the following line intact, so that it shows up in code reviews that
// modify the field. // modify the field.
.fingerprint = 0xf2a3560362615322, // Changing this has security and trust implications. .fingerprint = 0x387d26e34b457035, // Changing this has security and trust implications.
// Tracks the earliest Zig version that the package considers to be a // Tracks the earliest Zig version that the package considers to be a
// supported use case. // supported use case.
@ -80,7 +80,7 @@
"build.zig.zon", "build.zig.zon",
"src", "src",
// For example... // For example...
//"LICENSE", "LICENSE",
//"README.md", //"README.md",
}, },
} }

View file

@ -1,11 +0,0 @@
const std = @import("std");
pub fn main() !void {
const stdout_file = std.io.getStdOut().writer();
var bw = std.io.bufferedWriter(stdout_file);
const stdout = bw.writer();
try stdout.print("Hello, World.\n", .{});
try bw.flush();
}

View file

@ -1 +1,86 @@
const std = @import("std"); const std = @import("std");
pub const SmolStr = struct {
pub const max_inline_len: usize = 22;
const Inner = union(enum) {
inl: struct {
len: u8,
data: [22]u8,
},
heap: []const u8,
};
inner: Inner,
pub fn init(alloc: std.mem.Allocator, s: []const u8) !SmolStr {
if (s.len <= max_inline_len) {
return initInline(s);
}
const s_ = try alloc.dupe(u8, s);
const inner = Inner{
.heap = s_,
};
return .{ .inner = inner };
}
pub fn initInline(s: []const u8) SmolStr {
if (s.len > max_inline_len) {
var buf: [1024]u8 = undefined;
var msg: []const u8 = undefined;
msg = std.fmt.bufPrint(&buf, "Tried to inline string \"{s}\" of len {}", .{ s, s.len }) catch |e| switch (e) {
error.NoSpaceLeft => blk: {
break :blk std.fmt.bufPrint(&buf, "Tried to inline string of len {}", .{s.len}) catch unreachable;
},
};
@panic(msg);
}
var inner = Inner{ .inl = .{
.len = @intCast(s.len),
.data = undefined,
} };
@memcpy(inner.inl.data[0..s.len], s);
return .{ .inner = inner };
}
pub fn deinit(self: SmolStr, alloc: std.mem.Allocator) void {
switch (self.inner) {
.inl => {},
.heap => |s| alloc.free(s),
}
}
pub fn str(self: *const SmolStr) []const u8 {
switch (self.inner) {
.inl => |*inl| return inl.data[0..self.len()],
.heap => |s| return s,
}
}
pub fn len(self: SmolStr) usize {
switch (self.inner) {
.inl => |*inl| return inl.len,
.heap => |s| return s.len,
}
}
pub fn format(self: SmolStr, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
_ = fmt;
_ = options;
try writer.print("{s}", .{self.str()});
}
};
test {
try std.testing.expectEqual(24, @sizeOf(SmolStr));
}

View file

@ -1 +1,38 @@
const std = @import("std"); const std = @import("std");
const SmolStr = @import("smolstr").SmolStr;
test "inline" {
const alloc = std.testing.allocator;
const s = "abcdefghijklmnopqrstuvwxyz";
for (1..SmolStr.max_inline_len + 1) |i| {
const sub_s: []const u8 = s[0..i];
const str = try SmolStr.init(alloc, sub_s);
defer str.deinit(alloc);
try std.testing.expectEqual(i, str.inner.inl.len);
try std.testing.expectEqualStrings(s[0..i], str.inner.inl.data[0..str.inner.inl.len]);
try std.testing.expectEqual(i, str.len());
try std.testing.expectEqualStrings(s[0..i], str.str());
try std.testing.expectEqual(s[0], str.str()[0]);
}
}
test "heap" {
const alloc = std.testing.allocator;
const s = "abcdefghijklmnopqrstuvwxyz";
const str = try SmolStr.init(alloc, s);
defer str.deinit(alloc);
try std.testing.expectEqualStrings(s, str.inner.heap);
try std.testing.expectEqual(s.len, str.len());
try std.testing.expectEqualStrings(s, str.str());
}