mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 15:48:51 +00:00
Merge branch 'odin-lang:master' into master
This commit is contained in:
@@ -98,6 +98,14 @@ when ODIN_OS == .Haiku {
|
|||||||
ERANGE :: B_POSIX_ERROR_BASE + 17
|
ERANGE :: B_POSIX_ERROR_BASE + 17
|
||||||
}
|
}
|
||||||
|
|
||||||
|
when ODIN_OS == .JS {
|
||||||
|
_ :: libc
|
||||||
|
_get_errno :: proc "c" () -> ^int {
|
||||||
|
@(static) errno: int
|
||||||
|
return &errno
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Odin has no way to make an identifier "errno" behave as a function call to
|
// Odin has no way to make an identifier "errno" behave as a function call to
|
||||||
// read the value, or to produce an lvalue such that you can assign a different
|
// read the value, or to produce an lvalue such that you can assign a different
|
||||||
// error value to errno. To work around this, just expose it as a function like
|
// error value to errno. To work around this, just expose it as a function like
|
||||||
|
|||||||
@@ -89,6 +89,30 @@ when ODIN_OS == .Linux {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
when ODIN_OS == .JS {
|
||||||
|
fpos_t :: struct #raw_union { _: [16]char, _: longlong, _: double, }
|
||||||
|
|
||||||
|
_IOFBF :: 0
|
||||||
|
_IOLBF :: 1
|
||||||
|
_IONBF :: 2
|
||||||
|
|
||||||
|
BUFSIZ :: 1024
|
||||||
|
|
||||||
|
EOF :: int(-1)
|
||||||
|
|
||||||
|
FOPEN_MAX :: 1000
|
||||||
|
|
||||||
|
FILENAME_MAX :: 4096
|
||||||
|
|
||||||
|
L_tmpnam :: 20
|
||||||
|
|
||||||
|
SEEK_SET :: 0
|
||||||
|
SEEK_CUR :: 1
|
||||||
|
SEEK_END :: 2
|
||||||
|
|
||||||
|
TMP_MAX :: 308915776
|
||||||
|
}
|
||||||
|
|
||||||
when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
|
when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
|
||||||
fpos_t :: distinct i64
|
fpos_t :: distinct i64
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ when ODIN_OS == .Windows {
|
|||||||
foreign import libc "system:c"
|
foreign import libc "system:c"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require)
|
||||||
|
import "base:runtime"
|
||||||
|
|
||||||
when ODIN_OS == .Windows {
|
when ODIN_OS == .Windows {
|
||||||
RAND_MAX :: 0x7fff
|
RAND_MAX :: 0x7fff
|
||||||
|
|
||||||
@@ -145,6 +148,10 @@ aligned_alloc :: #force_inline proc "c" (alignment, size: size_t) -> rawptr {
|
|||||||
_aligned_malloc :: proc(size, alignment: size_t) -> rawptr ---
|
_aligned_malloc :: proc(size, alignment: size_t) -> rawptr ---
|
||||||
}
|
}
|
||||||
return _aligned_malloc(size=size, alignment=alignment)
|
return _aligned_malloc(size=size, alignment=alignment)
|
||||||
|
} else when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 {
|
||||||
|
context = runtime.default_context()
|
||||||
|
data, _ := runtime.mem_alloc_bytes(auto_cast size, auto_cast alignment)
|
||||||
|
return raw_data(data)
|
||||||
} else {
|
} else {
|
||||||
foreign libc {
|
foreign libc {
|
||||||
aligned_alloc :: proc(alignment, size: size_t) -> rawptr ---
|
aligned_alloc :: proc(alignment, size: size_t) -> rawptr ---
|
||||||
@@ -160,6 +167,9 @@ aligned_free :: #force_inline proc "c" (ptr: rawptr) {
|
|||||||
_aligned_free :: proc(ptr: rawptr) ---
|
_aligned_free :: proc(ptr: rawptr) ---
|
||||||
}
|
}
|
||||||
_aligned_free(ptr)
|
_aligned_free(ptr)
|
||||||
|
} else when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 {
|
||||||
|
context = runtime.default_context()
|
||||||
|
runtime.mem_free(ptr)
|
||||||
} else {
|
} else {
|
||||||
free(ptr)
|
free(ptr)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ when ODIN_OS == .Windows {
|
|||||||
foreign import libc "system:c"
|
foreign import libc "system:c"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(default_calling_convention="c")
|
||||||
foreign libc {
|
foreign libc {
|
||||||
// 7.24.2 Copying functions
|
// 7.24.2 Copying functions
|
||||||
memcpy :: proc(s1, s2: rawptr, n: size_t) -> rawptr ---
|
memcpy :: proc(s1, s2: rawptr, n: size_t) -> rawptr ---
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ when ODIN_OS == .Windows {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Haiku {
|
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Haiku || ODIN_OS == .JS {
|
||||||
@(default_calling_convention="c")
|
@(default_calling_convention="c")
|
||||||
foreign libc {
|
foreign libc {
|
||||||
// 7.27.2 Time manipulation functions
|
// 7.27.2 Time manipulation functions
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ when ODIN_OS == .Windows {
|
|||||||
wctrans_t :: distinct wchar_t
|
wctrans_t :: distinct wchar_t
|
||||||
wctype_t :: distinct ushort
|
wctype_t :: distinct ushort
|
||||||
|
|
||||||
} else when ODIN_OS == .Linux {
|
} else when ODIN_OS == .Linux || ODIN_OS == .JS {
|
||||||
wctrans_t :: distinct intptr_t
|
wctrans_t :: distinct intptr_t
|
||||||
wctype_t :: distinct ulong
|
wctype_t :: distinct ulong
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
#+build js
|
#+build js
|
||||||
package os
|
package os
|
||||||
|
|
||||||
import "base:runtime"
|
|
||||||
|
|
||||||
foreign import "odin_env"
|
foreign import "odin_env"
|
||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
# WASM on the Web
|
||||||
|
|
||||||
|
This directory is for use when targeting the `js_wasm32` target and the packages that rely on it.
|
||||||
|
|
||||||
|
The `js_wasm32` target assumes that the WASM output will be ran within a web browser rather than a standalone VM. In the VM cases, either `wasi_wasm32` or `freestanding_wasm32` should be used accordingly.
|
||||||
|
|
||||||
|
## Example for `js_wasm32`
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!-- Copy `core:sys/wasm/js/odin.js` into your web server -->
|
||||||
|
<script type="text/javascript" src="odin.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
odin.runWasm(pathToWasm, consolePreElement);
|
||||||
|
</script>
|
||||||
|
```
|
||||||
@@ -90,4 +90,4 @@ window_get_scroll :: proc "contextless" () -> (x, y: f64) {
|
|||||||
scroll: [2]f64
|
scroll: [2]f64
|
||||||
_window_get_scroll(&scroll)
|
_window_get_scroll(&scroll)
|
||||||
return scroll.x, scroll.y
|
return scroll.x, scroll.y
|
||||||
}
|
}
|
||||||
@@ -35,7 +35,7 @@ Event_Kind :: enum u32 {
|
|||||||
Submit,
|
Submit,
|
||||||
Blur,
|
Blur,
|
||||||
Change,
|
Change,
|
||||||
HashChange,
|
Hash_Change,
|
||||||
Select,
|
Select,
|
||||||
|
|
||||||
Animation_Start,
|
Animation_Start,
|
||||||
@@ -82,6 +82,9 @@ Event_Kind :: enum u32 {
|
|||||||
|
|
||||||
Context_Menu,
|
Context_Menu,
|
||||||
|
|
||||||
|
Gamepad_Connected,
|
||||||
|
Gamepad_Disconnected,
|
||||||
|
|
||||||
Custom,
|
Custom,
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -117,7 +120,7 @@ event_kind_string := [Event_Kind]string{
|
|||||||
.Submit = "submit",
|
.Submit = "submit",
|
||||||
.Blur = "blur",
|
.Blur = "blur",
|
||||||
.Change = "change",
|
.Change = "change",
|
||||||
.HashChange = "hashchange",
|
.Hash_Change = "hashchange",
|
||||||
.Select = "select",
|
.Select = "select",
|
||||||
|
|
||||||
.Animation_Start = "animationstart",
|
.Animation_Start = "animationstart",
|
||||||
@@ -164,6 +167,9 @@ event_kind_string := [Event_Kind]string{
|
|||||||
|
|
||||||
.Context_Menu = "contextmenu",
|
.Context_Menu = "contextmenu",
|
||||||
|
|
||||||
|
.Gamepad_Connected = "gamepadconnected",
|
||||||
|
.Gamepad_Disconnected = "gamepaddisconnected",
|
||||||
|
|
||||||
.Custom = "?custom?",
|
.Custom = "?custom?",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,9 +186,15 @@ Key_Location :: enum u8 {
|
|||||||
Numpad = 3,
|
Numpad = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
KEYBOARD_MAX_KEY_SIZE :: 16
|
KEYBOARD_MAX_KEY_SIZE :: 16
|
||||||
KEYBOARD_MAX_CODE_SIZE :: 16
|
KEYBOARD_MAX_CODE_SIZE :: 16
|
||||||
|
|
||||||
|
GAMEPAD_MAX_ID_SIZE :: 64
|
||||||
|
GAMEPAD_MAX_MAPPING_SIZE :: 64
|
||||||
|
|
||||||
|
GAMEPAD_MAX_BUTTONS :: 64
|
||||||
|
GAMEPAD_MAX_AXES :: 16
|
||||||
|
|
||||||
Event_Target_Kind :: enum u32 {
|
Event_Target_Kind :: enum u32 {
|
||||||
Element = 0,
|
Element = 0,
|
||||||
Document = 1,
|
Document = 1,
|
||||||
@@ -203,6 +215,30 @@ Event_Option :: enum u8 {
|
|||||||
}
|
}
|
||||||
Event_Options :: distinct bit_set[Event_Option; u8]
|
Event_Options :: distinct bit_set[Event_Option; u8]
|
||||||
|
|
||||||
|
Gamepad_Button :: struct {
|
||||||
|
value: f64,
|
||||||
|
pressed: bool,
|
||||||
|
touched: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
Gamepad_State :: struct {
|
||||||
|
id: string,
|
||||||
|
mapping: string,
|
||||||
|
index: int,
|
||||||
|
connected: bool,
|
||||||
|
timestamp: f64,
|
||||||
|
|
||||||
|
button_count: int,
|
||||||
|
axis_count: int,
|
||||||
|
buttons: [GAMEPAD_MAX_BUTTONS]Gamepad_Button `fmt:"v,button_count"`,
|
||||||
|
axes: [GAMEPAD_MAX_AXES]f64 `fmt:"v,axes_count"`,
|
||||||
|
|
||||||
|
_id_len: int `fmt:"-"`,
|
||||||
|
_mapping_len: int `fmt:"-"`,
|
||||||
|
_id_buf: [GAMEPAD_MAX_ID_SIZE]byte `fmt:"-"`,
|
||||||
|
_mapping_buf: [GAMEPAD_MAX_MAPPING_SIZE]byte `fmt:"-"`,
|
||||||
|
}
|
||||||
|
|
||||||
Event :: struct {
|
Event :: struct {
|
||||||
kind: Event_Kind,
|
kind: Event_Kind,
|
||||||
target_kind: Event_Target_Kind,
|
target_kind: Event_Target_Kind,
|
||||||
@@ -260,6 +296,8 @@ Event :: struct {
|
|||||||
button: i16,
|
button: i16,
|
||||||
buttons: bit_set[0..<16; u16],
|
buttons: bit_set[0..<16; u16],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
gamepad: Gamepad_State,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
@@ -336,7 +374,18 @@ remove_custom_event_listener :: proc(id: string, name: string, user_data: rawptr
|
|||||||
return _remove_event_listener(id, name, user_data, callback)
|
return _remove_event_listener(id, name, user_data, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
import "core:fmt"
|
get_gamepad_state :: proc "contextless" (index: int, s: ^Gamepad_State) -> bool {
|
||||||
|
@(default_calling_convention="contextless")
|
||||||
|
foreign dom_lib {
|
||||||
|
@(link_name="get_gamepad_state")
|
||||||
|
_get_gamepad_state :: proc(index: int, s: ^Gamepad_State) -> bool ---
|
||||||
|
}
|
||||||
|
|
||||||
|
if s == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return _get_gamepad_state(index, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@(export, link_name="odin_dom_do_event_callback")
|
@(export, link_name="odin_dom_do_event_callback")
|
||||||
@@ -355,9 +404,13 @@ do_event_callback :: proc(user_data: rawptr, callback: proc(e: Event)) {
|
|||||||
|
|
||||||
init_event_raw(&event)
|
init_event_raw(&event)
|
||||||
|
|
||||||
if event.kind == .Key_Up || event.kind == .Key_Down || event.kind == .Key_Press {
|
#partial switch event.kind {
|
||||||
|
case .Key_Up, .Key_Down, .Key_Press:
|
||||||
event.key.key = string(event.key._key_buf[:event.key._key_len])
|
event.key.key = string(event.key._key_buf[:event.key._key_len])
|
||||||
event.key.code = string(event.key._code_buf[:event.key._code_len])
|
event.key.code = string(event.key._code_buf[:event.key._code_len])
|
||||||
|
case .Gamepad_Connected, .Gamepad_Disconnected:
|
||||||
|
event.gamepad.id = string(event.gamepad._id_buf[:event.gamepad._id_len])
|
||||||
|
event.gamepad.mapping = string(event.gamepad._mapping_buf[:event.gamepad._mapping_len])
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(event)
|
callback(event)
|
||||||
@@ -284,5 +284,4 @@ add_custom_event_listener :: proc(id: string, name: string, user_data: rawptr, c
|
|||||||
}
|
}
|
||||||
remove_custom_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc(e: Event)) -> bool {
|
remove_custom_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc(e: Event)) -> bool {
|
||||||
panic("vendor:wasm/js not supported on non JS targets")
|
panic("vendor:wasm/js not supported on non JS targets")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1533,15 +1533,49 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory, ev
|
|||||||
|
|
||||||
wmi.storeU8(off(1), !!e.repeat);
|
wmi.storeU8(off(1), !!e.repeat);
|
||||||
|
|
||||||
wmi.storeI32(off(W), e.key.length)
|
wmi.storeInt(off(W, W), e.key.length)
|
||||||
wmi.storeI32(off(W), e.code.length)
|
wmi.storeInt(off(W, W), e.code.length)
|
||||||
wmi.storeString(off(16, 1), e.key);
|
wmi.storeString(off(16, 1), e.key);
|
||||||
wmi.storeString(off(16, 1), e.code);
|
wmi.storeString(off(16, 1), e.code);
|
||||||
} else if (e.type === 'scroll') {
|
} else if (e.type === 'scroll') {
|
||||||
wmi.storeF64(off(8), window.scrollX);
|
wmi.storeF64(off(8, 8), window.scrollX);
|
||||||
wmi.storeF64(off(8), window.scrollY);
|
wmi.storeF64(off(8, 8), window.scrollY);
|
||||||
} else if (e.type === 'visibilitychange') {
|
} else if (e.type === 'visibilitychange') {
|
||||||
wmi.storeU8(off(1), !document.hidden);
|
wmi.storeU8(off(1), !document.hidden);
|
||||||
|
} else if (e instanceof GamepadEvent) {
|
||||||
|
const idPtr = off(W*2, W);
|
||||||
|
const mappingPtr = off(W*2, W);
|
||||||
|
|
||||||
|
wmi.storeI32(off(W, W), e.gamepad.index);
|
||||||
|
wmi.storeU8(off(1), !!e.gamepad.connected);
|
||||||
|
wmi.storeF64(off(8, 8), e.gamepad.timestamp);
|
||||||
|
|
||||||
|
wmi.storeInt(off(W, W), e.gamepad.buttons.length);
|
||||||
|
wmi.storeInt(off(W, W), e.gamepad.axes.length);
|
||||||
|
|
||||||
|
for (let i = 0; i < 64; i++) {
|
||||||
|
if (i < e.gamepad.buttons.length) {
|
||||||
|
let b = e.gamepad.buttons[i];
|
||||||
|
wmi.storeF64(off(8, 8), b.value);
|
||||||
|
wmi.storeU8(off(1), !!b.pressed);
|
||||||
|
wmi.storeU8(off(1), !!b.touched);
|
||||||
|
} else {
|
||||||
|
off(16, 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = 0; i < 16; i++) {
|
||||||
|
if (i < e.gamepad.axes.length) {
|
||||||
|
let a = e.gamepad.axes[i];
|
||||||
|
wmi.storeF64(off(8, 8), a);
|
||||||
|
} else {
|
||||||
|
off(8, 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wmi.storeInt(off(W, W), e.gamepad.id.length)
|
||||||
|
wmi.storeInt(off(W, W), e.gamepad.mapping.length)
|
||||||
|
wmi.storeString(off(64, 1), e.gamepad.id);
|
||||||
|
wmi.storeString(off(64, 1), e.gamepad.mapping);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -1646,6 +1680,76 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory, ev
|
|||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
get_gamepad_state: (gamepad_id, ep) => {
|
||||||
|
let index = gamepad_id;
|
||||||
|
let gps = navigator.getGamepads();
|
||||||
|
if (0 <= index && index < gps.length) {
|
||||||
|
let gamepad = gps[index];
|
||||||
|
if (!gamepad) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const W = wasmMemoryInterface.intSize;
|
||||||
|
let offset = ep;
|
||||||
|
let off = (amount, alignment) => {
|
||||||
|
if (alignment === undefined) {
|
||||||
|
alignment = Math.min(amount, W);
|
||||||
|
}
|
||||||
|
if (offset % alignment != 0) {
|
||||||
|
offset += alignment - (offset%alignment);
|
||||||
|
}
|
||||||
|
let x = offset;
|
||||||
|
offset += amount;
|
||||||
|
return x;
|
||||||
|
};
|
||||||
|
|
||||||
|
let align = (alignment) => {
|
||||||
|
const modulo = offset & (alignment-1);
|
||||||
|
if (modulo != 0) {
|
||||||
|
offset += alignment - modulo
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let wmi = wasmMemoryInterface;
|
||||||
|
|
||||||
|
const idPtr = off(W*2, W);
|
||||||
|
const mappingPtr = off(W*2, W);
|
||||||
|
|
||||||
|
wmi.storeI32(off(W), gamepad.index);
|
||||||
|
wmi.storeU8(off(1), !!gamepad.connected);
|
||||||
|
wmi.storeF64(off(8), gamepad.timestamp);
|
||||||
|
|
||||||
|
wmi.storeInt(off(W), gamepad.buttons.length);
|
||||||
|
wmi.storeInt(off(W), gamepad.axes.length);
|
||||||
|
|
||||||
|
for (let i = 0; i < 64; i++) {
|
||||||
|
if (i < gamepad.buttons.length) {
|
||||||
|
let b = gamepad.buttons[i];
|
||||||
|
wmi.storeF64(off(8, 8), b.value);
|
||||||
|
wmi.storeU8(off(1), !!b.pressed);
|
||||||
|
wmi.storeU8(off(1), !!b.touched);
|
||||||
|
} else {
|
||||||
|
off(16, 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = 0; i < 16; i++) {
|
||||||
|
if (i < gamepad.axes.length) {
|
||||||
|
wmi.storeF64(off(8, 8), gamepad.axes[i]);
|
||||||
|
} else {
|
||||||
|
off(8, 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wmi.storeInt(off(W, W), gamepad.id.length)
|
||||||
|
wmi.storeInt(off(W, W), gamepad.mapping.length)
|
||||||
|
wmi.storeString(off(64, 1), gamepad.id);
|
||||||
|
wmi.storeString(off(64, 1), gamepad.mapping);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
get_element_value_f64: (id_ptr, id_len) => {
|
get_element_value_f64: (id_ptr, id_len) => {
|
||||||
let id = wasmMemoryInterface.loadString(id_ptr, id_len);
|
let id = wasmMemoryInterface.loadString(id_ptr, id_len);
|
||||||
let element = getElement(id);
|
let element = getElement(id);
|
||||||
+107
-1
@@ -187,4 +187,110 @@ scan_digits :: proc(s: string, sep: string, count: int) -> (res: int, ok: bool)
|
|||||||
found_sep |= rune(s[count]) == v
|
found_sep |= rune(s[count]) == v
|
||||||
}
|
}
|
||||||
return res, found_sep
|
return res, found_sep
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Serialize the timestamp as a RFC 3339 string.
|
||||||
|
|
||||||
|
The boolean `ok` is false if the `time` is not a valid datetime, or if allocating the result string fails.
|
||||||
|
|
||||||
|
**Inputs**:
|
||||||
|
- `utc_offset`: offset in minutes wrt UTC (ie. the timezone)
|
||||||
|
- `include_nanos`: whether to include nanoseconds in the result.
|
||||||
|
*/
|
||||||
|
time_to_rfc3339 :: proc(time: Time, utc_offset : int = 0, include_nanos := true, allocator := context.allocator) -> (res: string, ok: bool) {
|
||||||
|
utc_offset := utc_offset
|
||||||
|
|
||||||
|
// convert to datetime
|
||||||
|
datetime := time_to_datetime(time) or_return
|
||||||
|
|
||||||
|
if datetime.year < 0 || datetime.year >= 10_000 { return "", false }
|
||||||
|
|
||||||
|
temp_string := [36]u8{}
|
||||||
|
offset : uint = 0
|
||||||
|
|
||||||
|
print_as_fixed_int :: proc(dst: []u8, offset: ^uint, width: i8, i: i64) {
|
||||||
|
i := i
|
||||||
|
width := width
|
||||||
|
for digit_idx in 0..<width {
|
||||||
|
last_digit := i % 10
|
||||||
|
dst[offset^ + uint(width) - uint(digit_idx)-1] = '0' + u8(last_digit)
|
||||||
|
i = i / 10
|
||||||
|
}
|
||||||
|
|
||||||
|
offset^ += uint(width)
|
||||||
|
}
|
||||||
|
|
||||||
|
print_as_fixed_int(temp_string[:], &offset, 4, datetime.year)
|
||||||
|
temp_string[offset] = '-'
|
||||||
|
offset += 1
|
||||||
|
print_as_fixed_int(temp_string[:], &offset, 2, i64(datetime.month))
|
||||||
|
temp_string[offset] = '-'
|
||||||
|
offset += 1
|
||||||
|
print_as_fixed_int(temp_string[:], &offset, 2, i64(datetime.day))
|
||||||
|
temp_string[offset] = 'T'
|
||||||
|
offset += 1
|
||||||
|
print_as_fixed_int(temp_string[:], &offset, 2, i64(datetime.hour))
|
||||||
|
temp_string[offset] = ':'
|
||||||
|
offset += 1
|
||||||
|
print_as_fixed_int(temp_string[:], &offset, 2, i64(datetime.minute))
|
||||||
|
temp_string[offset] = ':'
|
||||||
|
offset += 1
|
||||||
|
print_as_fixed_int(temp_string[:], &offset, 2, i64(datetime.second))
|
||||||
|
|
||||||
|
// turn 123_450_000 to 12345, 5
|
||||||
|
strip_trailing_zeroes_nanos :: proc(n: i64) -> (res: i64, n_digits: i8) {
|
||||||
|
res = n
|
||||||
|
n_digits = 9
|
||||||
|
for res % 10 == 0 {
|
||||||
|
res = res / 10
|
||||||
|
n_digits -= 1
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// pre-epoch times: turn, say, -400ms to +600ms for display
|
||||||
|
nanos := time._nsec % 1_000_000_000
|
||||||
|
if nanos < 0 {
|
||||||
|
nanos += 1_000_000_000
|
||||||
|
}
|
||||||
|
|
||||||
|
if nanos != 0 && include_nanos {
|
||||||
|
temp_string[offset] = '.'
|
||||||
|
offset += 1
|
||||||
|
|
||||||
|
// remove trailing zeroes
|
||||||
|
nanos_nonzero, n_digits := strip_trailing_zeroes_nanos(nanos)
|
||||||
|
assert(nanos_nonzero != 0)
|
||||||
|
|
||||||
|
// write digits, right-to-left
|
||||||
|
for digit_idx : i8 = n_digits-1; digit_idx >= 0; digit_idx -= 1 {
|
||||||
|
digit := u8(nanos_nonzero % 10)
|
||||||
|
temp_string[offset + uint(digit_idx)] = '0' + u8(digit)
|
||||||
|
nanos_nonzero /= 10
|
||||||
|
}
|
||||||
|
offset += uint(n_digits)
|
||||||
|
}
|
||||||
|
|
||||||
|
if utc_offset == 0 {
|
||||||
|
temp_string[offset] = 'Z'
|
||||||
|
offset += 1
|
||||||
|
} else {
|
||||||
|
temp_string[offset] = utc_offset > 0 ? '+' : '-'
|
||||||
|
offset += 1
|
||||||
|
utc_offset = abs(utc_offset)
|
||||||
|
print_as_fixed_int(temp_string[:], &offset, 2, i64(utc_offset / 60))
|
||||||
|
temp_string[offset] = ':'
|
||||||
|
offset += 1
|
||||||
|
print_as_fixed_int(temp_string[:], &offset, 2, i64(utc_offset % 60))
|
||||||
|
}
|
||||||
|
|
||||||
|
res_as_slice, res_alloc := make_slice([]u8, len=offset, allocator = allocator)
|
||||||
|
if res_alloc != nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(res_as_slice, temp_string[:offset])
|
||||||
|
|
||||||
|
return string(res_as_slice), true
|
||||||
|
}
|
||||||
|
|||||||
@@ -196,6 +196,36 @@ test_parse_rfc3339_string :: proc(t: ^testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test
|
||||||
|
test_print_rfc3339 :: proc(t: ^testing.T) {
|
||||||
|
TestCase :: struct {
|
||||||
|
printed: string,
|
||||||
|
time: i64,
|
||||||
|
utc_offset: int,
|
||||||
|
}
|
||||||
|
|
||||||
|
tests :: [?]TestCase {
|
||||||
|
{"1985-04-12T23:20:50.52Z", 482196050520000000, 0},
|
||||||
|
{"1985-04-12T23:20:50.52001905Z", 482196050520019050, 0},
|
||||||
|
{"1996-12-19T16:39:57-08:00", 851013597000000000, -480},
|
||||||
|
{"1996-12-20T00:39:57Z", 851042397000000000, 0},
|
||||||
|
{"1937-01-01T12:00:27.87+00:20", -1041335972130000000, +20},
|
||||||
|
}
|
||||||
|
|
||||||
|
for test in tests {
|
||||||
|
timestamp := time.Time { _nsec = test.time }
|
||||||
|
printed_timestamp, ok := time.time_to_rfc3339(time=timestamp, utc_offset=test.utc_offset)
|
||||||
|
defer delete_string(printed_timestamp)
|
||||||
|
|
||||||
|
testing.expect(t, ok, "expected printing to work fine")
|
||||||
|
|
||||||
|
testing.expectf(
|
||||||
|
t, printed_timestamp == test.printed,
|
||||||
|
"expected is %w, printed is %w", test.printed, printed_timestamp,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@test
|
@test
|
||||||
test_parse_iso8601_string :: proc(t: ^testing.T) {
|
test_parse_iso8601_string :: proc(t: ^testing.T) {
|
||||||
for test in iso8601_tests {
|
for test in iso8601_tests {
|
||||||
@@ -318,4 +348,4 @@ date_component_roundtrip_test :: proc(t: ^testing.T, moment: dt.DateTime) {
|
|||||||
"Expected %4d-%2d-%2d %2d:%2d:%2d, got %4d-%2d-%2d %2d:%2d:%2d",
|
"Expected %4d-%2d-%2d %2d:%2d:%2d, got %4d-%2d-%2d %2d:%2d:%2d",
|
||||||
moment.year, moment.month, moment.day, moment.hour, moment.minute, moment.second, YYYY, MM, DD, hh, mm, ss,
|
moment.year, moment.month, moment.day, moment.hour, moment.minute, moment.second, YYYY, MM, DD, hh, mm, ss,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+1
-1
@@ -37,7 +37,7 @@ realloc :: proc "c" (ptr: rawptr, new_size: uint) -> rawptr {
|
|||||||
// Note that realloc does not actually care about alignment and is allowed to just align it to something
|
// Note that realloc does not actually care about alignment and is allowed to just align it to something
|
||||||
// else than the original allocation.
|
// else than the original allocation.
|
||||||
ptr, err := runtime.non_zero_mem_resize(ptr, -1, int(new_size))
|
ptr, err := runtime.non_zero_mem_resize(ptr, -1, int(new_size))
|
||||||
assert(err != nil, "realloc failure")
|
assert(err == nil, "realloc failure")
|
||||||
return raw_data(ptr)
|
return raw_data(ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Vendored
-1
@@ -20,7 +20,6 @@ when LIB != "" {
|
|||||||
|
|
||||||
when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 {
|
when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 {
|
||||||
foreign import stbi "../lib/stb_image_wasm.o"
|
foreign import stbi "../lib/stb_image_wasm.o"
|
||||||
foreign import stbi { LIB }
|
|
||||||
} else when LIB != "" {
|
} else when LIB != "" {
|
||||||
foreign import stbi { LIB }
|
foreign import stbi { LIB }
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
package stb_image
|
package stb_image
|
||||||
|
|
||||||
import c "core:c/libc"
|
import "core:c"
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
RESIZE_LIB :: (
|
RESIZE_LIB :: (
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
package stb_image
|
package stb_image
|
||||||
|
|
||||||
import c "core:c/libc"
|
import "core:c"
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
WRITE_LIB :: (
|
WRITE_LIB :: (
|
||||||
|
|||||||
Vendored
+3
-3
@@ -6,9 +6,9 @@ The `js_wasm32` target assumes that the WASM output will be ran within a web bro
|
|||||||
|
|
||||||
## Example for `js_wasm32`
|
## Example for `js_wasm32`
|
||||||
|
|
||||||
```js
|
```html
|
||||||
<!-- Copy `vendor:wasm/js/runtime.js` into your web server -->
|
<!-- Copy `core:sys/wasm/js/odin.js` into your web server -->
|
||||||
<script type="text/javascript" src="runtime.js"></script>
|
<script type="text/javascript" src="odin.js"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
odin.runWasm(pathToWasm, consolePreElement);
|
odin.runWasm(pathToWasm, consolePreElement);
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user