fleshing out oc_str* zig bindings
This commit is contained in:
parent
d46c00f0ec
commit
9c4e2125c1
|
@ -8,9 +8,7 @@ zig build run
|
|||
These two commands build the runtime - the native host executable - and the sample as a loadable wasm library, then runs it. To only build the sample without running it, use `zig build bundle`.
|
||||
|
||||
### Warning
|
||||
Zig integration is still in-progress and experimental. You may encounter bugs since not all the bound APIs have been tested extensively - this sample is currently the only code doing so! Additionally, not all APIs have zig coverage yet, notably:
|
||||
* `oc_str8`, `oc_str16`, `oc_str32`: partial coverage
|
||||
* `oc_ui`: none
|
||||
* `gles`: none
|
||||
|
||||
Please report any bugs you find on the Handmade discord in the #orca channel.
|
||||
Zig bindings for Orca are in-progress and experimental. You may encounter bugs since not all the bound APIs have been tested extensively - this sample is currently the only code doing so! Additionally, not all APIs have zig coverage yet, notably:
|
||||
* `oc_ui`
|
||||
* `gles`
|
||||
As more APIs get tested, there is a possibility of breaking changes. Please report any bugs you find on the Handmade discord in the #orca channel.
|
||||
|
|
|
@ -1,190 +0,0 @@
|
|||
#include <math.h>
|
||||
|
||||
#include <orca.h>
|
||||
|
||||
#define ARRAYSIZE(array) (sizeof(array) / sizeof(array[0]))
|
||||
|
||||
oc_vec2 frameSize = { 100, 100 };
|
||||
|
||||
oc_surface surface;
|
||||
oc_canvas canvas;
|
||||
oc_font font;
|
||||
|
||||
f64 lastSeconds = 0;
|
||||
|
||||
oc_mat2x3 mat_rotation(f32 radians);
|
||||
oc_mat2x3 mat_translation(f32 x, f32 y);
|
||||
oc_mat2x3 mat_transform(f32 x, f32 y, f32 radians);
|
||||
f32 minf(f32 a, f32 b);
|
||||
|
||||
ORCA_EXPORT void oc_on_init(void)
|
||||
{
|
||||
oc_window_set_title(OC_STR8("clock"));
|
||||
oc_runtime_window_set_size((oc_vec2){ .x = 400, .y = 400 });
|
||||
|
||||
surface = oc_surface_canvas();
|
||||
canvas = oc_canvas_create();
|
||||
|
||||
{
|
||||
oc_str8 filename = OC_STR8("/segoeui.ttf");
|
||||
oc_file file = oc_file_open(filename, OC_FILE_ACCESS_READ, 0);
|
||||
if(oc_file_last_error(file) != OC_IO_OK)
|
||||
{
|
||||
oc_log_error("Couldn't open file %s\n", oc_str8_to_cstring(oc_scratch(), filename));
|
||||
}
|
||||
u64 size = oc_file_size(file);
|
||||
char* buffer = oc_arena_push(oc_scratch(), size);
|
||||
oc_file_read(file, size, buffer);
|
||||
oc_file_close(file);
|
||||
|
||||
oc_unicode_range ranges[5] = { OC_UNICODE_BASIC_LATIN,
|
||||
OC_UNICODE_C1_CONTROLS_AND_LATIN_1_SUPPLEMENT,
|
||||
OC_UNICODE_LATIN_EXTENDED_A,
|
||||
OC_UNICODE_LATIN_EXTENDED_B,
|
||||
OC_UNICODE_SPECIALS };
|
||||
font = oc_font_create_from_memory(oc_str8_from_buffer(size, buffer), 5, ranges);
|
||||
}
|
||||
|
||||
oc_arena_clear(oc_scratch());
|
||||
}
|
||||
|
||||
ORCA_EXPORT void oc_on_resize(u32 width, u32 height)
|
||||
{
|
||||
frameSize.x = width;
|
||||
frameSize.y = height;
|
||||
}
|
||||
|
||||
ORCA_EXPORT void oc_on_frame_refresh(void)
|
||||
{
|
||||
const oc_str8 clock_number_strings[] = {
|
||||
OC_STR8("12"),
|
||||
OC_STR8("1"),
|
||||
OC_STR8("2"),
|
||||
OC_STR8("3"),
|
||||
OC_STR8("4"),
|
||||
OC_STR8("5"),
|
||||
OC_STR8("6"),
|
||||
OC_STR8("7"),
|
||||
OC_STR8("8"),
|
||||
OC_STR8("9"),
|
||||
OC_STR8("10"),
|
||||
OC_STR8("11"),
|
||||
};
|
||||
|
||||
oc_canvas_set_current(canvas);
|
||||
oc_surface_select(surface);
|
||||
oc_set_color_rgba(.05, .05, .05, 1);
|
||||
oc_clear();
|
||||
|
||||
const f64 timestampSecs = oc_clock_time(OC_CLOCK_DATE);
|
||||
const f64 secs = fmod(timestampSecs, 60);
|
||||
const f64 minutes = fmod(timestampSecs, 60 * 60) / 60;
|
||||
const f64 hours = fmod(timestampSecs, 60 * 60 * 24) / (60 * 60);
|
||||
const f64 hoursAs12Format = fmod(hours, 12.0);
|
||||
|
||||
if(lastSeconds != floor(secs))
|
||||
{
|
||||
lastSeconds = floor(secs);
|
||||
oc_log_info("current time: %.0f:%.0f:%.0f", floor(hours), floor(minutes), floor(secs));
|
||||
}
|
||||
|
||||
const f32 secondsRotation = (M_PI * 2) * (secs / 60.0) - (M_PI / 2);
|
||||
const f32 minutesRotation = (M_PI * 2) * (minutes / 60.0) - (M_PI / 2);
|
||||
const f32 hoursRotation = (M_PI * 2) * (hoursAs12Format / 12.0) - (M_PI / 2);
|
||||
|
||||
const f32 centerX = frameSize.x / 2;
|
||||
const f32 centerY = frameSize.y / 2;
|
||||
const f32 clockRadius = minf(frameSize.x, frameSize.y) * 0.5f * 0.85f;
|
||||
|
||||
const f32 DEFAULT_CLOCK_RADIUS = 260;
|
||||
const f32 uiScale = clockRadius / DEFAULT_CLOCK_RADIUS;
|
||||
|
||||
const f32 fontSize = 26 * uiScale;
|
||||
oc_set_font(font);
|
||||
oc_set_font_size(fontSize);
|
||||
|
||||
// clock backing
|
||||
oc_set_color_rgba(1, 1, 1, 1);
|
||||
oc_circle_fill(centerX, centerY, clockRadius);
|
||||
|
||||
// clock face
|
||||
for(int i = 0; i < ARRAYSIZE(clock_number_strings); ++i)
|
||||
{
|
||||
const f32 rot = -i * ((M_PI * 2) / 12.0f) + (M_PI / 2);
|
||||
const f32 sinRot = sinf(rot);
|
||||
const f32 cosRot = cosf(rot);
|
||||
|
||||
oc_rect textRect = oc_text_bounding_box(font, fontSize, clock_number_strings[i]);
|
||||
textRect.h -= 10 * uiScale; // oc_text_bounding_box height doesn't seem to be a tight fit around the glyph
|
||||
|
||||
const f32 x = cosRot * clockRadius * 0.8f - (textRect.w / 2) + centerX;
|
||||
const f32 y = -sinRot * clockRadius * 0.8f + (textRect.h / 2) + centerY;
|
||||
|
||||
oc_set_color_rgba(0.2, 0.2, 0.2, 1);
|
||||
oc_move_to(x, y);
|
||||
oc_text_outlines(clock_number_strings[i]);
|
||||
oc_fill();
|
||||
}
|
||||
|
||||
oc_matrix_push(mat_transform(centerX, centerY, hoursRotation));
|
||||
{
|
||||
oc_set_color_rgba(.2, 0.2, 0.2, 1);
|
||||
oc_rounded_rectangle_fill(0, -7.5 * uiScale, clockRadius * 0.5f, 15 * uiScale, 5 * uiScale);
|
||||
|
||||
oc_matrix_pop();
|
||||
}
|
||||
|
||||
oc_matrix_push(mat_transform(centerX, centerY, minutesRotation));
|
||||
{
|
||||
oc_set_color_rgba(.2, 0.2, 0.2, 1);
|
||||
oc_rounded_rectangle_fill(0, -5 * uiScale, clockRadius * 0.7f, 10 * uiScale, 5 * uiScale);
|
||||
|
||||
oc_matrix_pop();
|
||||
}
|
||||
|
||||
oc_matrix_push(mat_transform(centerX, centerY, secondsRotation));
|
||||
{
|
||||
oc_set_color_rgba(1, 0.2, 0.2, 1);
|
||||
oc_rounded_rectangle_fill(0, -2.5 * uiScale, clockRadius * 0.8f, 5 * uiScale, 5 * uiScale);
|
||||
|
||||
oc_matrix_pop();
|
||||
}
|
||||
|
||||
oc_set_color_rgba(.2, 0.2, 0.2, 1);
|
||||
oc_circle_fill(centerX, centerY, 10 * uiScale);
|
||||
|
||||
oc_render(surface, canvas);
|
||||
oc_surface_present(surface);
|
||||
}
|
||||
|
||||
oc_mat2x3 mat_rotation(f32 radians)
|
||||
{
|
||||
const f32 sinRot = sinf(radians);
|
||||
const f32 cosRot = cosf(radians);
|
||||
oc_mat2x3 rot = {
|
||||
cosRot, -sinRot, 0,
|
||||
sinRot, cosRot, 0
|
||||
};
|
||||
return rot;
|
||||
}
|
||||
|
||||
oc_mat2x3 mat_translation(f32 x, f32 y)
|
||||
{
|
||||
oc_mat2x3 translation = {
|
||||
1, 0, x,
|
||||
0, 1, y
|
||||
};
|
||||
return translation;
|
||||
}
|
||||
|
||||
oc_mat2x3 mat_transform(f32 x, f32 y, f32 radians)
|
||||
{
|
||||
oc_mat2x3 rotation = mat_rotation(radians);
|
||||
oc_mat2x3 translation = mat_translation(x, y);
|
||||
return oc_mat2x3_mul_m(translation, rotation);
|
||||
}
|
||||
|
||||
f32 minf(f32 a, f32 b)
|
||||
{
|
||||
return (a < b) ? a : b;
|
||||
}
|
|
@ -3,6 +3,7 @@ const oc = @import("orca");
|
|||
|
||||
const Vec2 = oc.Vec2;
|
||||
const Mat2x3 = oc.Mat2x3;
|
||||
const Str8 = oc.Str8;
|
||||
|
||||
var surface: oc.Surface = undefined;
|
||||
var canvas: oc.Canvas = undefined;
|
||||
|
@ -27,7 +28,7 @@ export fn oc_on_init() void {
|
|||
oc.assert(oc.Canvas.nil().isNil() == true, "nil canvas should be nil", .{}, @src());
|
||||
oc.assert(canvas.isNil() == false, "created canvas should not be nil", .{}, @src());
|
||||
|
||||
var ranges = oc.UnicodeRange.range(&[_]oc.UnicodeRange.Enum{
|
||||
const ranges = oc.UnicodeRange.range(&[_]oc.UnicodeRange.Enum{
|
||||
.BasicLatin,
|
||||
.C1ControlsAndLatin1Supplement,
|
||||
.LatinExtendedA,
|
||||
|
@ -37,7 +38,7 @@ export fn oc_on_init() void {
|
|||
font = oc.Font.createFromPath("/zig.ttf", &ranges);
|
||||
oc.assert(font.isNil() == false, "created font should not be nil", .{}, @src());
|
||||
|
||||
orca_image = oc.Image.createFromPath(surface, oc.Str8.fromSlice("/orca_jumping.jpg"), false);
|
||||
orca_image = oc.Image.createFromPath(surface, Str8.fromSlice("/orca_jumping.jpg"), false);
|
||||
oc.assert(orca_image.isNil() == false, "created image should not be nil", .{}, @src());
|
||||
}
|
||||
|
||||
|
@ -129,14 +130,28 @@ export fn oc_on_frame_refresh() void {
|
|||
}
|
||||
|
||||
{
|
||||
const str = oc.Str8.fromSlice("Hello from Zig!");
|
||||
var scratch_scope = oc.Arena.scratchBegin();
|
||||
defer scratch_scope.end();
|
||||
|
||||
var scratch: *oc.Arena = scratch_scope.arena;
|
||||
|
||||
var str1: Str8 = Str8.collate(scratch, &[_][]const u8{ "Hello", "from", "Zig!" }, ">> ", " ", " <<") catch |e| fatal(e, @src());
|
||||
|
||||
var str2_list = oc.Str8List.init();
|
||||
var tmp = Str8.fromSlice("All");
|
||||
str2_list.push(tmp, scratch) catch |e| fatal(e, @src());
|
||||
str2_list.pushSlice("your", scratch) catch |e| fatal(e, @src());
|
||||
str2_list.pushSlice("base!!", scratch) catch |e| fatal(e, @src());
|
||||
var str2: Str8 = str2_list.collate(scratch, Str8.fromSlice("<< "), Str8.fromSlice("-"), Str8.fromSlice(" >>")) catch |e| fatal(e, @src());
|
||||
|
||||
const font_size = 18;
|
||||
const text_rect = font.textMetrics(font_size, str).ink;
|
||||
const text_metrics = font.textMetrics(font_size, str1);
|
||||
const text_rect = text_metrics.ink;
|
||||
|
||||
const center_x = frame_size.x / 2;
|
||||
const text_begin_x = center_x - text_rect.Flat.w / 2;
|
||||
|
||||
Mat2x3.push(Mat2x3.translate(text_begin_x, 150));
|
||||
Mat2x3.push(Mat2x3.translate(text_begin_x, 100));
|
||||
defer Mat2x3.pop();
|
||||
|
||||
oc.setColorRgba(1.0, 0.05, 0.05, 1.0);
|
||||
|
@ -144,7 +159,30 @@ export fn oc_on_frame_refresh() void {
|
|||
oc.setFont(font);
|
||||
oc.setFontSize(font_size);
|
||||
oc.moveTo(0, 0);
|
||||
oc.textOutlines(str);
|
||||
oc.textOutlines(str1);
|
||||
oc.moveTo(0, 35);
|
||||
oc.textOutlines(str2);
|
||||
oc.fill();
|
||||
}
|
||||
|
||||
{
|
||||
var scratch_scope = oc.Arena.scratchBegin();
|
||||
defer scratch_scope.end();
|
||||
|
||||
var scratch: *oc.Arena = scratch_scope.arena;
|
||||
|
||||
var separators = oc.Str8List.init();
|
||||
separators.pushSlice(" ", scratch) catch |e| fatal(e, @src());
|
||||
separators.pushSlice("|", scratch) catch |e| fatal(e, @src());
|
||||
separators.pushSlice("-", scratch) catch |e| fatal(e, @src());
|
||||
|
||||
const big_string = Str8.fromSlice("This is |a one-word string that | has no spaces in it");
|
||||
var strings: oc.Str8List = big_string.split(scratch, separators) catch |e| fatal(e, @src());
|
||||
var collated = strings.join(scratch) catch |e| fatal(e, @src());
|
||||
|
||||
oc.setFontSize(12);
|
||||
oc.moveTo(0, 170);
|
||||
oc.textOutlines(collated);
|
||||
oc.fill();
|
||||
}
|
||||
|
||||
|
@ -171,3 +209,8 @@ export fn oc_on_frame_refresh() void {
|
|||
export fn oc_on_terminate() void {
|
||||
oc.log.info("byebye {}", .{counter}, @src());
|
||||
}
|
||||
|
||||
fn fatal(err: anyerror, source: std.builtin.SourceLocation) noreturn {
|
||||
oc.abort("Caught fatal {}", .{err}, source);
|
||||
unreachable;
|
||||
}
|
||||
|
|
359
src/orca.zig
359
src/orca.zig
|
@ -1,4 +1,5 @@
|
|||
const std = @import("std");
|
||||
const AllocError = std.mem.Allocator.Error;
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
// [PLATFORM]
|
||||
|
@ -23,14 +24,21 @@ pub const log = struct {
|
|||
Info,
|
||||
};
|
||||
|
||||
pub const Output = opaque {};
|
||||
|
||||
pub const Output = opaque {
|
||||
extern var OC_LOG_DEFAULT_OUTPUT: ?*Output;
|
||||
extern fn oc_log_set_level(level: Level) void;
|
||||
extern fn oc_log_set_output(output: *Output) void;
|
||||
|
||||
pub inline fn default() ?*Output {
|
||||
return OC_LOG_DEFAULT_OUTPUT;
|
||||
}
|
||||
|
||||
const set = oc_log_set_output;
|
||||
};
|
||||
|
||||
extern fn oc_log_set_level(level: Level) void;
|
||||
extern fn oc_log_ext(level: Level, function: [*]const u8, file: [*]const u8, line: c_int, fmt: [*]const u8, ...) void;
|
||||
|
||||
const DEFAULT_OUTPUT = OC_LOG_DEFAULT_OUTPUT;
|
||||
const setLevel = oc_log_set_level;
|
||||
|
||||
pub fn info(comptime fmt: []const u8, args: anytype, source: std.builtin.SourceLocation) void {
|
||||
ext(Level.Info, fmt, args, source);
|
||||
|
@ -66,7 +74,7 @@ pub fn assert(condition: bool, comptime fmt: []const u8, args: anytype, source:
|
|||
_ = std.fmt.bufPrintZ(&format_buf, fmt, args) catch 0;
|
||||
var line: c_int = @intCast(source.line);
|
||||
|
||||
oc_assert_fail(source.file.ptr, source.fn_name.ptr, line, "", format_buf[0..].ptr);
|
||||
oc_assert_fail(source.file.ptr, source.fn_name.ptr, line, "assertion failed", format_buf[0..].ptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,6 +93,28 @@ pub fn abort(comptime fmt: []const u8, args: anytype, source: std.builtin.Source
|
|||
pub const ListElt = extern struct {
|
||||
prev: ?*ListElt,
|
||||
next: ?*ListElt,
|
||||
|
||||
pub fn entry(self: *ListElt, comptime ParentType: type, comptime field_name_in_parent: []const u8) *ParentType {
|
||||
return @fieldParentPtr(ParentType, field_name_in_parent, self);
|
||||
}
|
||||
|
||||
pub fn nextEntry(comptime ParentType: type, comptime field_name_in_parent: []const u8, elt_parent: *ParentType) ?*ParentType {
|
||||
const elt: ?*ListElt = @field(elt_parent, field_name_in_parent);
|
||||
if (elt.next) |next| {
|
||||
return next.entry(ParentType, field_name_in_parent);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn prevEntry(comptime ParentType: type, comptime field_name_in_parent: []const u8, elt_parent: *ParentType) ?*ParentType {
|
||||
const elt: ?*ListElt = @field(elt_parent, field_name_in_parent);
|
||||
if (elt.prev) |prev| {
|
||||
return prev.entry(ParentType, field_name_in_parent);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
pub const List = extern struct {
|
||||
|
@ -110,22 +140,72 @@ pub const List = extern struct {
|
|||
return self.last;
|
||||
}
|
||||
|
||||
pub fn insert(self: *List, after_elt: *ListElt, elt: *ListElt) void {
|
||||
pub fn firstEntry(self: *List, comptime EltParentType: type, comptime field_name_in_parent: []const u8) ?*EltParentType {
|
||||
if (self.first) |elt| {
|
||||
return elt.entry(EltParentType, field_name_in_parent);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn lastEntry(self: *List, comptime EltParentType: type, comptime field_name_in_parent: []const u8) ?*EltParentType {
|
||||
if (self.last) |elt| {
|
||||
return elt.entry(EltParentType, field_name_in_parent);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const IterDirection = enum {
|
||||
Forward,
|
||||
Backward,
|
||||
};
|
||||
|
||||
pub fn makeIter(comptime direction: IterDirection, comptime EltParentType: type, comptime field_name_in_parent: []const u8) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
item: ?*ListElt,
|
||||
|
||||
pub fn next(self: *Self) ?*EltParentType {
|
||||
if (self.item) |elt| {
|
||||
var entry: *EltParentType = elt.entry(EltParentType, field_name_in_parent);
|
||||
self.item = if (direction == .Forward) elt.next else elt.prev;
|
||||
return entry;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn iter(self: *const List, comptime EltParentType: type, comptime field_name_in_parent: []const u8) makeIter(.Forward, EltParentType, field_name_in_parent) {
|
||||
const Iter = makeIter(.Forward, EltParentType, field_name_in_parent);
|
||||
return Iter{
|
||||
.item = self.first,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn iterReverse(self: *const List, comptime EltParentType: type, comptime field_name_in_parent: []const u8) makeIter(.Backward, EltParentType, field_name_in_parent) {
|
||||
const Iter = makeIter(.Backward, EltParentType, field_name_in_parent);
|
||||
return Iter{
|
||||
.item = self.last,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn insert(self: *List, after_elt: ?*ListElt, elt: *ListElt) void {
|
||||
elt.prev = after_elt;
|
||||
elt.next = after_elt.next;
|
||||
if (after_elt.next != null) {
|
||||
after_elt.next.prev = elt;
|
||||
if (after_elt.next) |elt_next| {
|
||||
after_elt.next.prev = elt_next;
|
||||
} else {
|
||||
self.last = elt;
|
||||
}
|
||||
after_elt.next = elt;
|
||||
}
|
||||
|
||||
pub fn insertBefore(self: *List, before_elt: *ListElt, elt: *ListElt) void {
|
||||
pub fn insertBefore(self: *List, before_elt: ?*ListElt, elt: *ListElt) void {
|
||||
elt.next = before_elt;
|
||||
elt.prev = before_elt.prev;
|
||||
if (before_elt.prev != null) {
|
||||
before_elt.prev.next = elt;
|
||||
if (before_elt.prev) |elt_prev| {
|
||||
before_elt.prev.next = elt_prev;
|
||||
} else {
|
||||
self.first = elt;
|
||||
}
|
||||
|
@ -153,19 +233,25 @@ pub const List = extern struct {
|
|||
pub fn push(self: *List, elt: *ListElt) void {
|
||||
elt.next = self.first;
|
||||
elt.prev = null;
|
||||
if (self.first != null) {
|
||||
self.first.prev = elt;
|
||||
if (self.first) |elt_first| {
|
||||
elt_first.prev = elt;
|
||||
} else {
|
||||
self.last = elt;
|
||||
}
|
||||
self.first = elt;
|
||||
}
|
||||
|
||||
pub fn pop(self: *List) ListElt {
|
||||
var elt: *ListElt = begin(self);
|
||||
if (elt != end(self)) {
|
||||
remove(self, elt);
|
||||
return elt;
|
||||
pub fn pop(self: *List) ?*ListElt {
|
||||
if (self.begin()) |elt_begin| {
|
||||
remove(self, elt_begin);
|
||||
return elt_begin;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn popEntry(self: *List, comptime EltParentType: type, comptime field_name_in_parent: []const u8) ?*EltParentType {
|
||||
if (self.pop()) |elt| {
|
||||
return elt.entry(EltParentType, field_name_in_parent);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -173,19 +259,18 @@ pub const List = extern struct {
|
|||
pub fn pushBack(self: *List, elt: *ListElt) void {
|
||||
elt.prev = self.last;
|
||||
elt.next = null;
|
||||
if (self.last != null) {
|
||||
self.last.next = elt;
|
||||
if (self.last) |last_elt| {
|
||||
last_elt.next = elt;
|
||||
} else {
|
||||
self.first = elt;
|
||||
}
|
||||
self.last = elt;
|
||||
}
|
||||
|
||||
pub fn popBack(self: *List) ListElt {
|
||||
var elt: *ListElt = last(self);
|
||||
if (elt != end(self)) {
|
||||
remove(self, elt);
|
||||
return elt;
|
||||
pub fn popBack(self: *List) ?*ListElt {
|
||||
if (self.last()) |last_elt| {
|
||||
remove(self, last_elt);
|
||||
return last_elt;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -220,6 +305,20 @@ pub const ArenaChunk = extern struct {
|
|||
cap: u64,
|
||||
};
|
||||
|
||||
pub const ArenaScope = extern struct {
|
||||
arena: *Arena,
|
||||
chunk: *ArenaChunk,
|
||||
offset: u64,
|
||||
|
||||
extern fn oc_arena_scope_end(scope: ArenaScope) void;
|
||||
pub const end = oc_arena_scope_end;
|
||||
};
|
||||
|
||||
pub const ArenaOptions = extern struct {
|
||||
base: ?*BaseAllocator,
|
||||
reserve: u64,
|
||||
};
|
||||
|
||||
pub const Arena = extern struct {
|
||||
base: ?*BaseAllocator,
|
||||
chunks: List,
|
||||
|
@ -229,11 +328,10 @@ pub const Arena = extern struct {
|
|||
extern fn oc_arena_init_with_options(arena: *Arena, options: *ArenaOptions) void;
|
||||
extern fn oc_arena_cleanup(arena: *Arena) void;
|
||||
extern fn oc_arena_push(arena: *Arena, size: u64) ?[*]u8;
|
||||
extern fn oc_arena_push_aligned(arena: *Arena, size: u64, alignment: u32) ?[*]u8;
|
||||
extern fn oc_arena_clear(arena: *Arena) void;
|
||||
extern fn oc_arena_scope_begin(arena: *Arena) ArenaScope;
|
||||
extern fn oc_arena_scope_end(scope: ArenaScope) void;
|
||||
|
||||
extern fn oc_scratch() *Arena;
|
||||
extern fn oc_scratch_next(used: *Arena) *Arena;
|
||||
extern fn oc_scratch_begin() ArenaScope;
|
||||
extern fn oc_scratch_begin_next(used: *Arena) ArenaScope;
|
||||
|
@ -249,50 +347,38 @@ pub const Arena = extern struct {
|
|||
return arena;
|
||||
}
|
||||
pub const deinit = oc_arena_cleanup;
|
||||
pub fn push(arena: *Arena, size: u64) ?[]u8 {
|
||||
if (oc_arena_push(arena, size)) |mem| {
|
||||
return mem[0..size];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub const clear = oc_arena_clear;
|
||||
pub const scopeBegin = oc_arena_scope_begin;
|
||||
pub const scopeEnd = oc_arena_scope_end;
|
||||
|
||||
pub fn pushType(arena: *Arena, comptime T: type) ?*T {
|
||||
if (arena.push(@sizeOf(T))) |mem| {
|
||||
std.debug.assert(mem.len >= @sizeOf(T));
|
||||
return @alignCast(@ptrCast(mem.ptr));
|
||||
pub fn push(arena: *Arena, size: usize) AllocError![]u8 {
|
||||
return arena.pushAligned(size, 1);
|
||||
}
|
||||
|
||||
return null;
|
||||
pub fn pushAligned(arena: *Arena, size: usize, alignment: u32) AllocError![]u8 {
|
||||
if (oc_arena_push_aligned(arena, size, alignment)) |mem| {
|
||||
return mem[0..size];
|
||||
}
|
||||
return AllocError.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn pushArray(arena: *Arena, comptime T: type, count: u64) ?[]T {
|
||||
if (arena.push(@sizeOf(T) * count)) |mem| {
|
||||
pub fn pushType(arena: *Arena, comptime T: type) AllocError!*T {
|
||||
var mem: []u8 = try arena.pushAligned(@sizeOf(T), @alignOf(T));
|
||||
assert(mem.len >= @sizeOf(T), "need at least {} bytes, but got {}", .{ mem.len, @sizeOf(T) }, @src());
|
||||
var p: *T = @alignCast(@ptrCast(mem.ptr));
|
||||
return p;
|
||||
}
|
||||
|
||||
pub fn pushArray(arena: *Arena, comptime T: type, count: usize) AllocError![]T {
|
||||
var mem: []u8 = try arena.pushAligned(@sizeOf(T) * count, @alignOf(T));
|
||||
std.debug.assert(mem.len >= @sizeOf(T) * count);
|
||||
var items: [*]T = @alignCast(@ptrCast(mem.ptr));
|
||||
return items[0..count];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub const scratch = oc_scratch;
|
||||
pub const scratchNext = oc_scratch_next;
|
||||
pub const scratchBegin = oc_scratch_begin;
|
||||
pub const scratchBeginNext = oc_scratch_begin_next;
|
||||
pub const scratchEnd = scopeEnd;
|
||||
};
|
||||
|
||||
pub const ArenaScope = extern struct {
|
||||
arena: ?*Arena,
|
||||
chunk: ?*ArenaChunk,
|
||||
offset: u64,
|
||||
};
|
||||
|
||||
pub const ArenaOptions = extern struct {
|
||||
base: ?*BaseAllocator,
|
||||
reserve: u64,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
|
@ -319,25 +405,82 @@ fn stringType(comptime CharType: type) type {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn push(list: *StrList, str: *Str, arena: *Arena) void {
|
||||
var elt: *StrListElt = arena.pushType(ListElt);
|
||||
pub fn push(list: *StrList, str: Str, arena: *Arena) AllocError!void {
|
||||
var elt: *StrListElt = try arena.pushType(StrListElt);
|
||||
elt.string = str;
|
||||
list.append(elt.list_elt);
|
||||
list.list.pushBack(&elt.list_elt);
|
||||
list.elt_count += 1;
|
||||
list.len += str.len;
|
||||
}
|
||||
|
||||
pub fn pushf(list: *StrList, arena: *Arena, comptime format: []const u8, args: anytype) Str {
|
||||
var str = Str.pushf(arena, format, args);
|
||||
list.push(str, arena);
|
||||
pub fn pushSlice(list: *StrList, str: []const CharType, arena: *Arena) AllocError!void {
|
||||
try list.push(Str.fromSlice(str), arena);
|
||||
}
|
||||
|
||||
// TODO
|
||||
// pub fn join(list: *const StrList, arena: *Arena) Str;
|
||||
// const empty = Str{ .ptr = null, .len = 0 };
|
||||
// return list.collate(arena, empty, empty, empty));
|
||||
// }
|
||||
// pub fn collate(list: *StrList, arena: *Arena, prefix: Str, separator: Str, postfix: Str) Str;
|
||||
pub fn pushf(list: *StrList, arena: *Arena, comptime format: []const u8, args: anytype) AllocError!Str {
|
||||
var str = try Str.pushf(arena, format, args);
|
||||
try list.list.push(str, arena);
|
||||
}
|
||||
|
||||
pub fn iter(list: *const StrList) List.makeIter(.Forward, StrListElt, "list_elt") {
|
||||
return list.list.iter(StrListElt, "list_elt");
|
||||
}
|
||||
|
||||
pub fn iterReverse(list: *const StrList) List.makeIter(.Backward, StrListElt, "list_elt") {
|
||||
return list.list.iterReverse(StrListElt, "list_elt");
|
||||
}
|
||||
|
||||
pub fn find(list: *const StrList, needle: *const Str) ?*StrListElt {
|
||||
return list.findSlice(needle.slice());
|
||||
}
|
||||
|
||||
pub fn findSlice(list: *const StrList, needle: []const CharType) ?*StrListElt {
|
||||
var iterator = list.iter();
|
||||
while (iterator.next()) |elt_string| {
|
||||
if (std.mem.eql(CharType, elt_string.string.slice(), needle)) {
|
||||
return elt_string;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn contains(list: *const StrList, needle: *const Str) bool {
|
||||
return list.findSlice(needle.slice()) != null;
|
||||
}
|
||||
|
||||
pub fn containsSlice(list: *const StrList, needle: []const CharType) bool {
|
||||
return list.findSlice(needle) != null;
|
||||
}
|
||||
|
||||
pub fn join(list: *const StrList, arena: *Arena) AllocError!Str {
|
||||
const empty = Str{ .ptr = null, .len = 0 };
|
||||
return try list.collate(arena, empty, empty, empty);
|
||||
}
|
||||
|
||||
pub fn collate(list: *const StrList, arena: *Arena, prefix: Str, separator: Str, postfix: Str) AllocError!Str {
|
||||
var str: Str = undefined;
|
||||
str.len = @intCast(prefix.len + list.len + (list.elt_count - 1) * separator.len + postfix.len);
|
||||
str.ptr = (try arena.pushArray(CharType, str.len + 1)).ptr;
|
||||
@memcpy(str.ptr.?[0..prefix.len], prefix.slice());
|
||||
|
||||
var offset = prefix.len;
|
||||
|
||||
var iterator = list.iter();
|
||||
var index: usize = 0;
|
||||
while (iterator.next()) |list_str| {
|
||||
if (index > 0) {
|
||||
@memcpy(str.ptr.?[offset .. offset + separator.len], separator.slice());
|
||||
offset += separator.len;
|
||||
}
|
||||
@memcpy(str.ptr.?[offset .. offset + list_str.string.len], list_str.string.slice());
|
||||
offset += list_str.string.len;
|
||||
index += 1;
|
||||
}
|
||||
|
||||
@memcpy(str.ptr.?[offset .. offset + postfix.len], postfix.slice());
|
||||
str.ptr.?[str.len] = 0;
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
||||
const Str = @This();
|
||||
|
@ -408,8 +551,84 @@ fn stringType(comptime CharType: type) type {
|
|||
return str;
|
||||
}
|
||||
|
||||
// TODO
|
||||
// pub fn split() void;
|
||||
pub fn join(arena: *Arena, strings: []const []const CharType) AllocError!Str {
|
||||
const empty = &[_]CharType{};
|
||||
return collate(arena, strings, empty, empty, empty);
|
||||
}
|
||||
|
||||
pub fn collate(
|
||||
arena: *Arena,
|
||||
strings: []const []const CharType,
|
||||
prefix: []const CharType,
|
||||
separator: []const CharType,
|
||||
postfix: []const CharType,
|
||||
) AllocError!Str {
|
||||
var strings_total_len: usize = 0;
|
||||
for (strings) |s| {
|
||||
strings_total_len += s.len;
|
||||
}
|
||||
|
||||
var str: Str = undefined;
|
||||
str.len = prefix.len + strings_total_len + (strings.len - 1) * separator.len + postfix.len;
|
||||
str.ptr = (try arena.pushArray(CharType, str.len + 1)).ptr;
|
||||
@memcpy(str.ptr.?[0..prefix.len], prefix);
|
||||
|
||||
var offset = prefix.len;
|
||||
|
||||
for (strings, 0..) |list_str, index| {
|
||||
if (index > 0) {
|
||||
@memcpy(str.ptr.?[offset .. offset + separator.len], separator);
|
||||
offset += separator.len;
|
||||
}
|
||||
@memcpy(str.ptr.?[offset .. offset + list_str.len], list_str);
|
||||
offset += list_str.len;
|
||||
}
|
||||
|
||||
@memcpy(str.ptr.?[offset .. offset + postfix.len], postfix);
|
||||
offset += postfix.len;
|
||||
str.ptr.?[str.len] = 0;
|
||||
return str;
|
||||
}
|
||||
|
||||
pub fn split(str: *const Str, arena: *Arena, separators: StrList) AllocError!StrList {
|
||||
var list = StrList.init();
|
||||
if (str.ptr == null) {
|
||||
return list;
|
||||
}
|
||||
|
||||
const ptr = str.ptr.?;
|
||||
|
||||
var offset: usize = 0;
|
||||
var offset_substring: usize = 0;
|
||||
|
||||
while (offset < str.len) {
|
||||
const haystack = ptr[offset..str.len];
|
||||
var separator_iter = separators.iter();
|
||||
while (separator_iter.next()) |list_sep| {
|
||||
if (std.mem.startsWith(CharType, haystack, list_sep.string.slice()) and list_sep.string.len > 0) {
|
||||
var substr = ptr[offset_substring..offset];
|
||||
if (separators.containsSlice(substr)) {
|
||||
substr = ptr[offset..offset];
|
||||
}
|
||||
try list.pushSlice(substr, arena);
|
||||
|
||||
// -1 / +1 to account for offset += 1 at the end of the loop
|
||||
offset += list_sep.string.len - 1;
|
||||
offset_substring = offset + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
if (offset_substring != offset) {
|
||||
var substr = ptr[offset_substring..offset];
|
||||
try list.pushSlice(substr, arena);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
pub fn cmp(a: *const Str, b: *const Str) std.math.Order {
|
||||
if (CharType != u8) {
|
||||
|
|
Loading…
Reference in New Issue