From 940221c6240759c4dc1952b1edee458220ffda63 Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Thu, 11 May 2023 11:57:05 -0500 Subject: [PATCH] cleanup: removed dependency on libc's strftime Removed needing to link against: strftime mktime localtime gmtime --- CHANGELOG | 3 + core/runtime/platform/onyx/platform.onyx | 4 - core/runtime/platform/wasi/platform.onyx | 15 +- core/std.onyx | 5 +- core/time/date.onyx | 6 +- core/time/time.onyx | 170 +++++++++++++++++++---- docs/ideas/platform_layer.md | 4 - runtime/onyx_runtime.c | 6 - runtime/src/ort_time.h | 28 ---- 9 files changed, 164 insertions(+), 77 deletions(-) delete mode 100644 runtime/src/ort_time.h diff --git a/CHANGELOG b/CHANGELOG index 7327c59f..1d076c9e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -7,12 +7,15 @@ Additions: * `#distinct` types can now be made over any type. - Used to be only primitives. * New `logo-new-256.ico` for favicon on website. +* `u8.to_upper` and `u8.to_lower` Removals: Changes: * Undefined function imports in `onyx run` no longer cause an immediate error. - Instead, an error is produced when the function is called. +* API for time remains the same, but reduced dependencies on external time things, + like strftime, localtime and mktime. Bugfixes: * Fixed missing `use core` in `optional.onyx`. diff --git a/core/runtime/platform/onyx/platform.onyx b/core/runtime/platform/onyx/platform.onyx index 8c750b72..5936fcc4 100644 --- a/core/runtime/platform/onyx/platform.onyx +++ b/core/runtime/platform/onyx/platform.onyx @@ -65,10 +65,6 @@ ProcessData :: #distinct u64 // Time and sleep __time :: () -> u64 --- - __time_localtime :: (time: u64, tm: &core.time.Timestamp) -> void --- - __time_gmtime :: (time: u64, tm: &core.time.Timestamp) -> void --- - __time_mktime :: (tm: &time.Timestamp) -> i64 --- - __time_strftime :: (buf: [] u8, format: cstr, tm: &time.Timestamp) -> u32 --- // Processes __process_spawn :: (path: str, args: [] str, non_blocking_io: bool, starting_directory: str) -> ProcessData --- diff --git a/core/runtime/platform/wasi/platform.onyx b/core/runtime/platform/wasi/platform.onyx index 6adeab59..0394fb3a 100644 --- a/core/runtime/platform/wasi/platform.onyx +++ b/core/runtime/platform/wasi/platform.onyx @@ -13,7 +13,8 @@ use wasi { IOVec, SubscriptionTagged, Subscription, Event, Size, poll_oneoff, fd_write, fd_datasync, fd_read, args_get, args_sizes_get, - proc_exit + proc_exit, + clock_time_get, Timestamp } use runtime { __runtime_initialize, @@ -29,7 +30,7 @@ Supports_Files :: true Supports_Directories :: true Supports_Os :: true Supports_Processes :: false -Supports_Time :: false +Supports_Time :: true Supports_Networking :: false Supports_Type_Info :: true Supports_Threads :: false @@ -80,6 +81,16 @@ __sleep :: (milliseconds: u32) { error_code := poll_oneoff(&subscription, &event, 1, &number_of_events); } +__time :: () -> i64 { + time: Timestamp; + if clock_time_get(.Realtime, 1, &time) != .Success { + return 0; + } + + // Need to return in milliseconds, not nanoseconds. + return time / 1000000; +} + // Sets up everything needed for execution. __start :: () { diff --git a/core/std.onyx b/core/std.onyx index 9764cdf4..22b9fe0e 100644 --- a/core/std.onyx +++ b/core/std.onyx @@ -52,6 +52,8 @@ use runtime #load "./test/testing" +#load "./time/time" + #load "./misc/arg_parse" #load "./misc/method_ops" @@ -81,9 +83,6 @@ use runtime #load "./os/process" } -#if runtime.platform.Supports_Time { - #load "./time/time" -} #if runtime.platform.Supports_Networking { #load "./net/net" diff --git a/core/time/date.onyx b/core/time/date.onyx index ddcf148e..eeb21b3e 100644 --- a/core/time/date.onyx +++ b/core/time/date.onyx @@ -18,10 +18,8 @@ Date :: struct { } - #if runtime.platform.Supports_Time { - today :: () -> Date { - return now()->as_date(); - } + today :: () -> Date { + return now()->as_date(); } add_months :: (d: Date, days: i32) -> Date { diff --git a/core/time/time.onyx b/core/time/time.onyx index 91d1bef0..b497a12d 100644 --- a/core/time/time.onyx +++ b/core/time/time.onyx @@ -2,20 +2,11 @@ package core.time use core use core.os +use core.io +use core.string use core.conv use runtime -use runtime.platform { - __time_gmtime, - __time_localtime, - __time_mktime, - __time_strftime -} - -#if !runtime.platform.Supports_Time { - #error "'core.time' should only be used with the Onyx runtime, for now."; -} - // // This structure has to match the 'struct tm' // defined in time.h of POSIX. @@ -67,21 +58,21 @@ now :: () -> Timestamp { // // Localtime operates on seconds, while os.time // returns milliseconds. - return localtime(current_time / 1000); + return gmtime(current_time / 1000); } -to_epoch :: __time_mktime +to_epoch :: tm_to_time localtime :: #match #local {} #overload -localtime :: __time_localtime +localtime :: time_to_tm #overload localtime :: (seconds: u64) -> Timestamp { t: Timestamp; - __time_localtime(seconds, &t); + localtime(seconds, &t); return t; } @@ -89,31 +80,98 @@ localtime :: (seconds: u64) -> Timestamp { gmtime :: #match #local {} #overload -gmtime :: __time_gmtime +gmtime :: time_to_tm #overload gmtime :: (seconds: u64) -> Timestamp { t: Timestamp; - __time_gmtime(seconds, &t); + gmtime(seconds, &t); return t; } -strftime :: (buf: [] u8, format: [] u8, tm: &Timestamp) -> str { - f := cast(cstr) core.alloc.from_stack(format.length + 1); - core.memory.copy(f, format.data, format.length); - f[format.length] = 0; +#local weekdays := str.[ "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" ]; +#local monthnames := str.[ "january", "february", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december" ]; + +strftime :: (buf: [] u8, format_: [] u8, tm: &Timestamp) -> str { + s := io.buffer_stream_make(buf, fixed=true); + w := io.writer_make(&s, 0); + + format := format_; - len := __time_strftime(buf, f, tm); - return buf[0..len]; + while format.length > 0 { + c := format[0]; + switch c { + case '%' { + string.advance(&format); + switch format[0] { + case 'A', 'a' { + day := weekdays[tm.wday]; + len := 3 if format[0] == 'a' else day.length; + + io.write_format(&w, "{}{}", day[0]->to_upper(), day[1..len]); + } + + case 'B', 'b', 'h' { + mon := monthnames[tm.mon]; + len := 3 if format[0] != 'B' else mon.length; + + io.write_format(&w, "{}{}", mon[0]->to_upper(), mon[1..len]); + } + + case 'd', 'e' { + io.write_format(&w, "{}", tm.mday); + } + + case 'D' { + io.write_format(&w, "{}/{}/{}", tm.mon + 1, tm.mday, tm.year - 100 if tm.year >= 100 else tm.year); + } + + case 'H' do io.write_format(&w, "{w2}", tm.hour); + case 'I' do io.write_format(&w, "{w2}", hour_to_12h(tm.hour)); + case 'j' do io.write_format(&w, "{}", tm.yday + 1); + case 'm' do io.write_format(&w, "{}", tm.mon + 1); + case 'M' do io.write_format(&w, "{w2}", tm.min); + case 'p' do io.write(&w, "am" if tm.hour < 12 else "pm"); + + case 'r' { + io.write_format(&w, "{w2}:{w2}:{w2} {}", hour_to_12h(tm.hour), tm.min, tm.sec, + "am" if tm.hour < 12 else "pm"); + } + + case 'R' { + io.write_format(&w, "{w2}:{w2}", tm.hour, tm.min); + } + + case 'S' do io.write_format(&w, "{w2}", tm.sec); + case 'T' do io.write_format(&w, "{w2}:{w2}:{w2}", tm.hour, tm.min, tm.sec); + case 'w' do io.write_format(&w, "{}", tm.wday); + case 'Y' do io.write_format(&w, "{}", tm.year + 1900); + case 'y' do io.write_format(&w, "{w2}", tm.year - 100 if tm.year >= 100 else tm.year); + case '%' do io.write(&w, "%"); + } + } + + case #default { + io.write(&w, c); + } + } + + string.advance(&format); + } + + hour_to_12h :: (h: i32) -> i32 { + if h == 0 do return 12; + if h <= 12 do return h; + return h - 12; + } + + return string.as_str(&s); } strptime :: (buf_: [] u8, format_: [] u8, tm: &Timestamp) -> bool { use core {*} - #persist weekdays := str.[ "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" ]; - #persist monthnames := str.[ "january", "february", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december" ]; - buf := buf_; format := format_; @@ -338,3 +396,63 @@ strptime :: (buf_: [] u8, format_: [] u8, tm: &Timestamp) -> bool { } } +#local +time_to_tm :: (ts: i64, tm: &Timestamp) { + leapyear :: (year) => year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); + + #persist year_lengths := ([12] i64).[ + i64.[31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], + i64.[31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], + ]; + + year := 1970; + dayclock := ts % 86400; + dayno := ts / 86400; + + tm.sec = ~~(dayclock % 60); + tm.min = ~~((dayclock % 3600) / 60); + tm.hour = ~~(dayclock / 3600); + tm.wday = ~~((dayno + 4) % 7); + while true { + yearsize := 366 if leapyear(year) else 365; + if dayno >= ~~yearsize { + dayno -= ~~yearsize; + year += 1; + } else { + break; + } + } + + tm.year = ~~(year - 1900); + tm.yday = ~~dayno; + + mon := 0; + year_length: [] i64 = year_lengths[1 if leapyear(year) else 0]; + while dayno >= year_length[mon] { + dayno -= year_length[mon]; + mon += 1; + } + tm.mon = ~~mon; + tm.mday = ~~(dayno + 1); + tm.isdst = 0; +} + +#local +tm_to_time :: (tm: &Timestamp) -> i64 { + y := cast(i64, tm.year + 1900); + m := cast(i64, tm.mon + 1); + if m <= 2 { + y -= 1; + m += 12; + } + d := cast(i64, tm.mday); + h := cast(i64, tm.hour); + mi := cast(i64, tm.min); + s := cast(i64, tm.sec); + + return (365 * y + y / 4 - y / 100 + y / 400 + 3 * (m + 1) / 5 + 30 * m + d - 719561) * 86400 + + 3600 * h + + 60 * mi + + s; +} + diff --git a/docs/ideas/platform_layer.md b/docs/ideas/platform_layer.md index e1ddb2b2..de0b39a6 100644 --- a/docs/ideas/platform_layer.md +++ b/docs/ideas/platform_layer.md @@ -94,10 +94,6 @@ this document will serve as that "header file" ### Procedures - `__time() -> u64` -- `__time_localtime(time: u64, tm: ^core.time.Timestamp) -> void` -- `__time_gmtime(time: u64, tm: ^core.time.Timestamp) -> void` -- `__time_gmtime(tm: ^core.time.Timestamp) -> i64` -- `__time_strftime(buf: [] u8, format: cstr, tm: ^core.time.Timestamp) -> u32` ### Values diff --git a/runtime/onyx_runtime.c b/runtime/onyx_runtime.c index 0472fa6c..a85e9f6a 100644 --- a/runtime/onyx_runtime.c +++ b/runtime/onyx_runtime.c @@ -28,7 +28,6 @@ #include "src/ort_threads.h" #include "src/ort_processes.h" #include "src/ort_os.h" -#include "src/ort_time.h" #include "src/ort_cptr.h" #include "src/ort_net.h" @@ -73,11 +72,6 @@ ONYX_LIBRARY { ONYX_FUNC(__lookup_env) ONYX_FUNC(__register_cleanup) - ONYX_FUNC(__time_localtime) - ONYX_FUNC(__time_gmtime) - ONYX_FUNC(__time_strftime) - ONYX_FUNC(__time_mktime) - ONYX_FUNC(__net_create_socket) ONYX_FUNC(__net_close_socket) ONYX_FUNC(__net_setting) diff --git a/runtime/src/ort_time.h b/runtime/src/ort_time.h deleted file mode 100644 index 86f75ba6..00000000 --- a/runtime/src/ort_time.h +++ /dev/null @@ -1,28 +0,0 @@ - -// -// Dates and Times -// -ONYX_DEF(__time_localtime, (WASM_I64, WASM_I32), ()) { - u64 t = params->data[0].of.i64; - *(struct tm *) ONYX_PTR(params->data[1].of.i32) = *localtime(&t); - return NULL; -} - -ONYX_DEF(__time_gmtime, (WASM_I64, WASM_I32), ()) { - u64 t = params->data[0].of.i64; - *(struct tm *) ONYX_PTR(params->data[1].of.i32) = *gmtime(&t); - return NULL; -} - -ONYX_DEF(__time_strftime, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) { - u32 len = strftime(ONYX_PTR(params->data[0].of.i32), params->data[1].of.i32, ONYX_PTR(params->data[2].of.i32), ONYX_PTR(params->data[3].of.i32)); - results->data[0] = WASM_I32_VAL(len); - return NULL; -} - -ONYX_DEF(__time_mktime, (WASM_I32), (WASM_I64)) { - struct tm *time = (struct tm *) ONYX_PTR(params->data[0].of.i32); - results->data[0] = WASM_I64_VAL(mktime(time)); - return NULL; -} - -- 2.25.1