alloc_arena := cast(^Arena) data;
if aa == .Alloc {
- // An allocation of this size does not fit into a single arena.
+ // An allocation of this size does not fit into a single arena,
+ // so make a new "special" arena that only stores this allocation.
if size > alloc_arena.arena_size - sizeof rawptr {
- return null;
+ ret_arena := cast(^ArenaBlock) raw_alloc(alloc_arena.backing_allocator, size + sizeof rawptr);
+ new_arena := cast(^ArenaBlock) raw_alloc(alloc_arena.backing_allocator, alloc_arena.arena_size);
+
+ if ret_arena == null || new_arena == null do return null;
+
+ alloc_arena.size = sizeof rawptr;
+
+ alloc_arena.current_arena.next = ret_arena;
+ ret_arena.next = new_arena;
+ new_arena.next = null;
+
+ alloc_arena.current_arena = new_arena;
+
+ return cast(rawptr) (cast(^u8) ret_arena + sizeof rawptr);
}
if alloc_arena.size % align != 0 {
}
#overload #precedence 10000
-as_iterator :: (x: $T/ImplicitIterator) => {
- extract_iterator_type :: macro (x: $T, next: (T) -> ($I, bool)) -> Iterator(I) {
- x->iter_open();
-
- return .{
- data = x,
- next = next,
- close = x.iter_close
- };
- }
-
- return extract_iterator_type(x, T.iter_next);
+as_iterator :: (x: ^$T/ImplicitIterator) => {
+ x->iter_open();
+ return generator(x, T.iter_next, T.iter_close);
}
//
// Maybe at some point an alternate allocator would be good
// for this? For now, I think the temporary allocator is sufficient.
+generator :: #match #local {}
+
+#overload
generator :: (ctx: ^$Ctx, gen: (^Ctx) -> ($T, bool)) -> Iterator(T) {
v := raw_alloc(context.temp_allocator, sizeof Ctx);
core.memory.copy(v, ctx, sizeof Ctx);
};
}
+#overload
+generator :: (ctx: ^$Ctx, gen: (^Ctx) -> ($T, bool), close: (^Ctx) -> void) -> Iterator(T) {
+ v := raw_alloc(context.temp_allocator, sizeof Ctx);
+ core.memory.copy(v, ctx, sizeof Ctx);
+
+ return .{
+ data = v,
+ next = gen,
+ close = close
+ };
+}
+
#if runtime.Multi_Threading_Enabled {
#local sync :: core.sync
--- /dev/null
+package core.encoding.csv
+
+use core {string, array, iter, conv, io}
+use core.misc {any_as}
+use runtime.info {
+ get_type_info,
+ Type_Info_Struct
+}
+
+CSV :: struct (Output_Type: type_expr) {
+ entries: [..] Output_Type;
+}
+
+CSV_Column :: struct {
+ name: str;
+}
+
+#inject CSV {
+ make :: ($T: type_expr) => {
+ r := CSV(T).{};
+ r.entries = make(typeof r.entries);
+
+ return r;
+ }
+
+ delete :: (csv: ^CSV) {
+ delete(^csv.entries);
+ }
+
+ ingress :: (csv: ^CSV, contents: str, headers_present := true) -> bool {
+ Header :: struct {
+ type: type_expr;
+ offset: i32;
+ }
+
+ s := contents;
+ any_headers := make([..] Header);
+ defer delete(^any_headers);
+
+ output_type_info: ^Type_Info_Struct = ~~ get_type_info(csv.Output_Type);
+
+ if headers_present {
+ header_line, s' := string.bisect(s, #char "\n");
+ for header: string.split_iter(header_line, #char ",") {
+ member := array.first(output_type_info.members, #(do {
+ if tag := array.first(it.tags, #(it.type == CSV_Column)); tag {
+ return any_as(*tag, CSV_Column).name == header;
+ }
+
+ return false;
+ }));
+
+ any_headers << (.{member.type, member.offset}) if member else .{void, -1};
+ }
+
+ } else {
+ for ^member: output_type_info.members {
+ any_headers << .{ member.type, member.offset };
+ }
+ }
+
+ for line: string.split_iter(s, #char "\n") {
+ out: csv.Output_Type;
+
+ for entry:
+ string.split_iter(line, #char ",")
+ |> iter.enumerate()
+ {
+ header := ^any_headers[entry.index];
+ if header.offset == -1 do continue;
+
+ target := cast(^u8) ^out + header.offset;
+
+ if header.type == str {
+ *cast(^str) target = string.alloc_copy(entry.value);
+ } else {
+ conv.parse_any(target, header.type, entry.value);
+ }
+ }
+
+ csv.entries << out;
+ }
+ }
+
+ egress :: (csv: ^CSV, writer: ^io.Writer, include_headers := true) {
+ output_type_info: ^Type_Info_Struct = ~~ get_type_info(csv.Output_Type);
+
+ if include_headers {
+ for ^member: output_type_info.members {
+ if !#first do io.write(writer, ",");
+
+ if tag := array.first(member.tags, #(it.type == CSV_Column)); tag {
+ io.write(writer, any_as(*tag, CSV_Column).name);
+ } else {
+ io.write(writer, member.name);
+ }
+ }
+
+ io.write(writer, "\n");
+ }
+
+ for ^it: csv.entries {
+ for ^member: output_type_info.members {
+ if !#first do io.write(writer, ",");
+
+ io.write_format_va(writer, "{}", .[ .{cast(^u8) it + member.offset, member.type} ]);
+ }
+
+ io.write(writer, "\n");
+ }
+ }
+}
+
done = false;
}
+#overload delete :: reader_free
reader_free :: (use reader: ^Reader) {
memory.free_slice(^buffer, buffer_allocator);
}
to_any :: macro (x: ^$T) => any.{x, T};
+any_as :: (a: any, $T: type_expr) -> ^T {
+ if a.type != T do return null;
+ return cast(^T) a.data;
+}
+
any_deference :: (v: any) -> any {
t := get_type_info(v.type);
if t.kind == .Pointer {
#load "./misc/any_utils"
+#load "./encoding/csv"
+
#local runtime :: package runtime
#if runtime.runtime == .Wasi || runtime.runtime == .Onyx {
#load "./os/file"
#load "./os/process"
#load "./time/time"
+ #load "./time/date"
#load "./net/net"
#load "./net/tcp"
return strarr[0 .. delim_count + 1];
}
+split_iter :: (s: str, delim: u8) -> Iterator(str) {
+ return iter.generator(
+ ^.{ s = s, delim = delim },
+
+ (ctx: ^$T) -> (str, bool) {
+ if string.empty(ctx.s) {
+ return "", false;
+ }
+
+ ret: str;
+ ret, ctx.s = bisect(ctx.s, ctx.delim);
+ return ret, true;
+ }
+ );
+}
+
//
// Splits a string into two parts, divided by the
// first instance of the provided character. Either
--- /dev/null
+package core.time
+
+use core
+
+@conv.Custom_Format.{_format}
+@conv.Custom_Parse.{_parse}
+Date :: struct {
+ year: i32;
+
+ // Note that `month` and `day` are 0-based.
+ month, day: i32;
+}
+
+#inject Date {
+ month_durations := u32.[31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
+
+ add_months :: (d: Date, days: i32) -> Date {
+ nd := d;
+
+ nd.month += 1;
+ if nd.month >= 12 {
+ nd.month = 0;
+ nd.year += 1;
+ }
+
+ return nd;
+ }
+
+ add_days :: (d: Date, days: i32) -> Date {
+ nd := d;
+ nd.day += days;
+
+ while nd.day >= Date.month_durations[nd.month] {
+ nd.day -= Date.month_durations[nd.month];
+
+ // February leap year case
+ if nd.month == 1 {
+ if nd.year % 4 == 0 && (nd.year % 100 != 0 || nd.year % 400 == 0) {
+ nd.day -= 1;
+ }
+ }
+
+ nd.month += 1;
+ if nd.month >= 12 {
+ nd.month = 0;
+ nd.year += 1;
+ }
+ }
+
+ return nd;
+ }
+
+ before :: (d1, d2: Date) -> bool {
+ if d1.year != d2.year do return d1.year < d2.year;
+ if d1.month != d2.month do return d1.month < d2.month;
+ return d1.day < d2.day;
+ }
+
+ after :: (d1, d2: Date) -> bool {
+ if d1.year != d2.year do return d1.year > d2.year;
+ if d1.month != d2.month do return d1.month > d2.month;
+ return d1.day > d2.day;
+ }
+
+ _parse :: (d: ^Date, text: str, _: Allocator) -> bool {
+ year, t := string.bisect(text, #char "-");
+ month, t' := string.bisect(t, #char "-");
+ day, t' := string.bisect(t, #char "-");
+
+ d.year = ~~ conv.str_to_i64(year);
+ d.month = ~~ (conv.str_to_i64(month) - 1);
+ d.day = ~~ (conv.str_to_i64(day) - 1);
+
+ return true;
+ }
+
+ _format :: (output: ^conv.Format_Output, format: ^conv.Format, date: ^Date) {
+ conv.format(output, "{}-{}-{}", date.year, date.month + 1, date.day + 1);
+ }
+}
+
+#operator + macro (d: Date, days: i32) => d->add_days(days);
+#operator - macro (d: Date, days: i32) => d->add_days(-days);
+
+#operator == (d1, d2: Date) =>
+ d1.year == d2.year &&
+ d1.month == d2.month &&
+ d1.day == d2.day;
+
use core
q :: (v: ^$C, g: (^C) -> $T) {
- println(v);
+ println(*v);
println(g(v));
println(T);
}
v := 5;
q(^v, _ => false);
-}
\ No newline at end of file
+}