mirror of
https://github.com/odin-lang/Odin.git
synced 2026-01-02 11:12:31 +00:00
Merge branch 'master' of https://github.com/odin-lang/Odin
This commit is contained in:
@@ -90,6 +90,7 @@ load_map_from_string :: proc(src: string, allocator: runtime.Allocator, options
|
||||
if allocated {
|
||||
return v, nil
|
||||
}
|
||||
return strings.clone(v), nil
|
||||
}
|
||||
return strings.clone(val)
|
||||
}
|
||||
|
||||
@@ -363,8 +363,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
||||
}
|
||||
|
||||
v := v
|
||||
v = reflect.any_base(v)
|
||||
ti := type_info_of(v.id)
|
||||
ti := reflect.type_info_base(type_info_of(v.id))
|
||||
|
||||
#partial switch t in ti.variant {
|
||||
case reflect.Type_Info_Struct:
|
||||
|
||||
@@ -143,7 +143,7 @@ when !ODIN_NO_RTTI {
|
||||
@(require_results)
|
||||
any_base :: proc(v: any) -> any {
|
||||
v := v
|
||||
if v != nil {
|
||||
if v.id != nil {
|
||||
v.id = typeid_base(v.id)
|
||||
}
|
||||
return v
|
||||
@@ -151,7 +151,7 @@ any_base :: proc(v: any) -> any {
|
||||
@(require_results)
|
||||
any_core :: proc(v: any) -> any {
|
||||
v := v
|
||||
if v != nil {
|
||||
if v.id != nil {
|
||||
v.id = typeid_core(v.id)
|
||||
}
|
||||
return v
|
||||
|
||||
@@ -1815,3 +1815,11 @@ EPoll_Ctl_Opcode :: enum i32 {
|
||||
DEL = 2,
|
||||
MOD = 3,
|
||||
}
|
||||
|
||||
/*
|
||||
Bits for execveat(2) flags.
|
||||
*/
|
||||
Execveat_Flags_Bits :: enum {
|
||||
AT_SYMLINK_NOFOLLOW = 8,
|
||||
AT_EMPTY_PATH = 12,
|
||||
}
|
||||
|
||||
@@ -749,17 +749,13 @@ getsockopt :: proc {
|
||||
getsockopt_base,
|
||||
}
|
||||
|
||||
// TODO(flysand): clone (probably not in this PR, maybe not ever)
|
||||
|
||||
/*
|
||||
Creates a copy of the running process.
|
||||
Available since Linux 1.0.
|
||||
*/
|
||||
fork :: proc "contextless" () -> (Pid, Errno) {
|
||||
when ODIN_ARCH == .arm64 {
|
||||
// Note(flysand): this syscall is not documented, but the bottom 8 bits of flags
|
||||
// are for exit signal
|
||||
ret := syscall(SYS_clone, Signal.SIGCHLD)
|
||||
ret := syscall(SYS_clone, u64(Signal.SIGCHLD), cast(rawptr) nil, cast(rawptr) nil, cast(rawptr) nil, u64(0))
|
||||
return errno_unwrap(ret, Pid)
|
||||
} else {
|
||||
ret := syscall(SYS_fork)
|
||||
@@ -789,8 +785,8 @@ execve :: proc "contextless" (name: cstring, argv: [^]cstring, envp: [^]cstring)
|
||||
ret := syscall(SYS_execve, cast(rawptr) name, cast(rawptr) argv, cast(rawptr) envp)
|
||||
return Errno(-ret)
|
||||
} else {
|
||||
ret := syscall(SYS_execveat, AT_FDCWD, cast(rawptr) name, cast(rawptr) argv, cast(rawptr) envp)
|
||||
return Errno(-ret)
|
||||
ret := syscall(SYS_execveat, AT_FDCWD, cast(rawptr) name, cast(rawptr) argv, cast(rawptr) envp, i32(0))
|
||||
return Errno(-ret)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2818,7 +2814,7 @@ getrandom :: proc "contextless" (buf: []u8, flags: Get_Random_Flags) -> (int, Er
|
||||
Execute program relative to a directory file descriptor.
|
||||
Available since Linux 3.19.
|
||||
*/
|
||||
execveat :: proc "contextless" (dirfd: Fd, name: cstring, argv: [^]cstring, envp: [^]cstring, flags: FD_Flags = {}) -> (Errno) {
|
||||
execveat :: proc "contextless" (dirfd: Fd, name: cstring, argv: [^]cstring, envp: [^]cstring, flags: Execveat_Flags = {}) -> (Errno) {
|
||||
ret := syscall(SYS_execveat, dirfd, cast(rawptr) name, cast(rawptr) argv, cast(rawptr) envp, transmute(i32) flags)
|
||||
return Errno(-ret)
|
||||
}
|
||||
|
||||
@@ -1303,3 +1303,8 @@ EPoll_Event :: struct #packed {
|
||||
events: EPoll_Event_Kind,
|
||||
data: EPoll_Data,
|
||||
}
|
||||
|
||||
/*
|
||||
Flags for execveat(2) syscall.
|
||||
*/
|
||||
Execveat_Flags :: bit_set[Execveat_Flags_Bits; i32]
|
||||
|
||||
@@ -6,12 +6,26 @@ import "base:intrinsics"
|
||||
|
||||
_ :: intrinsics
|
||||
|
||||
/*
|
||||
Value, specifying whether `core:thread` functionality is available on the
|
||||
current platform.
|
||||
*/
|
||||
IS_SUPPORTED :: _IS_SUPPORTED
|
||||
|
||||
/*
|
||||
Type for a procedure that will be run in a thread, after that thread has been
|
||||
started.
|
||||
*/
|
||||
Thread_Proc :: #type proc(^Thread)
|
||||
|
||||
/*
|
||||
Maximum number of user arguments for polymorphic thread procedures.
|
||||
*/
|
||||
MAX_USER_ARGUMENTS :: 8
|
||||
|
||||
/*
|
||||
Type representing the state/flags of the thread.
|
||||
*/
|
||||
Thread_State :: enum u8 {
|
||||
Started,
|
||||
Joined,
|
||||
@@ -19,44 +33,48 @@ Thread_State :: enum u8 {
|
||||
Self_Cleanup,
|
||||
}
|
||||
|
||||
/*
|
||||
Type representing a thread handle and the associated with that thread data.
|
||||
*/
|
||||
Thread :: struct {
|
||||
using specific: Thread_Os_Specific,
|
||||
flags: bit_set[Thread_State; u8],
|
||||
id: int,
|
||||
procedure: Thread_Proc,
|
||||
|
||||
/*
|
||||
These are values that the user can set as they wish, after the thread has been created.
|
||||
This data is easily available to the thread proc.
|
||||
|
||||
These fields can be assigned to directly.
|
||||
|
||||
Should be set after the thread is created, but before it is started.
|
||||
*/
|
||||
data: rawptr,
|
||||
user_index: int,
|
||||
user_args: [MAX_USER_ARGUMENTS]rawptr,
|
||||
|
||||
/*
|
||||
The context to be used as 'context' in the thread proc.
|
||||
|
||||
This field can be assigned to directly, after the thread has been created, but __before__ the thread has been started.
|
||||
This field must not be changed after the thread has started.
|
||||
|
||||
NOTE: If you __don't__ set this, the temp allocator will be managed for you;
|
||||
If you __do__ set this, then you're expected to handle whatever allocators you set, yourself.
|
||||
|
||||
IMPORTANT:
|
||||
By default, the thread proc will get the same context as `main()` gets.
|
||||
In this situation, the thread will get a new temporary allocator which will be cleaned up when the thread dies.
|
||||
***This does NOT happen when you set `init_context`.***
|
||||
This means that if you set `init_context`, but still have the `temp_allocator` field set to the default temp allocator,
|
||||
then you'll need to call `runtime.default_temp_allocator_destroy(auto_cast the_thread.init_context.temp_allocator.data)` manually,
|
||||
in order to prevent any memory leaks.
|
||||
This call ***must*** be done ***in the thread proc*** because the default temporary allocator uses thread local state!
|
||||
*/
|
||||
// Thread ID.
|
||||
id: int,
|
||||
// The thread procedure.
|
||||
procedure: Thread_Proc,
|
||||
// User-supplied pointer, that will be available to the thread once it is
|
||||
// started. Should be set after the thread has been created, but before
|
||||
// it is started.
|
||||
data: rawptr,
|
||||
// User-supplied integer, that will be available to the thread once it is
|
||||
// started. Should be set after the thread has been created, but before
|
||||
// it is started.
|
||||
user_index: int,
|
||||
// User-supplied array of arguments, that will be available to the thread,
|
||||
// once it is started. Should be set after the thread has been created,
|
||||
// but before it is started.
|
||||
user_args: [MAX_USER_ARGUMENTS]rawptr,
|
||||
// The thread context.
|
||||
// This field can be assigned to directly, after the thread has been
|
||||
// created, but __before__ the thread has been started. This field must
|
||||
// not be changed after the thread has started.
|
||||
//
|
||||
// **Note**: If this field is **not** set, the temp allocator will be managed
|
||||
// automatically. If it is set, the allocators must be handled manually.
|
||||
//
|
||||
// **IMPORTANT**:
|
||||
// By default, the thread proc will get the same context as `main()` gets.
|
||||
// In this situation, the thread will get a new temporary allocator which
|
||||
// will be cleaned up when the thread dies. ***This does NOT happen when
|
||||
// `init_context` field is initialized***.
|
||||
//
|
||||
// If `init_context` is initialized, and `temp_allocator` field is set to
|
||||
// the default temp allocator, then `runtime.default_temp_allocator_destroy()`
|
||||
// procedure needs to be called from the thread procedure, in order to prevent
|
||||
// any memory leaks.
|
||||
init_context: Maybe(runtime.Context),
|
||||
|
||||
// The allocator used to allocate data for the thread.
|
||||
creation_allocator: mem.Allocator,
|
||||
}
|
||||
|
||||
@@ -64,6 +82,9 @@ when IS_SUPPORTED {
|
||||
#assert(size_of(Thread{}.user_index) == size_of(uintptr))
|
||||
}
|
||||
|
||||
/*
|
||||
Type representing priority of a thread.
|
||||
*/
|
||||
Thread_Priority :: enum {
|
||||
Normal,
|
||||
Low,
|
||||
@@ -71,74 +92,178 @@ Thread_Priority :: enum {
|
||||
}
|
||||
|
||||
/*
|
||||
Creates a thread in a suspended state with the given priority.
|
||||
To start the thread, call `thread.start()`.
|
||||
Create a thread in a suspended state with the given priority.
|
||||
|
||||
See `thread.create_and_start()`.
|
||||
This procedure creates a thread that will be set to run the procedure
|
||||
specified by `procedure` parameter with a specified priority. The returned
|
||||
thread will be in a suspended state, until `start()` procedure is called.
|
||||
|
||||
To start the thread, call `start()`. Also the `create_and_start()`
|
||||
procedure can be called to create and start the thread immediately.
|
||||
*/
|
||||
create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^Thread {
|
||||
return _create(procedure, priority)
|
||||
}
|
||||
|
||||
/*
|
||||
Wait for the thread to finish and free all data associated with it.
|
||||
*/
|
||||
destroy :: proc(thread: ^Thread) {
|
||||
_destroy(thread)
|
||||
}
|
||||
|
||||
/*
|
||||
Start a suspended thread.
|
||||
*/
|
||||
start :: proc(thread: ^Thread) {
|
||||
_start(thread)
|
||||
}
|
||||
|
||||
/*
|
||||
Check if the thread has finished work.
|
||||
*/
|
||||
is_done :: proc(thread: ^Thread) -> bool {
|
||||
return _is_done(thread)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Wait for the thread to finish work.
|
||||
*/
|
||||
join :: proc(thread: ^Thread) {
|
||||
_join(thread)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Wait for all threads to finish work.
|
||||
*/
|
||||
join_multiple :: proc(threads: ..^Thread) {
|
||||
_join_multiple(..threads)
|
||||
}
|
||||
|
||||
/*
|
||||
Forcibly terminate a running thread.
|
||||
*/
|
||||
terminate :: proc(thread: ^Thread, exit_code: int) {
|
||||
_terminate(thread, exit_code)
|
||||
}
|
||||
|
||||
/*
|
||||
Yield the execution of the current thread to another OS thread or process.
|
||||
*/
|
||||
yield :: proc() {
|
||||
_yield()
|
||||
}
|
||||
|
||||
/*
|
||||
Run a procedure on a different thread.
|
||||
|
||||
This procedure runs the given procedure on another thread. The context
|
||||
specified by `init_context` will be used as the context in which `fn` is going
|
||||
to execute. The thread will have priority specified by the `priority` parameter.
|
||||
|
||||
**IMPORTANT**: If `init_context` is specified and the default temporary allocator
|
||||
is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
|
||||
in order to free the resources associated with the temporary allocations.
|
||||
*/
|
||||
run :: proc(fn: proc(), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) {
|
||||
create_and_start(fn, init_context, priority, true)
|
||||
}
|
||||
|
||||
/*
|
||||
Run a procedure with one pointer parameter on a different thread.
|
||||
|
||||
This procedure runs the given procedure on another thread. The context
|
||||
specified by `init_context` will be used as the context in which `fn` is going
|
||||
to execute. The thread will have priority specified by the `priority` parameter.
|
||||
|
||||
**IMPORTANT**: If `init_context` is specified and the default temporary allocator
|
||||
is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
|
||||
in order to free the resources associated with the temporary allocations.
|
||||
*/
|
||||
run_with_data :: proc(data: rawptr, fn: proc(data: rawptr), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) {
|
||||
create_and_start_with_data(data, fn, init_context, priority, true)
|
||||
}
|
||||
|
||||
/*
|
||||
Run a procedure with one polymorphic parameter on a different thread.
|
||||
|
||||
This procedure runs the given procedure on another thread. The context
|
||||
specified by `init_context` will be used as the context in which `fn` is going
|
||||
to execute. The thread will have priority specified by the `priority` parameter.
|
||||
|
||||
**IMPORTANT**: If `init_context` is specified and the default temporary allocator
|
||||
is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
|
||||
in order to free the resources associated with the temporary allocations.
|
||||
*/
|
||||
run_with_poly_data :: proc(data: $T, fn: proc(data: T), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
|
||||
where size_of(T) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
|
||||
create_and_start_with_poly_data(data, fn, init_context, priority, true)
|
||||
}
|
||||
|
||||
/*
|
||||
Run a procedure with two polymorphic parameters on a different thread.
|
||||
|
||||
This procedure runs the given procedure on another thread. The context
|
||||
specified by `init_context` will be used as the context in which `fn` is going
|
||||
to execute. The thread will have priority specified by the `priority` parameter.
|
||||
|
||||
**IMPORTANT**: If `init_context` is specified and the default temporary allocator
|
||||
is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
|
||||
in order to free the resources associated with the temporary allocations.
|
||||
*/
|
||||
run_with_poly_data2 :: proc(arg1: $T1, arg2: $T2, fn: proc(T1, T2), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
|
||||
where size_of(T1) + size_of(T2) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
|
||||
create_and_start_with_poly_data2(arg1, arg2, fn, init_context, priority, true)
|
||||
}
|
||||
|
||||
/*
|
||||
Run a procedure with three polymorphic parameters on a different thread.
|
||||
|
||||
This procedure runs the given procedure on another thread. The context
|
||||
specified by `init_context` will be used as the context in which `fn` is going
|
||||
to execute. The thread will have priority specified by the `priority` parameter.
|
||||
|
||||
**IMPORTANT**: If `init_context` is specified and the default temporary allocator
|
||||
is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
|
||||
in order to free the resources associated with the temporary allocations.
|
||||
*/
|
||||
run_with_poly_data3 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, fn: proc(arg1: T1, arg2: T2, arg3: T3), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
|
||||
where size_of(T1) + size_of(T2) + size_of(T3) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
|
||||
create_and_start_with_poly_data3(arg1, arg2, arg3, fn, init_context, priority, true)
|
||||
}
|
||||
|
||||
/*
|
||||
Run a procedure with four polymorphic parameters on a different thread.
|
||||
|
||||
This procedure runs the given procedure on another thread. The context
|
||||
specified by `init_context` will be used as the context in which `fn` is going
|
||||
to execute. The thread will have priority specified by the `priority` parameter.
|
||||
|
||||
**IMPORTANT**: If `init_context` is specified and the default temporary allocator
|
||||
is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
|
||||
in order to free the resources associated with the temporary allocations.
|
||||
*/
|
||||
run_with_poly_data4 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, arg4: $T4, fn: proc(arg1: T1, arg2: T2, arg3: T3, arg4: T4), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
|
||||
where size_of(T1) + size_of(T2) + size_of(T3) + size_of(T4) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
|
||||
create_and_start_with_poly_data4(arg1, arg2, arg3, arg4, fn, init_context, priority, true)
|
||||
}
|
||||
|
||||
/*
|
||||
Run a procedure on a different thread.
|
||||
|
||||
This procedure runs the given procedure on another thread. The context
|
||||
specified by `init_context` will be used as the context in which `fn` is going
|
||||
to execute. The thread will have priority specified by the `priority` parameter.
|
||||
|
||||
If `self_cleanup` is specified, after the thread finishes the execution of the
|
||||
`fn` procedure, the resources associated with the thread are going to be
|
||||
automatically freed. **Do not** dereference the `^Thread` pointer, if this
|
||||
flag is specified.
|
||||
|
||||
**IMPORTANT**: If `init_context` is specified and the default temporary allocator
|
||||
is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
|
||||
in order to free the resources associated with the temporary allocations.
|
||||
*/
|
||||
create_and_start :: proc(fn: proc(), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread {
|
||||
thread_proc :: proc(t: ^Thread) {
|
||||
fn := cast(proc())t.data
|
||||
@@ -154,9 +279,22 @@ create_and_start :: proc(fn: proc(), init_context: Maybe(runtime.Context) = nil,
|
||||
return t
|
||||
}
|
||||
|
||||
/*
|
||||
Run a procedure with one pointer parameter on a different thread.
|
||||
|
||||
This procedure runs the given procedure on another thread. The context
|
||||
specified by `init_context` will be used as the context in which `fn` is going
|
||||
to execute. The thread will have priority specified by the `priority` parameter.
|
||||
|
||||
If `self_cleanup` is specified, after the thread finishes the execution of the
|
||||
`fn` procedure, the resources associated with the thread are going to be
|
||||
automatically freed. **Do not** dereference the `^Thread` pointer, if this
|
||||
flag is specified.
|
||||
|
||||
**IMPORTANT**: If `init_context` is specified and the default temporary allocator
|
||||
is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
|
||||
in order to free the resources associated with the temporary allocations.
|
||||
*/
|
||||
create_and_start_with_data :: proc(data: rawptr, fn: proc(data: rawptr), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread {
|
||||
thread_proc :: proc(t: ^Thread) {
|
||||
fn := cast(proc(rawptr))t.data
|
||||
@@ -176,6 +314,22 @@ create_and_start_with_data :: proc(data: rawptr, fn: proc(data: rawptr), init_co
|
||||
return t
|
||||
}
|
||||
|
||||
/*
|
||||
Run a procedure with one polymorphic parameter on a different thread.
|
||||
|
||||
This procedure runs the given procedure on another thread. The context
|
||||
specified by `init_context` will be used as the context in which `fn` is going
|
||||
to execute. The thread will have priority specified by the `priority` parameter.
|
||||
|
||||
If `self_cleanup` is specified, after the thread finishes the execution of the
|
||||
`fn` procedure, the resources associated with the thread are going to be
|
||||
automatically freed. **Do not** dereference the `^Thread` pointer, if this
|
||||
flag is specified.
|
||||
|
||||
**IMPORTANT**: If `init_context` is specified and the default temporary allocator
|
||||
is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
|
||||
in order to free the resources associated with the temporary allocations.
|
||||
*/
|
||||
create_and_start_with_poly_data :: proc(data: $T, fn: proc(data: T), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread
|
||||
where size_of(T) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
|
||||
thread_proc :: proc(t: ^Thread) {
|
||||
@@ -201,6 +355,22 @@ create_and_start_with_poly_data :: proc(data: $T, fn: proc(data: T), init_contex
|
||||
return t
|
||||
}
|
||||
|
||||
/*
|
||||
Run a procedure with two polymorphic parameters on a different thread.
|
||||
|
||||
This procedure runs the given procedure on another thread. The context
|
||||
specified by `init_context` will be used as the context in which `fn` is going
|
||||
to execute. The thread will have priority specified by the `priority` parameter.
|
||||
|
||||
If `self_cleanup` is specified, after the thread finishes the execution of the
|
||||
`fn` procedure, the resources associated with the thread are going to be
|
||||
automatically freed. **Do not** dereference the `^Thread` pointer, if this
|
||||
flag is specified.
|
||||
|
||||
**IMPORTANT**: If `init_context` is specified and the default temporary allocator
|
||||
is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
|
||||
in order to free the resources associated with the temporary allocations.
|
||||
*/
|
||||
create_and_start_with_poly_data2 :: proc(arg1: $T1, arg2: $T2, fn: proc(T1, T2), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread
|
||||
where size_of(T1) + size_of(T2) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
|
||||
thread_proc :: proc(t: ^Thread) {
|
||||
@@ -232,6 +402,22 @@ create_and_start_with_poly_data2 :: proc(arg1: $T1, arg2: $T2, fn: proc(T1, T2),
|
||||
return t
|
||||
}
|
||||
|
||||
/*
|
||||
Run a procedure with three polymorphic parameters on a different thread.
|
||||
|
||||
This procedure runs the given procedure on another thread. The context
|
||||
specified by `init_context` will be used as the context in which `fn` is going
|
||||
to execute. The thread will have priority specified by the `priority` parameter.
|
||||
|
||||
If `self_cleanup` is specified, after the thread finishes the execution of the
|
||||
`fn` procedure, the resources associated with the thread are going to be
|
||||
automatically freed. **Do not** dereference the `^Thread` pointer, if this
|
||||
flag is specified.
|
||||
|
||||
**IMPORTANT**: If `init_context` is specified and the default temporary allocator
|
||||
is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
|
||||
in order to free the resources associated with the temporary allocations.
|
||||
*/
|
||||
create_and_start_with_poly_data3 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, fn: proc(arg1: T1, arg2: T2, arg3: T3), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread
|
||||
where size_of(T1) + size_of(T2) + size_of(T3) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
|
||||
thread_proc :: proc(t: ^Thread) {
|
||||
@@ -264,6 +450,23 @@ create_and_start_with_poly_data3 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, fn: pr
|
||||
start(t)
|
||||
return t
|
||||
}
|
||||
|
||||
/*
|
||||
Run a procedure with four polymorphic parameters on a different thread.
|
||||
|
||||
This procedure runs the given procedure on another thread. The context
|
||||
specified by `init_context` will be used as the context in which `fn` is going
|
||||
to execute. The thread will have priority specified by the `priority` parameter.
|
||||
|
||||
If `self_cleanup` is specified, after the thread finishes the execution of the
|
||||
`fn` procedure, the resources associated with the thread are going to be
|
||||
automatically freed. **Do not** dereference the `^Thread` pointer, if this
|
||||
flag is specified.
|
||||
|
||||
**IMPORTANT**: If `init_context` is specified and the default temporary allocator
|
||||
is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
|
||||
in order to free the resources associated with the temporary allocations.
|
||||
*/
|
||||
create_and_start_with_poly_data4 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, arg4: $T4, fn: proc(arg1: T1, arg2: T2, arg3: T3, arg4: T4), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread
|
||||
where size_of(T1) + size_of(T2) + size_of(T3) + size_of(T4) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
|
||||
thread_proc :: proc(t: ^Thread) {
|
||||
|
||||
@@ -1,16 +1,46 @@
|
||||
package datetime
|
||||
|
||||
// Ordinal 1 = Midnight Monday, January 1, 1 A.D. (Gregorian)
|
||||
// | Midnight Monday, January 3, 1 A.D. (Julian)
|
||||
/*
|
||||
Type representing a mononotic day number corresponding to a date.
|
||||
|
||||
Ordinal 1 = Midnight Monday, January 1, 1 A.D. (Gregorian)
|
||||
| Midnight Monday, January 3, 1 A.D. (Julian)
|
||||
*/
|
||||
Ordinal :: i64
|
||||
|
||||
/*
|
||||
*/
|
||||
EPOCH :: Ordinal(1)
|
||||
|
||||
// Minimum and maximum dates and ordinals. Chosen for safe roundtripping.
|
||||
/*
|
||||
Minimum valid value for date.
|
||||
|
||||
The value is chosen such that a conversion `date -> ordinal -> date` is always
|
||||
safe.
|
||||
*/
|
||||
MIN_DATE :: Date{year = -25_252_734_927_766_552, month = 1, day = 1}
|
||||
|
||||
/*
|
||||
Maximum valid value for date
|
||||
|
||||
The value is chosen such that a conversion `date -> ordinal -> date` is always
|
||||
safe.
|
||||
*/
|
||||
MAX_DATE :: Date{year = 25_252_734_927_766_552, month = 12, day = 31}
|
||||
|
||||
/*
|
||||
Minimum value for an ordinal
|
||||
*/
|
||||
MIN_ORD :: Ordinal(-9_223_372_036_854_775_234)
|
||||
|
||||
/*
|
||||
Maximum value for an ordinal
|
||||
*/
|
||||
MAX_ORD :: Ordinal( 9_223_372_036_854_774_869)
|
||||
|
||||
/*
|
||||
Possible errors returned by datetime functions.
|
||||
*/
|
||||
Error :: enum {
|
||||
None,
|
||||
Invalid_Year,
|
||||
@@ -24,12 +54,22 @@ Error :: enum {
|
||||
Invalid_Delta,
|
||||
}
|
||||
|
||||
/*
|
||||
A type representing a date.
|
||||
|
||||
The minimum and maximum values for a year can be found in `MIN_DATE` and
|
||||
`MAX_DATE` constants. The `month` field can range from 1 to 12, and the day
|
||||
ranges from 1 to however many days there are in the specified month.
|
||||
*/
|
||||
Date :: struct {
|
||||
year: i64,
|
||||
month: i8,
|
||||
day: i8,
|
||||
}
|
||||
|
||||
/*
|
||||
A type representing a time within a single day within a nanosecond precision.
|
||||
*/
|
||||
Time :: struct {
|
||||
hour: i8,
|
||||
minute: i8,
|
||||
@@ -37,17 +77,30 @@ Time :: struct {
|
||||
nano: i32,
|
||||
}
|
||||
|
||||
/*
|
||||
A type representing datetime.
|
||||
*/
|
||||
DateTime :: struct {
|
||||
using date: Date,
|
||||
using time: Time,
|
||||
}
|
||||
|
||||
/*
|
||||
A type representing a difference between two instances of datetime.
|
||||
|
||||
**Note**: All fields are i64 because we can also use it to add a number of
|
||||
seconds or nanos to a moment, that are then normalized within their respective
|
||||
ranges.
|
||||
*/
|
||||
Delta :: struct {
|
||||
days: i64, // These are all i64 because we can also use it to add a number of seconds or nanos to a moment,
|
||||
seconds: i64, // that are then normalized within their respective ranges.
|
||||
days: i64,
|
||||
seconds: i64,
|
||||
nanos: i64,
|
||||
}
|
||||
|
||||
/*
|
||||
Type representing one of the months.
|
||||
*/
|
||||
Month :: enum i8 {
|
||||
January = 1,
|
||||
February,
|
||||
@@ -63,6 +116,9 @@ Month :: enum i8 {
|
||||
December,
|
||||
}
|
||||
|
||||
/*
|
||||
Type representing one of the weekdays.
|
||||
*/
|
||||
Weekday :: enum i8 {
|
||||
Sunday = 0,
|
||||
Monday,
|
||||
|
||||
@@ -1,56 +1,113 @@
|
||||
/*
|
||||
Calendrical conversions using a proleptic Gregorian calendar.
|
||||
Calendrical conversions using a proleptic Gregorian calendar.
|
||||
|
||||
Implemented using formulas from: Calendrical Calculations Ultimate Edition, Reingold & Dershowitz
|
||||
Implemented using formulas from: Calendrical Calculations Ultimate Edition,
|
||||
Reingold & Dershowitz
|
||||
*/
|
||||
package datetime
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
// Procedures that return an Ordinal
|
||||
/*
|
||||
Obtain an ordinal from a date.
|
||||
|
||||
This procedure converts the specified date into an ordinal. If the specified
|
||||
date is not a valid date, an error is returned.
|
||||
*/
|
||||
date_to_ordinal :: proc "contextless" (date: Date) -> (ordinal: Ordinal, err: Error) {
|
||||
validate(date) or_return
|
||||
return unsafe_date_to_ordinal(date), .None
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain an ordinal from date components.
|
||||
|
||||
This procedure converts the specified date, provided by its individual
|
||||
components, into an ordinal. If the specified date is not a valid date, an error
|
||||
is returned.
|
||||
*/
|
||||
components_to_ordinal :: proc "contextless" (#any_int year, #any_int month, #any_int day: i64) -> (ordinal: Ordinal, err: Error) {
|
||||
validate(year, month, day) or_return
|
||||
return unsafe_date_to_ordinal({year, i8(month), i8(day)}), .None
|
||||
}
|
||||
|
||||
// Procedures that return a Date
|
||||
/*
|
||||
Obtain date using an Ordinal.
|
||||
|
||||
This provedure converts the specified ordinal into a date. If the ordinal is not
|
||||
a valid ordinal, an error is returned.
|
||||
*/
|
||||
ordinal_to_date :: proc "contextless" (ordinal: Ordinal) -> (date: Date, err: Error) {
|
||||
validate(ordinal) or_return
|
||||
return unsafe_ordinal_to_date(ordinal), .None
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain a date from date components.
|
||||
|
||||
This procedure converts date components, specified by a year, a month and a day,
|
||||
into a date object. If the provided date components don't represent a valid
|
||||
date, an error is returned.
|
||||
*/
|
||||
components_to_date :: proc "contextless" (#any_int year, #any_int month, #any_int day: i64) -> (date: Date, err: Error) {
|
||||
validate(year, month, day) or_return
|
||||
return Date{i64(year), i8(month), i8(day)}, .None
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain time from time components.
|
||||
|
||||
This procedure converts time components, specified by an hour, a minute, a second
|
||||
and nanoseconds, into a time object. If the provided time components don't
|
||||
represent a valid time, an error is returned.
|
||||
*/
|
||||
components_to_time :: proc "contextless" (#any_int hour, #any_int minute, #any_int second: i64, #any_int nanos := i64(0)) -> (time: Time, err: Error) {
|
||||
validate(hour, minute, second, nanos) or_return
|
||||
return Time{i8(hour), i8(minute), i8(second), i32(nanos)}, .None
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain datetime from components.
|
||||
|
||||
This procedure converts date components and time components into a datetime object.
|
||||
If the provided date components or time components don't represent a valid
|
||||
datetime, an error is returned.
|
||||
*/
|
||||
components_to_datetime :: proc "contextless" (#any_int year, #any_int month, #any_int day, #any_int hour, #any_int minute, #any_int second: i64, #any_int nanos := i64(0)) -> (datetime: DateTime, err: Error) {
|
||||
date := components_to_date(year, month, day) or_return
|
||||
time := components_to_time(hour, minute, second, nanos) or_return
|
||||
return {date, time}, .None
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain an datetime from an ordinal.
|
||||
|
||||
This procedure converts the value of an ordinal into a datetime. Since the
|
||||
ordinal only has the amount of days, the resulting time in the datetime
|
||||
object will always have the time equal to `00:00:00.000`.
|
||||
*/
|
||||
ordinal_to_datetime :: proc "contextless" (ordinal: Ordinal) -> (datetime: DateTime, err: Error) {
|
||||
d := ordinal_to_date(ordinal) or_return
|
||||
return {Date(d), {}}, .None
|
||||
}
|
||||
|
||||
/*
|
||||
Calculate the weekday from an ordinal.
|
||||
|
||||
This procedure takes the value of an ordinal and returns the day of week for
|
||||
that ordinal.
|
||||
*/
|
||||
day_of_week :: proc "contextless" (ordinal: Ordinal) -> (day: Weekday) {
|
||||
return Weekday((ordinal - EPOCH + 1) %% 7)
|
||||
}
|
||||
|
||||
/*
|
||||
Calculate the difference between two dates.
|
||||
|
||||
This procedure calculates the difference between two dates `a - b`, and returns
|
||||
a delta between the two dates in `days`. If either `a` or `b` is not a valid
|
||||
date, an error is returned.
|
||||
*/
|
||||
subtract_dates :: proc "contextless" (a, b: Date) -> (delta: Delta, err: Error) {
|
||||
ord_a := date_to_ordinal(a) or_return
|
||||
ord_b := date_to_ordinal(b) or_return
|
||||
@@ -59,6 +116,16 @@ subtract_dates :: proc "contextless" (a, b: Date) -> (delta: Delta, err: Error)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Calculate the difference between two datetimes.
|
||||
|
||||
This procedure calculates the difference between two datetimes, `a - b`, and
|
||||
returns a delta between the two dates. The difference is returned in all three
|
||||
fields of the `Delta` struct: the difference in days, the difference in seconds
|
||||
and the difference in nanoseconds.
|
||||
|
||||
If either `a` or `b` is not a valid datetime, an error is returned.
|
||||
*/
|
||||
subtract_datetimes :: proc "contextless" (a, b: DateTime) -> (delta: Delta, err: Error) {
|
||||
ord_a := date_to_ordinal(a) or_return
|
||||
ord_b := date_to_ordinal(b) or_return
|
||||
@@ -73,19 +140,42 @@ subtract_datetimes :: proc "contextless" (a, b: DateTime) -> (delta: Delta, err:
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Calculate a difference between two deltas.
|
||||
*/
|
||||
subtract_deltas :: proc "contextless" (a, b: Delta) -> (delta: Delta, err: Error) {
|
||||
delta = Delta{a.days - b.days, a.seconds - b.seconds, a.nanos - b.nanos}
|
||||
delta = normalize_delta(delta) or_return
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Calculate a difference between two datetimes, dates or deltas.
|
||||
*/
|
||||
sub :: proc{subtract_datetimes, subtract_dates, subtract_deltas}
|
||||
|
||||
/*
|
||||
Add certain amount of days to a date.
|
||||
|
||||
This procedure adds the specified amount of days to a date and returns a new
|
||||
date. The new date would have happened the specified amount of days after the
|
||||
specified date.
|
||||
*/
|
||||
add_days_to_date :: proc "contextless" (a: Date, days: i64) -> (date: Date, err: Error) {
|
||||
ord := date_to_ordinal(a) or_return
|
||||
ord += days
|
||||
return ordinal_to_date(ord)
|
||||
}
|
||||
|
||||
/*
|
||||
Add delta to a date.
|
||||
|
||||
This procedure adds a delta to a date, and returns a new date. The new date
|
||||
would have happened the time specified by `delta` after the specified date.
|
||||
|
||||
**Note**: The delta is assumed to be normalized. That is, if it contains seconds
|
||||
or milliseconds, regardless of the amount only the days will be added.
|
||||
*/
|
||||
add_delta_to_date :: proc "contextless" (a: Date, delta: Delta) -> (date: Date, err: Error) {
|
||||
ord := date_to_ordinal(a) or_return
|
||||
// Because the input is a Date, we add only the days from the Delta.
|
||||
@@ -93,6 +183,13 @@ add_delta_to_date :: proc "contextless" (a: Date, delta: Delta) -> (date: Date,
|
||||
return ordinal_to_date(ord)
|
||||
}
|
||||
|
||||
/*
|
||||
Add delta to datetime.
|
||||
|
||||
This procedure adds a delta to a datetime, and returns a new datetime. The new
|
||||
datetime would have happened the time specified by `delta` after the specified
|
||||
datetime.
|
||||
*/
|
||||
add_delta_to_datetime :: proc "contextless" (a: DateTime, delta: Delta) -> (datetime: DateTime, err: Error) {
|
||||
days := date_to_ordinal(a) or_return
|
||||
|
||||
@@ -110,8 +207,18 @@ add_delta_to_datetime :: proc "contextless" (a: DateTime, delta: Delta) -> (date
|
||||
datetime.time = components_to_time(hour, minute, second, sum_delta.nanos) or_return
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Add days to a date, delta to a date or delta to datetime.
|
||||
*/
|
||||
add :: proc{add_days_to_date, add_delta_to_date, add_delta_to_datetime}
|
||||
|
||||
/*
|
||||
Obtain the day number in a year
|
||||
|
||||
This procedure returns the number of the day in a year, starting from 1. If
|
||||
the date is not a valid date, an error is returned.
|
||||
*/
|
||||
day_number :: proc "contextless" (date: Date) -> (day_number: i64, err: Error) {
|
||||
validate(date) or_return
|
||||
|
||||
@@ -120,6 +227,13 @@ day_number :: proc "contextless" (date: Date) -> (day_number: i64, err: Error) {
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the remaining number of days in a year.
|
||||
|
||||
This procedure returns the number of days between the specified date and
|
||||
December 31 of the same year. If the date is not a valid date, an error is
|
||||
returned.
|
||||
*/
|
||||
days_remaining :: proc "contextless" (date: Date) -> (days_remaining: i64, err: Error) {
|
||||
// Alternative formulation `day_number` subtracted from 365 or 366 depending on leap year
|
||||
validate(date) or_return
|
||||
@@ -127,6 +241,12 @@ days_remaining :: proc "contextless" (date: Date) -> (days_remaining: i64, err:
|
||||
return delta.days, .None
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the last day of a given month on a given year.
|
||||
|
||||
This procedure returns the amount of days in a specified month on a specified
|
||||
date. If the specified year or month is not valid, an error is returned.
|
||||
*/
|
||||
last_day_of_month :: proc "contextless" (#any_int year: i64, #any_int month: i8) -> (day: i8, err: Error) {
|
||||
// Not using formula 2.27 from the book. This is far simpler and gives the same answer.
|
||||
|
||||
@@ -140,16 +260,33 @@ last_day_of_month :: proc "contextless" (#any_int year: i64, #any_int month: i8)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the new year date of a given year.
|
||||
|
||||
This procedure returns the January 1st date of the specified year. If the year
|
||||
is not valid, an error is returned.
|
||||
*/
|
||||
new_year :: proc "contextless" (#any_int year: i64) -> (new_year: Date, err: Error) {
|
||||
validate(year, 1, 1) or_return
|
||||
return {year, 1, 1}, .None
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the end year of a given date.
|
||||
|
||||
This procedure returns the December 31st date of the specified year. If the year
|
||||
is not valid, an error is returned.
|
||||
*/
|
||||
year_end :: proc "contextless" (#any_int year: i64) -> (year_end: Date, err: Error) {
|
||||
validate(year, 12, 31) or_return
|
||||
return {year, 12, 31}, .None
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the range of dates for a given year.
|
||||
|
||||
This procedure returns dates, for every day of a given year in a slice.
|
||||
*/
|
||||
year_range :: proc (#any_int year: i64, allocator := context.allocator) -> (range: []Date) {
|
||||
is_leap := is_leap_year(year)
|
||||
|
||||
@@ -171,6 +308,15 @@ year_range :: proc (#any_int year: i64, allocator := context.allocator) -> (rang
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Normalize the delta.
|
||||
|
||||
This procedure normalizes the delta in such a way that the number of seconds
|
||||
is between 0 and the number of seconds in the day and nanoseconds is between
|
||||
0 and 10^9.
|
||||
|
||||
If the value for `days` overflows during this operation, an error is returned.
|
||||
*/
|
||||
normalize_delta :: proc "contextless" (delta: Delta) -> (normalized: Delta, err: Error) {
|
||||
// Distribute nanos into seconds and remainder
|
||||
seconds, nanos := divmod(delta.nanos, 1e9)
|
||||
@@ -194,6 +340,12 @@ normalize_delta :: proc "contextless" (delta: Delta) -> (normalized: Delta, err:
|
||||
// The following procedures don't check whether their inputs are in a valid range.
|
||||
// They're still exported for those who know their inputs have been validated.
|
||||
|
||||
/*
|
||||
Obtain an ordinal from a date.
|
||||
|
||||
This procedure converts a date into an ordinal. If the date is not a valid date,
|
||||
the result is unspecified.
|
||||
*/
|
||||
unsafe_date_to_ordinal :: proc "contextless" (date: Date) -> (ordinal: Ordinal) {
|
||||
year_minus_one := date.year - 1
|
||||
|
||||
@@ -223,6 +375,12 @@ unsafe_date_to_ordinal :: proc "contextless" (date: Date) -> (ordinal: Ordinal)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain a year and a day of the year from an ordinal.
|
||||
|
||||
This procedure returns the year and the day of the year of a given ordinal.
|
||||
Of the ordinal is outside of its valid range, the result is unspecified.
|
||||
*/
|
||||
unsafe_ordinal_to_year :: proc "contextless" (ordinal: Ordinal) -> (year: i64, day_ordinal: i64) {
|
||||
// Days after epoch
|
||||
d0 := ordinal - EPOCH
|
||||
@@ -253,6 +411,12 @@ unsafe_ordinal_to_year :: proc "contextless" (ordinal: Ordinal) -> (year: i64, d
|
||||
return year + 1, day_ordinal
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain a date from an ordinal.
|
||||
|
||||
This procedure converts an ordinal into a date. If the ordinal is outside of
|
||||
its valid range, the result is unspecified.
|
||||
*/
|
||||
unsafe_ordinal_to_date :: proc "contextless" (ordinal: Ordinal) -> (date: Date) {
|
||||
year, _ := unsafe_ordinal_to_year(ordinal)
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//+private
|
||||
package datetime
|
||||
|
||||
// Internal helper functions for calendrical conversions
|
||||
|
||||
@@ -1,14 +1,29 @@
|
||||
package datetime
|
||||
|
||||
// Validation helpers
|
||||
|
||||
/*
|
||||
Check if a year is a leap year.
|
||||
*/
|
||||
is_leap_year :: proc "contextless" (#any_int year: i64) -> (leap: bool) {
|
||||
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
|
||||
}
|
||||
|
||||
/*
|
||||
Check for errors in date formation.
|
||||
|
||||
This procedure validates all fields of a date, and if any of the fields is
|
||||
outside of allowed range, an error is returned.
|
||||
*/
|
||||
validate_date :: proc "contextless" (date: Date) -> (err: Error) {
|
||||
return validate(date.year, date.month, date.day)
|
||||
}
|
||||
|
||||
/*
|
||||
Check for errors in date formation given date components.
|
||||
|
||||
This procedure checks whether a date formed by the specified year month and a
|
||||
day is a valid date. If not, an error is returned.
|
||||
*/
|
||||
validate_year_month_day :: proc "contextless" (#any_int year, #any_int month, #any_int day: i64) -> (err: Error) {
|
||||
if year < MIN_DATE.year || year > MAX_DATE.year {
|
||||
return .Invalid_Year
|
||||
@@ -29,6 +44,12 @@ validate_year_month_day :: proc "contextless" (#any_int year, #any_int month, #a
|
||||
return .None
|
||||
}
|
||||
|
||||
/*
|
||||
Check for errors in Ordinal
|
||||
|
||||
This procedure checks if the ordinal is in a valid range for roundtrip
|
||||
conversions with the dates. If not, an error is returned.
|
||||
*/
|
||||
validate_ordinal :: proc "contextless" (ordinal: Ordinal) -> (err: Error) {
|
||||
if ordinal < MIN_ORD || ordinal > MAX_ORD {
|
||||
return .Invalid_Ordinal
|
||||
@@ -36,10 +57,22 @@ validate_ordinal :: proc "contextless" (ordinal: Ordinal) -> (err: Error) {
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Check for errors in time formation
|
||||
|
||||
This procedure checks whether time has all fields in valid ranges, and if not
|
||||
an error is returned.
|
||||
*/
|
||||
validate_time :: proc "contextless" (time: Time) -> (err: Error) {
|
||||
return validate(time.hour, time.minute, time.second, time.nano)
|
||||
}
|
||||
|
||||
/*
|
||||
Check for errors in time formed by its components.
|
||||
|
||||
This procedure checks whether the time formed by its components is valid, and
|
||||
if not an error is returned.
|
||||
*/
|
||||
validate_hour_minute_second :: proc "contextless" (#any_int hour, #any_int minute, #any_int second, #any_int nano: i64) -> (err: Error) {
|
||||
if hour < 0 || hour > 23 {
|
||||
return .Invalid_Hour
|
||||
@@ -56,12 +89,21 @@ validate_hour_minute_second :: proc "contextless" (#any_int hour, #any_int minut
|
||||
return .None
|
||||
}
|
||||
|
||||
/*
|
||||
Check for errors in datetime formation.
|
||||
|
||||
This procedure checks whether all fields of date and time in the specified
|
||||
datetime are valid, and if not, an error is returned.
|
||||
*/
|
||||
validate_datetime :: proc "contextless" (datetime: DateTime) -> (err: Error) {
|
||||
validate(datetime.date) or_return
|
||||
validate(datetime.time) or_return
|
||||
return .None
|
||||
}
|
||||
|
||||
/*
|
||||
Check for errors in date, time or datetime.
|
||||
*/
|
||||
validate :: proc{
|
||||
validate_date,
|
||||
validate_year_month_day,
|
||||
|
||||
@@ -3,23 +3,62 @@ package time
|
||||
|
||||
import dt "core:time/datetime"
|
||||
|
||||
// Parses an ISO 8601 string and returns Time in UTC, with any UTC offset applied to it.
|
||||
// Only 4-digit years are accepted.
|
||||
// Optional pointer to boolean `is_leap` will return `true` if the moment was a leap second.
|
||||
// Leap seconds are smeared into 23:59:59.
|
||||
/*
|
||||
Parse an ISO 8601 string into a time with UTC offset applied to it.
|
||||
|
||||
This procedure parses an ISO 8601 string of roughly the following format:
|
||||
|
||||
```text
|
||||
YYYY-MM-DD[Tt]HH:mm:ss[.nn][Zz][+-]HH:mm
|
||||
```
|
||||
|
||||
And returns time, in UTC represented by that string. In case the timezone offset
|
||||
is specified in the string, that timezone is applied to time.
|
||||
|
||||
**Inputs**:
|
||||
- `iso_datetime`: The string to be parsed.
|
||||
- `is_leap`: Optional output parameter, specifying if the moment was a leap second.
|
||||
|
||||
**Returns**:
|
||||
- `res`: The time represented by `iso_datetime`, with UTC offset applied.
|
||||
- `consumed`: Number of bytes consumed by parsing the string.
|
||||
|
||||
**Notes**:
|
||||
- Only 4-digit years are accepted.
|
||||
- Leap seconds are smeared into 23:59:59.
|
||||
*/
|
||||
iso8601_to_time_utc :: proc(iso_datetime: string, is_leap: ^bool = nil) -> (res: Time, consumed: int) {
|
||||
offset: int
|
||||
|
||||
res, offset, consumed = iso8601_to_time_and_offset(iso_datetime, is_leap)
|
||||
res._nsec += (i64(-offset) * i64(Minute))
|
||||
return res, consumed
|
||||
}
|
||||
|
||||
// Parses an ISO 8601 string and returns Time and a UTC offset in minutes.
|
||||
// e.g. 1985-04-12T23:20:50.52Z
|
||||
// Note: Only 4-digit years are accepted.
|
||||
// Optional pointer to boolean `is_leap` will return `true` if the moment was a leap second.
|
||||
// Leap seconds are smeared into 23:59:59.
|
||||
/*
|
||||
Parse an ISO 8601 string into a time and a UTC offset in minutes.
|
||||
|
||||
This procedure parses an ISO 8601 string of roughly the following format:
|
||||
|
||||
```text
|
||||
YYYY-MM-DD[Tt]HH:mm:ss[.nn][Zz][+-]HH:mm
|
||||
```
|
||||
|
||||
And returns time, in UTC represented by that string, and the UTC offset, in
|
||||
minutes.
|
||||
|
||||
**Inputs**:
|
||||
- `iso_datetime`: The string to be parsed.
|
||||
- `is_leap`: Optional output parameter, specifying if the moment was a leap second.
|
||||
|
||||
**Returns**:
|
||||
- `res`: The time in UTC.
|
||||
- `utc_offset`: The UTC offset of the time, in minutes.
|
||||
- `consumed`: Number of bytes consumed by parsing the string.
|
||||
|
||||
**Notes**:
|
||||
- Only 4-digit years are accepted.
|
||||
- Leap seconds are smeared into 23:59:59.
|
||||
*/
|
||||
iso8601_to_time_and_offset :: proc(iso_datetime: string, is_leap: ^bool = nil) -> (res: Time, utc_offset: int, consumed: int) {
|
||||
moment, offset, leap_second, count := iso8601_to_components(iso_datetime)
|
||||
if count == 0 {
|
||||
@@ -37,9 +76,32 @@ iso8601_to_time_and_offset :: proc(iso_datetime: string, is_leap: ^bool = nil) -
|
||||
}
|
||||
}
|
||||
|
||||
// Parses an ISO 8601 string and returns Time and a UTC offset in minutes.
|
||||
// e.g. 1985-04-12T23:20:50.52Z
|
||||
// Performs no validation on whether components are valid, e.g. it'll return hour = 25 if that's what it's given
|
||||
/*
|
||||
Parse an ISO 8601 string into a datetime and a UTC offset in minutes.
|
||||
|
||||
This procedure parses an ISO 8601 string of roughly the following format:
|
||||
|
||||
```text
|
||||
YYYY-MM-DD[Tt]HH:mm:ss[.nn][Zz][+-]HH:mm
|
||||
```
|
||||
|
||||
And returns datetime, in UTC represented by that string, and the UTC offset, in
|
||||
minutes.
|
||||
|
||||
**Inputs**:
|
||||
- `iso_datetime`: The string to be parsed
|
||||
|
||||
**Returns**:
|
||||
- `res`: The parsed datetime, in UTC.
|
||||
- `utc_offset`: The UTC offset, in minutes.
|
||||
- `is_leap`: Specifies whether the moment was a leap second.
|
||||
- `consumed`: The number of bytes consumed by parsing the string.
|
||||
|
||||
**Notes**:
|
||||
- This procedure performs no validation on whether components are valid,
|
||||
e.g. it'll return hour = 25 if that's what it's given in the specified
|
||||
string.
|
||||
*/
|
||||
iso8601_to_components :: proc(iso_datetime: string) -> (res: dt.DateTime, utc_offset: int, is_leap: bool, consumed: int) {
|
||||
moment, offset, count, leap_second, ok := _iso8601_to_components(iso_datetime)
|
||||
if !ok {
|
||||
|
||||
@@ -3,18 +3,39 @@ package time
|
||||
import "base:runtime"
|
||||
import "base:intrinsics"
|
||||
|
||||
/*
|
||||
Type representing monotonic time, useful for measuring durations.
|
||||
*/
|
||||
Tick :: struct {
|
||||
_nsec: i64, // relative amount
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the current tick.
|
||||
*/
|
||||
tick_now :: proc "contextless" () -> Tick {
|
||||
return _tick_now()
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the difference between ticks.
|
||||
*/
|
||||
tick_diff :: proc "contextless" (start, end: Tick) -> Duration {
|
||||
d := end._nsec - start._nsec
|
||||
return Duration(d)
|
||||
}
|
||||
|
||||
/*
|
||||
Incrementally obtain durations since last tick.
|
||||
|
||||
This procedure returns the duration between the current tick and the tick
|
||||
stored in `prev` pointer, and then stores the current tick in location,
|
||||
specified by `prev`. If the prev pointer contains an zero-initialized tick,
|
||||
then the returned duration is 0.
|
||||
|
||||
This procedure is meant to be used in a loop, or in other scenarios, where one
|
||||
might want to obtain time between multiple ticks at specific points.
|
||||
*/
|
||||
tick_lap_time :: proc "contextless" (prev: ^Tick) -> Duration {
|
||||
d: Duration
|
||||
t := tick_now()
|
||||
@@ -25,17 +46,21 @@ tick_lap_time :: proc "contextless" (prev: ^Tick) -> Duration {
|
||||
return d
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the duration since last tick.
|
||||
*/
|
||||
tick_since :: proc "contextless" (start: Tick) -> Duration {
|
||||
return tick_diff(start, tick_now())
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Capture the duration the code in the current scope takes to execute.
|
||||
*/
|
||||
@(deferred_in_out=_tick_duration_end)
|
||||
SCOPED_TICK_DURATION :: proc "contextless" (d: ^Duration) -> Tick {
|
||||
return tick_now()
|
||||
}
|
||||
|
||||
|
||||
_tick_duration_end :: proc "contextless" (d: ^Duration, t: Tick) {
|
||||
d^ = tick_since(t)
|
||||
}
|
||||
@@ -62,6 +87,13 @@ when ODIN_OS != .Darwin && ODIN_OS != .Linux && ODIN_OS != .FreeBSD {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Check if the CPU has invariant TSC.
|
||||
|
||||
This procedure checks if the CPU contains an invariant TSC (Time stamp counter).
|
||||
Invariant TSC is a feature of modern processors that allows them to run their
|
||||
TSC at a fixed frequency, independent of ACPI state, and CPU frequency.
|
||||
*/
|
||||
has_invariant_tsc :: proc "contextless" () -> bool {
|
||||
when ODIN_ARCH == .amd64 {
|
||||
return x86_has_invariant_tsc()
|
||||
@@ -70,6 +102,17 @@ has_invariant_tsc :: proc "contextless" () -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the CPU's TSC frequency, in hertz.
|
||||
|
||||
This procedure tries to obtain the CPU's TSC frequency in hertz. If the CPU
|
||||
doesn't have an invariant TSC, this procedure returns with an error. Otherwise
|
||||
an attempt is made to fetch the TSC frequency from the OS. If this fails,
|
||||
the frequency is obtained by sleeping for the specified amount of time and
|
||||
dividing the readings from TSC by the duration of the sleep.
|
||||
|
||||
The duration of sleep can be controlled by `fallback_sleep` parameter.
|
||||
*/
|
||||
tsc_frequency :: proc "contextless" (fallback_sleep := 2 * Second) -> (u64, bool) {
|
||||
if !has_invariant_tsc() {
|
||||
return 0, false
|
||||
@@ -93,37 +136,64 @@ tsc_frequency :: proc "contextless" (fallback_sleep := 2 * Second) -> (u64, bool
|
||||
return hz, true
|
||||
}
|
||||
|
||||
/*
|
||||
Benchmark helpers
|
||||
*/
|
||||
// Benchmark helpers
|
||||
|
||||
/*
|
||||
Errors returned by the `benchmark()` procedure.
|
||||
*/
|
||||
Benchmark_Error :: enum {
|
||||
Okay = 0,
|
||||
Allocation_Error,
|
||||
}
|
||||
|
||||
/*
|
||||
Options for benchmarking.
|
||||
*/
|
||||
Benchmark_Options :: struct {
|
||||
// The initialization procedure. `benchmark()` will call this before taking measurements.
|
||||
setup: #type proc(options: ^Benchmark_Options, allocator: runtime.Allocator) -> (err: Benchmark_Error),
|
||||
// The procedure to benchmark.
|
||||
bench: #type proc(options: ^Benchmark_Options, allocator: runtime.Allocator) -> (err: Benchmark_Error),
|
||||
// The deinitialization procedure.
|
||||
teardown: #type proc(options: ^Benchmark_Options, allocator: runtime.Allocator) -> (err: Benchmark_Error),
|
||||
|
||||
// Field to be used by `bench()` procedure for any purpose.
|
||||
rounds: int,
|
||||
// Field to be used by `bench()` procedure for any purpose.
|
||||
bytes: int,
|
||||
// Field to be used by `bench()` procedure for any purpose.
|
||||
input: []u8,
|
||||
|
||||
// `bench()` writes to specify the count of elements processed.
|
||||
count: int,
|
||||
// `bench()` writes to specify the number of bytes processed.
|
||||
processed: int,
|
||||
// `bench()` can write the output slice here.
|
||||
output: []u8, // Unused for hash benchmarks
|
||||
// `bench()` can write the output hash here.
|
||||
hash: u128,
|
||||
|
||||
/*
|
||||
Performance
|
||||
*/
|
||||
// `benchmark()` procedure will output the duration of benchmark
|
||||
duration: Duration,
|
||||
// `benchmark()` procedure will output the average count of elements
|
||||
// processed per second, using the `count` field of this struct.
|
||||
rounds_per_second: f64,
|
||||
// `benchmark()` procedure will output the average number of megabytes
|
||||
// processed per second, using the `processed` field of this struct.
|
||||
megabytes_per_second: f64,
|
||||
}
|
||||
|
||||
/*
|
||||
Benchmark a procedure.
|
||||
|
||||
This procedure produces a benchmark. The procedure specified in the `bench`
|
||||
field of the `options` parameter will be benchmarked. The following metrics
|
||||
can be obtained:
|
||||
|
||||
- Run time of the procedure
|
||||
- Number of elements per second processed on average
|
||||
- Number of bytes per second this processed on average
|
||||
|
||||
In order to obtain these metrics, the `bench()` procedure writes to `options`
|
||||
struct the number of elements or bytes it has processed.
|
||||
*/
|
||||
benchmark :: proc(options: ^Benchmark_Options, allocator := context.allocator) -> (err: Benchmark_Error) {
|
||||
assert(options != nil)
|
||||
assert(options.bench != nil)
|
||||
|
||||
@@ -4,10 +4,33 @@ package time
|
||||
|
||||
import dt "core:time/datetime"
|
||||
|
||||
// Parses an RFC 3339 string and returns Time in UTC, with any UTC offset applied to it.
|
||||
// Only 4-digit years are accepted.
|
||||
// Optional pointer to boolean `is_leap` will return `true` if the moment was a leap second.
|
||||
// Leap seconds are smeared into 23:59:59.
|
||||
/*
|
||||
Parse an RFC 3339 string into time with a UTC offset applied to it.
|
||||
|
||||
This procedure parses the specified RFC 3339 strings of roughly the following
|
||||
format:
|
||||
|
||||
```text
|
||||
YYYY-MM-DD[Tt]HH:mm:ss[.nn][Zz][+-]HH:mm
|
||||
```
|
||||
|
||||
And returns the time that was represented by the RFC 3339 string, with the UTC
|
||||
offset applied to it.
|
||||
|
||||
**Inputs**:
|
||||
- `rfc_datetime`: An RFC 3339 string to parse.
|
||||
- `is_leap`: Optional output parameter specifying whether the moment was a leap
|
||||
second.
|
||||
|
||||
**Returns**:
|
||||
- `res`: The time, with UTC offset applied, that was parsed from the RFC 3339
|
||||
string.
|
||||
- `consumed`: The number of bytes consumed by parsing the RFC 3339 string.
|
||||
|
||||
**Notes**:
|
||||
- Only 4-digit years are accepted.
|
||||
- Leap seconds are smeared into 23:59:59.
|
||||
*/
|
||||
rfc3339_to_time_utc :: proc(rfc_datetime: string, is_leap: ^bool = nil) -> (res: Time, consumed: int) {
|
||||
offset: int
|
||||
|
||||
@@ -16,11 +39,34 @@ rfc3339_to_time_utc :: proc(rfc_datetime: string, is_leap: ^bool = nil) -> (res:
|
||||
return res, consumed
|
||||
}
|
||||
|
||||
// Parses an RFC 3339 string and returns Time and a UTC offset in minutes.
|
||||
// e.g. 1985-04-12T23:20:50.52Z
|
||||
// Note: Only 4-digit years are accepted.
|
||||
// Optional pointer to boolean `is_leap` will return `true` if the moment was a leap second.
|
||||
// Leap seconds are smeared into 23:59:59.
|
||||
/*
|
||||
Parse an RFC 3339 string into a time and a UTC offset in minutes.
|
||||
|
||||
This procedure parses the specified RFC 3339 strings of roughly the following
|
||||
format:
|
||||
|
||||
```text
|
||||
YYYY-MM-DD[Tt]HH:mm:ss[.nn][Zz][+-]HH:mm
|
||||
```
|
||||
|
||||
And returns the time, in UTC and a UTC offset, in minutes, that were represented
|
||||
by the RFC 3339 string.
|
||||
|
||||
**Inputs**:
|
||||
- `rfc_datetime`: The RFC 3339 string to be parsed.
|
||||
- `is_leap`: Optional output parameter specifying whether the moment was a
|
||||
leap second.
|
||||
|
||||
**Returns**:
|
||||
- `res`: The time, in UTC, that was parsed from the RFC 3339 string.
|
||||
- `utc_offset`: The UTC offset, in minutes, that was parsed from the RFC 3339
|
||||
string.
|
||||
- `consumed`: The number of bytes consumed by parsing the string.
|
||||
|
||||
**Notes**:
|
||||
- Only 4-digit years are accepted.
|
||||
- Leap seconds are smeared into 23:59:59.
|
||||
*/
|
||||
rfc3339_to_time_and_offset :: proc(rfc_datetime: string, is_leap: ^bool = nil) -> (res: Time, utc_offset: int, consumed: int) {
|
||||
moment, offset, leap_second, count := rfc3339_to_components(rfc_datetime)
|
||||
if count == 0 {
|
||||
@@ -38,9 +84,31 @@ rfc3339_to_time_and_offset :: proc(rfc_datetime: string, is_leap: ^bool = nil) -
|
||||
}
|
||||
}
|
||||
|
||||
// Parses an RFC 3339 string and returns Time and a UTC offset in minutes.
|
||||
// e.g. 1985-04-12T23:20:50.52Z
|
||||
// Performs no validation on whether components are valid, e.g. it'll return hour = 25 if that's what it's given
|
||||
/*
|
||||
Parse an RFC 3339 string into a datetime and a UTC offset in minutes.
|
||||
|
||||
This procedure parses the specified RFC 3339 strings of roughly the following
|
||||
format:
|
||||
|
||||
```text
|
||||
YYYY-MM-DD[Tt]HH:mm:ss[.nn][Zz][+-]HH:mm
|
||||
```
|
||||
|
||||
And returns the datetime, in UTC and the UTC offset, in minutes, that were
|
||||
represented by the RFC 3339 string.
|
||||
|
||||
**Inputs**:
|
||||
- `rfc_datetime`: The RFC 3339 string to parse.
|
||||
|
||||
**Returns**:
|
||||
- `res`: The datetime, in UTC, that was parsed from the RFC 3339 string.
|
||||
- `utc_offset`: The UTC offset, in minutes, that was parsed from the RFC 3339
|
||||
string.
|
||||
- `is_leap`: Specifies whether the moment was a leap second.
|
||||
- `consumed`: Number of bytes consumed by parsing the string.
|
||||
|
||||
Performs no validation on whether components are valid, e.g. it'll return hour = 25 if that's what it's given
|
||||
*/
|
||||
rfc3339_to_components :: proc(rfc_datetime: string) -> (res: dt.DateTime, utc_offset: int, is_leap: bool, consumed: int) {
|
||||
moment, offset, count, leap_second, ok := _rfc3339_to_components(rfc_datetime)
|
||||
if !ok {
|
||||
|
||||
@@ -3,24 +3,72 @@ package time
|
||||
import "base:intrinsics"
|
||||
import dt "core:time/datetime"
|
||||
|
||||
/*
|
||||
Type representing duration, with nanosecond precision.
|
||||
*/
|
||||
Duration :: distinct i64
|
||||
|
||||
/*
|
||||
The duration equal to one nanosecond (1e-9 seconds).
|
||||
*/
|
||||
Nanosecond :: Duration(1)
|
||||
|
||||
/*
|
||||
The duration equal to one microsecond (1e-6 seconds).
|
||||
*/
|
||||
Microsecond :: 1000 * Nanosecond
|
||||
|
||||
/*
|
||||
The duration equal to one millisecond (1e-3 seconds).
|
||||
*/
|
||||
Millisecond :: 1000 * Microsecond
|
||||
|
||||
/*
|
||||
The duration equal to one second.
|
||||
*/
|
||||
Second :: 1000 * Millisecond
|
||||
|
||||
/*
|
||||
The duration equal to one minute (60 seconds).
|
||||
*/
|
||||
Minute :: 60 * Second
|
||||
|
||||
/*
|
||||
The duration equal to one hour (3600 seconds).
|
||||
*/
|
||||
Hour :: 60 * Minute
|
||||
|
||||
/*
|
||||
Minimum representable duration.
|
||||
*/
|
||||
MIN_DURATION :: Duration(-1 << 63)
|
||||
|
||||
/*
|
||||
Maximum representable duration.
|
||||
*/
|
||||
MAX_DURATION :: Duration(1<<63 - 1)
|
||||
|
||||
/*
|
||||
Value specifying whether the time procedures are supported by the current
|
||||
platform.
|
||||
*/
|
||||
IS_SUPPORTED :: _IS_SUPPORTED
|
||||
|
||||
/*
|
||||
Specifies time since the UNIX epoch, with nanosecond precision.
|
||||
|
||||
Capable of representing any time within the following range:
|
||||
|
||||
- `min: 1677-09-21 00:12:44.145224192 +0000 UTC`
|
||||
- `max: 2262-04-11 23:47:16.854775807 +0000 UTC`
|
||||
*/
|
||||
Time :: struct {
|
||||
_nsec: i64, // Measured in UNIX nanonseconds
|
||||
}
|
||||
|
||||
/*
|
||||
Type representing a month.
|
||||
*/
|
||||
Month :: enum int {
|
||||
January = 1,
|
||||
February,
|
||||
@@ -36,6 +84,9 @@ Month :: enum int {
|
||||
December,
|
||||
}
|
||||
|
||||
/*
|
||||
Type representing a weekday.
|
||||
*/
|
||||
Weekday :: enum int {
|
||||
Sunday = 0,
|
||||
Monday,
|
||||
@@ -46,20 +97,37 @@ Weekday :: enum int {
|
||||
Saturday,
|
||||
}
|
||||
|
||||
/*
|
||||
Type representing a stopwatch.
|
||||
|
||||
The stopwatch is used for measuring the total time in multiple "runs". When the
|
||||
stopwatch is started, it starts counting time. When the stopwatch is stopped,
|
||||
the difference in time between the last start and the stop is added to the
|
||||
total. When the stopwatch resets, the total is reset.
|
||||
*/
|
||||
Stopwatch :: struct {
|
||||
running: bool,
|
||||
_start_time: Tick,
|
||||
_accumulation: Duration,
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the current time.
|
||||
*/
|
||||
now :: proc "contextless" () -> Time {
|
||||
return _now()
|
||||
}
|
||||
|
||||
/*
|
||||
Sleep for the specified duration.
|
||||
*/
|
||||
sleep :: proc "contextless" (d: Duration) {
|
||||
_sleep(d)
|
||||
}
|
||||
|
||||
/*
|
||||
Start the stopwatch.
|
||||
*/
|
||||
stopwatch_start :: proc "contextless" (stopwatch: ^Stopwatch) {
|
||||
if !stopwatch.running {
|
||||
stopwatch._start_time = tick_now()
|
||||
@@ -67,6 +135,9 @@ stopwatch_start :: proc "contextless" (stopwatch: ^Stopwatch) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Stop the stopwatch.
|
||||
*/
|
||||
stopwatch_stop :: proc "contextless" (stopwatch: ^Stopwatch) {
|
||||
if stopwatch.running {
|
||||
stopwatch._accumulation += tick_diff(stopwatch._start_time, tick_now())
|
||||
@@ -74,11 +145,21 @@ stopwatch_stop :: proc "contextless" (stopwatch: ^Stopwatch) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Reset the stopwatch.
|
||||
*/
|
||||
stopwatch_reset :: proc "contextless" (stopwatch: ^Stopwatch) {
|
||||
stopwatch._accumulation = {}
|
||||
stopwatch.running = false
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the total time, counted by the stopwatch.
|
||||
|
||||
This procedure obtains the total time, counted by the stopwatch. If the stopwatch
|
||||
isn't stopped at the time of calling this procedure, the time between the last
|
||||
start and the current time is also accounted for.
|
||||
*/
|
||||
stopwatch_duration :: proc "contextless" (stopwatch: Stopwatch) -> Duration {
|
||||
if !stopwatch.running {
|
||||
return stopwatch._accumulation
|
||||
@@ -86,40 +167,92 @@ stopwatch_duration :: proc "contextless" (stopwatch: Stopwatch) -> Duration {
|
||||
return stopwatch._accumulation + tick_diff(stopwatch._start_time, tick_now())
|
||||
}
|
||||
|
||||
/*
|
||||
Calculate the duration elapsed between two times.
|
||||
*/
|
||||
diff :: proc "contextless" (start, end: Time) -> Duration {
|
||||
d := end._nsec - start._nsec
|
||||
return Duration(d)
|
||||
}
|
||||
|
||||
/*
|
||||
Calculate the duration elapsed since a specific time.
|
||||
*/
|
||||
since :: proc "contextless" (start: Time) -> Duration {
|
||||
return diff(start, now())
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the number of nanoseconds in a duration.
|
||||
*/
|
||||
duration_nanoseconds :: proc "contextless" (d: Duration) -> i64 {
|
||||
return i64(d)
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the number of microseconds in a duration.
|
||||
*/
|
||||
duration_microseconds :: proc "contextless" (d: Duration) -> f64 {
|
||||
return duration_seconds(d) * 1e6
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the number of milliseconds in a duration.
|
||||
*/
|
||||
duration_milliseconds :: proc "contextless" (d: Duration) -> f64 {
|
||||
return duration_seconds(d) * 1e3
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the number of seconds in a duration.
|
||||
*/
|
||||
duration_seconds :: proc "contextless" (d: Duration) -> f64 {
|
||||
sec := d / Second
|
||||
nsec := d % Second
|
||||
return f64(sec) + f64(nsec)/1e9
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the number of minutes in a duration.
|
||||
*/
|
||||
duration_minutes :: proc "contextless" (d: Duration) -> f64 {
|
||||
min := d / Minute
|
||||
nsec := d % Minute
|
||||
return f64(min) + f64(nsec)/(60*1e9)
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the number of hours in a duration.
|
||||
*/
|
||||
duration_hours :: proc "contextless" (d: Duration) -> f64 {
|
||||
hour := d / Hour
|
||||
nsec := d % Hour
|
||||
return f64(hour) + f64(nsec)/(60*60*1e9)
|
||||
}
|
||||
|
||||
/*
|
||||
Round a duration to a specific unit.
|
||||
|
||||
This procedure rounds the duration to a specific unit.
|
||||
|
||||
**Inputs**:
|
||||
- `d`: The duration to round.
|
||||
- `m`: The unit to round to.
|
||||
|
||||
**Returns**:
|
||||
- The duration `d`, rounded to the unit specified by `m`.
|
||||
|
||||
**Example**:
|
||||
|
||||
In order to obtain the rough amount of seconds in a duration, the following call
|
||||
can be used:
|
||||
|
||||
```
|
||||
time.duration_round(my_duration, time.Second)
|
||||
```
|
||||
|
||||
**Note**: Any duration can be supplied as a unit.
|
||||
*/
|
||||
duration_round :: proc "contextless" (d, m: Duration) -> Duration {
|
||||
_less_than_half :: #force_inline proc "contextless" (x, y: Duration) -> bool {
|
||||
return u64(x)+u64(x) < u64(y)
|
||||
@@ -149,50 +282,103 @@ duration_round :: proc "contextless" (d, m: Duration) -> Duration {
|
||||
return MAX_DURATION
|
||||
}
|
||||
|
||||
/*
|
||||
Truncate the duration to the specified unit.
|
||||
|
||||
This procedure truncates the duration `d` to the unit specified by `m`.
|
||||
|
||||
**Inputs**:
|
||||
- `d`: The duration to truncate.
|
||||
- `m`: The unit to truncate to.
|
||||
|
||||
**Returns**:
|
||||
- The duration `d`, truncated to the unit specified by `m`.
|
||||
|
||||
**Example**:
|
||||
|
||||
In order to obtain the amount of whole seconds in a duration, the following call
|
||||
can be used:
|
||||
|
||||
```
|
||||
time.duration_round(my_duration, time.Second)
|
||||
```
|
||||
|
||||
**Note**: Any duration can be supplied as a unit.
|
||||
*/
|
||||
duration_truncate :: proc "contextless" (d, m: Duration) -> Duration {
|
||||
return d if m <= 0 else d - d%m
|
||||
}
|
||||
|
||||
/*
|
||||
Parse time into date components.
|
||||
*/
|
||||
date :: proc "contextless" (t: Time) -> (year: int, month: Month, day: int) {
|
||||
year, month, day, _ = _abs_date(_time_abs(t), true)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the year of the date specified by time.
|
||||
*/
|
||||
year :: proc "contextless" (t: Time) -> (year: int) {
|
||||
year, _, _, _ = _date(t, true)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the month of the date specified by time.
|
||||
*/
|
||||
month :: proc "contextless" (t: Time) -> (month: Month) {
|
||||
_, month, _, _ = _date(t, true)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the day of the date specified by time.
|
||||
*/
|
||||
day :: proc "contextless" (t: Time) -> (day: int) {
|
||||
_, _, day, _ = _date(t, true)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the week day of the date specified by time.
|
||||
*/
|
||||
weekday :: proc "contextless" (t: Time) -> (weekday: Weekday) {
|
||||
abs := _time_abs(t)
|
||||
sec := (abs + u64(Weekday.Monday) * SECONDS_PER_DAY) % SECONDS_PER_WEEK
|
||||
return Weekday(int(sec) / SECONDS_PER_DAY)
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the time components from a time, a duration or a stopwatch's total.
|
||||
*/
|
||||
clock :: proc { clock_from_time, clock_from_duration, clock_from_stopwatch }
|
||||
|
||||
/*
|
||||
Obtain the time components from a time.
|
||||
*/
|
||||
clock_from_time :: proc "contextless" (t: Time) -> (hour, min, sec: int) {
|
||||
return clock_from_seconds(_time_abs(t))
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the time components from a duration.
|
||||
*/
|
||||
clock_from_duration :: proc "contextless" (d: Duration) -> (hour, min, sec: int) {
|
||||
return clock_from_seconds(u64(d/1e9))
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the time components from a stopwatch's total.
|
||||
*/
|
||||
clock_from_stopwatch :: proc "contextless" (s: Stopwatch) -> (hour, min, sec: int) {
|
||||
return clock_from_duration(stopwatch_duration(s))
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the time components from the number of seconds.
|
||||
*/
|
||||
clock_from_seconds :: proc "contextless" (nsec: u64) -> (hour, min, sec: int) {
|
||||
sec = int(nsec % SECONDS_PER_DAY)
|
||||
hour = sec / SECONDS_PER_HOUR
|
||||
@@ -202,10 +388,16 @@ clock_from_seconds :: proc "contextless" (nsec: u64) -> (hour, min, sec: int) {
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Read the timestamp counter of the CPU.
|
||||
*/
|
||||
read_cycle_counter :: proc "contextless" () -> u64 {
|
||||
return u64(intrinsics.read_cycle_counter())
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain time from unix seconds and unix nanoseconds.
|
||||
*/
|
||||
unix :: proc "contextless" (sec: i64, nsec: i64) -> Time {
|
||||
sec, nsec := sec, nsec
|
||||
if nsec < 0 || nsec >= 1e9 {
|
||||
@@ -220,31 +412,59 @@ unix :: proc "contextless" (sec: i64, nsec: i64) -> Time {
|
||||
return Time{(sec*1e9 + nsec)}
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain time from unix nanoseconds.
|
||||
*/
|
||||
from_nanoseconds :: #force_inline proc "contextless" (nsec: i64) -> Time {
|
||||
return Time{nsec}
|
||||
}
|
||||
|
||||
/*
|
||||
Alias for `time_to_unix`.
|
||||
*/
|
||||
to_unix_seconds :: time_to_unix
|
||||
|
||||
/*
|
||||
Obtain the unix seconds from a time.
|
||||
*/
|
||||
time_to_unix :: proc "contextless" (t: Time) -> i64 {
|
||||
return t._nsec/1e9
|
||||
}
|
||||
|
||||
/*
|
||||
Alias for `time_to_unix_nano`.
|
||||
*/
|
||||
to_unix_nanoseconds :: time_to_unix_nano
|
||||
|
||||
/*
|
||||
Obtain the unix nanoseconds from a time.
|
||||
*/
|
||||
time_to_unix_nano :: proc "contextless" (t: Time) -> i64 {
|
||||
return t._nsec
|
||||
}
|
||||
|
||||
/*
|
||||
Add duration to a time.
|
||||
*/
|
||||
time_add :: proc "contextless" (t: Time, d: Duration) -> Time {
|
||||
return Time{t._nsec + i64(d)}
|
||||
}
|
||||
|
||||
// Accurate sleep borrowed from: https://blat-blatnik.github.io/computerBear/making-accurate-sleep-function/
|
||||
//
|
||||
// Accuracy seems to be pretty good out of the box on Linux, to within around 4µs worst case.
|
||||
// On Windows it depends but is comparable with regular sleep in the worst case.
|
||||
// To get the same kind of accuracy as on Linux, have your program call `windows.timeBeginPeriod(1)` to
|
||||
// tell Windows to use a more accurate timer for your process.
|
||||
// Additionally your program should call `windows.timeEndPeriod(1)` once you're done with `accurate_sleep`.
|
||||
/*
|
||||
Accurate sleep
|
||||
|
||||
This procedure sleeps for the duration specified by `d`, very accurately.
|
||||
|
||||
**Note**: Implementation borrowed from: [this source](https://blat-blatnik.github.io/computerBear/making-accurate-sleep-function/)
|
||||
|
||||
**Note(linux)**: The accuracy is within around 4µs (microseconds), in the worst case.
|
||||
|
||||
**Note(windows)**: The accuracy depends but is comparable with regular sleep in
|
||||
the worst case. To get the same kind of accuracy as on Linux, have your program
|
||||
call `windows.timeBeginPeriod(1)` to tell Windows to use a more accurate timer
|
||||
for your process. Additionally your program should call `windows.timeEndPeriod(1)`
|
||||
once you're done with `accurate_sleep`.
|
||||
*/
|
||||
accurate_sleep :: proc "contextless" (d: Duration) {
|
||||
to_sleep, estimate, mean, m2, count: Duration
|
||||
|
||||
@@ -362,6 +582,13 @@ _abs_date :: proc "contextless" (abs: u64, full: bool) -> (year: int, month: Mon
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Convert datetime components into time.
|
||||
|
||||
This procedure calculates the time from datetime components supplied in the
|
||||
arguments to this procedure. If the datetime components don't represent a valid
|
||||
datetime, the function returns `false` in the second argument.
|
||||
*/
|
||||
components_to_time :: proc "contextless" (#any_int year, #any_int month, #any_int day, #any_int hour, #any_int minute, #any_int second: i64, #any_int nsec := i64(0)) -> (t: Time, ok: bool) {
|
||||
this_date, err := dt.components_to_datetime(year, month, day, hour, minute, second, nsec)
|
||||
if err != .None {
|
||||
@@ -370,6 +597,12 @@ components_to_time :: proc "contextless" (#any_int year, #any_int month, #any_in
|
||||
return compound_to_time(this_date)
|
||||
}
|
||||
|
||||
/*
|
||||
Convert datetime into time.
|
||||
|
||||
If the datetime represents a time outside of a valid range, `false` is returned
|
||||
as the second return value. See `Time` for the representable range.
|
||||
*/
|
||||
compound_to_time :: proc "contextless" (datetime: dt.DateTime) -> (t: Time, ok: bool) {
|
||||
unix_epoch := dt.DateTime{{1970, 1, 1}, {0, 0, 0, 0}}
|
||||
delta, err := dt.sub(datetime, unix_epoch)
|
||||
@@ -387,12 +620,21 @@ compound_to_time :: proc "contextless" (datetime: dt.DateTime) -> (t: Time, ok:
|
||||
return Time{_nsec=i64(nanoseconds)}, true
|
||||
}
|
||||
|
||||
/*
|
||||
Convert datetime components into time.
|
||||
*/
|
||||
datetime_to_time :: proc{components_to_time, compound_to_time}
|
||||
|
||||
/*
|
||||
Check if a year is a leap year.
|
||||
*/
|
||||
is_leap_year :: proc "contextless" (year: int) -> (leap: bool) {
|
||||
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
|
||||
}
|
||||
|
||||
/*
|
||||
Days before each month in a year, not counting the leap day on february 29th.
|
||||
*/
|
||||
@(rodata)
|
||||
days_before := [?]i32{
|
||||
0,
|
||||
@@ -410,11 +652,37 @@ days_before := [?]i32{
|
||||
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Number of seconds in a minute (without leap seconds).
|
||||
*/
|
||||
SECONDS_PER_MINUTE :: 60
|
||||
|
||||
/*
|
||||
Number of seconds in an hour (without leap seconds).
|
||||
*/
|
||||
SECONDS_PER_HOUR :: 60 * SECONDS_PER_MINUTE
|
||||
|
||||
/*
|
||||
Number of seconds in a day (without leap seconds).
|
||||
*/
|
||||
SECONDS_PER_DAY :: 24 * SECONDS_PER_HOUR
|
||||
|
||||
/*
|
||||
Number of seconds in a week (without leap seconds).
|
||||
*/
|
||||
SECONDS_PER_WEEK :: 7 * SECONDS_PER_DAY
|
||||
|
||||
/*
|
||||
Days in 400 years, with leap days.
|
||||
*/
|
||||
DAYS_PER_400_YEARS :: 365*400 + 97
|
||||
|
||||
/*
|
||||
Days in 100 years, with leap days.
|
||||
*/
|
||||
DAYS_PER_100_YEARS :: 365*100 + 24
|
||||
|
||||
/*
|
||||
Days in 4 years, with leap days.
|
||||
*/
|
||||
DAYS_PER_4_YEARS :: 365*4 + 1
|
||||
|
||||
@@ -1837,7 +1837,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
|
||||
|
||||
if (rs->vals.count == 1) {
|
||||
Type *t = type_deref(operand.type);
|
||||
if (is_type_map(t) || is_type_bit_set(t)) {
|
||||
if (t != NULL && (is_type_map(t) || is_type_bit_set(t))) {
|
||||
gbString v = expr_to_string(rs->vals[0]);
|
||||
defer (gb_string_free(v));
|
||||
error_line("\tSuggestion: place parentheses around the expression\n");
|
||||
|
||||
@@ -2172,7 +2172,7 @@ gb_internal void print_show_help(String const arg0, String const &command) {
|
||||
if (LB_USE_NEW_PASS_SYSTEM) {
|
||||
print_usage_line(3, "-o:aggressive");
|
||||
}
|
||||
print_usage_line(2, "The default is -o:none.");
|
||||
print_usage_line(2, "The default is -o:minimal.");
|
||||
print_usage_line(0, "");
|
||||
}
|
||||
|
||||
|
||||
@@ -5609,7 +5609,7 @@ gb_internal AstPackage *try_add_import_path(Parser *p, String path, String const
|
||||
pkg->foreign_files.allocator = permanent_allocator();
|
||||
|
||||
// NOTE(bill): Single file initial package
|
||||
if (kind == Package_Init && string_ends_with(path, FILE_EXT)) {
|
||||
if (kind == Package_Init && !path_is_directory(path) && string_ends_with(path, FILE_EXT)) {
|
||||
FileInfo fi = {};
|
||||
fi.name = filename_from_path(path);
|
||||
fi.fullpath = path;
|
||||
@@ -6529,6 +6529,7 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) {
|
||||
GB_ASSERT(init_filename.text[init_filename.len] == 0);
|
||||
|
||||
String init_fullpath = path_to_full_path(permanent_allocator(), init_filename);
|
||||
|
||||
if (!path_is_directory(init_fullpath)) {
|
||||
String const ext = str_lit(".odin");
|
||||
if (!string_ends_with(init_fullpath, ext)) {
|
||||
@@ -6542,9 +6543,8 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) {
|
||||
}
|
||||
if ((build_context.command_kind & Command__does_build) &&
|
||||
build_context.build_mode == BuildMode_Executable) {
|
||||
String short_path = filename_from_path(path);
|
||||
char *cpath = alloc_cstring(temporary_allocator(), short_path);
|
||||
if (gb_file_exists(cpath)) {
|
||||
String output_path = path_to_string(temporary_allocator(), build_context.build_paths[8]);
|
||||
if (path_is_directory(output_path)) {
|
||||
error({}, "Please specify the executable name with -out:<string> as a directory exists with the same name in the current working directory");
|
||||
return ParseFile_DirectoryAlreadyExists;
|
||||
}
|
||||
|
||||
120
tests/core/encoding/ini/test_core_ini.odin
Normal file
120
tests/core/encoding/ini/test_core_ini.odin
Normal file
@@ -0,0 +1,120 @@
|
||||
package test_core_ini
|
||||
|
||||
import "base:runtime"
|
||||
import "core:encoding/ini"
|
||||
import "core:mem/virtual"
|
||||
import "core:strings"
|
||||
import "core:testing"
|
||||
|
||||
@test
|
||||
parse_ini :: proc(t: ^testing.T) {
|
||||
ini_data := `
|
||||
[LOG]
|
||||
level = "devel"
|
||||
file = "/var/log/testing.log"
|
||||
|
||||
[USER]
|
||||
first_name = "John"
|
||||
surname = "Smith"
|
||||
`
|
||||
|
||||
m, err := ini.load_map_from_string(ini_data, context.allocator)
|
||||
defer ini.delete_map(m)
|
||||
|
||||
testing.expectf(
|
||||
t,
|
||||
strings.contains(m["LOG"]["level"], "devel"),
|
||||
"Expected m[\"LOG\"][\"level\"] to be equal to 'devel' instead got %v",
|
||||
m["LOG"]["level"],
|
||||
)
|
||||
testing.expectf(
|
||||
t,
|
||||
strings.contains(m["LOG"]["file"], "/var/log/testing.log"),
|
||||
"Expected m[\"LOG\"][\"file\"] to be equal to '/var/log/testing.log' instead got %v",
|
||||
m["LOG"]["file"],
|
||||
)
|
||||
testing.expectf(
|
||||
t,
|
||||
strings.contains(m["USER"]["first_name"], "John"),
|
||||
"Expected m[\"USER\"][\"first_name\"] to be equal to 'John' instead got %v",
|
||||
m["USER"]["first_name"],
|
||||
)
|
||||
testing.expectf(
|
||||
t,
|
||||
strings.contains(m["USER"]["surname"], "Smith"),
|
||||
"Expected m[\"USER\"][\"surname\"] to be equal to 'Smith' instead got %v",
|
||||
m["USER"]["surname"],
|
||||
)
|
||||
|
||||
testing.expectf(t, err == nil, "Expected `ini.load_map_from_string` to return a nil error, got %v", err)
|
||||
}
|
||||
|
||||
@test
|
||||
ini_to_string :: proc(t: ^testing.T) {
|
||||
m := ini.Map{
|
||||
"LEVEL" = {
|
||||
"LOG" = "debug",
|
||||
},
|
||||
}
|
||||
|
||||
str := ini.save_map_to_string(m, context.allocator)
|
||||
defer delete(str)
|
||||
delete(m["LEVEL"])
|
||||
delete(m)
|
||||
|
||||
testing.expectf(
|
||||
t,
|
||||
strings.contains(str, "[LEVEL]LOG = debug"),
|
||||
"Expected `ini.save_map_to_string` to return a string equal to \"[LEVEL]LOG = debug\", got %v",
|
||||
str,
|
||||
)
|
||||
}
|
||||
|
||||
@test
|
||||
ini_iterator :: proc(t: ^testing.T) {
|
||||
ini_data := `
|
||||
[LOG]
|
||||
level = "devel"
|
||||
file = "/var/log/testing.log"
|
||||
|
||||
[USER]
|
||||
first_name = "John"
|
||||
surname = "Smith"
|
||||
`
|
||||
|
||||
i := 0
|
||||
iterator := ini.iterator_from_string(ini_data)
|
||||
for key, value in ini.iterate(&iterator) {
|
||||
if strings.contains(key, "level") {
|
||||
testing.expectf(
|
||||
t,
|
||||
strings.contains(value, "devel"),
|
||||
"Expected 'level' to be equal to 'devel' instead got '%v'",
|
||||
value,
|
||||
)
|
||||
} else if strings.contains(key, "file") {
|
||||
testing.expectf(
|
||||
t,
|
||||
strings.contains(value, "/var/log/testing.log"),
|
||||
"Expected 'file' to be equal to '/var/log/testing.log' instead got '%v'",
|
||||
value,
|
||||
)
|
||||
} else if strings.contains(key, "first_name") {
|
||||
testing.expectf(
|
||||
t,
|
||||
strings.contains(value, "John"),
|
||||
"Expected 'first_name' to be equal to 'John' instead got '%v'",
|
||||
value,
|
||||
)
|
||||
} else if strings.contains(key, "surname") {
|
||||
testing.expectf(
|
||||
t,
|
||||
strings.contains(value, "Smith"),
|
||||
"Expected 'surname' to be equal to 'Smith' instead got '%v'",
|
||||
value,
|
||||
)
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
testing.expectf(t, i == 4, "Expected to loop 4 times, only looped %v times", i)
|
||||
}
|
||||
@@ -349,6 +349,24 @@ unmarshal_json :: proc(t: ^testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
@test
|
||||
unmarshal_empty_struct :: proc(t: ^testing.T) {
|
||||
TestStruct :: struct {}
|
||||
test := make(map[string]TestStruct)
|
||||
input: = `{
|
||||
"test_1": {},
|
||||
"test_2": {}
|
||||
}`
|
||||
err := json.unmarshal(transmute([]u8)input, &test)
|
||||
defer {
|
||||
for k in test {
|
||||
delete(k)
|
||||
}
|
||||
delete(test)
|
||||
}
|
||||
testing.expect(t, err == nil, "Expected empty struct to unmarshal without error")
|
||||
}
|
||||
|
||||
@test
|
||||
surrogate :: proc(t: ^testing.T) {
|
||||
input := `+ + * 😃 - /`
|
||||
|
||||
4
vendor/lua/5.1/lua.odin
vendored
4
vendor/lua/5.1/lua.odin
vendored
@@ -16,7 +16,7 @@ when LUA_SHARED {
|
||||
} else when ODIN_OS == .Linux {
|
||||
foreign import lib "linux/liblua5.1.so"
|
||||
} else {
|
||||
foreign import lib "system:liblua.so.5.1"
|
||||
foreign import lib "system:lua5.1"
|
||||
}
|
||||
} else {
|
||||
when ODIN_OS == .Windows {
|
||||
@@ -24,7 +24,7 @@ when LUA_SHARED {
|
||||
} else when ODIN_OS == .Linux {
|
||||
foreign import lib "linux/liblua5.1.a"
|
||||
} else {
|
||||
foreign import lib "system:liblua5.1.a"
|
||||
foreign import lib "system:lua5.1"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
4
vendor/lua/5.2/lua.odin
vendored
4
vendor/lua/5.2/lua.odin
vendored
@@ -16,7 +16,7 @@ when LUA_SHARED {
|
||||
} else when ODIN_OS == .Linux {
|
||||
foreign import lib "linux/liblua52.so"
|
||||
} else {
|
||||
foreign import lib "system:liblua.so.5.2"
|
||||
foreign import lib "system:lua5.2"
|
||||
}
|
||||
} else {
|
||||
when ODIN_OS == .Windows {
|
||||
@@ -24,7 +24,7 @@ when LUA_SHARED {
|
||||
} else when ODIN_OS == .Linux {
|
||||
foreign import lib "linux/liblua52.a"
|
||||
} else {
|
||||
foreign import lib "system:liblua52.a"
|
||||
foreign import lib "system:lua5.2"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
4
vendor/lua/5.3/lua.odin
vendored
4
vendor/lua/5.3/lua.odin
vendored
@@ -16,7 +16,7 @@ when LUA_SHARED {
|
||||
} else when ODIN_OS == .Linux {
|
||||
foreign import lib "linux/liblua53.so"
|
||||
} else {
|
||||
foreign import lib "system:liblua.so.5.3"
|
||||
foreign import lib "system:lua5.3"
|
||||
}
|
||||
} else {
|
||||
when ODIN_OS == .Windows {
|
||||
@@ -24,7 +24,7 @@ when LUA_SHARED {
|
||||
} else when ODIN_OS == .Linux {
|
||||
foreign import lib "linux/liblua53.a"
|
||||
} else {
|
||||
foreign import lib "system:liblua53.a"
|
||||
foreign import lib "system:lua5.3"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
12
vendor/lua/5.4/lua.odin
vendored
12
vendor/lua/5.4/lua.odin
vendored
@@ -16,15 +16,7 @@ when LUA_SHARED {
|
||||
} else when ODIN_OS == .Linux {
|
||||
foreign import lib "linux/liblua54.so"
|
||||
} else {
|
||||
// Note(bumbread): My linux system has a few aliases for this shared object
|
||||
// lublua5.4.so, liblua.so, lublua.so.5.4, liblua.so.5.4.6. I don't know
|
||||
// who enforces these numbers (probably ld?), and if it can be done in a
|
||||
// unix-generic way, but in any way I think the most sane thing to do is to
|
||||
// keep it close to what linux does and if it breaks, just special case those
|
||||
// operating systems.
|
||||
// Also there was no alias for liblua54.so, that seems to suggest that way
|
||||
// of specifying it isn't portable
|
||||
foreign import lib "system:liblua.so.5.4"
|
||||
foreign import lib "system:lua5.4"
|
||||
}
|
||||
} else {
|
||||
when ODIN_OS == .Windows {
|
||||
@@ -32,7 +24,7 @@ when LUA_SHARED {
|
||||
} else when ODIN_OS == .Linux {
|
||||
foreign import lib "linux/liblua54.a"
|
||||
} else {
|
||||
foreign import lib "system:liblua54.a"
|
||||
foreign import lib "system:lua5.4"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
6
vendor/x11/xlib/xlib_procs.odin
vendored
6
vendor/x11/xlib/xlib_procs.odin
vendored
@@ -49,7 +49,7 @@ foreign xlib {
|
||||
DisplayString :: proc(display: ^Display) -> cstring ---
|
||||
// Display macros (defaults)
|
||||
DefaultColormap :: proc(display: ^Display, screen_no: i32) -> Colormap ---
|
||||
DefaultDepth :: proc(display: ^Display) -> i32 ---
|
||||
DefaultDepth :: proc(display: ^Display, screen_no: i32) -> i32 ---
|
||||
DefaultGC :: proc(display: ^Display, screen_no: i32) -> GC ---
|
||||
DefaultRootWindow :: proc(display: ^Display) -> Window ---
|
||||
DefaultScreen :: proc(display: ^Display) -> i32 ---
|
||||
@@ -138,8 +138,8 @@ foreign xlib {
|
||||
width: u32,
|
||||
height: u32,
|
||||
bordersz: u32,
|
||||
border: int,
|
||||
bg: int,
|
||||
border: uint,
|
||||
bg: uint,
|
||||
) -> Window ---
|
||||
DestroyWindow :: proc(display: ^Display, window: Window) ---
|
||||
DestroySubwindows :: proc(display: ^Display, window: Window) ---
|
||||
|
||||
Reference in New Issue
Block a user